示例#1
0
    def test_squared_dist_is_symmetric(
        self, metric_mat_at_identity, left_or_right, point_1, point_2
    ):
        group = SpecialOrthogonal(3, "vector")
        metric = self.metric(
            SpecialOrthogonal(n=3, point_type="vector"),
            metric_mat_at_identity=metric_mat_at_identity,
            left_or_right=left_or_right,
        )
        point_1 = group.regularize(point_1)
        point_2 = group.regularize(point_2)

        sq_dist_1_2 = gs.mod(metric.squared_dist(point_1, point_2) + 1e-4, gs.pi**2)
        sq_dist_2_1 = gs.mod(metric.squared_dist(point_2, point_1) + 1e-4, gs.pi**2)
        self.assertAllClose(sq_dist_1_2, sq_dist_2_1, atol=1e-4)
    def test_exp_and_log_and_projection_to_tangent_space_general_case(self):
        """
        Test that the riemannian exponential
        and the riemannian logarithm are inverse.

        Expect their composition to give the identity function.

        NB: points on the n-dimensional sphere are
        (n+1)-D vectors of norm 1.
        """
        # TODO(nina): Fix that this test fails, also in numpy
        # Riemannian Exp then Riemannian Log
        # General case
        # NB: Riemannian log gives a regularized tangent vector,
        # so we take the norm modulo 2 * pi.
        base_point = gs.array([0., -3., 0., 3., 4.])
        base_point = base_point / gs.linalg.norm(base_point)
        vector = gs.array([9., 5., 0., 0., -1.])
        vector = self.space.projection_to_tangent_space(vector=vector,
                                                        base_point=base_point)

        # exp = self.metric.exp(tangent_vec=vector, base_point=base_point)
        # result = self.metric.log(point=exp, base_point=base_point)

        expected = vector
        norm_expected = gs.linalg.norm(expected)
        regularized_norm_expected = gs.mod(norm_expected, 2 * gs.pi)
        expected = expected / norm_expected * regularized_norm_expected
        expected = helper.to_vector(expected)
示例#3
0
    def test_exp_and_log_and_projection_to_tangent_space_general_case(self):
        """Test Log and Exp.

        Test that the Riemannian exponential
        and the Riemannian logarithm are inverse.

        Expect their composition to give the identity function.

        NB: points on the n-dimensional sphere are
        (n+1)-D vectors of norm 1.
        """
        # Riemannian Exp then Riemannian Log
        # General case
        # NB: Riemannian log gives a regularized tangent vector,
        # so we take the norm modulo 2 * pi.
        base_point = gs.array([0.0, -3.0, 0.0, 3.0, 4.0])
        base_point = base_point / gs.linalg.norm(base_point)

        vector = gs.array([3.0, 2.0, 0.0, 0.0, -1.0])
        vector = self.space.to_tangent(vector=vector, base_point=base_point)

        exp = self.metric.exp(tangent_vec=vector, base_point=base_point)
        result = self.metric.log(point=exp, base_point=base_point)

        expected = vector
        norm_expected = gs.linalg.norm(expected)
        regularized_norm_expected = gs.mod(norm_expected, 2 * gs.pi)
        expected = expected / norm_expected * regularized_norm_expected

        # The Log can be the opposite vector on the tangent space,
        # whose Exp gives the base_point
        are_close = gs.allclose(result, expected)
        norm_2pi = gs.isclose(gs.linalg.norm(result - expected), 2 * gs.pi)
        self.assertTrue(are_close or norm_2pi)
