コード例 #1
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=[..., dim]
            Point in the group.

        Returns
        -------
        log : array-like, shape=[..., 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.metric_mat_at_identity
        inv_inner_prod_mat = GeneralLinear.inverse(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
コード例 #2
0
ファイル: grassmannian.py プロジェクト: alebrigant/geomstats
def submersion(point, k):
    r"""Submersion that defines the Grassmann manifold.

    The Grassmann manifold is defined here as embedded in the set of
    symmetric matrices, as the pre-image of the function defined around the
    projector on the space spanned by the first k columns of the identity
    matrix by (see Exercise E.25 in [Pau07]_).
    .. math:

            \begin{pmatrix} I_k + A & B^T \\ B & D \end{pmatrix} \mapsto
                (D - B(I_k + A)^{-1}B^T, A + A^2 + B^TB

    This map is a submersion and its zero space is the set of orthogonal
    rank-k projectors.

    References
    ----------
    .. [Pau07]   Paulin, Frédéric. “Géométrie différentielle élémentaire,” 2007.
                 https://www.imo.universite-paris-saclay.fr/~paulin
                 /notescours/cours_geodiff.pdf.
    """
    _, eigvecs = gs.linalg.eigh(point)
    eigvecs = gs.flip(eigvecs, -1)
    flipped_point = Matrices.mul(Matrices.transpose(eigvecs), point, eigvecs)
    b = flipped_point[..., k:, :k]
    d = flipped_point[..., k:, k:]
    a = flipped_point[..., :k, :k] - gs.eye(k)
    first = d - Matrices.mul(b, GeneralLinear.inverse(a + gs.eye(k)),
                             Matrices.transpose(b))
    second = a + Matrices.mul(a, a) + Matrices.mul(Matrices.transpose(b), b)
    row_1 = gs.concatenate([first, gs.zeros_like(b)], axis=-1)
    row_2 = gs.concatenate([Matrices.transpose(gs.zeros_like(b)), second],
                           axis=-1)
    return gs.concatenate([row_1, row_2], axis=-2)
コード例 #3
0
    def metric_matrix(self, base_point=None):
        """Compute inner product matrix at the tangent space at a base point.

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

        Returns
        -------
        metric_mat : array-like, shape=[..., dim, dim]
            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
        else:
            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.inverse(jacobian)
        inv_jacobian_transposed = Matrices.transpose(inv_jacobian)

        metric_mat = gs.einsum('...ij,...jk->...ik', inv_jacobian_transposed,
                               self.metric_mat_at_identity)
        metric_mat = gs.einsum('...ij,...jk->...ik', metric_mat, inv_jacobian)
        return metric_mat
コード例 #4
0
    def metric_matrix(self, base_point=None):
        """Compute inner product matrix at the tangent space at a base point.

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

        Returns
        -------
        metric_mat : array-like, shape=[..., dim, dim]
            Metric matrix at base_point.
        """
        if base_point is None:
            return self.metric_mat_at_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.inverse(jacobian)
        inv_jacobian_transposed = Matrices.transpose(inv_jacobian)

        metric_mat = Matrices.mul(
            inv_jacobian_transposed, self.metric_mat_at_identity, inv_jacobian)
        return metric_mat
コード例 #5
0
 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)
コード例 #6
0
 def test_horizontal_projection(self):
     mat = self.bundle.random_point()
     vec = self.bundle.random_point()
     horizontal_vec = self.bundle.horizontal_projection(vec, mat)
     product = Matrices.mul(horizontal_vec, GeneralLinear.inverse(mat))
     is_horizontal = Matrices.is_symmetric(product)
     self.assertTrue(is_horizontal)
コード例 #7
0
        def inner_prod(tangent_vec_a, tangent_vec_b, base_point):
            affine_part = self.bundle.ambient_metric.inner_product(
                tangent_vec_a, tangent_vec_b, base_point)
            n = tangent_vec_b.shape[-1]

            inverse_base_point = GeneralLinear.inverse(base_point)
            operator = gs.eye(n) + base_point * inverse_base_point
            inverse_operator = GeneralLinear.inverse(operator)

            diagonal_a = gs.einsum("...ij,...ji->...i", inverse_base_point,
                                   tangent_vec_a)
            diagonal_b = gs.einsum("...ij,...ji->...i", inverse_base_point,
                                   tangent_vec_b)
            aux = gs.einsum("...i,...j->...ij", diagonal_a, diagonal_b)
            other_part = 2 * Matrices.frobenius_product(aux, inverse_operator)
            return affine_part - other_part
コード例 #8
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, n]
        base_point : array-like, shape=[..., n, n]

        Returns
        -------
        exp : array-like, shape=[..., 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.inverse(
                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
コード例 #9
0
ファイル: grassmannian.py プロジェクト: alebrigant/geomstats
    def random_uniform(self, n_samples=1):
        """Sample random points from a uniform distribution.

        Following [Chikuse03]_, :math: `n_samples * n * k` scalars are sampled
        from a standard normal distribution and reshaped to matrices,
        the projectors on their first k columns follow a uniform distribution.

        Parameters
        ----------
        n_samples : int
            The number of points to sample
            Optional. default: 1.

        Returns
        -------
        projectors : array-like, shape=[..., n, n]
            Points following a uniform distribution.

        References
        ----------
        .. [Chikuse03] Yasuko Chikuse, Statistics on special manifolds,
        New York: Springer-Verlag. 2003, 10.1007/978-0-387-21540-2
        """
        points = gs.random.normal(size=(n_samples, self.n, self.k))
        full_rank = Matrices.mul(Matrices.transpose(points), points)
        projector = Matrices.mul(points, GeneralLinear.inverse(full_rank),
                                 Matrices.transpose(points))
        return projector[0] if n_samples == 1 else projector
コード例 #10
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=[..., 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)
コード例 #11
0
 def test_horizontal_projection(self, n, vec, mat):
     bundle = self.space(n)
     base = self.base(n)
     horizontal_vec = bundle.horizontal_projection(vec, mat)
     inverse = GeneralLinear.inverse(mat)
     product_1 = Matrices.mul(horizontal_vec, inverse)
     product_2 = Matrices.mul(inverse, horizontal_vec)
     is_horizontal = gs.all(
         base.is_tangent(product_1 + product_2, mat, atol=gs.atol * 10))
     self.assertTrue(is_horizontal)
コード例 #12
0
 def test_horizontal_projection(self):
     mat = self.bundle.random_point()
     vec = self.bundle.random_point()
     horizontal_vec = self.bundle.horizontal_projection(vec, mat)
     inverse = GeneralLinear.inverse(mat)
     product_1 = Matrices.mul(horizontal_vec, inverse)
     product_2 = Matrices.mul(inverse, horizontal_vec)
     is_horizontal = self.base.is_tangent(
         product_1 + product_2, mat, atol=gs.atol * 10)
     self.assertTrue(is_horizontal)
コード例 #13
0
 def horizontal_lift(self, tangent_vec, base_point=None, fiber_point=None):
     """Horizontal lift of a tangent vector."""
     if fiber_point is None:
         fiber_point = self.lift(base_point)
     transposed_point = Matrices.transpose(fiber_point)
     alignment = Matrices.mul(transposed_point, fiber_point)
     projector = Matrices.mul(fiber_point, GeneralLinear.inverse(alignment))
     right_term = Matrices.mul(transposed_point, tangent_vec, fiber_point)
     sylvester = gs.linalg.solve_sylvester(alignment, alignment, right_term)
     skew_term = Matrices.mul(projector, sylvester)
     orth_proj = gs.eye(self.n) - Matrices.mul(projector, transposed_point)
     orth_part = Matrices.mul(orth_proj, tangent_vec, projector)
     return skew_term + orth_part
コード例 #14
0
    def vertical_projection(self, tangent_vec, base_point, **kwargs):
        """Compute the vertical projection wrt the affine-invariant metric.

        Parameters
        ----------
        tangent_vec : array-like, shape=[..., n, n]
            Tangent vector.
        base_point : array-like, shape=[..., n, n]
            Base point.

        Returns
        -------
        ver : array-like, shape=[..., n, n]
            Vertical projection.
        """
        n = self.n
        inverse_base_point = GeneralLinear.inverse(base_point)
        operator = gs.eye(n) + base_point * inverse_base_point
        inverse_operator = GeneralLinear.inverse(operator)
        vector = gs.einsum("...ij,...ji->...i", inverse_base_point, tangent_vec)
        diagonal = gs.einsum("...ij,...j->...i", inverse_operator, vector)
        return base_point * (diagonal[..., None, :] + diagonal[..., :, None])
コード例 #15
0
    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
コード例 #16
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, n]
            Tangent vector at base point.
        tangent_vec_b : array-like, shape=[..., n, n]
            Tangent vector at base point.
        base_point : array-like, shape=[..., n, n]
            Base point.

        Returns
        -------
        inner_product : array-like, shape=[..., n, n]
            Inner-product.
        """
        power_affine = self.power_affine
        spd_space = SPDMatrices

        if power_affine == 1:
            inv_base_point = GeneralLinear.inverse(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
コード例 #17
0
ファイル: spd_matrices.py プロジェクト: xpennec/geomstats
    def parallel_transport(self,
                           tangent_vec,
                           base_point,
                           direction=None,
                           end_point=None):
        r"""Parallel transport of a tangent vector.

        Closed-form solution for the parallel transport of a tangent vector
        along the geodesic between two points `base_point` and `end_point`
        or alternatively defined by :math:`t\mapsto exp_(base_point)(
        t*direction)`.
        Denoting `tangent_vec_a` by `S`, `base_point` by `A`, and `end_point`
        by `B` or `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 : array-like, shape=[..., n, n]
            Tangent vector at base point to be transported.
        base_point : array-like, shape=[..., n, n]
            Point on the manifold of SPD matrices. Point to transport from
        direction : array-like, shape=[..., n, n]
            Tangent vector at base point, initial speed of the geodesic along
            which the parallel transport is computed. Unused if `end_point` is given.
            Optional, default: None.
        end_point : array-like, shape=[..., n, n]
            Point on the manifold of SPD matrices. Point to transport to.
            Optional, default: None.

        Returns
        -------
        transported_tangent_vec: array-like, shape=[..., n, n]
            Transported tangent vector at exp_(base_point)(tangent_vec_b).
        """
        if end_point is None:
            end_point = self.exp(direction, base_point)
        inverse_base_point = GeneralLinear.inverse(base_point)
        congruence_mat = Matrices.mul(end_point, inverse_base_point)
        congruence_mat = gs.linalg.sqrtm(congruence_mat)
        return Matrices.congruent(tangent_vec, congruence_mat)
コード例 #18
0
    def exp(self, tangent_vec, base_point, **kwargs):
        """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, n]
            Tangent vector at base point.
        base_point : array-like, shape=[..., n, n]
            Base point.

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

        if power_affine == 1:
            powers = SymmetricMatrices.powerm(base_point, [1.0 / 2, -1.0 / 2])
            exp = self._aux_exp(tangent_vec, powers[0], powers[1])
        else:
            modified_tangent_vec = SPDMatrices.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.inverse(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
コード例 #19
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)
        # We generate invertible matrices using so3_group
        self.so3_group = SpecialOrthogonal(n=self.n)

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

    @geomstats.tests.np_only
    def test_belongs(self):
        """
        A rotation matrix belongs to the matrix Lie group
        of invertible matrices.
        """
        rot_vec = gs.array([0.2, -0.1, 0.1])
        rot_mat = self.so3_group.matrix_from_rotation_vector(rot_vec)
        result = self.group.belongs(rot_mat)
        expected = gs.array([[True]])

        self.assertAllClose(result, expected)

    def test_compose(self):
        # 1. Composition by identity, on the right
        # Expect the original transformation
        rot_vec = gs.array([0.2, -0.1, 0.1])
        mat = self.so3_group.matrix_from_rotation_vector(rot_vec)

        result = self.group.compose(mat, self.group.identity)
        expected = mat
        expected = helper.to_matrix(mat)

        self.assertAllClose(result, expected)

        # 2. Composition by identity, on the left
        # Expect the original transformation
        rot_vec = gs.array([0.2, 0.1, -0.1])
        mat = self.so3_group.matrix_from_rotation_vector(rot_vec)

        result = self.group.compose(self.group.identity, mat)
        expected = mat

        self.assertAllClose(result, expected)

    def test_inverse(self):
        mat = gs.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 10.]])
        result = self.group.inverse(mat)
        expected = 1. / 3. * gs.array([[-2., -4., 3.], [-2., 11., -6.],
                                       [3., -6., 3.]])
        expected = helper.to_matrix(expected)

        self.assertAllClose(result, expected)

    def test_compose_and_inverse(self):
        # 1. Compose transformation by its inverse on the right
        # Expect the group identity
        rot_vec = gs.array([0.2, 0.1, 0.1])
        mat = self.so3_group.matrix_from_rotation_vector(rot_vec)
        inv_mat = self.group.inverse(mat)

        result = self.group.compose(mat, inv_mat)
        expected = self.group.identity
        expected = helper.to_matrix(expected)

        self.assertAllClose(result, expected)

        # 2. Compose transformation by its inverse on the left
        # Expect the group identity
        rot_vec = gs.array([0.7, 0.1, 0.1])
        mat = self.so3_group.matrix_from_rotation_vector(rot_vec)
        inv_mat = self.group.inverse(mat)

        result = self.group.compose(inv_mat, mat)
        expected = self.group.identity
        expected = helper.to_matrix(expected)

        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_group_log_and_exp(self):
        point = 5 * gs.eye(self.n)

        group_log = self.group.log(point)
        result = self.group.exp(group_log)
        expected = point
        expected = helper.to_matrix(expected)

        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_group_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_group_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_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)
コード例 #20
0
 def test_horizontal_projection(self, n, mat, vec):
     bundle = self.bundle(n)
     horizontal_vec = bundle.horizontal_projection(vec, mat)
     product = Matrices.mul(horizontal_vec, GeneralLinear.inverse(mat))
     is_horizontal = Matrices.is_symmetric(product)
     self.assertTrue(is_horizontal)
コード例 #21
0
class TestGeneralLinear(geomstats.tests.TestCase):
    def setUp(self):
        gs.random.seed(1234)
        self.n = 3
        self.n_samples = 2
        self.group = GeneralLinear(n=self.n)
        self.group_pos = GeneralLinear(self.n, positive_det=True)

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

    def test_belongs_shape(self):
        mat = gs.eye(3)
        result = self.group.belongs(mat)
        self.assertAllClose(gs.shape(result), ())

        mat = gs.ones((3, 3))
        result = self.group.belongs(mat)
        self.assertAllClose(gs.shape(result), ())

    def test_belongs(self):
        mat = gs.eye(3)
        result = self.group.belongs(mat)
        expected = True
        self.assertAllClose(result, expected)

        mat = gs.ones((3, 3))
        result = self.group.belongs(mat)
        expected = False
        self.assertAllClose(result, expected)

        mat = gs.ones(3)
        result = self.group.belongs(mat)
        expected = False
        self.assertAllClose(result, expected)

    def test_belongs_vectorization_shape(self):
        mats = gs.array([gs.eye(3), gs.ones((3, 3))])
        result = self.group.belongs(mats)
        self.assertAllClose(gs.shape(result), (2, ))

    def test_belongs_vectorization(self):
        mats = gs.array([gs.eye(3), gs.ones((3, 3))])
        result = self.group.belongs(mats)
        expected = gs.array([True, False])
        self.assertAllClose(result, expected)

    def test_random_and_belongs(self):
        for group in [self.group, self.group_pos]:
            point = group.random_point()
            result = group.belongs(point)
            self.assertTrue(result)

    def test_random_and_belongs_vectorization(self):
        n_samples = 4
        expected = gs.array([True] * n_samples)
        for group in [self.group, self.group_pos]:
            point = group.random_point(n_samples)
            result = group.belongs(point)
            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.inverse(mat_a)
        self.assertAllClose(result, expected)

    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.inverse(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)

    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]]])

        expected = gs.cast(expected, gs.float64)
        point = gs.cast(point, gs.float64)

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

    @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)

    @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.)]])
        identity = GeneralLinear(2).identity

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

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

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

        path = GeneralLinear(2).orbit(gs.stack([point] * 2), identity)
        time = gs.linspace(0., 1., 3)

        result = path(time)
        expected = gs.array([identity, sqrt, point])
        expected = gs.stack([expected] * 2)
        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)

    def test_projection_and_belongs(self):
        shape = (self.n_samples, self.n, self.n)
        result = helper.test_projection_and_belongs(self.group, shape)
        for res in result:
            self.assertTrue(res)

    def test_projection_and_belongs_pos(self):
        shape = (self.n_samples, self.n, self.n)
        result = helper.test_projection_and_belongs(self.group_pos, shape)
        for res in result:
            self.assertTrue(res)
コード例 #22
0
class TestGeneralLinear(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)

    def test_belongs_shape(self):
        mat = gs.eye(3)
        result = self.group.belongs(mat)
        self.assertAllClose(gs.shape(result), ())

        mat = gs.ones((3, 3))
        result = self.group.belongs(mat)
        self.assertAllClose(gs.shape(result), ())

    def test_belongs(self):
        mat = gs.eye(3)
        result = self.group.belongs(mat)
        expected = True
        self.assertAllClose(result, expected)

        mat = gs.ones((3, 3))
        result = self.group.belongs(mat)
        expected = False
        self.assertAllClose(result, expected)

    def test_belongs_vectorization_shape(self):
        mats = gs.array([gs.eye(3), gs.ones((3, 3))])
        result = self.group.belongs(mats)
        self.assertAllClose(gs.shape(result), (2, ))

    def test_belongs_vectorization(self):
        mats = gs.array([gs.eye(3), gs.ones((3, 3))])
        result = self.group.belongs(mats)
        expected = gs.array([True, False])
        self.assertAllClose(result, expected)

    def test_random_and_belongs(self):
        point = self.group.random_uniform()
        result = self.group.belongs(point)
        expected = True
        self.assertAllClose(result, expected)

    def test_random_and_belongs_vectorization(self):
        n_samples = 4
        point = self.group.random_uniform(n_samples)
        result = self.group.belongs(point)
        expected = gs.array([True] * n_samples)
        self.assertAllClose(result, expected)

    def test_replace_values(self):
        points = gs.ones((3, 3, 3))
        new_points = gs.zeros((2, 3, 3))
        indcs = [True, False, True]
        update = self.group._replace_values(points, new_points, indcs)
        self.assertAllClose(
            update,
            gs.stack([gs.zeros((3, 3)),
                      gs.ones((3, 3)),
                      gs.zeros((3, 3))]))

    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.inverse(mat_a)
        self.assertAllClose(result, expected)

    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.inverse(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)

    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)