def test_to_grassmanniann_vectorized(self): inf_rots = gs.array([gs.pi * r_z / n for n in [2, 3, 4]]) rots = GeneralLinear.exp(inf_rots) points = Matrices.mul(rots, point1) result = Stiefel.to_grassmannian(points) expected = gs.array([p_xy, p_xy, p_xy]) self.assertAllClose(result, expected)
def random_uniform(self, n_samples=1): """Define a log-uniform random sample of SPD matrices.""" n = self.n size = (n_samples, n, n) if n_samples != 1 else (n, n) mat = 2 * gs.random.rand(*size) - 1 spd_mat = GeneralLinear.exp(mat + Matrices.transpose(mat)) return spd_mat
def to_grassmannian_test_data(self): point1 = gs.array([[1.0, -1.0], [1.0, 1.0], [0.0, 0.0]]) / gs.sqrt(2.0) batch_points = Matrices.mul( GeneralLinear.exp(gs.array([gs.pi * r_z / n for n in [2, 3, 4]])), point1, ) smoke_data = [ dict(point=point1, expected=p_xy), dict(point=batch_points, expected=gs.array([p_xy, p_xy, p_xy])), ] return self.generate_tests(smoke_data)
def exp(self, tangent_vec, base_point=None, n_steps=10, step="rk4", **kwargs): """Exponential map associated to the cannonical metric. Exponential map at `base_point` of `tangent_vec`. The geodesics of this metric correspond to a direct product metric between rotation and translation: the translation part is a straight line, while the rotation part has constant angular velocity (which corresponds to one- parameter subgroups of the rotation group). Parameters ---------- tangent_vec : array-like, shape=[..., n + 1, n + 1] Tangent vector at the base point. base_point : array-like, shape=[..., n + 1, n + 1] Point on the manifold. Returns ------- exp : array-like, shape=[..., n + 1, n + 1] Point on the manifold. See Also -------- examples.plot_geodesics_se2 """ group = self.group if base_point is None: base_point = group.identity inf_rotation = tangent_vec[..., :self.n, :self.n] rotation = base_point[..., :self.n, :self.n] rotation_exp = GeneralLinear.exp(inf_rotation, rotation) translation_exp = (tangent_vec[..., :self.n, self.n] + base_point[..., :self.n, self.n]) exp = homogeneous_representation(rotation_exp, translation_exp, tangent_vec.shape, 1.0) return exp
def random_uniform(self, n_samples=1): """Sample in SPD(n) from the log-uniform distribution. Parameters ---------- n_samples : int Number of samples. Optional, default: 1. Returns ------- samples : array-like, shape=[..., n, n] Points sampled in SPD(n). """ n = self.n size = (n_samples, n, n) if n_samples != 1 else (n, n) mat = 2 * gs.random.rand(*size) - 1 spd_mat = GeneralLinear.exp(mat + Matrices.transpose(mat)) return spd_mat
def random_point(self, n_samples=1, bound=1.0): r"""Sample in PSD(n,k) from the log-uniform distribution. Parameters ---------- n_samples : int Number of samples. Optional, default: 1. bound : float Bound of the interval in which to sample in the tangent space. Optional, default: 1. Returns ------- samples : array-like, shape=[..., n, n] Points sampled in PSD(n,k). """ n = self.n size = (n_samples, n, n) if n_samples != 1 else (n, n) mat = bound * (2 * gs.random.rand(*size) - 1) spd_mat = GeneralLinear.exp(Matrices.to_symmetric(mat)) return self.projection(spd_mat)
class TestGeneralLinear(geomstats.tests.TestCase): def setUp(self): gs.random.seed(1234) self.n = 3 self.n_samples = 2 self.group = GeneralLinear(n=self.n) warnings.simplefilter('ignore', category=ImportWarning) def test_belongs_shape(self): mat = gs.eye(3) result = self.group.belongs(mat) self.assertAllClose(gs.shape(result), ()) mat = gs.ones((3, 3)) result = self.group.belongs(mat) self.assertAllClose(gs.shape(result), ()) def test_belongs(self): mat = gs.eye(3) result = self.group.belongs(mat) expected = True self.assertAllClose(result, expected) mat = gs.ones((3, 3)) result = self.group.belongs(mat) expected = False self.assertAllClose(result, expected) def test_belongs_vectorization_shape(self): mats = gs.array([gs.eye(3), gs.ones((3, 3))]) result = self.group.belongs(mats) self.assertAllClose(gs.shape(result), (2, )) def test_belongs_vectorization(self): mats = gs.array([gs.eye(3), gs.ones((3, 3))]) result = self.group.belongs(mats) expected = gs.array([True, False]) self.assertAllClose(result, expected) 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 = 4 point = self.group.random_uniform(n_samples) result = self.group.belongs(point) expected = gs.array([True] * n_samples) self.assertAllClose(result, expected) def test_replace_values(self): points = gs.ones((3, 3, 3)) new_points = gs.zeros((2, 3, 3)) indcs = [True, False, True] update = self.group._replace_values(points, new_points, indcs) self.assertAllClose( update, gs.stack([gs.zeros((3, 3)), gs.ones((3, 3)), gs.zeros((3, 3))])) def test_compose(self): mat1 = gs.array([[1., 0.], [0., 2.]]) mat2 = gs.array([[2., 0.], [0., 1.]]) result = self.group.compose(mat1, mat2) expected = 2. * GeneralLinear(2).identity self.assertAllClose(result, expected) def test_inv(self): mat_a = gs.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 10.]]) imat_a = 1. / 3. * gs.array([[-2., -4., 3.], [-2., 11., -6.], [3., -6., 3.]]) expected = imat_a result = self.group.inverse(mat_a) self.assertAllClose(result, expected) def test_inv_vectorized(self): mat_a = gs.array([[0., 1., 0.], [1., 0., 0.], [0., 0., 1.]]) mat_b = -gs.eye(3, 3) result = self.group.inverse(gs.array([mat_a, mat_b])) expected = gs.array([mat_a, mat_b]) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_log_and_exp(self): point = 5 * gs.eye(self.n) group_log = self.group.log(point) result = self.group.exp(group_log) expected = point self.assertAllClose(result, expected) def test_exp_vectorization(self): point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]], [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]]) expected = gs.array([[[7.38905609, 0., 0.], [0., 20.0855369, 0.], [0., 0., 54.5981500]], [[2.718281828, 0., 0.], [0., 148.413159, 0.], [0., 0., 403.42879349]]]) result = self.group.exp(point) self.assertAllClose(result, expected, rtol=1e-3) @geomstats.tests.np_and_tf_only def test_log_vectorization(self): point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]], [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]]) expected = gs.array([[[0.693147180, 0., 0.], [0., 1.09861228866, 0.], [0., 0., 1.38629436]], [[0., 0., 0.], [0., 1.609437912, 0.], [0., 0., 1.79175946]]]) result = self.group.log(point) self.assertAllClose(result, expected, atol=1e-4) @geomstats.tests.np_and_tf_only def test_orbit(self): point = gs.array([[gs.exp(4.), 0.], [0., gs.exp(2.)]]) sqrt = gs.array([[gs.exp(2.), 0.], [0., gs.exp(1.)]]) idty = GeneralLinear(2).identity path = GeneralLinear(2).orbit(point) time = gs.linspace(0., 1., 3) result = path(time) expected = gs.array([idty, sqrt, point]) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_expm_and_logm_vectorization_symmetric(self): point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]], [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]]) result = self.group.exp(self.group.log(point)) expected = point self.assertAllClose(result, expected)
def random_uniform(self, n_samples=1): """Define a log-uniform random sample of SPD matrices.""" mat = 2 * gs.random.rand(n_samples, self.n, self.n) - 1 spd_mat = GeneralLinear.exp(mat + Matrices.transpose(mat)) return spd_mat
class TestGeneralLinear(geomstats.tests.TestCase): def setUp(self): gs.random.seed(1234) self.n = 3 self.n_samples = 2 self.group = GeneralLinear(n=self.n) self.group_pos = GeneralLinear(self.n, positive_det=True) warnings.simplefilter('ignore', category=ImportWarning) def test_belongs_shape(self): mat = gs.eye(3) result = self.group.belongs(mat) self.assertAllClose(gs.shape(result), ()) mat = gs.ones((3, 3)) result = self.group.belongs(mat) self.assertAllClose(gs.shape(result), ()) def test_belongs(self): mat = gs.eye(3) result = self.group.belongs(mat) expected = True self.assertAllClose(result, expected) mat = gs.ones((3, 3)) result = self.group.belongs(mat) expected = False self.assertAllClose(result, expected) mat = gs.ones(3) result = self.group.belongs(mat) expected = False self.assertAllClose(result, expected) def test_belongs_vectorization_shape(self): mats = gs.array([gs.eye(3), gs.ones((3, 3))]) result = self.group.belongs(mats) self.assertAllClose(gs.shape(result), (2, )) def test_belongs_vectorization(self): mats = gs.array([gs.eye(3), gs.ones((3, 3))]) result = self.group.belongs(mats) expected = gs.array([True, False]) self.assertAllClose(result, expected) def test_random_and_belongs(self): for group in [self.group, self.group_pos]: point = group.random_point() result = group.belongs(point) self.assertTrue(result) def test_random_and_belongs_vectorization(self): n_samples = 4 expected = gs.array([True] * n_samples) for group in [self.group, self.group_pos]: point = group.random_point(n_samples) result = group.belongs(point) self.assertAllClose(result, expected) def test_compose(self): mat1 = gs.array([[1., 0.], [0., 2.]]) mat2 = gs.array([[2., 0.], [0., 1.]]) result = self.group.compose(mat1, mat2) expected = 2. * GeneralLinear(2).identity self.assertAllClose(result, expected) def test_inv(self): mat_a = gs.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 10.]]) imat_a = 1. / 3. * gs.array([[-2., -4., 3.], [-2., 11., -6.], [3., -6., 3.]]) expected = imat_a result = self.group.inverse(mat_a) self.assertAllClose(result, expected) def test_inv_vectorized(self): mat_a = gs.array([[0., 1., 0.], [1., 0., 0.], [0., 0., 1.]]) mat_b = -gs.eye(3, 3) result = self.group.inverse(gs.array([mat_a, mat_b])) expected = gs.array([mat_a, mat_b]) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_log_and_exp(self): point = 5 * gs.eye(self.n) group_log = self.group.log(point) result = self.group.exp(group_log) expected = point self.assertAllClose(result, expected) def test_exp_vectorization(self): point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]], [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]]) expected = gs.array([[[7.38905609, 0., 0.], [0., 20.0855369, 0.], [0., 0., 54.5981500]], [[2.718281828, 0., 0.], [0., 148.413159, 0.], [0., 0., 403.42879349]]]) expected = gs.cast(expected, gs.float64) point = gs.cast(point, gs.float64) result = self.group.exp(point) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_log_vectorization(self): point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]], [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]]) expected = gs.array([[[0.693147180, 0., 0.], [0., 1.09861228866, 0.], [0., 0., 1.38629436]], [[0., 0., 0.], [0., 1.609437912, 0.], [0., 0., 1.79175946]]]) result = self.group.log(point) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_orbit(self): point = gs.array([[gs.exp(4.), 0.], [0., gs.exp(2.)]]) sqrt = gs.array([[gs.exp(2.), 0.], [0., gs.exp(1.)]]) identity = GeneralLinear(2).identity path = GeneralLinear(2).orbit(point) time = gs.linspace(0., 1., 3) result = path(time) expected = gs.array([identity, sqrt, point]) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_orbit_vectorization(self): point = gs.array([[gs.exp(4.), 0.], [0., gs.exp(2.)]]) sqrt = gs.array([[gs.exp(2.), 0.], [0., gs.exp(1.)]]) identity = GeneralLinear(2).identity path = GeneralLinear(2).orbit(gs.stack([point] * 2), identity) time = gs.linspace(0., 1., 3) result = path(time) expected = gs.array([identity, sqrt, point]) expected = gs.stack([expected] * 2) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_expm_and_logm_vectorization_symmetric(self): point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]], [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]]) result = self.group.exp(self.group.log(point)) expected = point self.assertAllClose(result, expected) def test_projection_and_belongs(self): shape = (self.n_samples, self.n, self.n) result = helper.test_projection_and_belongs(self.group, shape) for res in result: self.assertTrue(res) def test_projection_and_belongs_pos(self): shape = (self.n_samples, self.n, self.n) result = helper.test_projection_and_belongs(self.group_pos, shape) for res in result: self.assertTrue(res)
class TestGeneralLinearMethods(geomstats.tests.TestCase): def setUp(self): gs.random.seed(1234) self.n = 3 self.n_samples = 2 self.group = GeneralLinear(n=self.n) # We generate invertible matrices using so3_group self.so3_group = SpecialOrthogonal(n=self.n) warnings.simplefilter('ignore', category=ImportWarning) @geomstats.tests.np_only def test_belongs(self): """ A rotation matrix belongs to the matrix Lie group of invertible matrices. """ rot_vec = gs.array([0.2, -0.1, 0.1]) rot_mat = self.so3_group.matrix_from_rotation_vector(rot_vec) result = self.group.belongs(rot_mat) expected = gs.array([[True]]) self.assertAllClose(result, expected) def test_compose(self): # 1. Composition by identity, on the right # Expect the original transformation rot_vec = gs.array([0.2, -0.1, 0.1]) mat = self.so3_group.matrix_from_rotation_vector(rot_vec) result = self.group.compose(mat, self.group.identity) expected = mat expected = helper.to_matrix(mat) self.assertAllClose(result, expected) # 2. Composition by identity, on the left # Expect the original transformation rot_vec = gs.array([0.2, 0.1, -0.1]) mat = self.so3_group.matrix_from_rotation_vector(rot_vec) result = self.group.compose(self.group.identity, mat) expected = mat self.assertAllClose(result, expected) def test_inverse(self): mat = gs.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 10.]]) result = self.group.inverse(mat) expected = 1. / 3. * gs.array([[-2., -4., 3.], [-2., 11., -6.], [3., -6., 3.]]) expected = helper.to_matrix(expected) self.assertAllClose(result, expected) def test_compose_and_inverse(self): # 1. Compose transformation by its inverse on the right # Expect the group identity rot_vec = gs.array([0.2, 0.1, 0.1]) mat = self.so3_group.matrix_from_rotation_vector(rot_vec) inv_mat = self.group.inverse(mat) result = self.group.compose(mat, inv_mat) expected = self.group.identity expected = helper.to_matrix(expected) self.assertAllClose(result, expected) # 2. Compose transformation by its inverse on the left # Expect the group identity rot_vec = gs.array([0.7, 0.1, 0.1]) mat = self.so3_group.matrix_from_rotation_vector(rot_vec) inv_mat = self.group.inverse(mat) result = self.group.compose(inv_mat, mat) expected = self.group.identity expected = helper.to_matrix(expected) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_group_log_and_exp(self): point = 5 * gs.eye(self.n) group_log = self.group.log(point) result = self.group.exp(group_log) expected = point expected = helper.to_matrix(expected) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_group_exp_vectorization(self): point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]], [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]]) expected = gs.array([[[7.38905609, 0., 0.], [0., 20.0855369, 0.], [0., 0., 54.5981500]], [[2.718281828, 0., 0.], [0., 148.413159, 0.], [0., 0., 403.42879349]]]) result = self.group.exp(point) self.assertAllClose(result, expected, rtol=1e-3) @geomstats.tests.np_and_tf_only def test_group_log_vectorization(self): point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]], [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]]) expected = gs.array([[[0.693147180, 0., 0.], [0., 1.09861228866, 0.], [0., 0., 1.38629436]], [[0., 0., 0.], [0., 1.609437912, 0.], [0., 0., 1.79175946]]]) result = self.group.log(point) self.assertAllClose(result, expected, atol=1e-4) @geomstats.tests.np_and_tf_only def test_expm_and_logm_vectorization_symmetric(self): point = gs.array([[[2., 0., 0.], [0., 3., 0.], [0., 0., 4.]], [[1., 0., 0.], [0., 5., 0.], [0., 0., 6.]]]) result = self.group.exp(self.group.log(point)) expected = point self.assertAllClose(result, expected)
def exp_from_identity(self, tangent_vec, point_type=None): """Compute group exponential of the tangent vector at the identity. Parameters ---------- tangent_vec: array-like, shape=[n_samples, {dim, [n + 1, n + 1]}] point_type: str, {'vector', 'matrix'}, optional default: self.default_point_type Returns ------- group_exp: array-like, shape=[n_samples, {dim, [n + 1, n + 1]}] the group exponential of the tangent vectors calculated at the identity """ if point_type == 'vector': rotations = self.rotations dim_rotations = rotations.dim rot_vec = tangent_vec[:, :dim_rotations] rot_vec = self.rotations.regularize(rot_vec, point_type=point_type) translation = tangent_vec[:, dim_rotations:] angle = gs.linalg.norm(rot_vec, axis=1) angle = gs.to_ndarray(angle, to_ndim=2, axis=1) skew_mat = self.rotations.skew_matrix_from_vector(rot_vec) sq_skew_mat = gs.matmul(skew_mat, skew_mat) mask_0 = gs.equal(angle, 0.) mask_close_0 = gs.isclose(angle, 0.) & ~mask_0 mask_else = ~mask_0 & ~mask_close_0 mask_0_float = gs.cast(mask_0, gs.float32) mask_close_0_float = gs.cast(mask_close_0, gs.float32) mask_else_float = gs.cast(mask_else, gs.float32) angle += mask_0_float * gs.ones_like(angle) coef_1 = gs.zeros_like(angle) coef_2 = gs.zeros_like(angle) coef_1 += mask_0_float * 1. / 2. * gs.ones_like(angle) coef_2 += mask_0_float * 1. / 6. * gs.ones_like(angle) coef_1 += mask_close_0_float * ( TAYLOR_COEFFS_1_AT_0[0] + TAYLOR_COEFFS_1_AT_0[2] * angle**2 + TAYLOR_COEFFS_1_AT_0[4] * angle**4 + TAYLOR_COEFFS_1_AT_0[6] * angle**6) coef_2 += mask_close_0_float * ( TAYLOR_COEFFS_2_AT_0[0] + TAYLOR_COEFFS_2_AT_0[2] * angle**2 + TAYLOR_COEFFS_2_AT_0[4] * angle**4 + TAYLOR_COEFFS_2_AT_0[6] * angle**6) coef_1 += mask_else_float * ((1. - gs.cos(angle)) / angle**2) coef_2 += mask_else_float * ((angle - gs.sin(angle)) / angle**3) n_tangent_vecs, _ = tangent_vec.shape exp_translation = gs.zeros((n_tangent_vecs, self.n)) for i in range(n_tangent_vecs): translation_i = translation[i] term_1_i = coef_1[i] * gs.dot(translation_i, gs.transpose(skew_mat[i])) term_2_i = coef_2[i] * gs.dot(translation_i, gs.transpose(sq_skew_mat[i])) mask_i_float = gs.get_mask_i_float(i, n_tangent_vecs) exp_translation += gs.outer( mask_i_float, translation_i + term_1_i + term_2_i) group_exp = gs.concatenate([rot_vec, exp_translation], axis=1) group_exp = self.regularize(group_exp, point_type=point_type) return group_exp if point_type == 'matrix': return GeneralLinear.exp(tangent_vec) raise ValueError('Invalid point_type, expected \'vector\' or ' '\'matrix\'.')