Beispiel #1
0
 def test_align(self):
     point, base_point = self.space.random_point(2)
     aligned = self.space.align(point, base_point)
     alignment = gs.matmul(Matrices.transpose(aligned), base_point)
     result = Matrices.is_symmetric(alignment)
     self.assertTrue(result)
Beispiel #2
0
 def sylv_p(mat_b):
     """Solves Sylvester equation for vertical component."""
     return gs.linalg.solve_sylvester(p_top_p, p_top_p,
                                      mat_b - Matrices.transpose(mat_b))
 def __init__(self, n, **kwargs):
     super(SymmetricMatrices,
           self).__init__(dim=int(n * (n + 1) / 2),
                          embedding_manifold=Matrices(n, n))
     self.n = n
class TestVisualization(geomstats.tests.TestCase):
    def setup_method(self):
        self.n_samples = 10
        self.SO3_GROUP = SpecialOrthogonal(n=3, point_type="vector")
        self.SE3_GROUP = SpecialEuclidean(n=3, point_type="vector")
        self.S1 = Hypersphere(dim=1)
        self.S2 = Hypersphere(dim=2)
        self.H2 = Hyperbolic(dim=2)
        self.H2_half_plane = PoincareHalfSpace(dim=2)
        self.M32 = Matrices(m=3, n=2)
        self.S32 = PreShapeSpace(k_landmarks=3, m_ambient=2)
        self.KS = visualization.KendallSphere()
        self.M33 = Matrices(m=3, n=3)
        self.S33 = PreShapeSpace(k_landmarks=3, m_ambient=3)
        self.KD = visualization.KendallDisk()
        self.spd = SPDMatrices(n=2)

        plt.figure()

    @staticmethod
    def test_tutorial_matplotlib():
        visualization.tutorial_matplotlib()

    def test_plot_points_so3(self):
        points = self.SO3_GROUP.random_uniform(self.n_samples)
        visualization.plot(points, space="SO3_GROUP")

    def test_plot_points_se3(self):
        points = self.SE3_GROUP.random_point(self.n_samples)
        visualization.plot(points, space="SE3_GROUP")

    def test_draw_pre_shape_2d(self):
        self.KS.draw()

    def test_draw_points_pre_shape_2d(self):
        points = self.S32.random_point(self.n_samples)
        visualization.plot(points, space="S32")
        points = self.M32.random_point(self.n_samples)
        visualization.plot(points, space="M32")
        self.KS.clear_points()

    def test_draw_curve_pre_shape_2d(self):
        self.KS.draw()
        base_point = self.S32.random_point()
        vec = self.S32.random_point()
        tangent_vec = self.S32.to_tangent(vec, base_point)
        times = gs.linspace(0.0, 1.0, 1000)
        speeds = gs.array([-t * tangent_vec for t in times])
        points = self.S32.ambient_metric.exp(speeds, base_point)
        self.KS.add_points(points)
        self.KS.draw_curve()
        self.KS.clear_points()

    def test_draw_vector_pre_shape_2d(self):
        self.KS.draw()
        base_point = self.S32.random_point()
        vec = self.S32.random_point()
        tangent_vec = self.S32.to_tangent(vec, base_point)
        self.KS.draw_vector(tangent_vec, base_point)

    def test_convert_to_spherical_coordinates_pre_shape_2d(self):
        points = self.S32.random_point(self.n_samples)
        coords = self.KS.convert_to_spherical_coordinates(points)
        x = coords[:, 0]
        y = coords[:, 1]
        z = coords[:, 2]
        result = x**2 + y**2 + z**2
        expected = 0.25 * gs.ones(self.n_samples)
        self.assertAllClose(result, expected)

    def test_rotation_pre_shape_2d(self):
        theta = gs.random.rand(1)[0]
        phi = gs.random.rand(1)[0]
        rot = self.KS.rotation(theta, phi)
        result = _SpecialOrthogonalMatrices(3).belongs(rot)
        expected = True
        self.assertAllClose(result, expected)

    def test_draw_pre_shape_3d(self):
        self.KD.draw()

    def test_draw_points_pre_shape_3d(self):
        points = self.S33.random_point(self.n_samples)
        visualization.plot(points, space="S33")
        points = self.M33.random_point(self.n_samples)
        visualization.plot(points, space="M33")
        self.KD.clear_points()

    def test_draw_curve_pre_shape_3d(self):
        self.KD.draw()
        base_point = self.S33.random_point()
        vec = self.S33.random_point()
        tangent_vec = self.S33.to_tangent(vec, base_point)
        tangent_vec = 0.5 * tangent_vec / self.S33.ambient_metric.norm(
            tangent_vec)
        times = gs.linspace(0.0, 1.0, 1000)
        speeds = gs.array([-t * tangent_vec for t in times])
        points = self.S33.ambient_metric.exp(speeds, base_point)
        self.KD.add_points(points)
        self.KD.draw_curve()
        self.KD.clear_points()

    def test_draw_vector_pre_shape_3d(self):
        self.KS.draw()
        base_point = self.S32.random_point()
        vec = self.S32.random_point()
        tangent_vec = self.S32.to_tangent(vec, base_point)
        self.KS.draw_vector(tangent_vec, base_point)

    def test_convert_to_planar_coordinates_pre_shape_3d(self):
        points = self.S33.random_point(self.n_samples)
        coords = self.KD.convert_to_planar_coordinates(points)
        x = coords[:, 0]
        y = coords[:, 1]
        radius = x**2 + y**2
        result = [r <= 1.0 for r in radius]
        self.assertTrue(gs.all(result))

    @geomstats.tests.np_autograd_and_torch_only
    def test_plot_points_s1(self):
        points = self.S1.random_uniform(self.n_samples)
        visualization.plot(points, space="S1")

    def test_plot_points_s2(self):
        points = self.S2.random_uniform(self.n_samples)
        visualization.plot(points, space="S2")

    def test_plot_points_h2_poincare_disk(self):
        points = self.H2.random_point(self.n_samples)
        visualization.plot(points, space="H2_poincare_disk")

    def test_plot_points_h2_poincare_half_plane_ext(self):
        points = self.H2.random_point(self.n_samples)
        visualization.plot(points,
                           space="H2_poincare_half_plane",
                           point_type="extrinsic")

    def test_plot_points_h2_poincare_half_plane_none(self):
        points = self.H2_half_plane.random_point(self.n_samples)
        visualization.plot(points, space="H2_poincare_half_plane")

    def test_plot_points_h2_poincare_half_plane_hs(self):
        points = self.H2_half_plane.random_point(self.n_samples)
        visualization.plot(points,
                           space="H2_poincare_half_plane",
                           point_type="half_space")

    def test_plot_points_h2_klein_disk(self):
        points = self.H2.random_point(self.n_samples)
        visualization.plot(points, space="H2_klein_disk")

    @staticmethod
    def test_plot_points_se2():
        points = SpecialEuclidean(n=2, point_type="vector").random_point(4)
        visu = visualization.SpecialEuclidean2(points, point_type="vector")
        ax = visu.set_ax()
        visu.draw_points(ax)

    def test_plot_points_spd2(self):
        one_point = self.spd.random_point()
        visualization.plot(one_point, space="SPD2")

        points = self.spd.random_point(4)
        visualization.plot(points, space="SPD2")

    def test_compute_coordinates_spd2(self):
        point = gs.eye(2)
        ellipsis = visualization.Ellipses(n_sampling_points=4)
        x, y = ellipsis.compute_coordinates(point)
        self.assertAllClose(x, gs.array([1, 0, -1, 0, 1]))
        self.assertAllClose(y, gs.array([0, 1, 0, -1, 0]))

    @staticmethod
    def teardown_method():
        plt.close()
