Source code for pymanopt.manifolds.positive

import numpy as np

from pymanopt.manifolds.manifold import Manifold

[docs]class Positive(Manifold):
r"""The (product) manifold of positive matrices.

Args:
m: The number of rows.
n: The number of columns.
k: The number of matrices in the product.
use_parallel_transport: Flag whether to use a parallel transport for
:meth:transport or a transporter (the default).

Note:
Points on the manifold are represented as arrays of size m x n
(when k is 1), and k x m x n otherwise.

The tangent spaces of the manifold correspond to copies of :math:\R^{m
\times n}.
As such, tangent vectors are represented as arrays of the same shape as
points on the manifold without any positivity constraints on the
individual elements.

The Riemannian metric is the bi-invariant metric for positive definite
matrices from chapter 6 of [Bha2007]_ on individual scalar coordinates
of matrices.

The second-order retraction is taken from [JVV2012]_.

The parallel transport that is used when use_parallel_transport is
True is taken from [SH2015]_.
"""

def __init__(
self,
m: int,
n: int,
*,
k: int = 1,
use_parallel_transport: bool = False,
):
self._m = m
self._n = n
self._k = k

if use_parallel_transport:
self._transport = self._parallel_transport
else:
self._transport = self._transporter

if k == 1:
name = f"Manifold of positive {m}x{n} matrices"
else:
name = f"Product manifold of {k} positive {m}x{n} matrices"
dimension = int(k * m * n)
super().__init__(name, dimension)

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

[docs]    def inner_product(self, point, tangent_vector_a, tangent_vector_b):
return np.tensordot(
tangent_vector_a / point,
tangent_vector_b / point,
axes=tangent_vector_a.ndim,
)

[docs]    def projection(self, point, vector):
return vector

to_tangent_space = projection

[docs]    def norm(self, point, tangent_vector):
return np.sqrt(
self.inner_product(point, tangent_vector, tangent_vector)
)

[docs]    def random_point(self):
point = np.exp(np.random.normal(size=(self._k, self._m, self._n)))
if self._k == 1:
return point[0]
return point

[docs]    def random_tangent_vector(self, point):
vector = np.random.normal(size=point.shape) * point
return vector / self.norm(point, vector)

[docs]    def zero_vector(self, point):
return np.zeros(point.shape)

[docs]    def dist(self, point_a, point_b):
log_ratio = np.log(point_a) - np.log(point_b)
return np.sqrt(np.tensordot(log_ratio, log_ratio, axes=point_a.ndim))

[docs]    def euclidean_to_riemannian_hessian(
):
return (
euclidean_hessian * point**2
+ tangent_vector * euclidean_gradient * point
)

[docs]    def exp(self, point, tangent_vector):
return point * np.exp(tangent_vector / point)

[docs]    def log(self, point_a, point_b):
return point_a * (np.log(point_b) - np.log(point_a))

[docs]    def retraction(self, point, tangent_vector):
return point + tangent_vector + tangent_vector**2 / point / 2

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

def _parallel_transport(self, point_a, point_b, tangent_vector_a):
return tangent_vector_a * point_b / point_a

[docs]    def transport(self, point_a, point_b, tangent_vector_a):
return self._transport(point_a, point_b, tangent_vector_a)