Module flowcon.transforms.orthogonal

Implementations of orthogonal transforms.

Classes

class HouseholderSequence (features, num_transforms)

A sequence of Householder transforms.

This class can be used as a way of parameterizing an orthogonal matrix.

Constructor.

Args

features
int, dimensionality of the input.
num_transforms
int, number of Householder transforms to use.

Raises

TypeError
if arguments are not the right type.
Expand source code
class HouseholderSequence(Transform):
    """A sequence of Householder transforms.

    This class can be used as a way of parameterizing an orthogonal matrix.
    """

    def __init__(self, features, num_transforms):
        """Constructor.

        Args:
            features: int, dimensionality of the input.
            num_transforms: int, number of Householder transforms to use.

        Raises:
            TypeError: if arguments are not the right type.
        """
        if not check.is_positive_int(features):
            raise TypeError("Number of features must be a positive integer.")
        if not check.is_positive_int(num_transforms):
            raise TypeError("Number of transforms must be a positive integer.")

        super().__init__()
        self.features = features
        self.num_transforms = num_transforms
        # TODO: are randn good initial values?
        # these vectors are orthogonal to the hyperplanes through which we reflect
        # self.q_vectors = nets.Parameter(torch.randn(num_transforms, features))
        # self.q_vectors = nets.Parameter(torch.eye(num_transforms // 2, features))
        import numpy as np

        def tile(a, dim, n_tile):
            if a.nelement() == 0:
                return a
            init_dim = a.size(dim)
            repeat_idx = [1] * a.dim()
            repeat_idx[dim] = n_tile
            a = a.repeat(*(repeat_idx))

            order_index = torch.Tensor(
                np.concatenate(
                    [init_dim * np.arange(n_tile) + i for i in range(init_dim)]
                )
            ).long()
            return torch.index_select(a, dim, order_index)

        qv = tile(torch.eye(num_transforms // 2, features), 0, 2)

        if np.mod(num_transforms, 2) != 0:  # odd number of transforms, including 1
            qv = torch.cat((qv, torch.zeros(1, features)))
            qv[-1, num_transforms // 2] = 1
        self.q_vectors = nn.Parameter(qv)

    def forward(self, inputs, context=None):
        # return self._apply_transforms(inputs, self.q_vectors)
        return _apply_batchwise_transforms(inputs, self.q_vectors)

    def inverse(self, inputs, context=None):
        # Each householder transform is its own inverse, so the total inverse is given by
        # simply performing each transform in the reverse order.
        reverse_idx = torch.arange(self.num_transforms - 1, -1, -1)
        return _apply_batchwise_transforms(inputs, self.q_vectors[reverse_idx])

    def matrix(self):
        """Returns the orthogonal matrix that is equivalent to the total transform.

        Costs O(KD^2), where:
        - K is number of transforms
        - D is dimensionality of inputs

        Returns:
            A Tensor of shape [D, D].
        """
        identity = torch.eye(self.features, self.features, device=self.q_vectors.device)
        outputs, _ = self.inverse(identity)
        return outputs

Ancestors

Class variables

var call_super_init : bool
var dump_patches : bool
var training : bool

Methods

def inverse(self, inputs, context=None)
def matrix(self)

Returns the orthogonal matrix that is equivalent to the total transform.

Costs O(KD^2), where: - K is number of transforms - D is dimensionality of inputs

Returns

A Tensor of shape [D, D].

Inherited members

class ParametrizedHouseHolder (q_vectors)

A sequence of Householder transforms.

This class can be used as a way of parameterizing an orthogonal matrix.

Constructor.

Args

features
int, dimensionality of the input.
num_transforms
int, number of Householder transforms to use.

Raises

TypeError
if arguments are not the right type.
Expand source code
class ParametrizedHouseHolder(Transform):
    """A sequence of Householder transforms.

    This class can be used as a way of parameterizing an orthogonal matrix.
    """

    def __init__(self, q_vectors):
        """Constructor.

        Args:
            features: int, dimensionality of the input.
            num_transforms: int, number of Householder transforms to use.

        Raises:
            TypeError: if arguments are not the right type.
        """

        super().__init__()
        self.features = q_vectors.shape[-1]
        self.num_transforms = q_vectors.shape[-2]
        self.q_vectors = q_vectors
        self.reverse_idx = torch.arange(self.num_transforms - 1, -1, -1).to(q_vectors.device)

    def forward(self, inputs, context=None):
        return _apply_batchwise_transforms(inputs, self.q_vectors)

    def inverse(self, inputs, context=None):
        # Each householder transform is its own inverse, so the total inverse is given by
        # simply performing each transform in the reverse order.
        return _apply_batchwise_transforms(inputs, self.q_vectors[..., self.reverse_idx, :])

    def matrix(self) -> torch.Tensor:
        """Returns the orthogonal matrix that is equivalent to the total transform.

        Costs O(KD^2), where:
        - K is number of transforms
        - D is dimensionality of inputs

        Returns:
            A Tensor of shape [D, D].
        """

        identity = torch.eye(self.features, self.features).to(self.q_vectors.device)
        if len(self.q_vectors.shape) > 2:
            identity = torch.repeat_interleave(identity[None, ...], self.q_vectors.shape[0], 0)
        # identity_flat = identity.view(self.features * self.q_vectors.shape[0], self.features)
        # outputs, _ = self.inverse(identity)
        outputs = []
        for i in range(self.features):
            outputs_, _ = self.inverse(identity[..., i, :])
            outputs.append(outputs_.unsqueeze(-2))
        outputs = torch.concatenate(outputs, -2)
        return outputs

Ancestors

Class variables

var call_super_init : bool
var dump_patches : bool
var training : bool

Methods

def inverse(self, inputs, context=None)
def matrix(self) ‑> torch.Tensor

Returns the orthogonal matrix that is equivalent to the total transform.

Costs O(KD^2), where: - K is number of transforms - D is dimensionality of inputs

Returns

A Tensor of shape [D, D].

Inherited members