Beispiel #5
0
    def integrability_tensor_derivative_parallel(self, horizontal_vec_x,
                                                 horizontal_vec_y,
                                                 horizontal_vec_z, base_point):
        r"""Compute derivative of the integrability tensor A (special case).

        The horizontal covariant derivative :math:`\nabla_X (A_Y Z)` of the
        integrability tensor A may be computed more efficiently in the case of
        parallel vector fields in the quotient space. :math:
        `\nabla_X (A_Y Z)` and :math:`A_Y Z` are computed here for the
        Kendall shape space with quotient-parallel vector fields :math:`X,
        Y, Z` extending the values horizontal_vec_x, horizontal_vec_y and
        horizontal_vec_z by parallel transport in a neighborhood of the
        base-space. Such vector fields verify :math:`\nabla_X^X = A_X X =
        0`, :math:`\nabla_X^Y = A_X Y` and similarly for Z.

        Parameters
        ----------
        horizontal_vec_x : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`.
        horizontal_vec_y : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`.
        horizontal_vec_z : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`.
        base_point : array-like, shape=[..., k_landmarks, m_ambient]
            Point of the total space.

        Returns
        -------
        nabla_x_a_y_z : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`, result of
            :math:`\nabla_X (A_Y Z)` with `X = horizontal_vec_x`,
            `Y = horizontal_vec_y` and `Z = horizontal_vec_z`.
        a_y_z : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`, result of :math:`A_Y Z`
            with `Y = horizontal_vec_y` and `Z = horizontal_vec_z`.

        References
        ----------
        .. [Pennec] Pennec, Xavier. Computing the curvature and its gradient
        in Kendall shape spaces. Unpublished.
        """
        # Vectors X and Y have to be horizontal.
        if not gs.all(self.is_centered(base_point)):
            raise ValueError("The base_point does not belong to the pre-shape"
                             " space")
        if not gs.all(self.is_horizontal(horizontal_vec_x, base_point)):
            raise ValueError("Tangent vector x is not horizontal")
        if not gs.all(self.is_horizontal(horizontal_vec_y, base_point)):
            raise ValueError("Tangent vector y is not horizontal")
        if not gs.all(self.is_horizontal(horizontal_vec_z, base_point)):
            raise ValueError("Tangent vector z is not horizontal")

        p_top = Matrices.transpose(base_point)
        p_top_p = gs.matmul(p_top, base_point)

        def sylv_p(mat_b):
            """Solves Sylvester equation for vertical component."""
            return gs.linalg.solve_sylvester(p_top_p, p_top_p,
                                             mat_b - Matrices.transpose(mat_b))

        z_top = Matrices.transpose(horizontal_vec_z)
        y_top = Matrices.transpose(horizontal_vec_y)
        omega_yz = sylv_p(gs.matmul(z_top, horizontal_vec_y))
        a_y_z = gs.matmul(base_point, omega_yz)
        omega_xy = sylv_p(gs.matmul(y_top, horizontal_vec_x))
        omega_xz = sylv_p(gs.matmul(z_top, horizontal_vec_x))

        omega_yz_x = gs.matmul(horizontal_vec_x, omega_yz)
        omega_xz_y = gs.matmul(horizontal_vec_y, omega_xz)
        omega_xy_z = gs.matmul(horizontal_vec_z, omega_xy)

        tangent_vec_f = 2.0 * omega_yz_x + omega_xz_y - omega_xy_z
        omega_fp = sylv_p(gs.matmul(p_top, tangent_vec_f))
        omega_fp_p = gs.matmul(base_point, omega_fp)

        nabla_x_a_y_z = omega_yz_x - omega_fp_p

        return nabla_x_a_y_z, a_y_z
