Source code for pymanopt.manifolds.euclidean

import numpy as np

from pymanopt.manifolds.manifold import RiemannianSubmanifold
from pymanopt.tools.multi import multiskew, multisym


class _Euclidean(RiemannianSubmanifold):
    def __init__(self, name, dimension, *shape):
        self._shape = shape
        super().__init__(name, dimension)

    @property
    def typical_dist(self):
        return np.sqrt(self.dim)

    def inner_product(self, point, tangent_vector_a, tangent_vector_b):
        return float(
            np.real(
                np.tensordot(
                    tangent_vector_a.conj(),
                    tangent_vector_b,
                    axes=tangent_vector_a.ndim,
                )
            )
        )

    def norm(self, point, tangent_vector):
        return np.linalg.norm(tangent_vector)

    def dist(self, point_a, point_b):
        return np.linalg.norm(point_a - point_b)

    def projection(self, point, vector):
        return vector

    to_tangent_space = projection

    def euclidean_to_riemannian_hessian(
        self, point, euclidean_gradient, euclidean_hessian, tangent_vector
    ):
        return euclidean_hessian

    def exp(self, point, tangent_vector):
        return point + tangent_vector

    retraction = exp

    def log(self, point_a, point_b):
        return point_b - point_a

    def random_point(self):
        return np.random.normal(size=self._shape)

    def random_tangent_vector(self, point):
        tangent_vector = self.random_point()
        return tangent_vector / self.norm(point, tangent_vector)

    def transport(self, point_a, point_b, tangent_vector_a):
        return tangent_vector_a

    def pair_mean(self, point_a, point_b):
        return (point_a + point_b) / 2

    def zero_vector(self, point):
        return np.zeros(self._shape)


[docs]class Euclidean(_Euclidean): r"""Euclidean manifold. Args: shape: Shape of points on the manifold. Note: If ``shape == (n,)``, this is the manifold of vectors with the standard Euclidean inner product, i.e., :math:`\R^n`. For ``shape == (m, n)``, it corresponds to the manifold of ``m x n`` matrices equipped with the standard trace inner product. For ``shape == (n1, n2, ..., nk)``, the class represents the manifold of tensors of shape ``n1 x n2 x ... x nk`` with the inner product corresponding to the usual tensor dot product. """ def __init__(self, *shape: int): if len(shape) == 0: raise TypeError("Need shape parameters") if len(shape) == 1: (n1,) = shape name = f"Euclidean manifold of {n1}-vectors" elif len(shape) == 2: n1, n2 = shape name = f"Euclidean manifold of {n1}x{n2} matrices" else: name = f"Euclidean manifold of shape {shape} tensors" dimension = np.prod(shape) super().__init__(name, dimension, *shape)
[docs]class ComplexEuclidean(_Euclidean): r"""Complex Euclidean manifold. Args: shape: Shape of points on the manifold. Note: If ``shape == (n,)``, this is the manifold of vectors with the standard Euclidean inner product, i.e., :math:`\C^n`. For ``shape == (m, n)``, it corresponds to the manifold of ``m x n`` matrices equipped with the standard trace inner product. For ``shape == (n1, n2, ..., nk)``, the class represents the manifold of tensors of shape ``n1 x n2 x ... x nk`` with the inner product corresponding to the usual tensor dot product. """ def __init__(self, *shape): if len(shape) == 0: raise TypeError("Need shape parameters") if len(shape) == 1: (n1,) = shape name = f"Complex Euclidean manifold of {n1}-vectors" elif len(shape) == 2: n1, n2 = shape name = f"Complex Euclidean manifold of {n1}x{n2} matrices" else: name = f"Complex Euclidean manifold of shape {shape} tensors" dimension = 2 * np.prod(shape) super().__init__(name, dimension, *shape)
[docs] def random_point(self): return np.random.randn(*self._shape) + 1j * np.random.randn( *self._shape )
[docs] def zero_vector(self, point): return np.zeros(self._shape, dtype=complex)
[docs]class Symmetric(_Euclidean): """(Product) manifold of symmetric matrices. Args: n: Number of rows and columns of matrices. k: Number of elements in the product manifold. Note: Manifold of ``n x n`` symmetric matrices as a Riemannian submanifold of Euclidean space. If ``k > 1`` then this is the product manifold of ``k`` symmetric ``n x n`` matrices represented as arrays of shape ``(k, n, n)``. """ def __init__(self, n: int, k: int = 1): if k == 1: shape = (n, n) name = f"Manifold of {n}x{n} symmetric matrices" elif k > 1: shape = (k, n, n) name = f"Product manifold of {k} {n}x{n} symmetric matrices" else: raise ValueError(f"k must be an integer no less than 1, got {k}") dimension = int(k * n * (n + 1) / 2) super().__init__(name, dimension, *shape)
[docs] def projection(self, point, vector): return multisym(vector)
[docs] def euclidean_to_riemannian_hessian( self, point, euclidean_gradient, euclidean_hessian, tangent_vector ): return multisym(euclidean_hessian)
[docs] def random_point(self): return multisym(np.random.normal(size=self._shape))
[docs] def random_tangent_vector(self, point): tangent_vector = self.random_point() return multisym(tangent_vector / self.norm(point, tangent_vector))
[docs]class SkewSymmetric(_Euclidean): """(Product) manifold of skew-symmetric matrices. Args: n: Number of rows and columns of matrices. k: Number of elements in the product manifold. Note: Manifold of ``n x n`` skew-symmetric matrices as a Riemannian submanifold of Euclidean space. If ``k > 1`` then this is the product manifold of ``k`` skew-symmetric ``n x n`` matrices represented as arrays of shape ``(k, n, n)``. """ def __init__(self, n, k=1): if k == 1: shape = (n, n) name = f"Manifold of {n}x{n} skew-symmetric matrices" elif k > 1: shape = (k, n, n) name = f"Product manifold of {k} {n}x{n} skew-symmetric matrices" else: raise ValueError("k must be an integer no less than 1") dimension = int(k * n * (n - 1) / 2) super().__init__(name, dimension, *shape)
[docs] def projection(self, point, vector): return multiskew(vector)
[docs] def euclidean_to_riemannian_hessian( self, point, euclidean_gradient, euclidean_hessian, tangent_vector ): return multiskew(euclidean_hessian)
[docs] def random_point(self): return multiskew(np.random.normal(size=self._shape))
[docs] def random_tangent_vector(self, point): tangent_vector = self.random_point() return multiskew(tangent_vector / self.norm(point, tangent_vector))