class TestIntegrator(geomstats.tests.TestCase): def setup_method(self): self.dimension = 4 self.dt = 0.1 self.euclidean = Euclidean(self.dimension) self.matrices = Matrices(self.dimension, self.dimension) self.intercept = self.euclidean.random_point() self.slope = Matrices.to_symmetric(self.matrices.random_point()) @staticmethod def function_linear(_state, _time): return 2.0 def _test_step(self, step): state = self.intercept result = step(self.function_linear, state, 0.0, self.dt) expected = state + 2 * self.dt self.assertAllClose(result, expected) def test_symplectic_euler_step(self): with pytest.raises(NotImplementedError): self._test_step(integrator.symplectic_euler_step) def test_leapfrog_step(self): with pytest.raises(NotImplementedError): self._test_step(integrator.leapfrog_step) def test_euler_step(self): self._test_step(integrator.euler_step) def test_rk2_step(self): self._test_step(integrator.rk2_step) def test_rk4_step(self): self._test_step(integrator.rk4_step) def test_integrator(self): initial_state = self.euclidean.random_point(2) def function(state, _time): _, velocity = state return gs.stack([velocity, gs.zeros_like(velocity)]) for step in ["euler", "rk2", "rk4"]: flow = integrator.integrate(function, initial_state, step=step) result = flow[-1][0] expected = initial_state[0] + initial_state[1] self.assertAllClose(result, expected)
def random_point(self, n_samples=1, bound=1.): """Sample from a uniform distribution. Parameters ---------- n_samples : int Number of samples. Optional, default: 1. bound : float Bound of the interval in which to sample each entry. Optional, default: 1. Returns ------- point : array-like, shape=[m, n] or [n_samples, m, n] Sample. """ return Matrices.to_symmetric(Matrices.random_point(n_samples, bound))
class TestIntegrator(geomstats.tests.TestCase): def setUp(self): self.dimension = 4 self.dt = 0.1 self.euclidean = Euclidean(self.dimension) self.matrices = Matrices(self.dimension, self.dimension) self.intercept = self.euclidean.random_point(1) self.slope = Matrices.to_symmetric(self.matrices.random_point(1)) def function_linear(self, point, vector): return point, -gs.dot(self.slope, vector) def test_euler_step(self): state = (self.intercept, self.slope) result = len( integrator.euler_step(state, self.function_linear, self.dt)) expected = len(state) self.assertAllClose(result, expected) def test_rk4_step(self): state = (self.intercept, self.slope) result = len(integrator.rk4_step(state, self.function_linear, self.dt)) expected = len(state) self.assertAllClose(result, expected) def test_integrator(self): initial_state = self.euclidean.random_point(2) def function(_, velocity): return velocity, gs.zeros_like(velocity) for step in ['euler', 'rk4']: flow, _ = integrator.integrate(function, initial_state, step=step) result = flow[-1] expected = initial_state[0] + initial_state[1] self.assertAllClose(result, expected)
class TestVisualization(geomstats.tests.TestCase): def setUp(self): self.n_samples = 10 self.SO3_GROUP = SpecialOrthogonal(n=3, point_type='vector') self.SE3_GROUP = SpecialEuclidean(n=3, point_type='vector') self.S1 = Hypersphere(dim=1) self.S2 = Hypersphere(dim=2) self.H2 = Hyperbolic(dim=2) self.H2_half_plane = PoincareHalfSpace(dim=2) self.M32 = Matrices(m=3, n=2) self.S32 = PreShapeSpace(k_landmarks=3, m_ambient=2) self.KS = visualization.KendallSphere() self.M33 = Matrices(m=3, n=3) self.S33 = PreShapeSpace(k_landmarks=3, m_ambient=3) self.KD = visualization.KendallDisk() plt.figure() @staticmethod def test_tutorial_matplotlib(): visualization.tutorial_matplotlib() def test_plot_points_so3(self): points = self.SO3_GROUP.random_uniform(self.n_samples) visualization.plot(points, space='SO3_GROUP') def test_plot_points_se3(self): points = self.SE3_GROUP.random_point(self.n_samples) visualization.plot(points, space='SE3_GROUP') def test_draw_pre_shape_2d(self): self.KS.draw() def test_draw_points_pre_shape_2d(self): points = self.S32.random_point(self.n_samples) visualization.plot(points, space='S32') points = self.M32.random_point(self.n_samples) visualization.plot(points, space='M32') self.KS.clear_points() def test_draw_curve_pre_shape_2d(self): self.KS.draw() base_point = self.S32.random_point() vec = self.S32.random_point() tangent_vec = self.S32.to_tangent(vec, base_point) times = gs.linspace(0., 1., 1000) speeds = gs.array([-t * tangent_vec for t in times]) points = self.S32.ambient_metric.exp(speeds, base_point) self.KS.add_points(points) self.KS.draw_curve() self.KS.clear_points() def test_draw_vector_pre_shape_2d(self): self.KS.draw() base_point = self.S32.random_point() vec = self.S32.random_point() tangent_vec = self.S32.to_tangent(vec, base_point) self.KS.draw_vector(tangent_vec, base_point) def test_convert_to_spherical_coordinates_pre_shape_2d(self): points = self.S32.random_point(self.n_samples) coords = self.KS.convert_to_spherical_coordinates(points) x = coords[:, 0] y = coords[:, 1] z = coords[:, 2] result = x**2 + y**2 + z**2 expected = .25 * gs.ones(self.n_samples) self.assertAllClose(result, expected) def test_rotation_pre_shape_2d(self): theta = gs.random.rand(1)[0] phi = gs.random.rand(1)[0] rot = self.KS.rotation(theta, phi) result = _SpecialOrthogonalMatrices(3).belongs(rot) expected = True self.assertAllClose(result, expected) def test_draw_pre_shape_3d(self): self.KD.draw() def test_draw_points_pre_shape_3d(self): points = self.S33.random_point(self.n_samples) visualization.plot(points, space='S33') points = self.M33.random_point(self.n_samples) visualization.plot(points, space='M33') self.KD.clear_points() def test_draw_curve_pre_shape_3d(self): self.KD.draw() base_point = self.S33.random_point() vec = self.S33.random_point() tangent_vec = self.S33.to_tangent(vec, base_point) tangent_vec = .5 * tangent_vec / self.S33.ambient_metric.norm( tangent_vec) times = gs.linspace(0., 1., 1000) speeds = gs.array([-t * tangent_vec for t in times]) points = self.S33.ambient_metric.exp(speeds, base_point) self.KD.add_points(points) self.KD.draw_curve() self.KD.clear_points() def test_draw_vector_pre_shape_3d(self): self.KS.draw() base_point = self.S32.random_point() vec = self.S32.random_point() tangent_vec = self.S32.to_tangent(vec, base_point) self.KS.draw_vector(tangent_vec, base_point) def test_convert_to_planar_coordinates_pre_shape_3d(self): points = self.S33.random_point(self.n_samples) coords = self.KD.convert_to_planar_coordinates(points) x = coords[:, 0] y = coords[:, 1] radius = x**2 + y**2 result = [r <= 1. for r in radius] self.assertTrue(gs.all(result)) @geomstats.tests.np_and_pytorch_only def test_plot_points_s1(self): points = self.S1.random_uniform(self.n_samples) visualization.plot(points, space='S1') def test_plot_points_s2(self): points = self.S2.random_uniform(self.n_samples) visualization.plot(points, space='S2') def test_plot_points_h2_poincare_disk(self): points = self.H2.random_point(self.n_samples) visualization.plot(points, space='H2_poincare_disk') def test_plot_points_h2_poincare_half_plane_ext(self): points = self.H2.random_point(self.n_samples) visualization.plot(points, space='H2_poincare_half_plane', point_type='extrinsic') def test_plot_points_h2_poincare_half_plane_none(self): points = self.H2_half_plane.random_point(self.n_samples) visualization.plot(points, space='H2_poincare_half_plane') def test_plot_points_h2_poincare_half_plane_hs(self): points = self.H2_half_plane.random_point(self.n_samples) visualization.plot(points, space='H2_poincare_half_plane', point_type='half_space') def test_plot_points_h2_klein_disk(self): points = self.H2.random_point(self.n_samples) visualization.plot(points, space='H2_klein_disk') @staticmethod def test_plot_points_se2(): points = SpecialEuclidean(n=2, point_type='vector').random_point(4) visu = visualization.SpecialEuclidean2(points, point_type='vector') ax = visu.set_ax() visu.draw(ax)
class TestMatrices(geomstats.tests.TestCase): def setUp(self): gs.random.seed(1234) self.m = 2 self.n = 3 self.space = Matrices(m=self.n, n=self.n) self.space_nonsquare = Matrices(m=self.m, n=self.n) self.metric = self.space.metric self.n_samples = 2 @geomstats.tests.np_only def test_mul(self): a = gs.eye(3, 3, 1) b = gs.eye(3, 3, -1) c = gs.array([ [1., 0., 0.], [0., 1., 0.], [0., 0., 0.]]) d = gs.array([ [0., 0., 0.], [0., 1., 0.], [0., 0., 1.]]) result = self.space.mul([a, b], [b, a]) expected = gs.array([c, d]) self.assertAllClose(result, expected) result = self.space.mul(a, [a, b]) expected = gs.array([gs.eye(3, 3, 2), c]) self.assertAllClose(result, expected) @geomstats.tests.np_only def test_bracket(self): x = gs.array([ [0., 0., 0.], [0., 0., -1.], [0., 1., 0.]]) y = gs.array([ [0., 0., 1.], [0., 0., 0.], [-1., 0., 0.]]) z = gs.array([ [0., -1., 0.], [1., 0., 0.], [0., 0., 0.]]) result = self.space.bracket([x, y], [y, z]) expected = gs.array([z, x]) self.assertAllClose(result, expected) result = self.space.bracket(x, [x, y, z]) expected = gs.array([gs.zeros((3, 3)), z, -y]) self.assertAllClose(result, expected) @geomstats.tests.np_only def test_transpose(self): tr = self.space.transpose ar = gs.array a = gs.eye(3, 3, 1) b = gs.eye(3, 3, -1) self.assertAllClose(tr(a), b) self.assertAllClose(tr(ar([a, b])), ar([b, a])) def test_is_symmetric(self): not_squared = gs.array([[1., 2.], [2., 1.], [3., 1.]]) result = self.space.is_symmetric(not_squared) expected = False self.assertAllClose(result, expected) sym_mat = gs.array([[1., 2.], [2., 1.]]) result = self.space.is_symmetric(sym_mat) expected = gs.array(True) self.assertAllClose(result, expected) not_a_sym_mat = gs.array([[1., 0.6, -3.], [6., -7., 0.], [0., 7., 8.]]) result = self.space.is_symmetric(not_a_sym_mat) expected = gs.array(False) self.assertAllClose(result, expected) @geomstats.tests.np_only def test_is_skew_symmetric(self): skew_mat = gs.array([[0, - 2.], [2., 0]]) result = self.space.is_skew_symmetric(skew_mat) expected = gs.array(True) self.assertAllClose(result, expected) not_a_sym_mat = gs.array([[1., 0.6, -3.], [6., -7., 0.], [0., 7., 8.]]) result = self.space.is_skew_symmetric(not_a_sym_mat) expected = gs.array(False) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_is_symmetric_vectorization(self): points = gs.array([ [[1., 2.], [2., 1.]], [[3., 4.], [4., 5.]], [[1., 2.], [3., 4.]]]) result = self.space.is_symmetric(points) expected = [True, True, False] self.assertAllClose(result, expected) @geomstats.tests.np_and_pytorch_only def test_make_symmetric(self): sym_mat = gs.array([[1., 2.], [2., 1.]]) result = self.space.to_symmetric(sym_mat) expected = sym_mat self.assertAllClose(result, expected) mat = gs.array([[1., 2., 3.], [0., 0., 0.], [3., 1., 1.]]) result = self.space.to_symmetric(mat) expected = gs.array([[1., 1., 3.], [1., 0., 0.5], [3., 0.5, 1.]]) self.assertAllClose(result, expected) mat = gs.array([[1e100, 1e-100, 1e100], [1e100, 1e-100, 1e100], [1e-100, 1e-100, 1e100]]) result = self.space.to_symmetric(mat) res = 0.5 * (1e100 + 1e-100) expected = gs.array([[1e100, res, res], [res, 1e-100, res], [res, res, 1e100]]) self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_make_symmetric_and_is_symmetric_vectorization(self): points = gs.array([ [[1., 2.], [3., 4.]], [[5., 6.], [4., 9.]]]) sym_points = self.space.to_symmetric(points) result = gs.all(self.space.is_symmetric(sym_points)) expected = True self.assertAllClose(result, expected) def test_inner_product(self): base_point = gs.array([ [1., 2., 3.], [0., 0., 0.], [3., 1., 1.]]) tangent_vector_1 = gs.array([ [1., 2., 3.], [0., -10., 0.], [30., 1., 1.]]) tangent_vector_2 = gs.array([ [1., 4., 3.], [5., 0., 0.], [3., 1., 1.]]) result = self.metric.inner_product( tangent_vector_1, tangent_vector_2, base_point=base_point) expected = gs.trace( gs.matmul( gs.transpose(tangent_vector_1), tangent_vector_2)) self.assertAllClose(result, expected) def test_cong(self): base_point = gs.array([ [1., 2., 3.], [0., 0., 0.], [3., 1., 1.]]) tangent_vector = gs.array([ [1., 2., 3.], [0., -10., 0.], [30., 1., 1.]]) result = self.space.congruent(tangent_vector, base_point) expected = gs.matmul( tangent_vector, gs.transpose(base_point)) expected = gs.matmul(base_point, expected) self.assertAllClose(result, expected) def test_belongs(self): base_point_square = gs.zeros((self.n, self.n)) base_point_nonsquare = gs.zeros((self.m, self.n)) result = self.space.belongs(base_point_square) expected = True self.assertAllClose(result, expected) result = self.space_nonsquare.belongs(base_point_square) expected = False self.assertAllClose(result, expected) result = self.space.belongs(base_point_nonsquare) expected = False self.assertAllClose(result, expected) result = self.space_nonsquare.belongs(base_point_nonsquare) expected = True self.assertAllClose(result, expected) result = self.space.belongs(gs.zeros((2, 2, 3))) self.assertFalse(gs.all(result)) result = self.space.belongs(gs.zeros((2, 3, 3))) self.assertTrue(gs.all(result)) def test_is_diagonal(self): base_point = gs.array([ [1., 2., 3.], [0., 0., 0.], [3., 1., 1.]]) result = self.space.is_diagonal(base_point) expected = False self.assertAllClose(result, expected) diagonal = gs.eye(3) result = self.space.is_diagonal(diagonal) self.assertTrue(result) base_point = gs.stack([base_point, diagonal]) result = self.space.is_diagonal(base_point) expected = gs.array([False, True]) self.assertAllClose(result, expected) base_point = gs.stack([diagonal] * 2) result = self.space.is_diagonal(base_point) self.assertTrue(gs.all(result)) base_point = gs.reshape(gs.arange(6), (2, 3)) result = self.space.is_diagonal(base_point) self.assertTrue(~result) def test_norm(self): for n_samples in [1, 2]: mat = self.space.random_point(n_samples) result = self.metric.norm(mat) expected = self.space.frobenius_product(mat, mat) ** .5 self.assertAllClose(result, expected)
class TestVisualization(geomstats.tests.TestCase): def setup_method(self): self.n_samples = 10 self.SO3_GROUP = SpecialOrthogonal(n=3, point_type="vector") self.SE3_GROUP = SpecialEuclidean(n=3, point_type="vector") self.S1 = Hypersphere(dim=1) self.S2 = Hypersphere(dim=2) self.H2 = Hyperbolic(dim=2) self.H2_half_plane = PoincareHalfSpace(dim=2) self.M32 = Matrices(m=3, n=2) self.S32 = PreShapeSpace(k_landmarks=3, m_ambient=2) self.KS = visualization.KendallSphere() self.M33 = Matrices(m=3, n=3) self.S33 = PreShapeSpace(k_landmarks=3, m_ambient=3) self.KD = visualization.KendallDisk() self.spd = SPDMatrices(n=2) plt.figure() @staticmethod def test_tutorial_matplotlib(): visualization.tutorial_matplotlib() def test_plot_points_so3(self): points = self.SO3_GROUP.random_uniform(self.n_samples) visualization.plot(points, space="SO3_GROUP") def test_plot_points_se3(self): points = self.SE3_GROUP.random_point(self.n_samples) visualization.plot(points, space="SE3_GROUP") def test_draw_pre_shape_2d(self): self.KS.draw() def test_draw_points_pre_shape_2d(self): points = self.S32.random_point(self.n_samples) visualization.plot(points, space="S32") points = self.M32.random_point(self.n_samples) visualization.plot(points, space="M32") self.KS.clear_points() def test_draw_curve_pre_shape_2d(self): self.KS.draw() base_point = self.S32.random_point() vec = self.S32.random_point() tangent_vec = self.S32.to_tangent(vec, base_point) times = gs.linspace(0.0, 1.0, 1000) speeds = gs.array([-t * tangent_vec for t in times]) points = self.S32.ambient_metric.exp(speeds, base_point) self.KS.add_points(points) self.KS.draw_curve() self.KS.clear_points() def test_draw_vector_pre_shape_2d(self): self.KS.draw() base_point = self.S32.random_point() vec = self.S32.random_point() tangent_vec = self.S32.to_tangent(vec, base_point) self.KS.draw_vector(tangent_vec, base_point) def test_convert_to_spherical_coordinates_pre_shape_2d(self): points = self.S32.random_point(self.n_samples) coords = self.KS.convert_to_spherical_coordinates(points) x = coords[:, 0] y = coords[:, 1] z = coords[:, 2] result = x**2 + y**2 + z**2 expected = 0.25 * gs.ones(self.n_samples) self.assertAllClose(result, expected) def test_rotation_pre_shape_2d(self): theta = gs.random.rand(1)[0] phi = gs.random.rand(1)[0] rot = self.KS.rotation(theta, phi) result = _SpecialOrthogonalMatrices(3).belongs(rot) expected = True self.assertAllClose(result, expected) def test_draw_pre_shape_3d(self): self.KD.draw() def test_draw_points_pre_shape_3d(self): points = self.S33.random_point(self.n_samples) visualization.plot(points, space="S33") points = self.M33.random_point(self.n_samples) visualization.plot(points, space="M33") self.KD.clear_points() def test_draw_curve_pre_shape_3d(self): self.KD.draw() base_point = self.S33.random_point() vec = self.S33.random_point() tangent_vec = self.S33.to_tangent(vec, base_point) tangent_vec = 0.5 * tangent_vec / self.S33.ambient_metric.norm( tangent_vec) times = gs.linspace(0.0, 1.0, 1000) speeds = gs.array([-t * tangent_vec for t in times]) points = self.S33.ambient_metric.exp(speeds, base_point) self.KD.add_points(points) self.KD.draw_curve() self.KD.clear_points() def test_draw_vector_pre_shape_3d(self): self.KS.draw() base_point = self.S32.random_point() vec = self.S32.random_point() tangent_vec = self.S32.to_tangent(vec, base_point) self.KS.draw_vector(tangent_vec, base_point) def test_convert_to_planar_coordinates_pre_shape_3d(self): points = self.S33.random_point(self.n_samples) coords = self.KD.convert_to_planar_coordinates(points) x = coords[:, 0] y = coords[:, 1] radius = x**2 + y**2 result = [r <= 1.0 for r in radius] self.assertTrue(gs.all(result)) @geomstats.tests.np_autograd_and_torch_only def test_plot_points_s1(self): points = self.S1.random_uniform(self.n_samples) visualization.plot(points, space="S1") def test_plot_points_s2(self): points = self.S2.random_uniform(self.n_samples) visualization.plot(points, space="S2") def test_plot_points_h2_poincare_disk(self): points = self.H2.random_point(self.n_samples) visualization.plot(points, space="H2_poincare_disk") def test_plot_points_h2_poincare_half_plane_ext(self): points = self.H2.random_point(self.n_samples) visualization.plot(points, space="H2_poincare_half_plane", point_type="extrinsic") def test_plot_points_h2_poincare_half_plane_none(self): points = self.H2_half_plane.random_point(self.n_samples) visualization.plot(points, space="H2_poincare_half_plane") def test_plot_points_h2_poincare_half_plane_hs(self): points = self.H2_half_plane.random_point(self.n_samples) visualization.plot(points, space="H2_poincare_half_plane", point_type="half_space") def test_plot_points_h2_klein_disk(self): points = self.H2.random_point(self.n_samples) visualization.plot(points, space="H2_klein_disk") @staticmethod def test_plot_points_se2(): points = SpecialEuclidean(n=2, point_type="vector").random_point(4) visu = visualization.SpecialEuclidean2(points, point_type="vector") ax = visu.set_ax() visu.draw_points(ax) def test_plot_points_spd2(self): one_point = self.spd.random_point() visualization.plot(one_point, space="SPD2") points = self.spd.random_point(4) visualization.plot(points, space="SPD2") def test_compute_coordinates_spd2(self): point = gs.eye(2) ellipsis = visualization.Ellipses(n_sampling_points=4) x, y = ellipsis.compute_coordinates(point) self.assertAllClose(x, gs.array([1, 0, -1, 0, 1])) self.assertAllClose(y, gs.array([0, 1, 0, -1, 0])) @staticmethod def teardown_method(): plt.close()
class _GraphSpace: r"""Class for the Graph Space. Graph Space to analyse populations of labelled and unlabelled graphs. The space focuses on graphs with scalar euclidean attributes on nodes and edges, with a finite number of nodes and both directed and undirected edges. For undirected graphs, use symmeric adjacency matrices. The space is a quotient space obtained by applying the permutation action of nodes to the space of adjacency matrices. Points are represented by :math:`nodes \times nodes` adjacency matrices. Parameters ---------- nodes : int Number of graph nodes p : int Dimension of euclidean parameter or label associated to a graph. References ---------- ..[Calissano2020] Calissano, A., Feragen, A., Vantini, S. “Graph Space: Geodesic Principal Components for a Population of Network-valued Data.” Mox report 14, 2020. https://mox.polimi.it/reports-and-theses/publication-results/?id=855. """ def __init__(self, nodes, p=None): self.nodes = nodes self.p = p self.adjmat = Matrices(self.nodes, self.nodes) def belongs(self, graph, atol=gs.atol): r"""Check if the matrix is an adjacency matrix. The adjacency matrix should be associated to the graph with n nodes. Parameters ---------- graph : array-like, shape=[..., n, n] Matrix to be checked. atol : float Tolerance. Optional, default: backend atol. Returns ------- belongs : array-like, shape=[...,n] Boolean denoting if graph belongs to the space. """ return self.adjmat.belongs(graph, atol=atol) def random_point(self, n_samples=1, bound=1.0): r"""Sample in Graph Space. 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 ------- graph_samples : array-like, shape=[..., n, n] Points sampled in GraphSpace(n). """ return self.adjmat.random_point(n_samples=n_samples, bound=bound) def permute(self, graph_to_permute, permutation): r"""Permutation action applied to graph observation. Parameters ---------- graph_to_permute : array-like, shape=[..., n, n] Input graphs to be permuted. permutation: array-like, shape=[..., n] Node permutations where in position i we have the value j meaning the node i should be permuted with node j. Returns ------- graphs_permuted : array-like, shape=[..., n, n] Graphs permuted. """ nodes = self.nodes single_graph = len(graph_to_permute.shape) < 3 if single_graph: graph_to_permute = [graph_to_permute] permutation = [permutation] result = [] for i, p in enumerate(permutation): if gs.all(gs.array(nodes) == gs.array(p)): result.append(graph_to_permute[i]) else: gtype = graph_to_permute[i].dtype permutation_matrix = gs.array_from_sparse( data=gs.ones(nodes, dtype=gtype), indices=list(zip(list(range(nodes)), p)), target_shape=(nodes, nodes), ) result.append( self.adjmat.mul( permutation_matrix, graph_to_permute[i], gs.transpose(permutation_matrix), )) return result[0] if single_graph else gs.array(result)