Beispiel #6
0
 def test_to_matrix_type_is_matrix_type(self, m, n, matrix_type, mat):
     cls_mn = Matrices(m, n)
     to_function = getattr(cls_mn, "to_" + matrix_type)
     is_function = getattr(cls_mn, "is_" + matrix_type)
     self.assertAllClose(gs.all(is_function(to_function(gs.array(mat)))),
                         True)
Beispiel #7
0
    class MatricesMetricTestData(_RiemannianMetricTestData):
        m_list = random.sample(range(3, 5), 2)
        n_list = random.sample(range(3, 5), 2)
        metric_args_list = list(zip(m_list, n_list))
        space_args_list = metric_args_list
        shape_list = space_args_list
        space_list = [Matrices(m, n) for m, n in metric_args_list]
        n_points_list = random.sample(range(1, 7), 2)
        n_tangent_vecs_list = random.sample(range(1, 7), 2)
        n_points_a_list = random.sample(range(1, 7), 2)
        n_points_b_list = [1]
        alpha_list = [1] * 2
        n_rungs_list = [1] * 2
        scheme_list = ["pole"] * 2

        def inner_product_test_data(self):
            smoke_data = [
                dict(
                    m=2,
                    n=2,
                    tangent_vec_a=[[-3.0, 1.0], [-1.0, -2.0]],
                    tangent_vec_b=[[-9.0, 0.0], [4.0, 2.0]],
                    expected=19.0,
                ),
                dict(
                    m=2,
                    n=2,
                    tangent_vec_a=[
                        [[-1.5, 0.0], [2.0, -3.0]],
                        [[0.5, 7.0], [0.5, -2.0]],
                    ],
                    tangent_vec_b=[
                        [[2.0, 0.0], [2.0, -3.0]],
                        [[-1.0, 0.0], [1.0, -2.0]],
                    ],
                    expected=[10.0, 4.0],
                ),
            ]
            return self.generate_tests(smoke_data)

        def norm_test_data(self):
            smoke_data = [
                dict(m=2,
                     n=2,
                     vector=[[1.0, 0.0], [0.0, 1.0]],
                     expected=SQRT_2),
                dict(
                    m=2,
                    n=2,
                    vector=[[[3.0, 0.0], [4.0, 0.0]], [[-3.0, 0.0],
                                                       [-4.0, 0.0]]],
                    expected=[5.0, 5.0],
                ),
            ]
            return self.generate_tests(smoke_data)

        def inner_product_norm_test_data(self):
            smoke_data = [
                dict(m=5, n=5, mat=Matrices(5, 5).random_point(100)),
                dict(m=10, n=10, mat=Matrices(5, 5).random_point(100)),
            ]
            return self.generate_tests(smoke_data)

        def exp_shape_test_data(self):
            return self._exp_shape_test_data(
                self.metric_args_list,
                self.space_list,
                self.shape_list,
            )

        def log_shape_test_data(self):
            return self._log_shape_test_data(
                self.metric_args_list,
                self.space_list,
            )

        def squared_dist_is_symmetric_test_data(self):
            return self._squared_dist_is_symmetric_test_data(
                self.metric_args_list,
                self.space_list,
                self.n_points_a_list,
                self.n_points_b_list,
                atol=gs.atol * 1000,
            )

        def exp_belongs_test_data(self):
            return self._exp_belongs_test_data(
                self.metric_args_list,
                self.space_list,
                self.shape_list,
                self.n_tangent_vecs_list,
                belongs_atol=gs.atol * 1000,
            )

        def log_is_tangent_test_data(self):
            return self._log_is_tangent_test_data(
                self.metric_args_list,
                self.space_list,
                self.n_points_list,
                is_tangent_atol=gs.atol * 1000,
            )

        def geodesic_ivp_belongs_test_data(self):
            return self._geodesic_ivp_belongs_test_data(
                self.metric_args_list,
                self.space_list,
                self.shape_list,
                self.n_points_list,
                belongs_atol=gs.atol * 1000,
            )

        def geodesic_bvp_belongs_test_data(self):
            return self._geodesic_bvp_belongs_test_data(
                self.metric_args_list,
                self.space_list,
                self.n_points_list,
                belongs_atol=gs.atol * 1000,
            )

        def log_then_exp_test_data(self):
            return self._log_then_exp_test_data(
                self.metric_args_list,
                self.space_list,
                self.n_points_list,
                rtol=gs.rtol * 100,
                atol=gs.atol * 10000,
            )

        def exp_then_log_test_data(self):
            return self._exp_then_log_test_data(
                self.metric_args_list,
                self.space_list,
                self.shape_list,
                self.n_tangent_vecs_list,
                rtol=gs.rtol * 100,
                atol=gs.atol * 10000,
            )

        def exp_ladder_parallel_transport_test_data(self):
            return self._exp_ladder_parallel_transport_test_data(
                self.metric_args_list,
                self.space_list,
                self.shape_list,
                self.n_tangent_vecs_list,
                self.n_rungs_list,
                self.alpha_list,
                self.scheme_list,
            )

        def exp_geodesic_ivp_test_data(self):
            return self._exp_geodesic_ivp_test_data(
                self.metric_args_list,
                self.space_list,
                self.shape_list,
                self.n_tangent_vecs_list,
                self.n_points_list,
                rtol=gs.rtol * 100000,
                atol=gs.atol * 100000,
            )

        def parallel_transport_ivp_is_isometry_test_data(self):
            return self._parallel_transport_ivp_is_isometry_test_data(
                self.metric_args_list,
                self.space_list,
                self.shape_list,
                self.n_tangent_vecs_list,
                is_tangent_atol=gs.atol * 1000,
                atol=gs.atol * 1000,
            )

        def parallel_transport_bvp_is_isometry_test_data(self):
            return self._parallel_transport_bvp_is_isometry_test_data(
                self.metric_args_list,
                self.space_list,
                self.shape_list,
                self.n_tangent_vecs_list,
                is_tangent_atol=gs.atol * 1000,
                atol=gs.atol * 1000,
            )

        def dist_is_symmetric_test_data(self):
            return self._dist_is_symmetric_test_data(
                self.metric_args_list,
                self.space_list,
                self.n_points_a_list,
                self.n_points_b_list,
            )

        def dist_is_positive_test_data(self):
            return self._dist_is_positive_test_data(
                self.metric_args_list,
                self.space_list,
                self.n_points_a_list,
                self.n_points_b_list,
            )

        def squared_dist_is_positive_test_data(self):
            return self._squared_dist_is_positive_test_data(
                self.metric_args_list,
                self.space_list,
                self.n_points_a_list,
                self.n_points_b_list,
            )

        def dist_is_norm_of_log_test_data(self):
            return self._dist_is_norm_of_log_test_data(
                self.metric_args_list,
                self.space_list,
                self.n_points_a_list,
                self.n_points_b_list,
            )

        def dist_point_to_itself_is_zero_test_data(self):
            return self._dist_point_to_itself_is_zero_test_data(
                self.metric_args_list, self.space_list, self.n_points_list)

        def inner_product_is_symmetric_test_data(self):
            return self._inner_product_is_symmetric_test_data(
                self.metric_args_list,
                self.space_list,
                self.shape_list,
                self.n_tangent_vecs_list,
            )

        def retraction_lifting_test_data(self):
            return self._exp_then_log_test_data(
                self.metric_args_list,
                self.space_list,
                self.shape_list,
                self.n_tangent_vecs_list,
                rtol=gs.rtol * 100,
                atol=gs.atol * 10000,
            )
