def christoffels_dim_2_test_data(self): def coefficients(param_a, param_b): """Christoffel coefficients for the beta distributions.""" poly1a = gs.polygamma(1, param_a) poly2a = gs.polygamma(2, param_a) poly1b = gs.polygamma(1, param_b) poly2b = gs.polygamma(2, param_b) poly1ab = gs.polygamma(1, param_a + param_b) poly2ab = gs.polygamma(2, param_a + param_b) metric_det = 2 * (poly1a * poly1b - poly1ab * (poly1a + poly1b)) c1 = (poly2a * (poly1b - poly1ab) - poly1b * poly2ab) / metric_det c2 = -poly1b * poly2ab / metric_det c3 = (poly2b * poly1ab - poly1b * poly2ab) / metric_det return c1, c2, c3 gs.random.seed(123) n_points = 3 points = self.space(2).random_point(n_points) param_a, param_b = points[:, 0], points[:, 1] c1, c2, c3 = coefficients(param_a, param_b) c4, c5, c6 = coefficients(param_b, param_a) vector_0 = gs.stack([c1, c2, c3], axis=-1) vector_1 = gs.stack([c6, c5, c4], axis=-1) gamma_0 = SymmetricMatrices.from_vector(vector_0) gamma_1 = SymmetricMatrices.from_vector(vector_1) random_data = [ dict(point=points, expected=gs.stack([gamma_0, gamma_1], axis=-3)) ] return self.generate_tests([], random_data)
def test_christoffels(self): """Test Christoffel symbols in dimension 2. Check the Christoffel symbols in dimension 2. """ gs.random.seed(123) dirichlet2 = DirichletDistributions(2) points = dirichlet2.random_point(self.n_points) result = dirichlet2.metric.christoffels(points) def coefficients(param_a, param_b): """Christoffel coefficients for the beta distributions.""" poly1a = gs.polygamma(1, param_a) poly2a = gs.polygamma(2, param_a) poly1b = gs.polygamma(1, param_b) poly2b = gs.polygamma(2, param_b) poly1ab = gs.polygamma(1, param_a + param_b) poly2ab = gs.polygamma(2, param_a + param_b) metric_det = 2 * (poly1a * poly1b - poly1ab * (poly1a + poly1b)) c1 = (poly2a * (poly1b - poly1ab) - poly1b * poly2ab) / metric_det c2 = -poly1b * poly2ab / metric_det c3 = (poly2b * poly1ab - poly1b * poly2ab) / metric_det return c1, c2, c3 param_a, param_b = points[:, 0], points[:, 1] c1, c2, c3 = coefficients(param_a, param_b) c4, c5, c6 = coefficients(param_b, param_a) vector_0 = gs.stack([c1, c2, c3], axis=-1) vector_1 = gs.stack([c6, c5, c4], axis=-1) gamma_0 = SymmetricMatrices.from_vector(vector_0) gamma_1 = SymmetricMatrices.from_vector(vector_1) expected = gs.stack([gamma_0, gamma_1], axis=-3) self.assertAllClose(result, expected)
def christoffels(self, base_point): """Compute the Christoffel symbols. Compute the Christoffel symbols of the Fisher information metric on Beta. Parameters ---------- base_point : array-like, shape=[..., 2] Base point. Returns ------- christoffels : array-like, shape=[..., 2, 2, 2] Christoffel symbols. """ def coefficients(param_a, param_b): metric_det = 2 * self.metric_det(param_a, param_b) poly_2_ab = gs.polygamma(2, param_a + param_b) poly_1_ab = gs.polygamma(1, param_a + param_b) poly_1_b = gs.polygamma(1, param_b) c1 = (gs.polygamma(2, param_a) * (poly_1_b - poly_1_ab) - poly_1_b * poly_2_ab) / metric_det c2 = - poly_1_b * poly_2_ab / metric_det c3 = (gs.polygamma(2, param_b) * poly_1_ab - poly_1_b * poly_2_ab) / metric_det return c1, c2, c3 point_a, point_b = base_point[..., 0], base_point[..., 1] c4, c5, c6 = coefficients(point_b, point_a) vector_0 = gs.stack(coefficients(point_a, point_b), axis=-1) vector_1 = gs.stack([c6, c5, c4], axis=-1) gamma_0 = SymmetricMatrices.from_vector(vector_0) gamma_1 = SymmetricMatrices.from_vector(vector_1) return gs.stack([gamma_0, gamma_1], axis=-3)
def metric_matrix(self, base_point=None): """Compute inner-product matrix at the tangent space at base point. Parameters ---------- base_point : array-like, shape=[..., 2] Base point. Returns ------- mat : array-like, shape=[..., 2, 2] Inner-product matrix. """ if base_point is None: raise ValueError('A base point must be given to compute the ' 'metric matrix') param_a = base_point[..., 0] param_b = base_point[..., 1] polygamma_ab = gs.polygamma(1, param_a + param_b) polygamma_a = gs.polygamma(1, param_a) polygamma_b = gs.polygamma(1, param_b) vector = gs.stack( [polygamma_a - polygamma_ab, - polygamma_ab, polygamma_b - polygamma_ab], axis=-1) return SymmetricMatrices.from_vector(vector)
def test_metric_matrix_dim_2(self, point): param_a = point[..., 0] param_b = point[..., 1] vector = gs.stack( [ gs.polygamma(1, param_a) - gs.polygamma(1, param_a + param_b), -gs.polygamma(1, param_a + param_b), gs.polygamma(1, param_b) - gs.polygamma(1, param_a + param_b), ], axis=-1, ) expected = SymmetricMatrices.from_vector(vector) return self.assertAllClose( self.metric(2).metric_matrix(point), expected)
def test_metric_matrix_dim2(self): """Test metric matrix in dimension 2. Check the metric matrix in dimension 2. """ dirichlet2 = DirichletDistributions(2) points = dirichlet2.random_point(self.n_points) result = dirichlet2.metric.metric_matrix(points) param_a = points[:, 0] param_b = points[:, 1] polygamma_ab = gs.polygamma(1, param_a + param_b) polygamma_a = gs.polygamma(1, param_a) polygamma_b = gs.polygamma(1, param_b) vector = gs.stack( [polygamma_a - polygamma_ab, -polygamma_ab, polygamma_b - polygamma_ab], axis=-1, ) expected = SymmetricMatrices.from_vector(vector) self.assertAllClose(result, expected)
def upper_triangular_matrix_from_vector(point): """Compute the upper triangular matrix representation of the vector. The 3D Heisenberg group can also be represented as 3x3 upper triangular matrices. This function computes this representation of the vector 'point'. Parameters ---------- point : array-like, shape=[..., 3] Point in the vector-represention. Returns ------- upper_triangular_mat : array-like, shape=[..., 3, 3] Upper triangular matrix. """ n_points = gs.ndim(point) element_02 = point[..., 2] + 1 / 2 * point[..., 0] * point[..., 1] if n_points == 1: modified_point = gs.array( [1, point[0], element_02, 1, point[1], 1]) else: modified_point = gs.stack( ( gs.ones(n_points), point[..., 0], element_02, gs.ones(n_points), point[..., 1], gs.ones(n_points), ), axis=1, ) return gs.triu(SymmetricMatrices.from_vector(modified_point))
def inner_product_matrix(self, base_point): """Compute inner-product matrix at the tangent space at base point. Parameters ---------- base_point : array-like, shape=[..., 2] Base point. Returns ------- mat : array-like, shape=[..., 2, 2] Inner-product matrix. """ param_a = base_point[..., 0] param_b = base_point[..., 1] polygamma_ab = gs.polygamma(1, param_a + param_b) polygamma_a = gs.polygamma(1, param_a) polygamma_b = gs.polygamma(1, param_b) vector = gs.stack( [polygamma_a - polygamma_ab, - polygamma_ab, polygamma_b - polygamma_ab], axis=-1) return SymmetricMatrices.from_vector(vector)
def inner_product_matrix(self, base_point=None): """Compute inner product matrix at the tangent space at base point. Parameters ---------- base_point : array-like, shape=[..., 2] Returns ------- base_point : array-like, shape=[..., 2, 2] """ if base_point is None: raise ValueError('The metric depends on the base point.') param_a = base_point[..., 0] param_b = base_point[..., 1] polygamma_ab = gs.polygamma(1, param_a + param_b) polygamma_a = gs.polygamma(1, param_a) polygamma_b = gs.polygamma(1, param_b) vector = gs.stack([ polygamma_a - polygamma_ab, -polygamma_ab, polygamma_b - polygamma_ab ], axis=-1) return SymmetricMatrices.from_vector(vector)
class TestSymmetricMatrices(geomstats.tests.TestCase): """Test of SymmetricMatrices methods.""" def setUp(self): """Set up the test.""" warnings.simplefilter("ignore", category=ImportWarning) gs.random.seed(1234) self.n = 3 self.space = SymmetricMatrices(self.n) def test_belongs(self): """Test of belongs method.""" sym_n = self.space mat_sym = gs.array([[1.0, 2.0, 3.0], [2.0, 4.0, 5.0], [3.0, 5.0, 6.0]]) mat_not_sym = gs.array([[1.0, 0.0, 3.0], [2.0, 4.0, 5.0], [3.0, 5.0, 6.0]]) result = sym_n.belongs(mat_sym) expected = True self.assertAllClose(result, expected) result = sym_n.belongs(mat_not_sym) expected = False self.assertAllClose(result, expected) def test_basis(self): """Test of belongs method.""" sym_n = SymmetricMatrices(2) mat_sym_1 = gs.array([[1.0, 0.0], [0, 0]]) mat_sym_2 = gs.array([[0, 1.0], [1.0, 0]]) mat_sym_3 = gs.array([[0, 0.0], [0, 1.0]]) expected = gs.stack([mat_sym_1, mat_sym_2, mat_sym_3]) result = sym_n.basis self.assertAllClose(result, expected) def test_expm(self): """Test of expm method.""" sym_n = SymmetricMatrices(self.n) v = gs.array([[0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]]) result = sym_n.expm(v) c = math.cosh(1) s = math.sinh(1) e = math.exp(1) expected = gs.array([[c, s, 0.0], [s, c, 0.0], [0.0, 0.0, e]]) four_dim_v = gs.broadcast_to(v, (2, 2) + v.shape) four_dim_expected = gs.broadcast_to(expected, (2, 2) + expected.shape) four_dim_result = sym_n.expm(four_dim_v) self.assertAllClose(result, expected) self.assertAllClose(four_dim_result, four_dim_expected) def test_powerm(self): """Test of powerm method.""" sym_n = SymmetricMatrices(self.n) expected = gs.array( [[[1, 1.0 / 4.0, 0.0], [1.0 / 4, 2.0, 0.0], [0.0, 0.0, 1.0]]] ) power = gs.array(1.0 / 2.0) result = sym_n.powerm(expected, power) result = gs.matmul(result, gs.transpose(result, (0, 2, 1))) self.assertAllClose(result, expected) def test_vector_from_symmetric_matrix_and_symmetric_matrix_from_vector(self): """Test for matrix to vector and vector to matrix conversions.""" sym_mat_1 = gs.array([[1.0, 0.6, -3.0], [0.6, 7.0, 0.0], [-3.0, 0.0, 8.0]]) vector_1 = self.space.to_vector(sym_mat_1) result_1 = self.space.from_vector(vector_1) expected_1 = sym_mat_1 self.assertTrue(gs.allclose(result_1, expected_1)) vector_2 = gs.array([1, 2, 3, 4, 5, 6]) sym_mat_2 = self.space.from_vector(vector_2) result_2 = self.space.to_vector(sym_mat_2) expected_2 = vector_2 self.assertTrue(gs.allclose(result_2, expected_2)) def test_vector_and_symmetric_matrix_vectorization(self): """Test of vectorization.""" n_samples = 5 vector = gs.random.rand(n_samples, 6) sym_mat = self.space.from_vector(vector) result = self.space.to_vector(sym_mat) expected = vector self.assertTrue(gs.allclose(result, expected)) vector = self.space.to_vector(sym_mat) result = self.space.from_vector(vector) expected = sym_mat self.assertTrue(gs.allclose(result, expected)) def test_symmetric_matrix_from_vector(self): vector_2 = gs.array([1, 2, 3, 4, 5, 6]) result = self.space.from_vector(vector_2) expected = gs.array([[1.0, 2.0, 3.0], [2.0, 4.0, 5.0], [3.0, 5.0, 6.0]]) self.assertAllClose(result, expected) def test_projection_and_belongs(self): shape = (2, self.n, self.n) result = helper.test_projection_and_belongs(self.space, shape) for res in result: self.assertTrue(res) def test_random_and_belongs(self): mat = self.space.random_point() result = self.space.belongs(mat) self.assertTrue(result) def test_dim(self): result = self.space.dim n = self.space.n expected = int(n * (n + 1) / 2) self.assertAllClose(result, expected)
class TestSymmetricMatrices(geomstats.tests.TestCase): """Test of SymmetricMatrices methods.""" def setUp(self): """Set up the test.""" warnings.simplefilter('ignore', category=ImportWarning) gs.random.seed(1234) self.n = 3 self.space = SymmetricMatrices(self.n) def test_belongs(self): """Test of belongs method.""" sym_n = self.space mat_sym = gs.array([[1., 2., 3.], [2., 4., 5.], [3., 5., 6.]]) mat_not_sym = gs.array([[1., 0., 3.], [2., 4., 5.], [3., 5., 6.]]) result = sym_n.belongs(mat_sym) expected = True self.assertAllClose(result, expected) result = sym_n.belongs(mat_not_sym) expected = False self.assertAllClose(result, expected) @geomstats.tests.np_and_pytorch_only def test_basis(self): """Test of belongs method.""" sym_n = SymmetricMatrices(2) mat_sym_1 = gs.array([[1., 0.], [0, 0]]) mat_sym_2 = gs.array([[0, 1.], [1., 0]]) mat_sym_3 = gs.array([[0, 0.], [0, 1.]]) expected = gs.stack([mat_sym_1, mat_sym_2, mat_sym_3]) result = sym_n.basis self.assertAllClose(result, expected) def test_expm(self): """Test of expm method.""" sym_n = SymmetricMatrices(self.n) v = gs.array([[0., 1., 0.], [1., 0., 0.], [0., 0., 1.]]) result = sym_n.expm(v) c = math.cosh(1) s = math.sinh(1) e = math.exp(1) expected = gs.array([[c, s, 0.], [s, c, 0.], [0., 0., e]]) self.assertAllClose(result, expected) def test_powerm(self): """Test of powerm method.""" sym_n = SymmetricMatrices(self.n) expected = gs.array([[[1, 1. / 4., 0.], [1. / 4, 2., 0.], [0., 0., 1.]]]) expected = gs.cast(expected, gs.float64) power = gs.array(1. / 2) power = gs.cast(power, gs.float64) result = sym_n.powerm(expected, power) result = gs.matmul(result, gs.transpose(result, (0, 2, 1))) self.assertAllClose(result, expected) @geomstats.tests.np_and_pytorch_only def test_vector_from_symmetric_matrix_and_symmetric_matrix_from_vector( self): """Test for matrix to vector and vector to matrix conversions.""" sym_mat_1 = gs.array([[1., 0.6, -3.], [0.6, 7., 0.], [-3., 0., 8.]]) vector_1 = self.space.to_vector(sym_mat_1) result_1 = self.space.from_vector(vector_1) expected_1 = sym_mat_1 self.assertTrue(gs.allclose(result_1, expected_1)) vector_2 = gs.array([1, 2, 3, 4, 5, 6]) sym_mat_2 = self.space.from_vector(vector_2) result_2 = self.space.to_vector(sym_mat_2) expected_2 = vector_2 self.assertTrue(gs.allclose(result_2, expected_2)) @geomstats.tests.np_and_pytorch_only def test_vector_and_symmetric_matrix_vectorization(self): """Test of vectorization.""" n_samples = 5 vector = gs.random.rand(n_samples, 6) sym_mat = self.space.from_vector(vector) result = self.space.to_vector(sym_mat) expected = vector self.assertTrue(gs.allclose(result, expected)) vector = self.space.to_vector(sym_mat) result = self.space.from_vector(vector) expected = sym_mat self.assertTrue(gs.allclose(result, expected)) def test_symmetric_matrix_from_vector(self): vector_2 = gs.array([1, 2, 3, 4, 5, 6]) result = self.space.from_vector(vector_2) expected = gs.array([[1., 2., 3.], [2., 4., 5.], [3., 5., 6.]]) self.assertAllClose(result, expected)