Ejemplo n.º 1
0
    def test_projection_and_belongs(self):
        gs.random.seed(3)
        group = SpecialOrthogonal(n=4)
        mat = gs.random.rand(4, 4)
        point = group.projection(mat)
        result = group.belongs(point)
        self.assertTrue(result)

        mat = gs.random.rand(2, 4, 4)
        point = group.projection(mat)
        result = group.belongs(point, atol=1e-4)
        self.assertTrue(gs.all(result))
Ejemplo n.º 2
0
class _SpecialEuclideanMatrices(MatrixLieGroup, LevelSet):
    """Class for special Euclidean group.

    Parameters
    ----------
    n : int
        Integer dimension of the underlying Euclidean space. Matrices will
        be of size: (n+1) x (n+1).

    Attributes
    ----------
    rotations : SpecialOrthogonal
        Subgroup of rotations of size n.
    translations : Euclidean
        Subgroup of translations of size n.
    left_canonical_metric : InvariantMetric
        The left invariant metric that corresponds to the Frobenius inner
        product at the identity.
    right_canonical_metric : InvariantMetric
        The right invariant metric that corresponds to the Frobenius inner
        product at the identity.
    metric :  MatricesMetric
        The Euclidean (Frobenius) inner product.
    """
    def __init__(self, n, **kwargs):
        super().__init__(n=n + 1,
                         dim=int((n * (n + 1)) / 2),
                         embedding_space=GeneralLinear(n + 1,
                                                       positive_det=True),
                         submersion=submersion,
                         value=gs.eye(n + 1),
                         tangent_submersion=tangent_submersion,
                         lie_algebra=SpecialEuclideanMatrixLieAlgebra(n=n),
                         **kwargs)
        self.rotations = SpecialOrthogonal(n=n)
        self.translations = Euclidean(dim=n)
        self.n = n

        self.left_canonical_metric = SpecialEuclideanMatrixCannonicalLeftMetric(
            group=self)
        if self._metric is None:
            self._metric = self.left_canonical_metric

    @property
    def identity(self):
        """Return the identity matrix."""
        return gs.eye(self.n + 1, self.n + 1)

    def random_point(self, n_samples=1, bound=1.0):
        """Sample in SE(n) from the uniform distribution.

        Parameters
        ----------
        n_samples : int
            Number of samples.
            Optional, default: 1.
        bound: float
            Bound of the interval in which to sample each entry of the
            translation part.
            Optional, default: 1.

        Returns
        -------
        samples : array-like, shape=[..., n + 1, n + 1]
            Sample in SE(n).
        """
        random_translation = self.translations.random_point(n_samples)
        random_rotation = self.rotations.random_uniform(n_samples)
        output_shape = ((n_samples, self.n + 1,
                         self.n + 1) if n_samples != 1 else (self.n + 1, ) * 2)
        random_point = homogeneous_representation(random_rotation,
                                                  random_translation,
                                                  output_shape)
        return random_point

    @classmethod
    def inverse(cls, point):
        """Return the inverse of a point.

        Parameters
        ----------
        point : array-like, shape=[..., n + 1, n + 1]
            Point to be inverted.

        Returns
        -------
        inverse : array-like, shape=[..., n + 1, n + 1]
            Inverse of point.
        """
        n = point.shape[-1] - 1
        transposed_rot = Matrices.transpose(point[..., :n, :n])
        translation = point[..., :n, -1]
        translation = gs.einsum("...ij,...j->...i", transposed_rot,
                                translation)
        return homogeneous_representation(transposed_rot, -translation,
                                          point.shape)

    def projection(self, mat):
        """Project a matrix on SE(n).

        The upper-left n x n block is projected to SO(n) by minimizing the
        Frobenius norm. The last columns is kept unchanged and used as the
        translation part. The last row is discarded.

        Parameters
        ----------
        mat : array-like, shape=[..., n + 1, n + 1]
            Matrix.

        Returns
        -------
        projected : array-like, shape=[..., n + 1, n + 1]
            Rotation-translation matrix in homogeneous representation.
        """
        n = mat.shape[-1] - 1
        projected_rot = self.rotations.projection(mat[..., :n, :n])
        translation = mat[..., :n, -1]
        return homogeneous_representation(projected_rot, translation,
                                          mat.shape)