Beispiel #8
0
 def test_frobenius_product(self, mat_a, mat_b, expected):
     self.assertAllClose(
         Matrices.frobenius_product(gs.array(mat_a), gs.array(mat_b)),
         gs.array(expected),
     )
Beispiel #9
0
 def test_trace_product(self, mat_a, mat_b, expected):
     self.assertAllClose(
         Matrices.trace_product(gs.array(mat_a), gs.array(mat_b)),
         gs.array(expected))
Beispiel #10
0
 def test_bracket(self, mat_a, mat_b, expected):
     self.assertAllClose(Matrices.bracket(gs.array(mat_a), gs.array(mat_b)),
                         gs.array(expected))
Beispiel #11
0
 def test_congruent(self, mat_a, mat_b, expected):
     self.assertAllClose(
         Matrices.congruent(gs.array(mat_a), gs.array(mat_b)),
         gs.array(expected))
Beispiel #12
0
 def test_mul(self, mat, expected):
     self.assertAllClose(Matrices.mul(*mat), gs.array(expected))
Beispiel #13
0
 def test_equal(self, m, n, mat1, mat2, expected):
     self.assertAllClose(
         Matrices(m, n).equal(gs.array(mat1), gs.array(mat2)),
         gs.array(expected))
Beispiel #14
0
 def test_belongs(self, m, n, mat, expected):
     self.assertAllClose(
         Matrices(m, n).belongs(gs.array(mat)), gs.array(expected))
Beispiel #15
0
 def test_to_lower_triangular_diagonal_scaled(self, m, n, mat, expected):
     self.assertAllClose(
         Matrices(m, n).to_lower_triangular_diagonal_scaled(gs.array(mat)),
         gs.array(expected),
     )
