Example #1
0
    def setup_method(self):
        gs.random.seed(1234)

        self.k_landmarks = 4
        self.m_ambient = 3
        self.space = PreShapeSpace(self.k_landmarks, self.m_ambient)
        self.matrices = self.space.embedding_space
        self.n_samples = 10
        self.shape_metric = KendallShapeMetric(self.k_landmarks,
                                               self.m_ambient)

        self.base_point = self.space.random_point()
        vector = gs.random.rand(11, self.k_landmarks, self.m_ambient)
        tg_vec_0 = self.space.to_tangent(vector[0], self.base_point)
        self.hor_x = self.space.horizontal_projection(tg_vec_0,
                                                      self.base_point)
        tg_vec_1 = self.space.to_tangent(vector[1], self.base_point)
        self.hor_y = self.space.horizontal_projection(tg_vec_1,
                                                      self.base_point)
        tg_vec_2 = self.space.to_tangent(vector[2], self.base_point)
        self.hor_z = self.space.horizontal_projection(tg_vec_2,
                                                      self.base_point)
        tg_vec_3 = self.space.to_tangent(vector[3], self.base_point)
        self.hor_h = self.space.horizontal_projection(tg_vec_3,
                                                      self.base_point)
        tg_vec_4 = self.space.to_tangent(vector[4], self.base_point)
        self.ver_v = self.space.vertical_projection(tg_vec_4, self.base_point)
        tg_vec_5 = self.space.to_tangent(vector[5], self.base_point)
        self.ver_w = self.space.vertical_projection(tg_vec_5, self.base_point)
        tg_vec_6 = self.space.to_tangent(vector[6], self.base_point)
        hor_dy = self.space.horizontal_projection(tg_vec_6, self.base_point)
        tg_vec_7 = self.space.to_tangent(vector[7], self.base_point)
        hor_dz = self.space.horizontal_projection(tg_vec_7, self.base_point)
        tg_vec_8 = self.space.to_tangent(vector[8], self.base_point)
        ver_dv = self.space.vertical_projection(tg_vec_8, self.base_point)
        tg_vec_9 = self.space.to_tangent(vector[9], self.base_point)
        ver_dw = self.space.vertical_projection(tg_vec_9, self.base_point)
        tg_vec_10 = self.space.to_tangent(vector[10], self.base_point)
        hor_dh = self.space.horizontal_projection(tg_vec_10, self.base_point)

        # generate valid derivatives of horizontal / vertical vector fields.
        a_x_y = self.space.integrability_tensor(self.hor_x, self.hor_y,
                                                self.base_point)
        self.nabla_x_y = hor_dy + a_x_y
        a_x_z = self.space.integrability_tensor(self.hor_x, self.hor_z,
                                                self.base_point)
        self.nabla_x_z = hor_dz + a_x_z
        a_x_v = self.space.integrability_tensor(self.hor_x, self.ver_v,
                                                self.base_point)
        self.nabla_x_v = ver_dv + a_x_v
        a_x_w = self.space.integrability_tensor(self.hor_x, self.ver_w,
                                                self.base_point)
        self.nabla_x_w = ver_dw + a_x_w
        a_x_h = self.space.integrability_tensor(self.hor_x, self.hor_h,
                                                self.base_point)
        self.nabla_x_h = hor_dh + a_x_h
Example #2
0
    def setUp(self):
        gs.random.seed(1234)

        self.k_landmarks = 4
        self.m_ambient = 3
        self.space = PreShapeSpace(self.k_landmarks, self.m_ambient)
        self.matrices = self.space.embedding_manifold
        self.n_samples = 10
        self.shape_metric = KendallShapeMetric(self.k_landmarks,
                                               self.m_ambient)
Example #3
0
    def fit(self):
        """
        This is the main entry of fitting PNS to data

        """
        ## 0. make sure the data are distributed on a unit sphere
        d, n = self.data.shape
        if not is_on_unit_sphere(self.data):
            print("Mapping data to preshape space")
            data_in_3d = np.reshape(self.data, (-1, 3, n))
            _, k_landmarks, _ = data_in_3d.shape
            from geomstats.geometry.pre_shape import PreShapeSpace

            preshape = PreShapeSpace(m_ambient=3, k_landmarks=k_landmarks)
            data_preshape = preshape.projection(data_in_3d)
            base_point = data_preshape[0]

            data_shape = preshape.align(point=data_preshape,
                                        base_point=base_point)

            self.data = np.reshape(data_shape, (d, n))
        ## 1. rotate data to get a tight space, excluding the null space
        eps = 1e-15

        u, s, _ = np.linalg.svd(self.data, full_matrices=False)
        small_singular_val = np.where(s < eps)[0]
        maxd = len(small_singular_val)
        if maxd == 0:
            maxd = np.min([d, n]) + 1

        ## the dimension of null space
        nullspdim = d - maxd + 1

        ## 2. intrinsic dimension of sphere is 1 dimension lower than extrinsic_dim
        dm = maxd - 2

        basisu = []
        if nullspdim > 0:
            basisu = u[:, :dm + 1]
            ## extract the signal by projecting to the kernel space (complementary of the null space)
            currentSphere = np.matmul(u[:, :dm + 1].T, self.data)

        else:
            currentSphere = self.data

        if self.itype == 9:
            ## Use hypothesis testing (Kurtosis test) to decide whether great or small circle for EACH subsphere
            self.output = self.automatic_fit_subspheres(
                currentSphere, dm, nullspdim, basisu)
        else:
            ## Otherwise, always fit data with one particular circle type (great or small)
            self.output = self.fit_with_subspheres(currentSphere, dm,
                                                   nullspdim, basisu)