Ejemplo n.º 3
0
class TestSpecialOrthogonal2(geomstats.tests.TestCase):
    def setup_method(self):
        warnings.simplefilter("ignore", category=ImportWarning)
        warnings.simplefilter("ignore", category=UserWarning)

        gs.random.seed(1234)

        self.group = SpecialOrthogonal(n=2, point_type="vector")

        # -- Set attributes
        self.n_samples = 4

    def test_projection(self):
        # Test 2D and nD cases
        rot_mat = gs.eye(2)
        delta = 1e-12 * gs.ones((2, 2))
        rot_mat_plus_delta = rot_mat + delta
        result = self.group.projection(rot_mat_plus_delta)
        expected = rot_mat
        self.assertAllClose(result, expected)

    def test_projection_vectorization(self):
        n_samples = self.n_samples
        mats = gs.ones((n_samples, 2, 2))
        result = self.group.projection(mats)
        self.assertAllClose(gs.shape(result), (n_samples, 2, 2))

    def test_skew_matrix_from_vector(self):
        rot_vec = gs.array([0.9])
        skew_matrix = self.group.skew_matrix_from_vector(rot_vec)
        result = gs.matmul(skew_matrix, skew_matrix)
        diag = gs.array([-0.81, -0.81])
        expected = algebra_utils.from_vector_to_diagonal_matrix(diag)
        self.assertAllClose(result, expected)

    def test_skew_matrix_and_vector(self):
        rot_vec = gs.array([0.8])

        skew_mat = self.group.skew_matrix_from_vector(rot_vec)
        result = self.group.vector_from_skew_matrix(skew_mat)
        expected = rot_vec

        self.assertAllClose(result, expected)

    def test_skew_matrix_from_vector_vectorization(self):
        n_samples = self.n_samples
        rot_vecs = self.group.random_uniform(n_samples=n_samples)
        result = self.group.skew_matrix_from_vector(rot_vecs)

        self.assertAllClose(gs.shape(result), (n_samples, 2, 2))

    def test_random_uniform_shape(self):
        result = self.group.random_uniform()
        self.assertAllClose(gs.shape(result), (self.group.dim, ))

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

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

    def test_regularize(self):
        angle = 2 * gs.pi + 1
        result = self.group.regularize(gs.array([angle]))
        expected = gs.array([1.0])
        self.assertAllClose(result, expected)

    def test_regularize_vectorization(self):
        n_samples = self.n_samples
        rot_vecs = self.group.random_uniform(n_samples=n_samples)
        result = self.group.regularize(rot_vecs)

        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

    def test_matrix_from_rotation_vector(self):
        angle = gs.pi / 3
        expected = gs.array([[1.0 / 2, -gs.sqrt(3.0) / 2],
                             [gs.sqrt(3.0) / 2, 1.0 / 2]])
        result = self.group.matrix_from_rotation_vector(gs.array([angle]))
        self.assertAllClose(result, expected)

    def test_matrix_from_rotation_vector_vectorization(self):
        n_samples = self.n_samples
        rot_vecs = self.group.random_uniform(n_samples=n_samples)

        rot_mats = self.group.matrix_from_rotation_vector(rot_vecs)

        self.assertAllClose(gs.shape(rot_mats),
                            (n_samples, self.group.n, self.group.n))

    def test_rotation_vector_from_matrix(self):
        angle = 0.12
        rot_mat = gs.array([[gs.cos(angle), -gs.sin(angle)],
                            [gs.sin(angle), gs.cos(angle)]])
        result = self.group.rotation_vector_from_matrix(rot_mat)
        expected = gs.array([0.12])

        self.assertAllClose(result, expected)

    def test_rotation_vector_and_rotation_matrix(self):
        """
        This tests that the composition of
        rotation_vector_from_matrix
        and
        matrix_from_rotation_vector
        is the identity.
        """
        # TODO(nguigs): bring back a 1d representation of SO2
        point = gs.array([0.78])

        rot_mat = self.group.matrix_from_rotation_vector(point)
        result = self.group.rotation_vector_from_matrix(rot_mat)

        expected = point

        self.assertAllClose(result, expected)

    def test_rotation_vector_and_rotation_matrix_vectorization(self):
        rot_vecs = gs.array([[2.0], [1.3], [0.8], [0.03]])

        rot_mats = self.group.matrix_from_rotation_vector(rot_vecs)
        result = self.group.rotation_vector_from_matrix(rot_mats)

        expected = self.group.regularize(rot_vecs)

        self.assertAllClose(result, expected)

    def test_compose(self):
        point_a = gs.array([0.12])
        point_b = gs.array([-0.15])
        result = self.group.compose(point_a, point_b)
        expected = self.group.regularize(gs.array([-0.03]))
        self.assertAllClose(result, expected)

    def test_compose_and_inverse(self):
        angle = 0.986
        point = gs.array([angle])
        inv_point = self.group.inverse(point)
        result = self.group.compose(point, inv_point)
        expected = self.group.identity
        self.assertAllClose(result, expected)

        result = self.group.compose(inv_point, point)
        expected = self.group.identity
        self.assertAllClose(result, expected)

    def test_compose_vectorization(self):
        point_type = "vector"
        self.group.default_point_type = point_type

        n_samples = self.n_samples
        n_points_a = self.group.random_uniform(n_samples=n_samples)
        n_points_b = self.group.random_uniform(n_samples=n_samples)
        one_point = self.group.random_uniform(n_samples=1)

        result = self.group.compose(one_point, n_points_a)
        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

        result = self.group.compose(n_points_a, one_point)
        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

        result = self.group.compose(n_points_a, n_points_b)
        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

    def test_inverse_vectorization(self):
        n_samples = self.n_samples
        points = self.group.random_uniform(n_samples=n_samples)
        result = self.group.inverse(points)

        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

    def test_group_exp(self):
        """
        The Riemannian exp and log are inverse functions of each other.
        This test is the inverse of test_log's.
        """
        rot_vec_base_point = gs.array([gs.pi / 5])
        rot_vec = gs.array([2 * gs.pi / 5])

        expected = gs.array([3 * gs.pi / 5])
        result = self.group.exp(base_point=rot_vec_base_point,
                                tangent_vec=rot_vec)
        self.assertAllClose(result, expected)

    def test_group_exp_vectorization(self):
        n_samples = self.n_samples

        one_tangent_vec = self.group.random_uniform(n_samples=1)
        one_base_point = self.group.random_uniform(n_samples=1)
        n_tangent_vec = self.group.random_uniform(n_samples=n_samples)
        n_base_point = self.group.random_uniform(n_samples=n_samples)

        # Test with the 1 base point, and n tangent vecs
        result = self.group.exp(n_tangent_vec, one_base_point)
        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

        # Test with the several base point, and one tangent vec
        result = self.group.exp(one_tangent_vec, n_base_point)
        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

        # Test with the same number n of base point and n tangent vec
        result = self.group.exp(n_tangent_vec, n_base_point)
        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

    def test_group_log(self):
        """
        The Riemannian exp and log are inverse functions of each other.
        This test is the inverse of test_exp's.
        """
        rot_vec_base_point = gs.array([gs.pi / 5])
        rot_vec = gs.array([2 * gs.pi / 5])

        expected = gs.array([1 * gs.pi / 5])
        result = self.group.log(point=rot_vec, base_point=rot_vec_base_point)
        self.assertAllClose(result, expected)

    def test_group_log_vectorization(self):
        n_samples = self.n_samples

        one_point = self.group.random_uniform(n_samples=1)
        one_base_point = self.group.random_uniform(n_samples=1)
        n_point = self.group.random_uniform(n_samples=n_samples)
        n_base_point = self.group.random_uniform(n_samples=n_samples)

        # Test with the 1 base point, and several different points
        result = self.group.log(n_point, one_base_point)
        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

        # Test with the several base point, and 1 point
        result = self.group.log(one_point, n_base_point)
        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

        # Test with the same number n of base point and point
        result = self.group.log(n_point, n_base_point)
        self.assertAllClose(gs.shape(result), (n_samples, self.group.dim))

    def test_group_exp_then_log_from_identity(self):
        """
        Test that the group exponential
        and the group logarithm are inverse.
        Expect their composition to give the identity function.
        """
        tangent_vec = gs.array([0.12])
        result = helper.group_exp_then_log_from_identity(
            group=self.group, tangent_vec=tangent_vec)
        expected = self.group.regularize(tangent_vec)
        self.assertAllClose(result, expected)

    def test_group_log_then_exp_from_identity(self):
        """
        Test that the group exponential
        and the group logarithm are inverse.
        Expect their composition to give the identity function.
        """
        point = gs.array([0.12])
        result = helper.group_log_then_exp_from_identity(group=self.group,
                                                         point=point)
        expected = self.group.regularize(point)
        self.assertAllClose(result, expected)

    def test_group_exp_then_log(self):
        """
        This tests that the composition of
        log and exp gives identity.

        """
        base_point = gs.array([0.12])
        tangent_vec = gs.array([0.35])

        result = helper.group_exp_then_log(group=self.group,
                                           tangent_vec=tangent_vec,
                                           base_point=base_point)

        expected = self.group.regularize_tangent_vec(tangent_vec=tangent_vec,
                                                     base_point=base_point)

        self.assertAllClose(result, expected)

    def test_group_log_then_exp(self):
        """
        This tests that the composition of
        log and exp gives identity.
        """
        base_point = gs.array([0.12])
        point = gs.array([0.35])

        result = helper.group_log_then_exp(group=self.group,
                                           point=point,
                                           base_point=base_point)

        expected = self.group.regularize(point)

        self.assertAllClose(result, expected)