Beispiel #16
0
 def test_flatten(self, m, n, mat, expected):
     self.assertAllClose(
         Matrices(m, n).flatten(gs.array(mat)), gs.array(expected))
Beispiel #17
0
 def test_flatten_reshape(self, m, n, mat):
     cls_mn = Matrices(m, n)
     self.assertAllClose(cls_mn.reshape(cls_mn.flatten(gs.array(mat))),
                         gs.array(mat))
Beispiel #18
0
 def test_transpose(self, m, n, mat, expected):
     self.assertAllClose(
         Matrices(m, n).transpose(gs.array(mat)), gs.array(expected))
Beispiel #19
0
 def test_basis(self, m, n, expected):
     result = Matrices(m, n).basis
     self.assertAllClose(result, expected)
Beispiel #20
0
 def test_is_spd(self, m, n, mat, expected):
     self.assertAllClose(
         Matrices(m, n).is_spd(gs.array(mat)), gs.array(expected))
Beispiel #21
0
 def inner_product_norm_test_data(self):
     smoke_data = [
         dict(m=5, n=5, mat=Matrices(5, 5).random_point(100)),
         dict(m=10, n=10, mat=Matrices(5, 5).random_point(100)),
     ]
     return self.generate_tests(smoke_data)
Beispiel #22
0
 def test_is_lower_triangular(self, m, n, mat, expected):
     self.assertAllClose(
         Matrices(m, n).is_lower_triangular(gs.array(mat)),
         gs.array(expected))
Beispiel #23
0
    def integrability_tensor_derivative(
        self,
        horizontal_vec_x,
        horizontal_vec_y,
        nabla_x_y,
        tangent_vec_e,
        nabla_x_e,
        base_point,
    ):
        r"""Compute the covariant derivative of the integrability tensor A.

        The horizontal covariant derivative :math:`\nabla_X (A_Y E)` is
        necessary to compute the covariant derivative of the curvature in a
        submersion.
        The components :math:`\nabla_X (A_Y E)` and :math:`A_Y E` are
        computed here for the Kendall shape space at base-point
        :math:`P = base\_point` for horizontal vector fields fields :math:
        `X, Y` extending the values :math:`X|_P = horizontal\_vec\_x`,
        :math:`Y|_P = horizontal\_vec\_y` and a general vector field
        :math:`E` extending :math:`E|_P = tangent\_vec\_e` in a neighborhood
        of the base-point P with covariant derivatives
        :math:`\nabla_X Y |_P = nabla_x_y` and
        :math:`\nabla_X E |_P = nabla_x_e`.

        Parameters
        ----------
        horizontal_vec_x : array-like, shape=[..., k_landmarks, m_ambient]
            Horizontal tangent vector at `base_point`.
        horizontal_vec_y : array-like, shape=[..., k_landmarks, m_ambient]
            Horizontal tangent vector at `base_point`.
        nabla_x_y : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`.
        tangent_vec_e : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`.
        nabla_x_e : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`.
        base_point : array-like, shape=[..., k_landmarks, m_ambient]
            Point of the total space.

        Returns
        -------
        nabla_x_a_y_e : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`, result of :math:`\nabla_X^S
            (A_Y E)`.
        a_y_e : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`, result of :math:`A_Y E`.

        References
        ----------
        .. [Pennec] Pennec, Xavier. Computing the curvature and its gradient
        in Kendall shape spaces. Unpublished.
        """
        if not gs.all(self.belongs(base_point)):
            raise ValueError("The base_point does not belong to the pre-shape"
                             " space")
        if not gs.all(self.is_horizontal(horizontal_vec_x, base_point)):
            raise ValueError("Tangent vector x is not horizontal")
        if not gs.all(self.is_horizontal(horizontal_vec_y, base_point)):
            raise ValueError("Tangent vector y is not horizontal")
        if not gs.all(self.is_tangent(nabla_x_y, base_point)):
            raise ValueError("Vector nabla_x_y is not tangent")
        a_x_y = self.integrability_tensor(horizontal_vec_x, horizontal_vec_y,
                                          base_point)
        if not gs.all(self.is_horizontal(nabla_x_y - a_x_y, base_point)):
            raise ValueError("Tangent vector nabla_x_y is not the gradient "
                             "of a horizontal distrinbution")
        if not gs.all(self.is_tangent(tangent_vec_e, base_point)):
            raise ValueError("Tangent vector e is not tangent")
        if not gs.all(self.is_tangent(nabla_x_e, base_point)):
            raise ValueError("Vector nabla_x_e is not tangent")

        p_top = Matrices.transpose(base_point)
        p_top_p = gs.matmul(p_top, base_point)
        e_top = Matrices.transpose(tangent_vec_e)
        x_top = Matrices.transpose(horizontal_vec_x)
        y_top = Matrices.transpose(horizontal_vec_y)

        def sylv_p(mat_b):
            """Solves Sylvester equation for vertical component."""
            return gs.linalg.solve_sylvester(p_top_p, p_top_p,
                                             mat_b - Matrices.transpose(mat_b))

        omega_ep = sylv_p(gs.matmul(p_top, tangent_vec_e))
        omega_ye = sylv_p(gs.matmul(e_top, horizontal_vec_y))
        tangent_vec_b = gs.matmul(horizontal_vec_x, omega_ye)
        tangent_vec_e_sym = tangent_vec_e - 2.0 * gs.matmul(
            base_point, omega_ep)

        a_y_e = gs.matmul(base_point, omega_ye) + gs.matmul(
            horizontal_vec_y, omega_ep)

        tmp_tangent_vec_p = (gs.matmul(e_top, nabla_x_y) -
                             gs.matmul(y_top, nabla_x_e) -
                             2.0 * gs.matmul(p_top, tangent_vec_b))

        tmp_tangent_vec_y = gs.matmul(p_top, nabla_x_e) + gs.matmul(
            x_top, tangent_vec_e_sym)

        scal_x_a_y_e = self.ambient_metric.inner_product(
            horizontal_vec_x, a_y_e, base_point)

        nabla_x_a_y_e = (
            gs.matmul(base_point, sylv_p(tmp_tangent_vec_p)) +
            gs.matmul(horizontal_vec_y, sylv_p(tmp_tangent_vec_y)) +
            gs.matmul(nabla_x_y, omega_ep) + tangent_vec_b +
            gs.einsum("...,...ij->...ij", scal_x_a_y_e, base_point))

        return nabla_x_a_y_e, a_y_e