Example #4
0
 def integrability_tensor_test_data(self):
     space = PreShapeSpace(4, 3)
     vector = gs.random.rand(2, 4, 3)
     base_point = space.random_point()
     random_data = [
         dict(
             k_landmarks=4,
             m_ambient=3,
             tangent_vec_a=space.to_tangent(vector[0], base_point),
             tangent_vec_b=space.to_tangent(vector[1], base_point),
             base_point=base_point,
         )
     ]
     return self.generate_tests(random_data)
    def setUp(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()

        plt.figure()
Example #6
0
 def vertical_projection_test_data(self):
     vector = gs.random.rand(10, 4, 3)
     space = PreShapeSpace(4, 3)
     point = space.random_point()
     smoke_data = [
         dict(
             k_landmarks=4,
             m_ambient=3,
             tangent_vec=space.to_tangent(vector[0], point),
             point=point,
         ),
         dict(
             k_landmarks=4,
             m_ambient=3,
             tangent_vec=space.to_tangent(vector, point),
             point=point,
         ),
     ]
     return self.generate_tests(smoke_data)
Example #7
0
 def alignment_is_symmetric_test_data(self):
     space = PreShapeSpace(4, 3)
     random_data = [
         dict(
             k_landmarks=4,
             m_ambient=3,
             point=space.random_point(),
             base_point=space.random_point(),
         ),
         dict(
             k_landmarks=4,
             m_ambient=3,
             point=space.random_point(),
             base_point=space.random_point(2),
         ),
         dict(
             k_landmarks=4,
             m_ambient=3,
             point=space.random_point(2),
             base_point=space.random_point(2),
         ),
     ]
     return self.generate_tests([], random_data)
Example #8
0
    def parallel_transport_test_data(self):
        k_landmarks = 4
        m_ambient = 3
        n_samples = 10
        space = PreShapeSpace(4, 3)
        base_point = space.projection(gs.eye(4)[:, :3])
        vec_a = gs.random.rand(n_samples, k_landmarks, m_ambient)
        tangent_vec_a = space.to_tangent(space.center(vec_a), base_point)

        vec_b = gs.random.rand(n_samples, k_landmarks, m_ambient)
        tangent_vec_b = space.to_tangent(space.center(vec_b), base_point)
        smoke_data = [
            dict(
                k_landmarks=k_landmarks,
                m_ambient=m_ambient,
                tangent_vec_a=tangent_vec_a,
                tangent_vec_b=tangent_vec_b,
                base_point=base_point,
            )
        ]
        return self.generate_tests(smoke_data)
Example #9
0
class TestPreShapeSpace(geomstats.tests.TestCase):
    def setUp(self):
        gs.random.seed(1234)

        self.k_landmarks = 4
        self.m_ambient = 3
        self.space = PreShapeSpace(self.k_landmarks, self.m_ambient)
        self.matrices = self.space.embedding_manifold
        self.n_samples = 10
        self.shape_metric = KendallShapeMetric(self.k_landmarks,
                                               self.m_ambient)

    def test_belongs(self):
        point = gs.random.rand(self.m_ambient - 1, self.k_landmarks)
        result = self.space.belongs(point)
        self.assertFalse(result)

        point = gs.random.rand(self.n_samples, self.m_ambient - 1,
                               self.k_landmarks)
        result = self.space.belongs(point)
        self.assertFalse(gs.all(result))

    def test_random_point_and_belongs(self):
        """Test random uniform and belongs.

        Test that the random uniform method samples
        on the pre-shape space.
        """
        n_samples = self.n_samples
        point = self.space.random_point(n_samples)
        result = self.space.belongs(point)
        expected = gs.array([True] * n_samples)

        self.assertAllClose(expected, result)

    def test_random_point_shape(self):
        point = self.space.random_point()
        result = gs.shape(point)
        expected = (
            self.k_landmarks,
            self.m_ambient,
        )

        self.assertAllClose(result, expected)

        point = self.space.random_point(self.n_samples)
        result = gs.shape(point)
        expected = (
            self.n_samples,
            self.k_landmarks,
            self.m_ambient,
        )
        self.assertAllClose(result, expected)

    def test_projection_and_belongs(self):
        point = Matrices.transpose(
            gs.array([[1., 0., 0., 1.], [0., 1., 0., 1.], [0., 0., 1., 1.]]))
        proj = self.space.projection(point)
        result = self.space.belongs(proj)
        expected = True

        self.assertAllClose(expected, result)

    def test_is_centered(self):
        point = gs.ones((self.k_landmarks, self.m_ambient))
        result = self.space.is_centered(point)
        self.assertFalse(result)

        point = gs.zeros((self.k_landmarks, self.m_ambient))
        result = self.space.is_centered(point)
        self.assertTrue(result)

    def test_to_center_is_center(self):
        point = gs.ones((self.k_landmarks, self.m_ambient))
        point = self.space.center(point)
        result = self.space.is_centered(point)
        self.assertTrue(result)

    def test_to_center_is_centered_vectorization(self):
        point = gs.ones((self.n_samples, self.k_landmarks, self.m_ambient))
        point = self.space.center(point)
        result = gs.all(self.space.is_centered(point))
        self.assertTrue(result)

    def test_is_tangent_to_tangent(self):
        point, vector = self.matrices.random_point(2)
        point = self.space.projection(point)

        result = self.space.is_tangent(vector, point)
        self.assertFalse(result)

        tangent_vec = self.space.to_tangent(vector, point)
        result = self.space.is_tangent(tangent_vec, point)
        self.assertTrue(result)

        vec = gs.array([tangent_vec, vector])
        result = self.space.is_tangent(vec, point)
        expected = gs.array([True, False])
        self.assertAllClose(result, expected)

    def test_vertical_projection(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        vertical = self.space.vertical_projection(tan, point)
        transposed_point = Matrices.transpose(point)

        tmp_expected = gs.matmul(transposed_point, tan)
        expected = Matrices.transpose(tmp_expected) - tmp_expected

        tmp_result = gs.matmul(transposed_point, vertical)
        result = Matrices.transpose(tmp_result) - tmp_result
        self.assertAllClose(result, expected)

    def test_vertical_projection_vectorization(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point(self.n_samples)
        tan = self.space.to_tangent(vector, point)
        vertical = self.space.vertical_projection(tan, point)
        transposed_point = Matrices.transpose(point)

        tmp_expected = gs.matmul(transposed_point, tan)
        expected = Matrices.transpose(tmp_expected) - tmp_expected

        tmp_result = gs.matmul(transposed_point, vertical)
        result = Matrices.transpose(tmp_result) - tmp_result
        self.assertAllClose(result, expected)

    def test_horizontal_projection(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        horizontal = self.space.horizontal_projection(tan, point)
        transposed_point = Matrices.transpose(point)
        result = gs.matmul(transposed_point, horizontal)
        expected = Matrices.transpose(result)

        self.assertAllClose(result, expected)

    def test_horizontal_projection_vectorized(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point(self.n_samples)
        tan = self.space.to_tangent(vector, point)
        horizontal = self.space.horizontal_projection(tan, point)
        transposed_point = Matrices.transpose(point)
        result = gs.matmul(transposed_point, horizontal)
        expected = Matrices.transpose(result)

        self.assertAllClose(result, expected)

    def test_horizontal_and_is_tangent(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        horizontal = self.space.horizontal_projection(tan, point)

        horizontal = gs.stack([horizontal, vector])
        result = self.space.is_tangent(horizontal, point)
        expected = gs.array([True, False])

        self.assertAllClose(result, expected)

    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)

    def test_align_vectorization(self):
        base_point = self.space.random_point()
        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(gs.all(result))

        base_point = self.space.random_point(2)
        point = self.space.random_point()
        aligned = self.space.align(point, base_point)
        alignment = gs.matmul(Matrices.transpose(aligned), base_point)
        result = Matrices.is_symmetric(alignment)
        self.assertTrue(gs.all(result))

    def test_inner_product_shape(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        inner = self.space.ambient_metric.inner_product(tan, tan, point)
        self.assertAllClose(inner.shape, (self.n_samples, ))

    def test_exp_and_belongs(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        exp = self.space.ambient_metric.exp(tan, point)
        result = self.space.belongs(exp)
        self.assertTrue(result)

        exp = self.space.ambient_metric.exp(gs.zeros_like(point), point)
        result = gs.isclose(point, exp)
        self.assertTrue(gs.all(result))

    def test_exp_and_belongs_vectorization(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point(self.n_samples)
        tan = self.space.to_tangent(vector, point)
        exp = self.space.ambient_metric.exp(tan, point)
        result = self.space.belongs(exp)
        self.assertTrue(gs.all(result))

        point = point[0]
        tan = self.space.to_tangent(vector, point)
        exp = self.space.ambient_metric.exp(tan, point)
        result = self.space.belongs(exp)
        self.assertTrue(gs.all(result))

    def test_log_and_exp(self):
        point, base_point = self.space.random_point(2)
        log = self.space.ambient_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(result)

        exp = self.space.ambient_metric.exp(log, base_point)
        self.assertAllClose(exp, point)

    def test_exp_and_log(self):
        base_point = self.space.random_point()
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        tangent_vec = self.space.to_tangent(vector, base_point)
        point = self.space.ambient_metric.exp(tangent_vec, base_point)
        log = self.space.ambient_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(result)

        self.assertAllClose(tangent_vec, log)

    def test_log_vectorization(self):
        point = self.space.random_point(self.n_samples)
        base_point = self.space.random_point()
        log = self.space.ambient_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(gs.all(result))

        exp = self.space.ambient_metric.exp(log, base_point)
        self.assertAllClose(exp, point)

        log = self.space.ambient_metric.log(base_point, point)
        result = self.space.is_tangent(log, point)
        self.assertTrue(gs.all(result))

        exp = self.space.ambient_metric.exp(log, point)
        expected = gs.stack([base_point] * self.n_samples)
        self.assertAllClose(exp, expected)

    def test_kendall_inner_product_shape(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        inner = self.shape_metric.inner_product(tan, tan, point)
        self.assertAllClose(inner.shape, (self.n_samples, ))

    def test_kendall_log_and_exp(self):
        point, base_point = self.space.random_point(2)
        expected = self.space.align(point, base_point)
        log = self.shape_metric.log(expected, base_point)
        result = self.space.is_horizontal(log, base_point)
        self.assertTrue(result)

        exp = self.shape_metric.exp(log, base_point)
        self.assertAllClose(exp, expected)

    def test_kendall_exp_and_log(self):
        base_point = self.space.random_point()
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        tangent_vec = self.space.to_tangent(vector, base_point)
        point = self.shape_metric.exp(tangent_vec, base_point)
        log = self.shape_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(result)

        expected = self.space.horizontal_projection(tangent_vec, base_point)
        self.assertAllClose(expected, log)

    def test_dist_extreme_case(self):
        point = self.space.projection(gs.eye(self.k_landmarks, self.m_ambient))
        result = self.shape_metric.dist(point, point)
        expected = 0.
        self.assertAllClose(result, expected)

    def test_dist(self):
        point, base_point = self.space.random_point(2)
        result = self.shape_metric.dist(point, base_point)
        log = self.shape_metric.log(point, base_point)
        expected = self.shape_metric.norm(log, base_point)
        self.assertAllClose(result, expected)

    def test_dist_vectorization(self):
        point = self.space.random_point(self.n_samples)
        base_point = self.space.random_point(self.n_samples)
        aligned = self.space.align(point, base_point)
        result = self.shape_metric.dist(aligned, base_point)
        log = self.shape_metric.log(aligned, base_point)
        expected = self.shape_metric.norm(log, base_point)
        self.assertAllClose(result, expected)

    def test_curvature_is_skew_operator(self):
        space = self.space
        base_point = space.random_point(2)
        vector = gs.random.rand(4, self.k_landmarks, self.m_ambient)
        tangent_vec_a = space.to_tangent(vector[:2], base_point)
        tangent_vec_b = space.to_tangent(vector[2:], base_point)

        result = self.shape_metric.curvature(tangent_vec_a, tangent_vec_a,
                                             tangent_vec_b, base_point)
        expected = gs.zeros_like(result)
        self.assertAllClose(result, expected)

    def test_curvature_bianchi_identity(self):
        space = self.space
        base_point = space.random_point()
        vector = gs.random.rand(3, self.k_landmarks, self.m_ambient)
        tangent_vec_a = space.to_tangent(vector[0], base_point)
        tangent_vec_b = space.to_tangent(vector[1], base_point)
        tangent_vec_c = space.to_tangent(vector[2], base_point)

        curvature_1 = self.shape_metric.curvature(tangent_vec_a, tangent_vec_b,
                                                  tangent_vec_c, base_point)
        curvature_2 = self.shape_metric.curvature(tangent_vec_b, tangent_vec_c,
                                                  tangent_vec_a, base_point)
        curvature_3 = self.shape_metric.curvature(tangent_vec_c, tangent_vec_a,
                                                  tangent_vec_b, base_point)

        result = curvature_1 + curvature_2 + curvature_3
        expected = gs.zeros_like(result)
        self.assertAllClose(result, expected)

    def test_integrability_tensor(self):
        space = self.space
        base_point = space.random_point()
        vector = gs.random.rand(2, self.k_landmarks, self.m_ambient)
        tangent_vec_a = space.to_tangent(vector[0], base_point)
        tangent_vec_b = space.to_tangent(vector[1], base_point)
        result_ab = space.integrability_tensor(tangent_vec_a, tangent_vec_b,
                                               base_point)

        result = space.ambient_metric.inner_product(tangent_vec_b, result_ab,
                                                    base_point)
        expected = 0.
        self.assertAllClose(result, expected)

        horizontal_b = space.horizontal_projection(tangent_vec_b, base_point)
        horizontal_a = space.horizontal_projection(tangent_vec_a, base_point)
        result = space.integrability_tensor(horizontal_a, horizontal_b,
                                            base_point)
        expected = -space.integrability_tensor(horizontal_b, horizontal_a,
                                               base_point)
        self.assertAllClose(result, expected)

        is_vertical = space.is_vertical(result, base_point)
        self.assertTrue(is_vertical)

        vertical_b = tangent_vec_b - horizontal_b
        result = space.integrability_tensor(horizontal_a, vertical_b,
                                            base_point)
        is_horizontal = space.is_horizontal(result, base_point)
        self.assertTrue(is_horizontal)

    def test_kendall_directional_curvature(self):
        space = self.space
        kendall = self.shape_metric
        n_samples = 4 * self.k_landmarks * self.m_ambient
        base_point = self.space.random_point(1)

        vec_a = gs.random.rand(n_samples, self.k_landmarks, self.m_ambient)
        tg_vec_a = space.to_tangent(space.center(vec_a), base_point)
        hor_a = space.horizontal_projection(tg_vec_a, base_point)

        vec_b = gs.random.rand(n_samples, self.k_landmarks, self.m_ambient)
        tg_vec_b = space.to_tangent(space.center(vec_b), base_point)
        hor_b = space.horizontal_projection(tg_vec_b, base_point)

        tidal = kendall.directional_curvature(hor_a, hor_b, base_point)

        numerator = kendall.inner_product(tidal, hor_b, base_point)
        denominator = \
            kendall.inner_product(hor_a, hor_a, base_point) * \
            kendall.inner_product(hor_b, hor_b, base_point) - \
            kendall.inner_product(hor_a, hor_b, base_point) ** 2
        condition = ~gs.isclose(denominator, 0.)
        kappa = numerator[condition] / denominator[condition]
        kappa_direct = \
            kendall.sectional_curvature(hor_a, hor_b, base_point)[condition]
        self.assertAllClose(kappa, kappa_direct)
        result = (kappa > 1.0 - 1e-12)
        self.assertTrue(gs.all(result))

    def test_parallel_transport(self):
        space = self.space
        metric = self.shape_metric
        shape = (self.n_samples, self.k_landmarks, self.m_ambient)

        point = space.projection(gs.eye(4)[:, :3])
        tan_b = gs.random.rand(*shape)
        tan_b = space.to_tangent(tan_b, point)
        tan_b = space.horizontal_projection(tan_b, point)

        # use a vector orthonormal to tan_b
        tan_a = gs.random.rand(*shape)
        tan_a = space.to_tangent(tan_a, point)
        tan_a = space.horizontal_projection(tan_a, point)

        # orthonormalize and move to base_point
        tan_a -= gs.einsum(
            '...,...ij->...ij',
            metric.inner_product(tan_a, tan_b, point) /
            metric.squared_norm(tan_b, point), tan_b)
        tan_b = gs.einsum('...ij,...->...ij', tan_b,
                          1. / metric.norm(tan_b, point))
        tan_a = gs.einsum('...ij,...->...ij', tan_a,
                          1. / metric.norm(tan_a, point))

        transported = metric.parallel_transport(tan_a,
                                                tan_b,
                                                point,
                                                n_steps=100,
                                                step='rk4')
        end_point = metric.exp(tan_b, point)
        result = metric.norm(transported, end_point)
        expected = metric.norm(tan_a, point)
        self.assertAllClose(result, expected)

        is_tangent = space.is_tangent(transported, end_point)
        is_horizontal = space.is_horizontal(transported, end_point)
        self.assertTrue(gs.all(is_tangent))
        self.assertTrue(gs.all(is_horizontal))
Example #10
0
from geomstats.geometry.poincare_half_space import PoincareHalfSpace
from geomstats.geometry.pre_shape import KendallShapeMetric, PreShapeSpace
from geomstats.geometry.special_euclidean import SpecialEuclidean
from geomstats.geometry.special_orthogonal import SpecialOrthogonal
from mpl_toolkits.mplot3d import Axes3D  # NOQA

SE3_GROUP = SpecialEuclidean(n=3, point_type='vector')
SE2_GROUP = SpecialEuclidean(n=2, point_type='matrix')
SE2_VECT = SpecialEuclidean(n=2, point_type='vector')
SO3_GROUP = SpecialOrthogonal(n=3, point_type='vector')
S1 = Hypersphere(dim=1)
S2 = Hypersphere(dim=2)
H2 = Hyperboloid(dim=2)
POINCARE_HALF_PLANE = PoincareHalfSpace(dim=2)
M32 = Matrices(m=3, n=2)
S32 = PreShapeSpace(k_landmarks=3, m_ambient=2)
METRIC_S32 = KendallShapeMetric(k_landmarks=3, m_ambient=2)
M33 = Matrices(m=3, n=3)
S33 = PreShapeSpace(k_landmarks=3, m_ambient=3)
METRIC_S33 = KendallShapeMetric(k_landmarks=3, m_ambient=3)

AX_SCALE = 1.2

IMPLEMENTED = [
    'SO3_GROUP', 'SE3_GROUP', 'SE2_GROUP', 'S1', 'S2', 'H2_poincare_disk',
    'H2_poincare_half_plane', 'H2_klein_disk', 'poincare_polydisk', 'S32',
    'M32', 'S33', 'M33'
]


def tutorial_matplotlib():
Example #11
0
class PreShapeMetricTestData(_RiemannianMetricTestData):
    k_landmarks_list = random.sample(range(3, 6), 2)
    m_ambient_list = [random.sample(range(2, n), 1)[0] for n in k_landmarks_list]
    metric_args_list = list(zip(k_landmarks_list, m_ambient_list))

    shape_list = metric_args_list
    space_list = [PreShapeSpace(k, m) for k, m in metric_args_list]
    n_points_list = random.sample(range(1, 7), 2)
    n_samples_list = random.sample(range(1, 7), 2)
    n_points_a_list = random.sample(range(1, 7), 2)
    n_points_b_list = [1]
    batch_size_list = random.sample(range(2, 7), 2)
    n_tangent_vecs_list = random.sample(range(2, 7), 2)
    alpha_list = [1] * 2
    n_rungs_list = [1] * 2
    scheme_list = ["pole"] * 2

    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_samples_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_samples_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 exp_after_log_test_data(self):
        return self._exp_after_log_test_data(
            self.metric_args_list,
            self.space_list,
            self.n_samples_list,
            rtol=gs.rtol * 100,
            atol=1e-4,
        )

    def log_after_exp_test_data(self):
        return self._log_after_exp_test_data(
            self.metric_args_list,
            self.space_list,
            self.shape_list,
            self.n_samples_list,
            rtol=gs.rtol * 100,
            atol=1e-2,
        )

    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_samples_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_samples_list,
            self.n_points_list,
            rtol=gs.rtol * 10000,
            atol=gs.atol * 10000,
        )

    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_samples_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_samples_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 triangle_inequality_of_dist_test_data(self):
        return self._triangle_inequality_of_dist_test_data(
            self.metric_args_list, self.space_list, self.n_points_list
        )
Example #12
0
import random

import geomstats.backend as gs
from geomstats.geometry.pre_shape import PreShapeSpace
from tests.data_generation import _LevelSetTestData, _RiemannianMetricTestData

smoke_space = PreShapeSpace(4, 3)
vector = gs.random.rand(11, 4, 3)
base_point = smoke_space.random_point()
tg_vec_0 = smoke_space.to_tangent(vector[0], base_point)
hor_x = smoke_space.horizontal_projection(tg_vec_0, base_point)
tg_vec_1 = smoke_space.to_tangent(vector[1], base_point)
hor_y = smoke_space.horizontal_projection(tg_vec_1, base_point)
tg_vec_2 = smoke_space.to_tangent(vector[2], base_point)
hor_z = smoke_space.horizontal_projection(tg_vec_2, base_point)
tg_vec_3 = smoke_space.to_tangent(vector[3], base_point)
hor_h = smoke_space.horizontal_projection(tg_vec_3, base_point)
tg_vec_4 = smoke_space.to_tangent(vector[4], base_point)
ver_v = smoke_space.vertical_projection(tg_vec_4, base_point)
tg_vec_5 = smoke_space.to_tangent(vector[5], base_point)
ver_w = smoke_space.vertical_projection(tg_vec_5, base_point)
tg_vec_6 = smoke_space.to_tangent(vector[6], base_point)
hor_dy = smoke_space.horizontal_projection(tg_vec_6, base_point)
tg_vec_7 = smoke_space.to_tangent(vector[7], base_point)
hor_dz = smoke_space.horizontal_projection(tg_vec_7, base_point)
tg_vec_8 = smoke_space.to_tangent(vector[8], base_point)
ver_dv = smoke_space.vertical_projection(tg_vec_8, base_point)
tg_vec_9 = smoke_space.to_tangent(vector[9], base_point)
ver_dw = smoke_space.vertical_projection(tg_vec_9, base_point)
tg_vec_10 = smoke_space.to_tangent(vector[10], base_point)
hor_dh = smoke_space.horizontal_projection(tg_vec_10, base_point)
Example #13
0
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()
Example #14
0
class TestPreShapeSpace(geomstats.tests.TestCase):
    def setUp(self):
        gs.random.seed(1234)

        self.k_landmarks = 4
        self.m_ambient = 3
        self.space = PreShapeSpace(self.k_landmarks, self.m_ambient)
        self.matrices = self.space.embedding_manifold
        self.n_samples = 10
        self.shape_metric = KendallShapeMetric(self.k_landmarks,
                                               self.m_ambient)

    def test_belongs(self):
        point = gs.random.rand(self.m_ambient - 1, self.k_landmarks)
        result = self.space.belongs(point)
        self.assertFalse(result)

        point = gs.random.rand(self.n_samples, self.m_ambient - 1,
                               self.k_landmarks)
        result = self.space.belongs(point)
        self.assertFalse(gs.all(result))

    def test_random_point_and_belongs(self):
        """Test random uniform and belongs.

        Test that the random uniform method samples
        on the pre-shape space.
        """
        n_samples = self.n_samples
        point = self.space.random_point(n_samples)
        result = self.space.belongs(point)
        expected = gs.array([True] * n_samples)

        self.assertAllClose(expected, result)

    def test_random_point_shape(self):
        point = self.space.random_point()
        result = gs.shape(point)
        expected = (
            self.k_landmarks,
            self.m_ambient,
        )

        self.assertAllClose(result, expected)

        point = self.space.random_point(self.n_samples)
        result = gs.shape(point)
        expected = (
            self.n_samples,
            self.k_landmarks,
            self.m_ambient,
        )
        self.assertAllClose(result, expected)

    def test_projection_and_belongs(self):
        point = Matrices.transpose(
            gs.array([[1., 0., 0., 1.], [0., 1., 0., 1.], [0., 0., 1., 1.]]))
        proj = self.space.projection(point)
        result = self.space.belongs(proj)
        expected = True

        self.assertAllClose(expected, result)

    def test_is_centered(self):
        point = gs.ones((self.k_landmarks, self.m_ambient))
        result = self.space.is_centered(point)
        self.assertFalse(result)

        point = gs.zeros((self.k_landmarks, self.m_ambient))
        result = self.space.is_centered(point)
        self.assertTrue(result)

    def test_to_center_is_center(self):
        point = gs.ones((self.k_landmarks, self.m_ambient))
        point = self.space.center(point)
        result = self.space.is_centered(point)
        self.assertTrue(result)

    def test_to_center_is_centered_vectorization(self):
        point = gs.ones((self.n_samples, self.k_landmarks, self.m_ambient))
        point = self.space.center(point)
        result = gs.all(self.space.is_centered(point))
        self.assertTrue(result)

    def test_is_tangent_to_tangent(self):
        point, vector = self.matrices.random_point(2)
        point = self.space.projection(point)

        result = self.space.is_tangent(vector, point)
        self.assertFalse(result)

        tangent_vec = self.space.to_tangent(vector, point)
        result = self.space.is_tangent(tangent_vec, point)
        self.assertTrue(result)

        vec = gs.array([tangent_vec, vector])
        result = self.space.is_tangent(vec, point)
        expected = gs.array([True, False])
        self.assertAllClose(result, expected)

    def test_vertical_projection(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        vertical = self.space.vertical_projection(tan, point)
        transposed_point = Matrices.transpose(point)

        tmp_expected = gs.matmul(transposed_point, tan)
        expected = Matrices.transpose(tmp_expected) - tmp_expected

        tmp_result = gs.matmul(transposed_point, vertical)
        result = Matrices.transpose(tmp_result) - tmp_result
        self.assertAllClose(result, expected)

    def test_vertical_projection_vectorization(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point(self.n_samples)
        tan = self.space.to_tangent(vector, point)
        vertical = self.space.vertical_projection(tan, point)
        transposed_point = Matrices.transpose(point)

        tmp_expected = gs.matmul(transposed_point, tan)
        expected = Matrices.transpose(tmp_expected) - tmp_expected

        tmp_result = gs.matmul(transposed_point, vertical)
        result = Matrices.transpose(tmp_result) - tmp_result
        self.assertAllClose(result, expected)

    def test_horizontal_projection(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        horizontal = self.space.horizontal_projection(tan, point)
        transposed_point = Matrices.transpose(point)
        result = gs.matmul(transposed_point, horizontal)
        expected = Matrices.transpose(result)

        self.assertAllClose(result, expected)

    def test_horizontal_projection_vectorized(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point(self.n_samples)
        tan = self.space.to_tangent(vector, point)
        horizontal = self.space.horizontal_projection(tan, point)
        transposed_point = Matrices.transpose(point)
        result = gs.matmul(transposed_point, horizontal)
        expected = Matrices.transpose(result)

        self.assertAllClose(result, expected)

    def test_horizontal_and_is_tangent(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        horizontal = self.space.horizontal_projection(tan, point)

        horizontal = gs.stack([horizontal, vector])
        result = self.space.is_tangent(horizontal, point)
        expected = gs.array([True, False])

        self.assertAllClose(result, expected)

    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)

    def test_align_vectorization(self):
        base_point = self.space.random_point()
        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(gs.all(result))

        base_point = self.space.random_point(2)
        point = self.space.random_point()
        aligned = self.space.align(point, base_point)
        alignment = gs.matmul(Matrices.transpose(aligned), base_point)
        result = Matrices.is_symmetric(alignment)
        self.assertTrue(gs.all(result))

    def test_inner_product_shape(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        inner = self.space.ambient_metric.inner_product(tan, tan, point)
        self.assertAllClose(inner.shape, (self.n_samples, ))

    def test_exp_and_belongs(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        exp = self.space.ambient_metric.exp(tan, point)
        result = self.space.belongs(exp)
        self.assertTrue(result)

        exp = self.space.ambient_metric.exp(gs.zeros_like(point), point)
        result = gs.isclose(point, exp)
        self.assertTrue(gs.all(result))

    def test_exp_and_belongs_vectorization(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point(self.n_samples)
        tan = self.space.to_tangent(vector, point)
        exp = self.space.ambient_metric.exp(tan, point)
        result = self.space.belongs(exp)
        self.assertTrue(gs.all(result))

        point = point[0]
        tan = self.space.to_tangent(vector, point)
        exp = self.space.ambient_metric.exp(tan, point)
        result = self.space.belongs(exp)
        self.assertTrue(gs.all(result))

    def test_log_and_exp(self):
        point, base_point = self.space.random_point(2)
        log = self.space.ambient_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(result)

        exp = self.space.ambient_metric.exp(log, base_point)
        self.assertAllClose(exp, point)

    def test_exp_and_log(self):
        base_point = self.space.random_point()
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        tangent_vec = self.space.to_tangent(vector, base_point)
        point = self.space.ambient_metric.exp(tangent_vec, base_point)
        log = self.space.ambient_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(result)

        self.assertAllClose(tangent_vec, log)

    def test_log_vectorization(self):
        point = self.space.random_point(self.n_samples)
        base_point = self.space.random_point()
        log = self.space.ambient_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(gs.all(result))

        exp = self.space.ambient_metric.exp(log, base_point)
        self.assertAllClose(exp, point)

        log = self.space.ambient_metric.log(base_point, point)
        result = self.space.is_tangent(log, point)
        self.assertTrue(gs.all(result))

        exp = self.space.ambient_metric.exp(log, point)
        expected = gs.stack([base_point] * self.n_samples)
        self.assertAllClose(exp, expected)

    def test_kendall_inner_product_shape(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        inner = self.shape_metric.inner_product(tan, tan, point)
        self.assertAllClose(inner.shape, (self.n_samples, ))

    def test_kendall_log_and_exp(self):
        point, base_point = self.space.random_point(2)
        expected = self.space.align(point, base_point)
        log = self.shape_metric.log(expected, base_point)
        result = self.space.is_horizontal(log, base_point)
        self.assertTrue(result)

        exp = self.shape_metric.exp(log, base_point)
        self.assertAllClose(exp, expected)

    def test_kendall_exp_and_log(self):
        base_point = self.space.random_point()
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        tangent_vec = self.space.to_tangent(vector, base_point)
        point = self.shape_metric.exp(tangent_vec, base_point)
        log = self.shape_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(result)

        expected = self.space.horizontal_projection(tangent_vec, base_point)
        self.assertAllClose(expected, log)

    def test_dist_extreme_case(self):
        point = self.space.projection(gs.eye(self.k_landmarks, self.m_ambient))
        result = self.shape_metric.dist(point, point)
        expected = 0.
        self.assertAllClose(result, expected)

    def test_dist(self):
        point, base_point = self.space.random_point(2)
        result = self.shape_metric.dist(point, base_point)
        log = self.shape_metric.log(point, base_point)
        expected = self.shape_metric.norm(log, base_point)
        self.assertAllClose(result, expected)

    def test_dist_vectorization(self):
        point = self.space.random_point(self.n_samples)
        base_point = self.space.random_point(self.n_samples)
        aligned = self.space.align(point, base_point)
        result = self.shape_metric.dist(aligned, base_point)
        log = self.shape_metric.log(aligned, base_point)
        expected = self.shape_metric.norm(log, base_point)
        self.assertAllClose(result, expected)

    def test_curvature_is_skew_operator(self):
        space = self.space
        base_point = space.random_point(2)
        vector = gs.random.rand(4, self.k_landmarks, self.m_ambient)
        tangent_vec_a = space.to_tangent(vector[:2], base_point)
        tangent_vec_b = space.to_tangent(vector[2:], base_point)

        result = self.shape_metric.curvature(tangent_vec_a, tangent_vec_a,
                                             tangent_vec_b, base_point)
        expected = gs.zeros_like(result)
        self.assertAllClose(result, expected)

    def test_curvature_bianchi_identity(self):
        space = self.space
        base_point = space.random_point()
        vector = gs.random.rand(3, self.k_landmarks, self.m_ambient)
        tangent_vec_a = space.to_tangent(vector[0], base_point)
        tangent_vec_b = space.to_tangent(vector[1], base_point)
        tangent_vec_c = space.to_tangent(vector[2], base_point)

        curvature_1 = self.shape_metric.curvature(tangent_vec_a, tangent_vec_b,
                                                  tangent_vec_c, base_point)
        curvature_2 = self.shape_metric.curvature(tangent_vec_b, tangent_vec_c,
                                                  tangent_vec_a, base_point)
        curvature_3 = self.shape_metric.curvature(tangent_vec_c, tangent_vec_a,
                                                  tangent_vec_b, base_point)

        result = curvature_1 + curvature_2 + curvature_3
        expected = gs.zeros_like(result)
        self.assertAllClose(result, expected)

    def test_integrability_tensor(self):
        space = self.space
        base_point = space.random_point()
        vector = gs.random.rand(2, self.k_landmarks, self.m_ambient)
        tangent_vec_a = space.to_tangent(vector[0], base_point)
        tangent_vec_b = space.to_tangent(vector[1], base_point)
        result_ab = space.integrability_tensor(tangent_vec_a, tangent_vec_b,
                                               base_point)

        result = space.ambient_metric.inner_product(tangent_vec_b, result_ab,
                                                    base_point)
        expected = 0.
        self.assertAllClose(result, expected)

        horizontal_b = space.horizontal_projection(tangent_vec_b, base_point)
        horizontal_a = space.horizontal_projection(tangent_vec_a, base_point)
        result = space.integrability_tensor(horizontal_a, horizontal_b,
                                            base_point)
        expected = -space.integrability_tensor(horizontal_b, horizontal_a,
                                               base_point)
        self.assertAllClose(result, expected)

        is_vertical = space.is_vertical(result, base_point)
        self.assertTrue(is_vertical)

        vertical_b = tangent_vec_b - horizontal_b
        result = space.integrability_tensor(horizontal_a, vertical_b,
                                            base_point)
        is_horizontal = space.is_horizontal(result, base_point)
        self.assertTrue(is_horizontal)
class TestVisualization(geomstats.tests.TestCase):
    def setUp(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()

        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., 1., 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 = .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 = .5 * tangent_vec / self.S33.ambient_metric.norm(
            tangent_vec)
        times = gs.linspace(0., 1., 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. for r in radius]
        self.assertTrue(gs.all(result))

    @geomstats.tests.np_and_pytorch_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(ax)
Example #16
0
class KendallShapeMetricTestData(_RiemannianMetricTestData):
    k_landmarks_list = random.sample(range(3, 6), 2)
    m_ambient_list = [random.sample(range(2, n), 1)[0] for n in k_landmarks_list]
    metric_args_list = list(zip(k_landmarks_list, m_ambient_list))

    shape_list = metric_args_list
    space_list = [PreShapeSpace(k, m) for k, m in metric_args_list]
    n_points_list = random.sample(range(1, 4), 2)
    n_samples_list = random.sample(range(1, 4), 2)
    n_points_a_list = random.sample(range(1, 4), 2)
    n_points_b_list = [1]
    n_tangent_vecs_list = random.sample(range(1, 4), 2)
    batch_size_list = random.sample(range(2, 4), 2)
    alpha_list = [1] * 2
    n_rungs_list = [1] * 2
    scheme_list = ["pole"] * 2

    def curvature_is_skew_operator_test_data(self):
        base_point = smoke_space.random_point(2)
        vec = gs.random.rand(4, 4, 3)
        smoke_data = [dict(k_landmarks=4, m_ambient=3, vec=vec, base_point=base_point)]
        return self.generate_tests(smoke_data)

    def curvature_bianchi_identity_test_data(self):
        smoke_data = [
            dict(
                k_landmarks=4,
                m_ambient=3,
                tangent_vec_a=tg_vec_0,
                tangent_vec_b=tg_vec_1,
                tangent_vec_cs=tg_vec_2,
                base_point=base_point,
            )
        ]
        return self.generate_tests(smoke_data)

    def kendall_sectional_curvature_test_data(self):
        k_landmarks = 4
        m_ambient = 3
        space = smoke_space
        n_samples = 4 * k_landmarks * m_ambient
        base_point = space.random_point(1)

        vec_a = gs.random.rand(n_samples, k_landmarks, m_ambient)
        tg_vec_a = space.to_tangent(space.center(vec_a), base_point)

        vec_b = gs.random.rand(n_samples, k_landmarks, m_ambient)
        tg_vec_b = space.to_tangent(space.center(vec_b), base_point)

        smoke_data = [
            dict(
                k_landmarks=4,
                m_ambient=3,
                tangent_vec_a=tg_vec_a,
                tangent_vec_b=tg_vec_b,
                base_point=base_point,
            )
        ]
        return self.generate_tests(smoke_data)

    def kendall_curvature_derivative_bianchi_identity_test_data(self):
        smoke_data = [
            dict(
                k_landmarks=4,
                m_ambient=3,
                hor_x=hor_x,
                hor_y=hor_y,
                hor_z=hor_z,
                hor_h=hor_h,
                base_point=base_point,
            )
        ]
        return self.generate_tests(smoke_data)

    def curvature_derivative_is_skew_operator_test_data(self):
        smoke_data = [
            dict(
                k_landmarks=4,
                m_ambient=3,
                hor_x=hor_x,
                hor_y=hor_y,
                hor_z=hor_z,
                base_point=base_point,
            )
        ]
        return self.generate_tests(smoke_data)

    def directional_curvature_derivative_test_data(self):
        smoke_data = [
            dict(
                k_landmarks=4,
                m_ambient=3,
                hor_x=hor_x,
                hor_y=hor_y,
                base_point=base_point,
            )
        ]
        return self.generate_tests(smoke_data)

    def directional_curvature_derivative_is_quadratic_test_data(self):
        coef_x = -2.5
        coef_y = 1.5
        smoke_data = [
            dict(
                k_landmarks=4,
                m_ambient=3,
                coef_x=coef_x,
                coef_y=coef_y,
                hor_x=hor_x,
                hor_y=hor_y,
                base_point=base_point,
            )
        ]
        return self.generate_tests(smoke_data)

    def parallel_transport_test_data(self):
        k_landmarks = 4
        m_ambient = 3
        n_samples = 10
        space = PreShapeSpace(4, 3)
        base_point = space.projection(gs.eye(4)[:, :3])
        vec_a = gs.random.rand(n_samples, k_landmarks, m_ambient)
        tangent_vec_a = space.to_tangent(space.center(vec_a), base_point)

        vec_b = gs.random.rand(n_samples, k_landmarks, m_ambient)
        tangent_vec_b = space.to_tangent(space.center(vec_b), base_point)
        smoke_data = [
            dict(
                k_landmarks=k_landmarks,
                m_ambient=m_ambient,
                tangent_vec_a=tangent_vec_a,
                tangent_vec_b=tangent_vec_b,
                base_point=base_point,
            )
        ]
        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_samples_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_samples_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 exp_after_log_test_data(self):
        return self._exp_after_log_test_data(
            self.metric_args_list,
            self.space_list,
            self.n_samples_list,
            rtol=gs.rtol * 100,
            atol=gs.atol * 10000,
        )

    def log_after_exp_test_data(self):
        return self._log_after_exp_test_data(
            self.metric_args_list,
            self.space_list,
            self.shape_list,
            self.n_samples_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_samples_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_samples_list,
            self.n_points_list,
            rtol=gs.rtol * 10000,
            atol=gs.atol * 10000,
        )

    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_samples_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_samples_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 triangle_inequality_of_dist_test_data(self):
        return self._triangle_inequality_of_dist_test_data(
            self.metric_args_list, self.space_list, self.n_points_list
        )
Example #17
0
class TestPreShapeSpace(geomstats.tests.TestCase):
    def setup_method(self):
        gs.random.seed(1234)

        self.k_landmarks = 4
        self.m_ambient = 3
        self.space = PreShapeSpace(self.k_landmarks, self.m_ambient)
        self.matrices = self.space.embedding_space
        self.n_samples = 10
        self.shape_metric = KendallShapeMetric(self.k_landmarks,
                                               self.m_ambient)

        self.base_point = self.space.random_point()
        vector = gs.random.rand(11, self.k_landmarks, self.m_ambient)
        tg_vec_0 = self.space.to_tangent(vector[0], self.base_point)
        self.hor_x = self.space.horizontal_projection(tg_vec_0,
                                                      self.base_point)
        tg_vec_1 = self.space.to_tangent(vector[1], self.base_point)
        self.hor_y = self.space.horizontal_projection(tg_vec_1,
                                                      self.base_point)
        tg_vec_2 = self.space.to_tangent(vector[2], self.base_point)
        self.hor_z = self.space.horizontal_projection(tg_vec_2,
                                                      self.base_point)
        tg_vec_3 = self.space.to_tangent(vector[3], self.base_point)
        self.hor_h = self.space.horizontal_projection(tg_vec_3,
                                                      self.base_point)
        tg_vec_4 = self.space.to_tangent(vector[4], self.base_point)
        self.ver_v = self.space.vertical_projection(tg_vec_4, self.base_point)
        tg_vec_5 = self.space.to_tangent(vector[5], self.base_point)
        self.ver_w = self.space.vertical_projection(tg_vec_5, self.base_point)
        tg_vec_6 = self.space.to_tangent(vector[6], self.base_point)
        hor_dy = self.space.horizontal_projection(tg_vec_6, self.base_point)
        tg_vec_7 = self.space.to_tangent(vector[7], self.base_point)
        hor_dz = self.space.horizontal_projection(tg_vec_7, self.base_point)
        tg_vec_8 = self.space.to_tangent(vector[8], self.base_point)
        ver_dv = self.space.vertical_projection(tg_vec_8, self.base_point)
        tg_vec_9 = self.space.to_tangent(vector[9], self.base_point)
        ver_dw = self.space.vertical_projection(tg_vec_9, self.base_point)
        tg_vec_10 = self.space.to_tangent(vector[10], self.base_point)
        hor_dh = self.space.horizontal_projection(tg_vec_10, self.base_point)

        # generate valid derivatives of horizontal / vertical vector fields.
        a_x_y = self.space.integrability_tensor(self.hor_x, self.hor_y,
                                                self.base_point)
        self.nabla_x_y = hor_dy + a_x_y
        a_x_z = self.space.integrability_tensor(self.hor_x, self.hor_z,
                                                self.base_point)
        self.nabla_x_z = hor_dz + a_x_z
        a_x_v = self.space.integrability_tensor(self.hor_x, self.ver_v,
                                                self.base_point)
        self.nabla_x_v = ver_dv + a_x_v
        a_x_w = self.space.integrability_tensor(self.hor_x, self.ver_w,
                                                self.base_point)
        self.nabla_x_w = ver_dw + a_x_w
        a_x_h = self.space.integrability_tensor(self.hor_x, self.hor_h,
                                                self.base_point)
        self.nabla_x_h = hor_dh + a_x_h

    def test_belongs(self):
        point = gs.random.rand(self.m_ambient - 1, self.k_landmarks)
        result = self.space.belongs(point)
        self.assertFalse(result)

        point = gs.random.rand(self.n_samples, self.m_ambient - 1,
                               self.k_landmarks)
        result = self.space.belongs(point)
        self.assertFalse(gs.all(result))

    def test_random_point_and_belongs(self):
        """Test random uniform and belongs.

        Test that the random uniform method samples
        on the pre-shape space.
        """
        n_samples = self.n_samples
        point = self.space.random_point(n_samples)
        result = self.space.belongs(point)
        expected = gs.array([True] * n_samples)

        self.assertAllClose(expected, result)

    def test_random_point_shape(self):
        point = self.space.random_point()
        result = gs.shape(point)
        expected = (
            self.k_landmarks,
            self.m_ambient,
        )

        self.assertAllClose(result, expected)

        point = self.space.random_point(self.n_samples)
        result = gs.shape(point)
        expected = (
            self.n_samples,
            self.k_landmarks,
            self.m_ambient,
        )
        self.assertAllClose(result, expected)

    def test_projection_and_belongs(self):
        point = Matrices.transpose(
            gs.array([
                [1.0, 0.0, 0.0, 1.0],
                [0.0, 1.0, 0.0, 1.0],
                [0.0, 0.0, 1.0, 1.0],
            ]))
        proj = self.space.projection(point)
        result = self.space.belongs(proj)
        expected = True

        self.assertAllClose(expected, result)

    def test_is_centered(self):
        point = gs.ones((self.k_landmarks, self.m_ambient))
        result = self.space.is_centered(point)
        self.assertFalse(result)

        point = gs.zeros((self.k_landmarks, self.m_ambient))
        result = self.space.is_centered(point)
        self.assertTrue(result)

    def test_to_center_is_center(self):
        point = gs.ones((self.k_landmarks, self.m_ambient))
        point = self.space.center(point)
        result = self.space.is_centered(point)
        self.assertTrue(result)

    def test_to_center_is_centered_vectorization(self):
        point = gs.ones((self.n_samples, self.k_landmarks, self.m_ambient))
        point = self.space.center(point)
        result = gs.all(self.space.is_centered(point))
        self.assertTrue(result)

    def test_is_tangent_to_tangent(self):
        point, vector = self.matrices.random_point(2)
        point = self.space.projection(point)

        result = self.space.is_tangent(vector, point)
        self.assertFalse(result)

        tangent_vec = self.space.to_tangent(vector, point)
        result = self.space.is_tangent(tangent_vec, point)
        self.assertTrue(result)

        vec = gs.array([tangent_vec, vector])
        result = self.space.is_tangent(vec, point)
        expected = gs.array([True, False])
        self.assertAllClose(result, expected)

    def test_vertical_projection(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        vertical = self.space.vertical_projection(tan, point)
        transposed_point = Matrices.transpose(point)

        tmp_expected = gs.matmul(transposed_point, tan)
        expected = Matrices.transpose(tmp_expected) - tmp_expected

        tmp_result = gs.matmul(transposed_point, vertical)
        result = Matrices.transpose(tmp_result) - tmp_result
        self.assertAllClose(result, expected)

    def test_vertical_projection_vectorization(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point(self.n_samples)
        tan = self.space.to_tangent(vector, point)
        vertical = self.space.vertical_projection(tan, point)
        transposed_point = Matrices.transpose(point)

        tmp_expected = gs.matmul(transposed_point, tan)
        expected = Matrices.transpose(tmp_expected) - tmp_expected

        tmp_result = gs.matmul(transposed_point, vertical)
        result = Matrices.transpose(tmp_result) - tmp_result
        self.assertAllClose(result, expected)

    def test_horizontal_projection(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        horizontal = self.space.horizontal_projection(tan, point)
        transposed_point = Matrices.transpose(point)
        result = gs.matmul(transposed_point, horizontal)
        expected = Matrices.transpose(result)

        self.assertAllClose(result, expected)

    def test_horizontal_projection_vectorized(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point(self.n_samples)
        tan = self.space.to_tangent(vector, point)
        horizontal = self.space.horizontal_projection(tan, point)
        transposed_point = Matrices.transpose(point)
        result = gs.matmul(transposed_point, horizontal)
        expected = Matrices.transpose(result)

        self.assertAllClose(result, expected)

    def test_horizontal_and_is_tangent(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        horizontal = self.space.horizontal_projection(tan, point)

        horizontal = gs.stack([horizontal, vector])
        result = self.space.is_tangent(horizontal, point)
        expected = gs.array([True, False])

        self.assertAllClose(result, expected)

    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)

    def test_align_vectorization(self):
        base_point = self.space.random_point()
        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(gs.all(result))

        base_point = self.space.random_point(2)
        point = self.space.random_point()
        aligned = self.space.align(point, base_point)
        alignment = gs.matmul(Matrices.transpose(aligned), base_point)
        result = Matrices.is_symmetric(alignment)
        self.assertTrue(gs.all(result))

    def test_inner_product_shape(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        inner = self.space.ambient_metric.inner_product(tan, tan, point)
        self.assertAllClose(inner.shape, (self.n_samples, ))

    def test_exp_and_belongs(self):
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        exp = self.space.ambient_metric.exp(tan, point)
        result = self.space.belongs(exp)
        self.assertTrue(result)

        exp = self.space.ambient_metric.exp(gs.zeros_like(point), point)
        result = gs.isclose(point, exp)
        self.assertTrue(gs.all(result))

    def test_exp_and_belongs_vectorization(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point(self.n_samples)
        tan = self.space.to_tangent(vector, point)
        exp = self.space.ambient_metric.exp(tan, point)
        result = self.space.belongs(exp)
        self.assertTrue(gs.all(result))

        point = point[0]
        tan = self.space.to_tangent(vector, point)
        exp = self.space.ambient_metric.exp(tan, point)
        result = self.space.belongs(exp)
        self.assertTrue(gs.all(result))

    def test_log_and_exp(self):
        point, base_point = self.space.random_point(2)
        log = self.space.ambient_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(result)

        exp = self.space.ambient_metric.exp(log, base_point)
        self.assertAllClose(exp, point)

    def test_exp_and_log(self):
        base_point = self.space.random_point()
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        tangent_vec = self.space.to_tangent(vector, base_point)
        point = self.space.ambient_metric.exp(tangent_vec, base_point)
        log = self.space.ambient_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(result)

        self.assertAllClose(tangent_vec, log)

    def test_log_vectorization(self):
        point = self.space.random_point(self.n_samples)
        base_point = self.space.random_point()
        log = self.space.ambient_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(gs.all(result))

        exp = self.space.ambient_metric.exp(log, base_point)
        self.assertAllClose(exp, point)

        log = self.space.ambient_metric.log(base_point, point)
        result = self.space.is_tangent(log, point)
        self.assertTrue(gs.all(result))

        exp = self.space.ambient_metric.exp(log, point)
        expected = gs.stack([base_point] * self.n_samples)
        self.assertAllClose(exp, expected)

    def test_kendall_inner_product_shape(self):
        vector = gs.random.rand(self.n_samples, self.k_landmarks,
                                self.m_ambient)
        point = self.space.random_point()
        tan = self.space.to_tangent(vector, point)
        inner = self.shape_metric.inner_product(tan, tan, point)
        self.assertAllClose(inner.shape, (self.n_samples, ))

    def test_kendall_log_and_exp(self):
        point, base_point = self.space.random_point(2)
        expected = self.space.align(point, base_point)
        log = self.shape_metric.log(expected, base_point)
        result = self.space.is_horizontal(log, base_point)
        self.assertTrue(result)

        exp = self.shape_metric.exp(log, base_point)
        self.assertAllClose(exp, expected)

    def test_kendall_exp_and_log(self):
        base_point = self.space.random_point()
        vector = gs.random.rand(self.k_landmarks, self.m_ambient)
        tangent_vec = self.space.to_tangent(vector, base_point)
        point = self.shape_metric.exp(tangent_vec, base_point)
        log = self.shape_metric.log(point, base_point)
        result = self.space.is_tangent(log, base_point)
        self.assertTrue(result)

        expected = self.space.horizontal_projection(tangent_vec, base_point)
        self.assertAllClose(expected, log, rtol=1e-3)

    def test_dist_extreme_case(self):
        point = self.space.projection(gs.eye(self.k_landmarks, self.m_ambient))
        result = self.shape_metric.dist(point, point)
        expected = 0.0
        self.assertAllClose(result, expected)

    def test_dist(self):
        point, base_point = self.space.random_point(2)
        result = self.shape_metric.dist(point, base_point)
        log = self.shape_metric.log(point, base_point)
        expected = self.shape_metric.norm(log, base_point)
        self.assertAllClose(result, expected)

    def test_dist_vectorization(self):
        point = self.space.random_point(self.n_samples)
        base_point = self.space.random_point(self.n_samples)
        aligned = self.space.align(point, base_point)
        result = self.shape_metric.dist(aligned, base_point)
        log = self.shape_metric.log(aligned, base_point)
        expected = self.shape_metric.norm(log, base_point)
        self.assertAllClose(result, expected)

    def test_curvature_is_skew_operator(self):
        """Pre-shape space curvature tensor is skew in the first two arguments.

        :math:`R(X,X)Y = 0`.
        """
        space = self.space
        base_point = space.random_point(2)
        vector = gs.random.rand(4, self.k_landmarks, self.m_ambient)
        tangent_vec_a = space.to_tangent(vector[:2], base_point)
        tangent_vec_b = space.to_tangent(vector[2:], base_point)

        result = self.shape_metric.curvature(tangent_vec_a, tangent_vec_a,
                                             tangent_vec_b, base_point)
        expected = gs.zeros_like(result)
        self.assertAllClose(result, expected)

    def test_curvature_bianchi_identity(self):
        """First Bianchi identity on curvature in pre-shape space.

        :math:`R(X,Y)Z + R(Y,Z)X + R(Z,X)Y = 0`.
        """
        space = self.space
        base_point = space.random_point()
        vector = gs.random.rand(3, self.k_landmarks, self.m_ambient)
        tangent_vec_a = space.to_tangent(vector[0], base_point)
        tangent_vec_b = space.to_tangent(vector[1], base_point)
        tangent_vec_c = space.to_tangent(vector[2], base_point)

        curvature_1 = self.shape_metric.curvature(tangent_vec_a, tangent_vec_b,
                                                  tangent_vec_c, base_point)
        curvature_2 = self.shape_metric.curvature(tangent_vec_b, tangent_vec_c,
                                                  tangent_vec_a, base_point)
        curvature_3 = self.shape_metric.curvature(tangent_vec_c, tangent_vec_a,
                                                  tangent_vec_b, base_point)

        result = curvature_1 + curvature_2 + curvature_3
        expected = gs.zeros_like(result)
        self.assertAllClose(result, expected)

    def test_integrability_tensor(self):
        """Identities of integrability tensor in kendall pre-shape space.

        The integrability tensor A_X E is skew-symmetric with respect to the
        pre-shape metric, :math:`< A_X E, F> + <E, A_X F> = 0`. By
        polarization, this is equivalent to :math:`< A_X E, E> = 0`.

        The integrability tensor is also alternating (:math:`A_X Y =
        - A_Y X`)  for horizontal vector fields :math:'X,Y',  and it is
        exchanging horizontal and vertical vector spaces.
        """
        space = self.space
        base_point = space.random_point()
        vector = gs.random.rand(2, self.k_landmarks, self.m_ambient)
        tangent_vec_a = space.to_tangent(vector[0], base_point)
        tangent_vec_b = space.to_tangent(vector[1], base_point)
        result_ab = space.integrability_tensor(tangent_vec_a, tangent_vec_b,
                                               base_point)

        result = space.ambient_metric.inner_product(tangent_vec_b, result_ab,
                                                    base_point)
        expected = 0.0
        self.assertAllClose(result, expected)

        horizontal_b = space.horizontal_projection(tangent_vec_b, base_point)
        horizontal_a = space.horizontal_projection(tangent_vec_a, base_point)
        result = space.integrability_tensor(horizontal_a, horizontal_b,
                                            base_point)
        expected = -space.integrability_tensor(horizontal_b, horizontal_a,
                                               base_point)
        self.assertAllClose(result, expected)

        is_vertical = space.is_vertical(result, base_point)
        self.assertTrue(is_vertical)

        vertical_b = tangent_vec_b - horizontal_b
        result = space.integrability_tensor(horizontal_a, vertical_b,
                                            base_point)
        is_horizontal = space.is_horizontal(result, base_point)
        self.assertTrue(is_horizontal)

    def test_integrability_tensor_old(self):
        """Test if old and new implementation give the same result."""
        space = self.space
        base_point = space.random_point()
        vector = gs.random.rand(2, self.k_landmarks, self.m_ambient)
        tangent_vec_x = space.to_tangent(vector[0], base_point)
        tangent_vec_e = space.to_tangent(vector[1], base_point)

        result = space.integrability_tensor_old(tangent_vec_x, tangent_vec_e,
                                                base_point)
        expected = space.integrability_tensor(tangent_vec_x, tangent_vec_e,
                                              base_point)
        self.assertAllClose(result, expected)

    def test_kendall_sectional_curvature(self):
        """Sectional curvature of Kendall shape space is larger than 1.

        The sectional curvature always increase by taking the quotient in a
        Riemannian submersion. Thus, it should larger in kendall shape space
        thane the sectional curvature of the pre-shape space which is 1 as it
        a hypersphere.
        The sectional curvature is computed here with the generic
        directional_curvature and sectional curvature methods.
        """
        space = self.space
        metric = self.shape_metric
        n_samples = 4 * self.k_landmarks * self.m_ambient
        base_point = self.space.random_point(1)

        vec_a = gs.random.rand(n_samples, self.k_landmarks, self.m_ambient)
        tg_vec_a = space.to_tangent(space.center(vec_a), base_point)
        hor_a = space.horizontal_projection(tg_vec_a, base_point)

        vec_b = gs.random.rand(n_samples, self.k_landmarks, self.m_ambient)
        tg_vec_b = space.to_tangent(space.center(vec_b), base_point)
        hor_b = space.horizontal_projection(tg_vec_b, base_point)

        tidal_force = metric.directional_curvature(hor_a, hor_b, base_point)

        numerator = metric.inner_product(tidal_force, hor_a, base_point)
        denominator = (metric.inner_product(hor_a, hor_a, base_point) *
                       metric.inner_product(hor_b, hor_b, base_point) -
                       metric.inner_product(hor_a, hor_b, base_point)**2)
        condition = ~gs.isclose(denominator, 0.0)
        kappa = numerator[condition] / denominator[condition]
        kappa_direct = metric.sectional_curvature(hor_a, hor_b,
                                                  base_point)[condition]
        self.assertAllClose(kappa, kappa_direct)
        result = kappa > 1.0 - 1e-12
        self.assertTrue(gs.all(result))

    def test_integrability_tensor_derivative_is_alternate(self):
        r"""Integrability tensor derivatives is alternate in pre-shape.

        For two horizontal vector fields :math:`X,Y` the integrability
        tensor (hence its derivatives) is alternate:
        :math:`\nabla_X ( A_Y Z + A_Z Y ) = 0`.
        """
        nabla_x_a_y_z, a_y_z = self.space.integrability_tensor_derivative(
            self.hor_x,
            self.hor_y,
            self.nabla_x_y,
            self.hor_z,
            self.nabla_x_z,
            self.base_point,
        )
        nabla_x_a_z_y, a_z_y = self.space.integrability_tensor_derivative(
            self.hor_x,
            self.hor_z,
            self.nabla_x_z,
            self.hor_y,
            self.nabla_x_y,
            self.base_point,
        )
        result = nabla_x_a_y_z + nabla_x_a_z_y
        self.assertAllClose(a_y_z + a_z_y, gs.zeros_like(result))
        self.assertAllClose(result, gs.zeros_like(result))

    def test_integrability_tensor_derivative_is_skew_symmetric(self):
        r"""Integrability tensor derivatives is skew-symmetric in pre-shape.

        For :math:`X,Y` horizontal and :math:`V,W` vertical:
        :math:`\nabla_X (< A_Y Z , V > + < A_Y V , Z >) = 0`.
        """
        scal = self.space.ambient_metric.inner_product

        nabla_x_a_y_z, a_y_z = self.space.integrability_tensor_derivative(
            self.hor_x,
            self.hor_y,
            self.nabla_x_y,
            self.hor_z,
            self.nabla_x_z,
            self.base_point,
        )

        nabla_x_a_y_v, a_y_v = self.space.integrability_tensor_derivative(
            self.hor_x,
            self.hor_y,
            self.nabla_x_y,
            self.ver_v,
            self.nabla_x_v,
            self.base_point,
        )

        result = (scal(nabla_x_a_y_z, self.ver_v) +
                  scal(a_y_z, self.nabla_x_v) +
                  scal(nabla_x_a_y_v, self.hor_z) +
                  scal(a_y_v, self.nabla_x_z))
        self.assertAllClose(result, gs.zeros_like(result))

    def test_integrability_tensor_derivative_reverses_hor_ver(self):
        r"""Integrability tensor derivatives exchanges hor & ver in pre-shape.

        For :math:`X,Y,Z` horizontal and :math:`V,W` vertical, the
        integrability tensor (and thus its derivative) reverses horizontal
        and vertical subspaces: :math:`\nabla_X < A_Y Z, H > = 0`  and
        :math:`nabla_X < A_Y V, W > = 0`.
        """
        scal = self.space.ambient_metric.inner_product

        nabla_x_a_y_z, a_y_z = self.space.integrability_tensor_derivative(
            self.hor_x,
            self.hor_y,
            self.nabla_x_y,
            self.hor_z,
            self.nabla_x_z,
            self.base_point,
        )
        result = scal(nabla_x_a_y_z, self.hor_h) + scal(a_y_z, self.nabla_x_h)
        self.assertAllClose(result, gs.zeros_like(result))

        nabla_x_a_y_v, a_y_v = self.space.integrability_tensor_derivative(
            self.hor_x,
            self.hor_y,
            self.nabla_x_y,
            self.ver_v,
            self.nabla_x_v,
            self.base_point,
        )
        result = scal(nabla_x_a_y_v, self.ver_w) + scal(a_y_v, self.nabla_x_w)
        self.assertAllClose(result, gs.zeros_like(result))

    def test_integrability_tensor_derivative_parallel(self):
        """Test optimized integrability tensor derivatives in pre-shape space.

        Optimized version for quotient-parallel vector fields should equal
        the general implementation.
        """
        (
            nabla_x_a_y_z_qp,
            a_y_z_qp,
        ) = self.space.integrability_tensor_derivative_parallel(
            self.hor_x, self.hor_y, self.hor_z, self.base_point)

        a_x_y = self.space.integrability_tensor(self.hor_x, self.hor_y,
                                                self.base_point)
        a_x_z = self.space.integrability_tensor(self.hor_x, self.hor_z,
                                                self.base_point)

        nabla_x_a_y_z, a_y_z = self.space.integrability_tensor_derivative(
            self.hor_x, self.hor_y, a_x_y, self.hor_z, a_x_z, self.base_point)

        self.assertAllClose(a_y_z, a_y_z_qp)
        self.assertAllClose(nabla_x_a_y_z, nabla_x_a_y_z_qp)

    def test_iterated_integrability_tensor_derivative_parallel(self):
        """Test optimized iterated integrability tensor derivatives.

        The optimized version of the iterated integrability tensor
        :math:`A_X A_Y A_X Y`, computed with the horizontal lift of
        quotient-parallel vector fields extending the tangent vectors
        :math:`X,Y` of Kendall shape spaces (identified to horizontal vectors
        of the pre-shape space), is the recursive application of two general
        integrability tensor derivatives with proper derivatives.
        Intermediate computations returned are also verified.
        """
        a_x_y = self.space.integrability_tensor(self.hor_x, self.hor_y,
                                                self.base_point)
        nabla_x_v, a_x_y = self.space.integrability_tensor_derivative(
            self.hor_x,
            self.hor_x,
            gs.zeros_like(self.hor_x),
            self.hor_y,
            a_x_y,
            self.base_point,
        )

        (
            nabla_x_a_y_a_x_y,
            a_y_a_x_y,
        ) = self.space.integrability_tensor_derivative(self.hor_x, self.hor_y,
                                                       a_x_y, a_x_y, nabla_x_v,
                                                       self.base_point)

        a_x_a_y_a_x_y = self.space.integrability_tensor(
            self.hor_x, a_y_a_x_y, self.base_point)

        (
            nabla_x_a_y_a_x_y_qp,
            a_x_a_y_a_x_y_qp,
            nabla_x_v_qp,
            a_y_a_x_y_qp,
            ver_v_qp,
        ) = self.space.iterated_integrability_tensor_derivative_parallel(
            self.hor_x, self.hor_y, self.base_point)
        self.assertAllClose(a_x_y, ver_v_qp)
        self.assertAllClose(a_y_a_x_y, a_y_a_x_y_qp)
        self.assertAllClose(nabla_x_v, nabla_x_v_qp)
        self.assertAllClose(a_x_a_y_a_x_y, a_x_a_y_a_x_y_qp)
        self.assertAllClose(nabla_x_a_y_a_x_y, nabla_x_a_y_a_x_y_qp)

    def test_kendall_curvature_derivative_bianchi_identity(self):
        r"""2nd Bianchi identity on curvature derivative in kendall space.

        For any 3 tangent vectors horizontally lifted from kendall shape
        space to Kendall pre-shape space, :math:`(\nabla_X R)(Y, Z)
        + (\nabla_Y R)(Z,X) + (\nabla_Z R)(X, Y) = 0`.
        """
        term_x = self.shape_metric.curvature_derivative(
            self.hor_x, self.hor_y, self.hor_z, self.hor_h, self.base_point)
        term_y = self.shape_metric.curvature_derivative(
            self.hor_y, self.hor_z, self.hor_x, self.hor_h, self.base_point)
        term_z = self.shape_metric.curvature_derivative(
            self.hor_z, self.hor_x, self.hor_y, self.hor_h, self.base_point)

        result = term_x + term_y + term_z
        self.assertAllClose(result, gs.zeros_like(result))

    def test_curvature_derivative_is_skew_operator(self):
        r"""Derivative of a skew operator is skew.

        For any 3 tangent vectors horizontally lifted from kendall shape space
        to Kendall pre-shape space, :math:`(\nabla_X R)(Y,Y)Z = 0`.
        """
        result = self.shape_metric.curvature_derivative(
            self.hor_x, self.hor_y, self.hor_y, self.hor_z, self.base_point)
        self.assertAllClose(result, gs.zeros_like(result))

    def test_directional_curvature_derivative(self):
        """Test equality of directional curvature derivative implementations.

        General formula based on curvature derivative, optimized method of
        KendallShapeMetric class, method from the QuotientMetric class and
        method from the Connection class have to give identical results.
        """
        metric = self.shape_metric

        # General formula based on curvature derivative
        expected = metric.curvature_derivative(self.hor_x, self.hor_y,
                                               self.hor_x, self.hor_y,
                                               self.base_point)

        # Optimized method of KendallShapeMetric class
        result_kendall_shape_metric = metric.directional_curvature_derivative(
            self.hor_x, self.hor_y, self.base_point)
        self.assertAllClose(result_kendall_shape_metric, expected)

        # Method from the QuotientMetric class
        result_quotient_metric = super(
            KendallShapeMetric,
            metric).directional_curvature_derivative(self.hor_x, self.hor_y,
                                                     self.base_point)
        self.assertAllClose(result_quotient_metric, expected)

        # Method from the Connection class
        from geomstats.geometry.quotient_metric import QuotientMetric

        result_connection = super(QuotientMetric,
                                  metric).directional_curvature_derivative(
                                      self.hor_x, self.hor_y, self.base_point)
        self.assertAllClose(result_connection, expected)

    def test_directional_curvature_derivative_is_quadratic(self):
        """Directional curvature derivative is quadratic in both variables."""
        coef_x = -2.5
        coef_y = 1.5
        result = self.shape_metric.directional_curvature_derivative(
            coef_x * self.hor_x, coef_y * self.hor_y, self.base_point)
        expected = (coef_x**2 * coef_y**2 *
                    self.shape_metric.directional_curvature_derivative(
                        self.hor_x, self.hor_y, self.base_point))
        self.assertAllClose(result, expected)

    @geomstats.tests.np_autograd_and_torch_only
    def test_parallel_transport(self):
        space = self.space
        metric = self.shape_metric
        shape = (self.n_samples, self.k_landmarks, self.m_ambient)

        point = space.projection(gs.eye(4)[:, :3])
        tan_b = gs.random.rand(*shape)
        tan_b = space.to_tangent(tan_b, point)
        tan_b = space.horizontal_projection(tan_b, point)

        # use a vector orthonormal to tan_b
        tan_a = gs.random.rand(*shape)
        tan_a = space.to_tangent(tan_a, point)
        tan_a = space.horizontal_projection(tan_a, point)

        # orthonormalize and move to base_point
        tan_a -= gs.einsum(
            "...,...ij->...ij",
            metric.inner_product(tan_a, tan_b, point) /
            metric.squared_norm(tan_b, point),
            tan_b,
        )
        tan_b = gs.einsum("...ij,...->...ij", tan_b,
                          1.0 / metric.norm(tan_b, point))
        tan_a = gs.einsum("...ij,...->...ij", tan_a,
                          1.0 / metric.norm(tan_a, point))

        transported = metric.parallel_transport(tan_a,
                                                point,
                                                tan_b,
                                                n_steps=150,
                                                step="rk4")
        end_point = metric.exp(tan_b, point)
        result = metric.norm(transported, end_point)
        expected = metric.norm(tan_a, point)
        self.assertAllClose(result, expected)

        is_tangent = space.is_tangent(transported, end_point)
        is_horizontal = space.is_horizontal(transported, end_point)
        self.assertTrue(gs.all(is_tangent))
        self.assertTrue(gs.all(is_horizontal))

        transported = metric.parallel_transport(tan_a[0],
                                                point,
                                                end_point=end_point[0])
        result = metric.norm(transported, end_point[0])
        self.assertAllClose(result, expected[0])