def parallel_transport(self, tangent_vec_a, tangent_vec_b, base_point): r"""Parallel transport of a tangent vector. Closed-form solution for the parallel transport of a tangent vector a along the geodesic defined by exp_(base_point)(tangent_vec_b). Denoting `tangent_vec_a` by `S`, `base_point` by `A`, let `B = Exp_A(tangent_vec_b)` and :math: `E = (BA^{- 1})^({ 1 / 2})`. Then the parallel transport to `B`is: ..math:: S' = ESE^T Parameters ---------- tangent_vec_a : array-like, shape=[..., dim + 1] Tangent vector at base point to be transported. tangent_vec_b : array-like, shape=[..., dim + 1] Tangent vector at base point, initial speed of the geodesic along which the parallel transport is computed. base_point : array-like, shape=[..., dim + 1] Point on the manifold of SPD matrices. Returns ------- transported_tangent_vec: array-like, shape=[..., dim + 1] Transported tangent vector at exp_(base_point)(tangent_vec_b). """ end_point = self.exp(tangent_vec_b, base_point) inverse_base_point = GeneralLinear.inverse(base_point) congruence_mat = GeneralLinear.mul(end_point, inverse_base_point) congruence_mat = gs.linalg.sqrtm(congruence_mat) return GeneralLinear.congruent(tangent_vec_a, congruence_mat)
def log(self, point, base_point): r"""Compute the Riemannian logarithm of point w.r.t. base_point. Given :math:`P, P'` in Gr(n, k) the logarithm from :math:`P` to :math:`P` is given by the infinitesimal rotation [Batzies2015]_: .. math:: \omega = \frac 1 2 \log \big((2 P' - 1)(2 P - 1)\big) Parameters ---------- point : array-like, shape=[n_samples, n, n] Point in the Grassmannian. base_point : array-like, shape=[n_samples, n, n] Point in the Grassmannian. Returns ------- tangent_vec : array-like, shape=[n_samples, n, n] Tangent vector at `base_point`. References ---------- .. [Batzies2015] Batzies, Hüper, Machado, Leite. "Geometric Mean and Geodesic Regression on Grassmannians" Linear Algebra and its Applications, 466, 83-101, 2015. """ GLn = GeneralLinear(self.n) id_n = GLn.identity() sym2 = 2 * point - id_n sym1 = 2 * base_point - id_n rot = GLn.mul(sym2, sym1) return GLn.log(rot) / 2
def test_horizontal_projection(self): mat = self.bundle.total_space.random_uniform() vec = self.bundle.total_space.random_uniform() horizontal_vec = self.bundle.horizontal_projection(vec, mat) product = GeneralLinear.mul(horizontal_vec, GeneralLinear.inverse(mat)) is_horizontal = GeneralLinear.is_symmetric(product) self.assertTrue(is_horizontal)
def transp(self, base_point, end_point, tangent): """ transports a tangent vector at a base_point to the tangent space at end_point. """ if self.exact_transport: # https://github.com/geomstats/geomstats/blob/master/geomstats/geometry/spd_matrices.py#L613 inverse_base_point = GeneralLinear.inverse(base_point) congruence_mat = GeneralLinear.mul(end_point, inverse_base_point) congruence_mat = gs.linalg.sqrtm(congruence_mat.cpu()).to(tangent) return GeneralLinear.congruent(tangent, congruence_mat) # https://github.com/NicolasBoumal/manopt/blob/master/manopt/manifolds/symfixedrank/sympositivedefinitefactory.m#L181 return tangent
def log(self, point, base_point): r"""Compute the Riemannian logarithm of point w.r.t. base_point. Given :math:`P, P'` in Gr(n, k) the logarithm from :math:`P` to :math:`P` is induced by the infinitesimal rotation [Batzies2015]_: .. math:: Y = \frac 1 2 \log \big((2 P' - 1)(2 P - 1)\big) The tangent vector :math:`X` at :math:`P` is then recovered by :math:`X = [Y, P]`. Parameters ---------- point : array-like, shape=[..., n, n] Point. base_point : array-like, shape=[..., n, n] Base point. Returns ------- tangent_vec : array-like, shape=[..., n, n] Riemannian logarithm, a tangent vector at `base_point`. References ---------- .. [Batzies2015] Batzies, Hüper, Machado, Leite. "Geometric Mean and Geodesic Regression on Grassmannians" Linear Algebra and its Applications, 466, 83-101, 2015. """ GLn = GeneralLinear(self.n) id_n = GLn.identity id_n, point, base_point = gs.convert_to_wider_dtype( [id_n, point, base_point]) sym2 = 2 * point - id_n sym1 = 2 * base_point - id_n rot = GLn.mul(sym2, sym1) return Matrices.bracket(GLn.log(rot) / 2, base_point)
def horizontal_lift(tangent_vec, point, base_point=None): if base_point is None: base_point = submersion(point) sylvester = gs.linalg.solve_sylvester(base_point, base_point, tangent_vec) return GeneralLinear.mul(sylvester, point)
def tangent_submersion(tangent_vec, base_point): product = GeneralLinear.mul(base_point, GeneralLinear.transpose(tangent_vec)) return 2 * GeneralLinear.to_symmetric(product)
def submersion(point): return GeneralLinear.mul(point, GeneralLinear.transpose(point))