Beispiel #24
0
 def test_to_diagonal(self, m, n, mat, expected):
     self.assertAllClose(
         Matrices(m, n).to_diagonal(gs.array(mat)), gs.array(expected))
Beispiel #25
0
    def iterated_integrability_tensor_derivative_parallel(
            self, horizontal_vec_x, horizontal_vec_y, base_point):
        r"""Compute iterated derivatives of the integrability tensor A.

        The iterated horizontal covariant derivative
        :math:`\nabla_X (A_Y A_X Y)` (where :math:`X` and :math:`Y` are
        horizontal vector fields) is a key ingredient in the computation of
        the covariant derivative of the directional curvature in a submersion.

        The components :math:`\nabla_X (A_Y A_X Y)`, :math:`A_X A_Y A_X Y`,
        :math:`\nabla_X (A_X Y)`,  and intermediate computations
        :math:`A_Y A_X Y` and :math:`A_X Y` are computed here for the
        Kendall shape space in the special case of quotient-parallel vector
        fields :math:`X, Y` extending the values horizontal_vec_x and
        horizontal_vec_y by parallel transport in a neighborhood.
        Such vector fields verify :math:`\nabla_X^X = A_X X` and :math:
        `\nabla_X^Y = A_X Y`.

        Parameters
        ----------
        horizontal_vec_x : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`.
        horizontal_vec_y : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`.
        base_point : array-like, shape=[..., k_landmarks, m_ambient]
            Point of the total space.

        Returns
        -------
        nabla_x_a_y_a_x_y : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`, result of
            :math:`\nabla_X^S (A_Y A_X Y)` with
            `X = horizontal_vec_x` and `Y = horizontal_vec_y`.
        a_x_a_y_a_x_y : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`, result of
            :math:`A_X A_Y A_X Y` with
            `X = horizontal_vec_x` and `Y = horizontal_vec_y`.
        nabla_x_a_x_y : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`, result of
            :math:`\nabla_X^S (A_X Y)` with
            `X = horizontal_vec_x` and `Y = horizontal_vec_y`.
        a_y_a_x_y : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`, result of :math:`A_Y A_X Y` with
            `X = horizontal_vec_x` and `Y = horizontal_vec_y`.
        a_x_y : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at `base_point`, result of :math:`A_X Y` with
            `X = horizontal_vec_x` and `Y = horizontal_vec_y`.

        References
        ----------
        .. [Pennec] Pennec, Xavier. Computing the curvature and its gradient
        in Kendall shape spaces. Unpublished.
        """
        if not gs.all(self.is_centered(base_point)):
            raise ValueError("The base_point does not belong to the pre-shape"
                             " space")
        if not gs.all(self.is_horizontal(horizontal_vec_x, base_point)):
            raise ValueError("Tangent vector x is not horizontal")
        if not gs.all(self.is_horizontal(horizontal_vec_y, base_point)):
            raise ValueError("Tangent vector y is not horizontal")

        p_top = Matrices.transpose(base_point)
        p_top_p = gs.matmul(p_top, base_point)

        def sylv_p(mat_b):
            """Solves Sylvester equation for vertical component."""
            return gs.linalg.solve_sylvester(p_top_p, p_top_p,
                                             mat_b - Matrices.transpose(mat_b))

        y_top = Matrices.transpose(horizontal_vec_y)
        x_top = Matrices.transpose(horizontal_vec_x)
        x_y_top = gs.matmul(y_top, horizontal_vec_x)
        omega_xy = sylv_p(x_y_top)
        vertical_vec_v = gs.matmul(base_point, omega_xy)
        omega_xy_x = gs.matmul(horizontal_vec_x, omega_xy)
        omega_xy_y = gs.matmul(horizontal_vec_y, omega_xy)

        v_top = Matrices.transpose(vertical_vec_v)
        x_v_top = gs.matmul(v_top, horizontal_vec_x)
        omega_xv = sylv_p(x_v_top)
        omega_xv_p = gs.matmul(base_point, omega_xv)

        y_v_top = gs.matmul(v_top, horizontal_vec_y)
        omega_yv = sylv_p(y_v_top)
        omega_yv_p = gs.matmul(base_point, omega_yv)

        nabla_x_v = 3.0 * omega_xv_p + omega_xy_x
        a_y_a_x_y = omega_yv_p + omega_xy_y
        tmp_mat = gs.matmul(x_top, a_y_a_x_y)
        a_x_a_y_a_x_y = -gs.matmul(base_point, sylv_p(tmp_mat))

        omega_xv_y = gs.matmul(horizontal_vec_y, omega_xv)
        omega_yv_x = gs.matmul(horizontal_vec_x, omega_yv)
        omega_xy_v = gs.matmul(vertical_vec_v, omega_xy)
        norms = Matrices.frobenius_product(vertical_vec_v, vertical_vec_v)
        sq_norm_v_p = gs.einsum("...,...ij->...ij", norms, base_point)

        tmp_mat = gs.matmul(p_top, 3.0 * omega_xv_y +
                            2.0 * omega_yv_x) + gs.matmul(y_top, omega_xy_x)

        nabla_x_a_y_v = (3.0 * omega_xv_y + omega_yv_x + omega_xy_v -
                         gs.matmul(base_point, sylv_p(tmp_mat)) + sq_norm_v_p)

        return nabla_x_a_y_v, a_x_a_y_a_x_y, nabla_x_v, a_y_a_x_y, vertical_vec_v