示例#4
0
    def test_exp_and_dist_and_projection_to_tangent_space(self):
        base_point = gs.array([16., -2., -2.5, 84., 3.])
        base_point = base_point / gs.linalg.norm(base_point)

        vector = gs.array([9., 0., -1., -2., 1.])
        tangent_vec = self.space.projection_to_tangent_space(
            vector=vector, base_point=base_point)
        exp = self.metric.exp(tangent_vec=tangent_vec, base_point=base_point)

        result = self.metric.dist(base_point, exp)
        expected = gs.mod(gs.linalg.norm(tangent_vec), 2 * gs.pi)
        expected = helper.to_scalar(expected)

        gs.testing.assert_allclose(result, expected)
    def rotation_vector_from_matrix(self, rot_mat):
        """
        In 3D, convert rotation matrix to rotation vector
        (axis-angle representation).

        Get the angle through the trace of the rotation matrix:
        The eigenvalues are:
        1, cos(angle) + i sin(angle), cos(angle) - i sin(angle)
        so that: trace = 1 + 2 cos(angle), -1 <= trace <= 3

        Get the rotation vector through the formula:
        S_r = angle / ( 2 * sin(angle) ) (R - R^T)

        For the edge case where the angle is close to pi,
        the formulation is derived by going from rotation matrix to unit
        quaternion to axis-angle:
         r = angle * v / |v|, where (w, v) is a unit quaternion.

        In nD, the rotation vector stores the n(n-1)/2 values of the
        skew-symmetric matrix representing the rotation.
        """
        rot_mat = gs.to_ndarray(rot_mat, to_ndim=3)
        n_rot_mats, mat_dim_1, mat_dim_2 = rot_mat.shape
        assert mat_dim_1 == mat_dim_2 == self.n

        rot_mat = closest_rotation_matrix(rot_mat)

        if self.n == 3:
            trace = gs.trace(rot_mat, axis1=1, axis2=2)
            trace = gs.to_ndarray(trace, to_ndim=2, axis=1)
            assert trace.shape == (n_rot_mats, 1), trace.shape

            cos_angle = .5 * (trace - 1)
            cos_angle = gs.clip(cos_angle, -1, 1)
            angle = gs.arccos(cos_angle)

            rot_mat_transpose = gs.transpose(rot_mat, axes=(0, 2, 1))
            rot_vec = vector_from_skew_matrix(rot_mat - rot_mat_transpose)

            mask_0 = gs.isclose(angle, 0)
            mask_0 = gs.squeeze(mask_0, axis=1)
            rot_vec[mask_0] = (rot_vec[mask_0] * (.5 -
                                                  (trace[mask_0] - 3.) / 12.))

            mask_pi = gs.isclose(angle, gs.pi)
            mask_pi = gs.squeeze(mask_pi, axis=1)

            # choose the largest diagonal element
            # to avoid a square root of a negative number
            a = 0
            if gs.any(mask_pi):
                a = gs.argmax(gs.diagonal(rot_mat[mask_pi], axis1=1, axis2=2))
            b = gs.mod(a + 1, 3)
            c = gs.mod(a + 2, 3)

            # compute the axis vector
            sq_root = gs.sqrt(
                (rot_mat[mask_pi, a, a] - rot_mat[mask_pi, b, b] -
                 rot_mat[mask_pi, c, c] + 1.))
            rot_vec_pi = gs.zeros((sum(mask_pi), self.dimension))
            rot_vec_pi[:, a] = sq_root / 2.
            rot_vec_pi[:, b] = (
                (rot_mat[mask_pi, b, a] + rot_mat[mask_pi, a, b]) /
                (2. * sq_root))
            rot_vec_pi[:, c] = (
                (rot_mat[mask_pi, c, a] + rot_mat[mask_pi, a, c]) /
                (2. * sq_root))

            rot_vec[mask_pi] = (angle[mask_pi] * rot_vec_pi /
                                gs.linalg.norm(rot_vec_pi))

            mask_else = ~mask_0 & ~mask_pi
            rot_vec[mask_else] = (angle[mask_else] /
                                  (2. * gs.sin(angle[mask_else])) *
                                  rot_vec[mask_else])
        else:
            skew_mat = self.embedding_manifold.group_log_from_identity(rot_mat)
            rot_vec = vector_from_skew_matrix(skew_mat)

        return self.regularize(rot_vec)