# Source code for pyro.distributions.permute

```
from __future__ import absolute_import, division, print_function
import torch
from torch.distributions.transforms import Transform
from torch.distributions.utils import lazy_property
from torch.distributions import constraints
from pyro.distributions.util import copy_docs_from
[docs]@copy_docs_from(Transform)
class PermuteTransform(Transform):
"""
A bijection that reorders the input dimensions, that is, multiplies the input by a permutation matrix.
This is useful in between :class:`~pyro.distributions.InverseAutoregressiveFlow` transforms to increase the
flexibility of the resulting distribution and stabilize learning. Whilst not being an autoregressive transform,
the log absolute determinate of the Jacobian is easily calculable as 0. Note that reordering the input dimension
between two layers of :class:`~pyro.distributions.InverseAutoregressiveFlow` is not equivalent to reordering
the dimension inside the MADE networks that those IAFs use; using a PermuteTransform results in a distribution
with more flexibility.
Example usage:
>>> from pyro.nn import AutoRegressiveNN
>>> from pyro.distributions import InverseAutoregressiveFlow, PermuteTransform
>>> base_dist = dist.Normal(torch.zeros(10), torch.ones(10))
>>> iaf1 = InverseAutoregressiveFlow(AutoRegressiveNN(10, [40]))
>>> ff = PermuteTransform(torch.randperm(10, dtype=torch.long))
>>> iaf2 = InverseAutoregressiveFlow(AutoRegressiveNN(10, [40]))
>>> iaf_dist = dist.TransformedDistribution(base_dist, [iaf1, ff, iaf2])
>>> iaf_dist.sample() # doctest: +SKIP
tensor([-0.4071, -0.5030, 0.7924, -0.2366, -0.2387, -0.1417, 0.0868,
0.1389, -0.4629, 0.0986])
:param permutation: a permutation ordering that is applied to the inputs.
:type permutation: torch.LongTensor
"""
codomain = constraints.real
bijective = True
event_dim = 1
def __init__(self, permutation):
super(PermuteTransform, self).__init__(cache_size=1)
self.permutation = permutation
[docs] @lazy_property
def inv_permutation(self):
result = torch.empty_like(self.permutation, dtype=torch.long)
result[self.permutation] = torch.arange(self.permutation.size(0),
dtype=torch.long,
device=self.permutation.device)
return result
def _call(self, x):
"""
:param x: the input into the bijection
:type x: torch.Tensor
Invokes the bijection x=>y; in the prototypical context of a TransformedDistribution `x` is a
sample from the base distribution (or the output of a previous transform)
"""
return x[..., self.permutation]
def _inverse(self, y):
"""
:param y: the output of the bijection
:type y: torch.Tensor
Inverts y => x.
"""
return y[..., self.inv_permutation]
[docs] def log_abs_det_jacobian(self, x, y):
"""
Calculates the elementwise determinant of the log Jacobian, i.e. log(abs([dy_0/dx_0, ..., dy_{N-1}/dx_{N-1}])).
Note that this type of transform is not autoregressive, so the log Jacobian is not the sum of the previous
expression. However, it turns out it's always 0 (since the determinant is -1 or +1), and so returning a
vector of zeros works.
"""
return torch.zeros(x.size()[:-1])
```