예제 #1
0
    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=[n_samples, dimension + 1]
            Tangent vector at base point to be transported.
        tangent_vec_b : array-like, shape=[n_samples, dimension + 1]
            Tangent vector at base point, initial speed of the geodesic along
            which the parallel transport is computed.
        base_point : array-like, shape=[n_samples, dimension + 1]
            point on the manifold of SPD matrices

        Returns
        -------
        transported_tangent_vec: array-like, shape=[n_samples, dimension + 1]
            Transported tangent vector at exp_(base_point)(tangent_vec_b).
        """
        end_point = self.exp(tangent_vec_b, base_point)
        inverse_base_point = GeneralLinear.inv(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)
예제 #2
0
    def exp(self, tangent_vec, base_point):
        """Compute the affine-invariant exponential map.

        Compute the Riemannian exponential at point base_point
        of tangent vector tangent_vec wrt the metric defined in inner_product.
        This gives a symmetric positive definite matrix.

        Parameters
        ----------
        tangent_vec : array-like, shape=[n_samples, n, n]
        base_point : array-like, shape=[n_samples, n, n]

        Returns
        -------
        exp : array-like, shape=[n_samples, n, n]
        """
        power_affine = self.power_affine

        if power_affine == 1:
            sqrt_base_point = SymmetricMatrices.powerm(base_point, 1. / 2)
            inv_sqrt_base_point = SymmetricMatrices.powerm(sqrt_base_point, -1)
            exp = self._aux_exp(tangent_vec, sqrt_base_point,
                                inv_sqrt_base_point)
        else:
            modified_tangent_vec = self.space.differential_power(
                power_affine, tangent_vec, base_point)
            power_sqrt_base_point = SymmetricMatrices.powerm(
                base_point, power_affine / 2)
            power_inv_sqrt_base_point = GeneralLinear.inv(
                power_sqrt_base_point)
            exp = self._aux_exp(modified_tangent_vec, power_sqrt_base_point,
                                power_inv_sqrt_base_point)
            exp = SymmetricMatrices.powerm(exp, 1 / power_affine)

        return exp
예제 #3
0
    def left_log_from_identity(self, point):
        """Compute Riemannian log of a point wrt. id of left-invar. metric.

        Compute Riemannian logarithm of a point wrt the identity associated
        to the left-invariant metric.

        If the method is called by a right-invariant metric, it uses the
        left-invariant metric associated to the same inner-product matrix
        at the identity.

        Parameters
        ----------
        point : array-like, shape=[n_samples, dim]
            Point in the group.

        Returns
        -------
        log : array-like, shape=[n_samples, dim]
            Tangent vector at the identity equal to the Riemannian logarithm
            of point at the identity.
        """
        point = self.group.regularize(point)
        inner_prod_mat = self.inner_product_mat_at_identity
        inv_inner_prod_mat = GeneralLinear.inv(inner_prod_mat)
        sqrt_inv_inner_prod_mat = gs.linalg.sqrtm(inv_inner_prod_mat)
        log = gs.einsum('...i,...ij->...j', point, sqrt_inv_inner_prod_mat)
        log = self.group.regularize_tangent_vec_at_identity(tangent_vec=log,
                                                            metric=self)
        return log
예제 #4
0
    def inner_product_matrix(self, base_point=None):
        """Compute inner product matrix at the tangent space at a base point.

        Parameters
        ----------
        base_point : array-like, shape=[n_samples, dim], optional
            Point in the group (the default is identity).

        Returns
        -------
        metric_mat : array-like, shape=[n_samples, dim, dim]
            The metric matrix at base_point.
        """
        if self.group.default_point_type == 'matrix':
            raise NotImplementedError(
                'inner_product_matrix not implemented for Lie groups'
                ' whose elements are represented as matrices.')

        if base_point is None:
            base_point = self.group.identity
        base_point = self.group.regularize(base_point)

        jacobian = self.group.jacobian_translation(
            point=base_point, left_or_right=self.left_or_right)

        inv_jacobian = GeneralLinear.inv(jacobian)
        inv_jacobian_transposed = Matrices.transpose(inv_jacobian)

        metric_mat = gs.einsum('...ij,...jk->...ik', inv_jacobian_transposed,
                               self.inner_product_mat_at_identity)
        metric_mat = gs.einsum('...ij,...jk->...ik', metric_mat, inv_jacobian)
        return metric_mat
예제 #5
0
    def inner_product(self, tangent_vec_a, tangent_vec_b, base_point):
        """Compute the affine-invariant inner product.

        Compute the inner product of tangent_vec_a and tangent_vec_b
        at point base_point using the affine invariant Riemannian metric.

        Parameters
        ----------
        tangent_vec_a : array-like, shape=[n_samples, n, n]
        tangent_vec_b : array-like, shape=[n_samples, n, n]
        base_point : array-like, shape=[n_samples, n, n]

        Returns
        -------
        inner_product : array-like, shape=[n_samples, n, n]
        """
        power_affine = self.power_affine
        spd_space = self.space

        if power_affine == 1:
            inv_base_point = GeneralLinear.inv(base_point)
            inner_product = self._aux_inner_product(tangent_vec_a,
                                                    tangent_vec_b,
                                                    inv_base_point)
        else:
            modified_tangent_vec_a = spd_space.differential_power(
                power_affine, tangent_vec_a, base_point)
            modified_tangent_vec_b = spd_space.differential_power(
                power_affine, tangent_vec_b, base_point)
            power_inv_base_point = SymmetricMatrices.powerm(
                base_point, -power_affine)
            inner_product = self._aux_inner_product(modified_tangent_vec_a,
                                                    modified_tangent_vec_b,
                                                    power_inv_base_point)

            inner_product = inner_product / (power_affine**2)

        return inner_product
예제 #6
0
class TestGeneralLinearMethods(geomstats.tests.TestCase):
    def setUp(self):
        gs.random.seed(1234)
        self.n = 3
        self.n_samples = 2
        self.group = GeneralLinear(n=self.n)

        warnings.simplefilter('ignore', category=ImportWarning)

    @geomstats.tests.np_only
    def test_belongs(self):
        mats = gs.array([[[1., 0.], [0., 1]], [[0., 1.], [0., 1.]]])
        result = self.group.belongs(mats)
        expected = gs.array([True, False])
        self.assertAllClose(result, expected)

    def test_compose(self):
        mat1 = gs.array([[1., 0.], [0., 2.]])
        mat2 = gs.array([[2., 0.], [0., 1.]])
        result = self.group.compose(mat1, mat2)
        expected = 2. * GeneralLinear(2).identity()
        self.assertAllClose(result, expected)

    def test_inv(self):
        mat_a = gs.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 10.]])
        imat_a = 1. / 3. * gs.array([[-2., -4., 3.], [-2., 11., -6.],
                                     [3., -6., 3.]])
        expected = imat_a
        result = self.group.inv(mat_a)
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_inv_vectorized(self):
        mat_a = gs.array([[0., 1., 0.], [1., 0., 0.], [0., 0., 1.]])
        mat_b = -gs.eye(3, 3)
        result = self.group.inv(gs.array([mat_a, mat_b]))
        expected = gs.array([mat_a, mat_b])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_log_and_exp(self):
        point = 5 * gs.eye(self.n)
        group_log = self.group.log(point)

        result = self.group.exp(group_log)
        expected = point
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_exp_vectorization(self):
        point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]],
                          [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]])

        expected = gs.array([[[7.38905609, 0., 0.], [0., 20.0855369, 0.],
                              [0., 0., 54.5981500]],
                             [[2.718281828, 0., 0.], [0., 148.413159, 0.],
                              [0., 0., 403.42879349]]])
        result = self.group.exp(point)
        self.assertAllClose(result, expected, rtol=1e-3)

    @geomstats.tests.np_and_tf_only
    def test_log_vectorization(self):
        point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]],
                          [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]])
        expected = gs.array([[[0.693147180, 0., 0.], [0., 1.09861228866, 0.],
                              [0., 0., 1.38629436]],
                             [[0., 0., 0.], [0., 1.609437912, 0.],
                              [0., 0., 1.79175946]]])
        result = self.group.log(point)
        self.assertAllClose(result, expected, atol=1e-4)

    @geomstats.tests.np_and_tf_only
    def test_orbit(self):
        point = gs.array([[gs.exp(4.), 0.], [0., gs.exp(2.)]])
        sqrt = gs.array([[gs.exp(2.), 0.], [0., gs.exp(1.)]])
        idty = GeneralLinear(2).identity()

        path = GeneralLinear(2).orbit(point)
        time = gs.linspace(0., 1., 3)

        result = path(time)
        expected = gs.array([idty, sqrt, point])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_expm_and_logm_vectorization_symmetric(self):
        point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]],
                          [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]])
        result = self.group.exp(self.group.log(point))
        expected = point
        self.assertAllClose(result, expected)