Beispiel #26
0
 def test_to_symmetric(self, m, n, mat, expected):
     self.assertAllClose(
         Matrices(m, n).to_symmetric(gs.array(mat)), gs.array(expected))
 def __init__(self, n):
     super(FullRankCorrelationMatrices, self).__init__(
         dim=int(n * (n - 1) / 2), embedding_space=SPDMatrices(n=n),
         submersion=Matrices.diagonal, value=gs.ones(n),
         tangent_submersion=lambda v, x: Matrices.diagonal(v))
     self.n = n
Beispiel #28
0
 def test_to_strictly_upper_triangular(self, m, n, mat, expected):
     self.assertAllClose(
         Matrices(m, n).to_strictly_upper_triangular(gs.array(mat)),
         gs.array(expected),
     )
Beispiel #29
0
class TestMatrices(geomstats.tests.TestCase):
    def setUp(self):
        gs.random.seed(1234)

        self.n = 3
        self.space = Matrices(m=self.n, n=self.n)
        self.metric = self.space.metric
        self.n_samples = 2

    @geomstats.tests.np_only
    def test_mul(self):
        a = gs.eye(3, 3, 1)
        b = gs.eye(3, 3, -1)
        c = gs.array([[1., 0., 0.], [0., 1., 0.], [0., 0., 0.]])
        d = gs.array([[0., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
        result = self.space.mul([a, b], [b, a])
        expected = gs.array([c, d])
        self.assertAllClose(result, expected)

        result = self.space.mul(a, [a, b])
        expected = gs.array([gs.eye(3, 3, 2), c])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_only
    def test_bracket(self):
        x = gs.array([[0., 0., 0.], [0., 0., -1.], [0., 1., 0.]])
        y = gs.array([[0., 0., 1.], [0., 0., 0.], [-1., 0., 0.]])
        z = gs.array([[0., -1., 0.], [1., 0., 0.], [0., 0., 0.]])
        result = self.space.bracket([x, y], [y, z])
        expected = gs.array([z, x])
        self.assertAllClose(result, expected)

        result = self.space.bracket(x, [x, y, z])
        expected = gs.array([gs.zeros((3, 3)), z, -y])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_only
    def test_transpose(self):
        tr = self.space.transpose
        ar = gs.array
        a = gs.eye(3, 3, 1)
        b = gs.eye(3, 3, -1)
        self.assertAllClose(tr(a), b)
        self.assertAllClose(tr(ar([a, b])), ar([b, a]))

    @geomstats.tests.np_only
    def test_is_symmetric(self):
        sym_mat = gs.array([[1., 2.], [2., 1.]])
        result = self.space.is_symmetric(sym_mat)
        expected = gs.array(True)
        self.assertAllClose(result, expected)

        not_a_sym_mat = gs.array([[1., 0.6, -3.], [6., -7., 0.], [0., 7., 8.]])
        result = self.space.is_symmetric(not_a_sym_mat)
        expected = gs.array(False)
        self.assertAllClose(result, expected)

    @geomstats.tests.np_only
    def test_is_skew_symmetric(self):
        skew_mat = gs.array([[0, -2.], [2., 0]])
        result = self.space.is_skew_symmetric(skew_mat)
        expected = gs.array(True)
        self.assertAllClose(result, expected)

        not_a_sym_mat = gs.array([[1., 0.6, -3.], [6., -7., 0.], [0., 7., 8.]])
        result = self.space.is_skew_symmetric(not_a_sym_mat)
        expected = gs.array(False)
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_is_symmetric_vectorization(self):
        points = gs.array([[[1., 2.], [2., 1.]], [[3., 4.], [4., 5.]],
                           [[1., 2.], [3., 4.]]])
        result = self.space.is_symmetric(points)
        expected = [True, True, False]
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_pytorch_only
    def test_make_symmetric(self):
        sym_mat = gs.array([[1., 2.], [2., 1.]])
        result = self.space.to_symmetric(sym_mat)
        expected = sym_mat
        self.assertAllClose(result, expected)

        mat = gs.array([[1., 2., 3.], [0., 0., 0.], [3., 1., 1.]])
        result = self.space.to_symmetric(mat)
        expected = gs.array([[1., 1., 3.], [1., 0., 0.5], [3., 0.5, 1.]])
        self.assertAllClose(result, expected)

        mat = gs.array([[1e100, 1e-100, 1e100], [1e100, 1e-100, 1e100],
                        [1e-100, 1e-100, 1e100]])
        result = self.space.to_symmetric(mat)

        res = 0.5 * (1e100 + 1e-100)

        expected = gs.array([[1e100, res, res], [res, 1e-100, res],
                             [res, res, 1e100]])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_make_symmetric_and_is_symmetric_vectorization(self):
        points = gs.array([[[1., 2.], [3., 4.]], [[5., 6.], [4., 9.]]])

        sym_points = self.space.to_symmetric(points)
        result = gs.all(self.space.is_symmetric(sym_points))
        expected = True
        self.assertAllClose(result, expected)

    def test_inner_product(self):
        base_point = gs.array([[1., 2., 3.], [0., 0., 0.], [3., 1., 1.]])

        tangent_vector_1 = gs.array([[1., 2., 3.], [0., -10., 0.],
                                     [30., 1., 1.]])

        tangent_vector_2 = gs.array([[1., 4., 3.], [5., 0., 0.], [3., 1., 1.]])

        result = self.metric.inner_product(tangent_vector_1,
                                           tangent_vector_2,
                                           base_point=base_point)

        expected = gs.trace(
            gs.matmul(gs.transpose(tangent_vector_1), tangent_vector_2))

        self.assertAllClose(result, expected)

    def test_cong(self):
        base_point = gs.array([[1., 2., 3.], [0., 0., 0.], [3., 1., 1.]])

        tangent_vector = gs.array([[1., 2., 3.], [0., -10., 0.], [30., 1.,
                                                                  1.]])

        result = self.space.congruent(tangent_vector, base_point)
        expected = gs.matmul(tangent_vector, gs.transpose(base_point))
        expected = gs.matmul(base_point, expected)

        self.assertAllClose(result, expected)

    def test_belongs(self):
        base_point = gs.array([[1., 2., 3.], [0., 0., 0.], [3., 1., 1.]])
        result = self.space.belongs(base_point)
        expected = True
        self.assertAllClose(result, expected)
Beispiel #30
0
    def log(self, point, base_point, max_iter=30, tol=1e-6):
        """Compute the Riemannian logarithm of a point.

        Based on [ZR2017]_.

        References
        ----------
        .. [ZR2017] Zimmermann, Ralf. "A Matrix-Algebraic Algorithm for the
          Riemannian Logarithm on the Stiefel Manifold under the Canonical
          Metric" SIAM J. Matrix Anal. & Appl., 38(2), 322–342, 2017.
          https://arxiv.org/pdf/1604.05054.pdf

        Parameters
        ----------
        point : array-like, shape=[..., n, p]
            Point in the Stiefel manifold.
        base_point : array-like, shape=[..., n, p]
            Point in the Stiefel manifold.
        max_iter: int
            Maximum number of iterations to perform during the algorithm.
            Optional, default: 30.
        tol: float
            Tolerance to reach convergence. The matrix 2-norm is used as
            criterion.
            Optional, default: 1e-6.

        Returns
        -------
        log : array-like, shape=[..., dim + 1]
            Tangent vector at the base point equal to the Riemannian logarithm
            of point at the base point.
        """
        p = base_point.shape[-1]

        transpose_base_point = Matrices.transpose(base_point)
        matrix_m = gs.matmul(transpose_base_point, point)

        matrix_q, matrix_n = StiefelCanonicalMetric._normal_component_qr(
            point, base_point, matrix_m)

        matrix_v = StiefelCanonicalMetric._orthogonal_completion(
            matrix_m, matrix_n)

        matrix_v = StiefelCanonicalMetric._procrustes_preprocessing(
            p, matrix_v, matrix_m, matrix_n)

        for _ in range(max_iter):
            matrix_lv = gs.linalg.logm(matrix_v)

            matrix_c = matrix_lv[:, p:2 * p, p:2 * p]

            norm_matrix_c = gs.linalg.norm(matrix_c)

            if gs.less_equal(norm_matrix_c, tol):
                break

            matrix_phi = gs.linalg.expm(-matrix_c)

            aux_matrix = gs.matmul(
                matrix_v[:, :, p:2 * p], matrix_phi)

            matrix_v = gs.concatenate(
                [matrix_v[:, :, 0:p],
                 aux_matrix],
                axis=2)

        matrix_xv = gs.matmul(base_point, matrix_lv[:, 0:p, 0:p])
        matrix_qv = gs.matmul(matrix_q, matrix_lv[:, p:2 * p, 0:p])

        return matrix_xv + matrix_qv