class TestLieAlgebraMethods(geomstats.tests.TestCase): def setUp(self): self.n = 4 self.dim = int(self.n * (self.n - 1) / 2) self.algebra = SkewSymmetricMatrices(n=self.n) def test_dimension(self): result = self.algebra.dim expected = self.dim self.assertAllClose(result, expected) @geomstats.tests.np_only def test_matrix_representation_and_belongs(self): n_samples = 2 point = gs.random.rand(n_samples * self.dim) point = gs.reshape(point, (n_samples, self.dim)) mat = self.algebra.matrix_representation(point) result = self.algebra.belongs(mat).all() expected = True self.assertAllClose(result, expected) @geomstats.tests.np_only def test_basis_and_matrix_representation(self): n_samples = 2 expected = gs.random.rand(n_samples * self.dim) expected = gs.reshape(expected, (n_samples, self.dim)) mat = self.algebra.matrix_representation(expected) result = self.algebra.basis_representation(mat) self.assertAllClose(result, expected)
class _SpecialOrthogonalMatrices(GeneralLinear, LieGroup): """Class for special orthogonal groups in matrix representation. Parameters ---------- n : int Integer representing the shape of the matrices: n x n. """ def __init__(self, n): super(_SpecialOrthogonalMatrices, self).__init__(dim=int((n * (n - 1)) / 2), default_point_type='matrix', n=n) self.lie_algebra = SkewSymmetricMatrices(n=n) self.bi_invariant_metric = BiInvariantMetric(group=self) def belongs(self, point): """Check whether point is an orthogonal matrix.""" return self.equal(self.mul(point, self.transpose(point)), self.identity) @classmethod def inverse(cls, point): """Return the transpose matrix of point.""" return cls.transpose(point) def _is_in_lie_algebra(self, tangent_vec, atol=ATOL): return self.lie_algebra.belongs(tangent_vec, atol=atol) @classmethod def _to_lie_algebra(cls, tangent_vec): """Project vector onto skew-symmetric matrices.""" return cls.to_skew_symmetric(tangent_vec) def random_uniform(self, n_samples=1, tol=1e-6): """Sample in SO(n) from the uniform distribution. Parameters ---------- n_samples : int, optional (1) Number of samples. tol : unused Returns ------- samples : array-like, shape=[..., n, n] Points sampled on the SO(n). """ if n_samples == 1: random_mat = gs.random.rand(self.n, self.n) else: random_mat = gs.random.rand(n_samples, self.n, self.n) skew = self.to_tangent(random_mat) return self.exp(skew)
class TestLieAlgebra(geomstats.tests.TestCase): def setUp(self): self.n = 4 self.dim = int(self.n * (self.n - 1) / 2) self.algebra = SkewSymmetricMatrices(n=self.n) def test_dimension(self): result = self.algebra.dim expected = self.dim self.assertAllClose(result, expected) def test_matrix_representation_and_belongs(self): n_samples = 2 point = gs.random.rand(n_samples * self.dim) point = gs.reshape(point, (n_samples, self.dim)) mat = self.algebra.matrix_representation(point) result = gs.all(self.algebra.belongs(mat)) expected = True self.assertAllClose(result, expected) @geomstats.tests.np_and_pytorch_only def test_basis_and_matrix_representation(self): n_samples = 2 expected = gs.random.rand(n_samples * self.dim) expected = gs.reshape(expected, (n_samples, self.dim)) mat = self.algebra.matrix_representation(expected) result = self.algebra.basis_representation(mat) self.assertAllClose(result, expected) def test_orthonormal_basis(self): group = SpecialOrthogonal(3) lie_algebra = SkewSymmetricMatrices(3) metric = InvariantMetric(group=group, algebra=lie_algebra) basis = lie_algebra.orthonormal_basis(metric.metric_mat_at_identity) result = metric.inner_product_at_identity(basis[0], basis[1]) self.assertAllClose(result, 0.) result = metric.inner_product_at_identity(basis[1], basis[1]) self.assertAllClose(result, 1.) metric_mat = from_vector_to_diagonal_matrix(gs.array([1., 2., 3.])) metric = InvariantMetric(group=group, algebra=lie_algebra, metric_mat_at_identity=metric_mat) basis = lie_algebra.orthonormal_basis(metric.metric_mat_at_identity) result = metric.inner_product_at_identity(basis[0], basis[1]) self.assertAllClose(result, 0.) result = metric.inner_product_at_identity(basis[1], basis[1]) self.assertAllClose(result, 1.)
class SpecialEuclideanMatrixLieAlgebra(MatrixLieAlgebra): r"""Lie Algebra of the special Euclidean group. This is the tangent space at the identity. It is identified with the :math:`n + 1 \times n + 1` block matrices of the form: .. math: ((A, t), (0, 0)) where A is an :math:`n \times n` skew-symmetric matrix, :math: `t` is an n-dimensional vector. Parameters ---------- n : int Integer dimension of the underlying Euclidean space. Matrices will be of size: (n+1) x (n+1). """ def __init__(self, n): dim = int(n * (n + 1) / 2) super(SpecialEuclideanMatrixLieAlgebra, self).__init__(dim, n + 1) self.skew = SkewSymmetricMatrices(n) self.n = n def _create_basis(self): """Create the canonical basis.""" n = self.n basis = homogeneous_representation( self.skew.basis, gs.zeros((self.skew.dim, n)), (self.skew.dim, n + 1, n + 1), 0.0, ) basis = list(basis) for row in gs.arange(n): basis.append( gs.array_from_sparse([(row, n)], [1.0], (n + 1, n + 1))) return gs.stack(basis) def belongs(self, mat, atol=ATOL): """Evaluate if the rotation part of mat is a skew-symmetric matrix. Parameters ---------- mat : array-like, shape=[..., n + 1, n + 1] Square matrix to check. atol : float Tolerance for the equality evaluation. Optional, default: backend atol. Returns ------- belongs : array-like, shape=[...,] Boolean evaluating if rotation part of matrix is skew symmetric. """ point_dim1, point_dim2 = mat.shape[-2:] belongs = point_dim1 == point_dim2 == self.n + 1 rotation = mat[..., :self.n, :self.n] rot_belongs = self.skew.belongs(rotation, atol=atol) belongs = gs.logical_and(belongs, rot_belongs) last_line = mat[..., -1, :] all_zeros = ~gs.any(last_line, axis=-1) belongs = gs.logical_and(belongs, all_zeros) return belongs def random_point(self, n_samples=1, bound=1.0): """Sample in the lie algebra with a uniform distribution in a box. Parameters ---------- n_samples : int Number of samples. Optional, default: 1. bound : float Side of hypercube support of the uniform distribution. Optional, default: 1.0 Returns ------- point : array-like, shape=[..., n + 1, n + 1] Sample. """ point = super(SpecialEuclideanMatrixLieAlgebra, self).random_point(n_samples, bound) return self.projection(point) def projection(self, mat): """Project a matrix to the Lie Algebra. Compute the skew-symmetric projection of the rotation part of matrix. Parameters ---------- mat : array-like, shape=[..., n + 1, n + 1] Matrix. Returns ------- projected : array-like, shape=[..., n + 1, n + 1] Matrix belonging to Lie Algebra. """ rotation = mat[..., :self.n, :self.n] skew = SkewSymmetricMatrices.projection(rotation) return homogeneous_representation(skew, mat[..., :self.n, self.n], mat.shape, 0.0) def basis_representation(self, matrix_representation): """Calculate the coefficients of given matrix in the basis. Compute a 1d-array that corresponds to the input matrix in the basis representation. Parameters ---------- matrix_representation : array-like, shape=[..., n + 1, n + 1] Matrix. Returns ------- basis_representation : array-like, shape=[..., dim] Representation in the basis. """ skew_part = self.skew.basis_representation( matrix_representation[..., :self.n, :self.n]) translation_part = matrix_representation[..., :-1, self.n] return gs.concatenate([skew_part, translation_part[..., :]], axis=-1)
class TestLieAlgebra(geomstats.tests.TestCase): def setup_method(self): self.n = 4 self.dim = int(self.n * (self.n - 1) / 2) self.algebra = SkewSymmetricMatrices(n=self.n) def test_dimension(self): result = self.algebra.dim expected = self.dim self.assertAllClose(result, expected) def test_matrix_representation_and_belongs(self): n_samples = 2 point = gs.random.rand(n_samples * self.dim) point = gs.reshape(point, (n_samples, self.dim)) mat = self.algebra.matrix_representation(point) result = gs.all(self.algebra.belongs(mat)) self.assertTrue(result) def test_basis_and_matrix_representation(self): n_samples = 2 expected = gs.random.rand(n_samples * self.dim) expected = gs.reshape(expected, (n_samples, self.dim)) mat = self.algebra.matrix_representation(expected) result = self.algebra.basis_representation(mat) self.assertAllClose(result, expected) def test_orthonormal_basis(self): group = SpecialOrthogonal(3) lie_algebra = SkewSymmetricMatrices(3) metric = InvariantMetric(group=group) basis = metric.normal_basis(lie_algebra.basis) result = metric.inner_product_at_identity(basis[0], basis[1]) self.assertAllClose(result, 0.0) result = metric.inner_product_at_identity(basis[1], basis[1]) self.assertAllClose(result, 1.0) metric_mat = from_vector_to_diagonal_matrix(gs.array([1.0, 2.0, 3.0])) metric = InvariantMetric(group=group, metric_mat_at_identity=metric_mat) basis = metric.normal_basis(lie_algebra.basis) result = metric.inner_product_at_identity(basis[0], basis[1]) self.assertAllClose(result, 0.0) result = metric.inner_product_at_identity(basis[1], basis[1]) self.assertAllClose(result, 1.0) def test_orthonormal_basis_se3(self): group = SpecialEuclidean(3) lie_algebra = group.lie_algebra metric = InvariantMetric(group=group) basis = metric.normal_basis(lie_algebra.basis) for i, x in enumerate(basis): for y in basis[i:]: result = metric.inner_product_at_identity(x, y) expected = 0.0 if gs.any(x != y) else 1.0 self.assertAllClose(result, expected) metric_mat = from_vector_to_diagonal_matrix( gs.cast(gs.arange(1, group.dim + 1), gs.float32) ) metric = InvariantMetric(group=group, metric_mat_at_identity=metric_mat) basis = metric.normal_basis(lie_algebra.basis) for i, x in enumerate(basis): for y in basis[i:]: result = metric.inner_product_at_identity(x, y) expected = 0.0 if gs.any(x != y) else 1.0 self.assertAllClose(result, expected)