def test_exp_after_log_intrinsic_ball_extrinsic( self, dim, x_intrinsic, y_intrinsic ): intrinsic_manifold = Hyperboloid(dim=dim, coords_type="intrinsic") extrinsic_manifold = Hyperbolic(dim=dim, coords_type="extrinsic") ball_manifold = PoincareBall(dim) x_extr = intrinsic_manifold.to_coordinates( x_intrinsic, to_coords_type="extrinsic" ) y_extr = intrinsic_manifold.to_coordinates( y_intrinsic, to_coords_type="extrinsic" ) x_ball = extrinsic_manifold.to_coordinates(x_extr, to_coords_type="ball") y_ball = extrinsic_manifold.to_coordinates(y_extr, to_coords_type="ball") x_ball_exp_after_log = ball_manifold.metric.exp( ball_manifold.metric.log(y_ball, x_ball), x_ball ) x_extr_a = extrinsic_manifold.metric.exp( extrinsic_manifold.metric.log(y_extr, x_extr), x_extr ) x_extr_b = extrinsic_manifold.from_coordinates( x_ball_exp_after_log, from_coords_type="ball" ) self.assertAllClose(x_extr_a, x_extr_b, atol=3e-4)
def test_distance_ball_extrinsic_intrinsic(self, dim, x_intrinsic, y_intrinsic): intrinsic_manifold = Hyperboloid(dim, coords_type="intrinsic") extrinsic_manifold = Hyperboloid(dim, coords_type="extrinsic") x_extr = intrinsic_manifold.to_coordinates( x_intrinsic, to_coords_type="extrinsic" ) y_extr = intrinsic_manifold.to_coordinates( y_intrinsic, to_coords_type="extrinsic" ) x_ball = extrinsic_manifold.to_coordinates(x_extr, to_coords_type="ball") y_ball = extrinsic_manifold.to_coordinates(y_extr, to_coords_type="ball") dst_ball = PoincareBall(dim).metric.dist(x_ball, y_ball) dst_extr = extrinsic_manifold.metric.dist(x_extr, y_extr) self.assertAllClose(dst_ball, dst_extr)
def test_distance_ball_extrinsic_from_extr_4_dim(self): x_int = gs.array([10, 0.2, 3, 4]) y_int = gs.array([1, 6, 2.0, 1]) ball_manifold = PoincareBall(4) extrinsic_manifold = Hyperboloid(4) ball_metric = ball_manifold.metric extrinsic_metric = extrinsic_manifold.metric x_extr = extrinsic_manifold.from_coordinates( x_int, from_coords_type="intrinsic") y_extr = extrinsic_manifold.from_coordinates( y_int, from_coords_type="intrinsic") x_ball = extrinsic_manifold.to_coordinates(x_extr, to_coords_type="ball") y_ball = extrinsic_manifold.to_coordinates(y_extr, to_coords_type="ball") dst_ball = ball_metric.dist(x_ball, y_ball) dst_extr = extrinsic_metric.dist(x_extr, y_extr) self.assertAllClose(dst_ball, dst_extr)
def test_distance_ball_extrinsic_from_extr_4_dim(self): x_int = gs.array([[10, 0.2, 3, 4]]) y_int = gs.array([[1, 6, 2., 1]]) ball_manifold = PoincareBall(4) extrinsic_manifold = Hyperboloid(4) ball_metric = ball_manifold.metric extrinsic_metric = extrinsic_manifold.metric x_extr = extrinsic_manifold.from_coordinates( x_int, from_coords_type='intrinsic') y_extr = extrinsic_manifold.from_coordinates( y_int, from_coords_type='intrinsic') x_ball = extrinsic_manifold.to_coordinates( x_extr, to_coords_type='ball') y_ball = extrinsic_manifold.to_coordinates( y_extr, to_coords_type='ball') dst_ball = ball_metric.dist(x_ball, y_ball) dst_extr = extrinsic_metric.dist(x_extr, y_extr) # TODO(nmiolane): Remove this when ball is properly vectorized dst_extr = gs.to_ndarray(dst_extr, to_ndim=2, axis=1) self.assertAllClose(dst_ball, dst_extr)
class TestHyperbolic(geomstats.tests.TestCase): def setup_method(self): gs.random.seed(1234) self.dimension = 3 self.space = Hyperboloid(dim=self.dimension) self.metric = self.space.metric self.ball_manifold = PoincareBall(dim=2) self.n_samples = 10 def test_belongs_intrinsic(self): self.space.coords_type = "intrinsic" point = gs.random.rand(self.n_samples, self.dimension) result = self.space.belongs(point) self.assertTrue(gs.all(result)) def test_regularize_intrinsic(self): self.space.coords_type = "intrinsic" point = gs.random.rand(self.n_samples, self.dimension) regularized = self.space.regularize(point) self.space.coords_type = "extrinsic" result = self.space.belongs(regularized) self.assertTrue(gs.all(result)) def test_regularize_zero_norm(self): point = gs.array([-1.0, 1.0, 0.0, 0.0]) with pytest.raises(ValueError): self.space.regularize(point) with pytest.raises(NameError): self.space.extrinsic_to_intrinsic_coords(point) def test_random_uniform_and_belongs(self): point = self.space.random_point() result = self.space.belongs(point) expected = True self.assertAllClose(result, expected) def test_random_uniform(self): result = self.space.random_point() self.assertAllClose(gs.shape(result), (self.dimension + 1,)) def test_projection_to_tangent_space(self): base_point = gs.array([1.0, 0.0, 0.0, 0.0]) belongs = self.space.belongs(base_point) self.assertTrue(belongs) tangent_vec = self.space.to_tangent( vector=gs.array([1.0, 2.0, 1.0, 3.0]), base_point=base_point ) result = self.metric.inner_product(tangent_vec, base_point) expected = 0.0 self.assertAllClose(result, expected) result = self.space.to_tangent( vector=gs.array([1.0, 2.0, 1.0, 3.0]), base_point=base_point ) expected = tangent_vec self.assertAllClose(result, expected) def test_intrinsic_and_extrinsic_coords(self): """ Test that the composition of intrinsic_to_extrinsic_coords and extrinsic_to_intrinsic_coords gives the identity. """ point_int = gs.ones(self.dimension) point_ext = self.space.intrinsic_to_extrinsic_coords(point_int) result = self.space.extrinsic_to_intrinsic_coords(point_ext) expected = point_int self.assertAllClose(result, expected) point_ext = gs.array([2.0, 1.0, 1.0, 1.0]) point_int = self.space.to_coordinates(point_ext, "intrinsic") result = self.space.from_coordinates(point_int, "intrinsic") expected = point_ext self.assertAllClose(result, expected) def test_intrinsic_and_extrinsic_coords_vectorization(self): """ Test that the composition of intrinsic_to_extrinsic_coords and extrinsic_to_intrinsic_coords gives the identity. """ point_int = gs.array( [ [0.1, 0.0, 0.0, 0.1, 0.0, 0.0], [0.1, 0.1, 0.1, 0.4, 0.1, 0.0], [0.1, 0.3, 0.0, 0.1, 0.0, 0.0], [-0.1, 0.1, -0.4, 0.1, -0.01, 0.0], [0.0, 0.0, 0.1, 0.1, -0.08, -0.1], [0.1, 0.1, 0.1, 0.1, 0.0, -0.5], ] ) point_ext = self.space.from_coordinates(point_int, "intrinsic") result = self.space.to_coordinates(point_ext, "intrinsic") expected = point_int expected = helper.to_vector(expected) self.assertAllClose(result, expected) point_ext = gs.array( [ [2.0, 1.0, 1.0, 1.0], [4.0, 1.0, 3.0, math.sqrt(5.0)], [3.0, 2.0, 0.0, 2.0], ] ) point_int = self.space.to_coordinates(point_ext, "intrinsic") result = self.space.from_coordinates(point_int, "intrinsic") expected = point_ext expected = helper.to_vector(expected) self.assertAllClose(result, expected) def test_log_and_exp_general_case(self): """ Test that the Riemannian exponential and the Riemannian logarithm are inverse. Expect their composition to give the identity function. """ # Riemannian Log then Riemannian Exp # General case base_point = gs.array([4.0, 1.0, 3.0, math.sqrt(5.0)]) point = gs.array([2.0, 1.0, 1.0, 1.0]) log = self.metric.log(point=point, base_point=base_point) result = self.metric.exp(tangent_vec=log, base_point=base_point) expected = point self.assertAllClose(result, expected) def test_log_and_exp_general_case_general_dim(self): """ Test that the Riemannian exponential and the Riemannian logarithm are inverse. Expect their composition to give the identity function. """ # Riemannian Log then Riemannian Exp dim = 5 n_samples = self.n_samples h5 = Hyperboloid(dim=dim) h5_metric = h5.metric base_point = h5.random_point() point = h5.random_point() point = gs.cast(point, gs.float64) base_point = gs.cast(base_point, gs.float64) one_log = h5_metric.log(point=point, base_point=base_point) result = h5_metric.exp(tangent_vec=one_log, base_point=base_point) expected = point self.assertAllClose(result, expected) # Test vectorization of log base_point = gs.stack([base_point] * n_samples, axis=0) point = gs.stack([point] * n_samples, axis=0) expected = gs.stack([one_log] * n_samples, axis=0) log = h5_metric.log(point=point, base_point=base_point) result = log self.assertAllClose(gs.shape(result), (n_samples, dim + 1)) self.assertAllClose(result, expected) result = h5_metric.exp(tangent_vec=log, base_point=base_point) expected = point self.assertAllClose(gs.shape(result), (n_samples, dim + 1)) self.assertAllClose(result, expected) # Test vectorization of exp tangent_vec = gs.stack([one_log] * n_samples, axis=0) exp = h5_metric.exp(tangent_vec=tangent_vec, base_point=base_point) result = exp expected = point self.assertAllClose(gs.shape(result), (n_samples, dim + 1)) self.assertAllClose(result, expected) def test_exp_and_belongs(self): H2 = Hyperboloid(dim=2) METRIC = H2.metric base_point = gs.array([1.0, 0.0, 0.0]) self.assertTrue(H2.belongs(base_point)) tangent_vec = H2.to_tangent( vector=gs.array([1.0, 2.0, 1.0]), base_point=base_point ) exp = METRIC.exp(tangent_vec=tangent_vec, base_point=base_point) self.assertTrue(H2.belongs(exp)) def test_exp_small_vec(self): H2 = Hyperboloid(dim=2) METRIC = H2.metric base_point = H2.regularize(gs.array([1.0, 0.0, 0.0])) self.assertTrue(H2.belongs(base_point)) tangent_vec = 1e-9 * H2.to_tangent( vector=gs.array([1.0, 2.0, 1.0]), base_point=base_point ) exp = METRIC.exp(tangent_vec=tangent_vec, base_point=base_point) self.assertTrue(H2.belongs(exp)) def test_exp_vectorization(self): n_samples = 3 dim = self.dimension + 1 one_vec = gs.array([2.0, 1.0, 1.0, 1.0]) one_base_point = gs.array([4.0, 3.0, 1.0, math.sqrt(5)]) n_vecs = gs.array( [ [2.0, 1.0, 1.0, 1.0], [4.0, 1.0, 3.0, math.sqrt(5.0)], [3.0, 2.0, 0.0, 2.0], ] ) n_base_points = gs.array( [ [2.0, 0.0, 1.0, math.sqrt(2)], [5.0, math.sqrt(8), math.sqrt(8), math.sqrt(8)], [1.0, 0.0, 0.0, 0.0], ] ) one_tangent_vec = self.space.to_tangent(one_vec, base_point=one_base_point) result = self.metric.exp(one_tangent_vec, one_base_point) self.assertAllClose(gs.shape(result), (dim,)) n_tangent_vecs = self.space.to_tangent(n_vecs, base_point=one_base_point) result = self.metric.exp(n_tangent_vecs, one_base_point) self.assertAllClose(gs.shape(result), (n_samples, dim)) expected = [] for i in range(n_samples): expected.append(self.metric.exp(n_tangent_vecs[i], one_base_point)) expected = gs.stack(expected, axis=0) expected = helper.to_vector(gs.array(expected)) self.assertAllClose(result, expected, atol=1e-2) one_tangent_vec = self.space.to_tangent(one_vec, base_point=n_base_points) result = self.metric.exp(one_tangent_vec, n_base_points) self.assertAllClose(gs.shape(result), (n_samples, dim)) expected = [] for i in range(n_samples): expected.append(self.metric.exp(one_tangent_vec[i], n_base_points[i])) expected = gs.stack(expected, axis=0) expected = helper.to_vector(gs.array(expected)) self.assertAllClose(result, expected) n_tangent_vecs = self.space.to_tangent(n_vecs, base_point=n_base_points) result = self.metric.exp(n_tangent_vecs, n_base_points) self.assertAllClose(gs.shape(result), (n_samples, dim)) expected = [] for i in range(n_samples): expected.append(self.metric.exp(n_tangent_vecs[i], n_base_points[i])) expected = gs.stack(expected, axis=0) expected = helper.to_vector(gs.array(expected)) self.assertAllClose(result, expected) def test_log_vectorization(self): n_samples = 3 dim = self.dimension + 1 one_point = gs.array([2.0, 1.0, 1.0, 1.0]) one_base_point = gs.array([4.0, 3.0, 1.0, math.sqrt(5)]) n_points = gs.array( [[2.0, 1.0, 1.0, 1.0], [4.0, 1.0, 3.0, math.sqrt(5)], [3.0, 2.0, 0.0, 2.0]] ) n_base_points = gs.array( [ [2.0, 0.0, 1.0, math.sqrt(2)], [5.0, math.sqrt(8), math.sqrt(8), math.sqrt(8)], [1.0, 0.0, 0.0, 0.0], ] ) result = self.metric.log(one_point, one_base_point) self.assertAllClose(gs.shape(result), (dim,)) result = self.metric.log(n_points, one_base_point) self.assertAllClose(gs.shape(result), (n_samples, dim)) result = self.metric.log(one_point, n_base_points) self.assertAllClose(gs.shape(result), (n_samples, dim)) result = self.metric.log(n_points, n_base_points) self.assertAllClose(gs.shape(result), (n_samples, dim)) def test_inner_product(self): """ Test that the inner product between two tangent vectors is the Minkowski inner product. """ minkowski_space = Minkowski(self.dimension + 1) base_point = gs.array([1.16563816, 0.36381045, -0.47000603, 0.07381469]) tangent_vec_a = self.space.to_tangent( vector=gs.array([10.0, 200.0, 1.0, 1.0]), base_point=base_point ) tangent_vec_b = self.space.to_tangent( vector=gs.array([11.0, 20.0, -21.0, 0.0]), base_point=base_point ) result = self.metric.inner_product(tangent_vec_a, tangent_vec_b, base_point) expected = minkowski_space.metric.inner_product( tangent_vec_a, tangent_vec_b, base_point ) self.assertAllClose(result, expected) def test_squared_norm_and_squared_dist(self): """ Test that the squared distance between two points is the squared norm of their logarithm. """ point_a = gs.array([2.0, 1.0, 1.0, 1.0]) point_b = gs.array([4.0, 1.0, 3.0, math.sqrt(5)]) log = self.metric.log(point=point_a, base_point=point_b) result = self.metric.squared_norm(vector=log) expected = self.metric.squared_dist(point_a, point_b) self.assertAllClose(result, expected) def test_norm_and_dist(self): """ Test that the distance between two points is the norm of their logarithm. """ point_a = gs.array([2.0, 1.0, 1.0, 1.0]) point_b = gs.array([4.0, 1.0, 3.0, math.sqrt(5)]) log = self.metric.log(point=point_a, base_point=point_b) result = self.metric.norm(vector=log) expected = self.metric.dist(point_a, point_b) self.assertAllClose(result, expected) def test_log_and_exp_edge_case(self): """ Test that the Riemannian exponential and the Riemannian logarithm are inverse. Expect their composition to give the identity function. """ # Riemannian Log then Riemannian Exp # Edge case: two very close points, base_point_2 and point_2, # form an angle < epsilon base_point_intrinsic = gs.array([1.0, 2.0, 3.0]) base_point = self.space.from_coordinates(base_point_intrinsic, "intrinsic") point_intrinsic = base_point_intrinsic + 1e-12 * gs.array([-1.0, -2.0, 1.0]) point = self.space.from_coordinates(point_intrinsic, "intrinsic") log = self.metric.log(point=point, base_point=base_point) result = self.metric.exp(tangent_vec=log, base_point=base_point) expected = point self.assertAllClose(result, expected) def test_exp_and_log_and_projection_to_tangent_space_general_case(self): """ Test that the Riemannian exponential and the Riemannian logarithm are inverse. Expect their composition to give the identity function. """ # Riemannian Exp then Riemannian Log # General case base_point = gs.array([4.0, 1.0, 3.0, math.sqrt(5)]) vector = gs.array([2.0, 1.0, 1.0, 1.0]) vector = self.space.to_tangent(vector=vector, base_point=base_point) exp = self.metric.exp(tangent_vec=vector, base_point=base_point) result = self.metric.log(point=exp, base_point=base_point) expected = vector self.assertAllClose(result, expected) def test_dist(self): # Distance between a point and itself is 0. point_a = gs.array([4.0, 1.0, 3.0, math.sqrt(5)]) point_b = point_a result = self.metric.dist(point_a, point_b) expected = 0 self.assertAllClose(result, expected) def test_exp_and_dist_and_projection_to_tangent_space(self): base_point = gs.array([4.0, 1.0, 3.0, math.sqrt(5)]) vector = gs.array([0.001, 0.0, -0.00001, -0.00003]) tangent_vec = self.space.to_tangent(vector=vector, base_point=base_point) exp = self.metric.exp(tangent_vec=tangent_vec, base_point=base_point) result = self.metric.dist(base_point, exp) sq_norm = self.metric.embedding_metric.squared_norm(tangent_vec) expected = sq_norm self.assertAllClose(result, expected, atol=1e-2) def test_geodesic_and_belongs(self): initial_point = gs.array([4.0, 1.0, 3.0, math.sqrt(5)]) n_geodesic_points = 100 vector = gs.array([1.0, 0.0, 0.0, 0.0]) initial_tangent_vec = self.space.to_tangent( vector=vector, base_point=initial_point ) geodesic = self.metric.geodesic( initial_point=initial_point, initial_tangent_vec=initial_tangent_vec ) t = gs.linspace(start=0.0, stop=1.0, num=n_geodesic_points) points = geodesic(t) result = gs.all(self.space.belongs(points)) self.assertTrue(result) def test_geodesic_and_belongs_large_initial_velocity(self): initial_point = gs.array([4.0, 1.0, 3.0, math.sqrt(5)]) n_geodesic_points = 100 vector = gs.array([2.0, 0.0, 0.0, 0.0]) initial_tangent_vec = self.space.to_tangent( vector=vector, base_point=initial_point ) geodesic = self.metric.geodesic( initial_point=initial_point, initial_tangent_vec=initial_tangent_vec ) t = gs.linspace(start=0.0, stop=1.0, num=n_geodesic_points) points = geodesic(t) result = gs.all(self.space.belongs(points, atol=gs.atol * 1e4)) self.assertTrue(result) def test_exp_and_log_and_projection_to_tangent_space_edge_case(self): """ Test that the Riemannian exponential and the Riemannian logarithm are inverse. Expect their composition to give the identity function. """ # Riemannian Exp then Riemannian Log # Edge case: tangent vector has norm < epsilon base_point = gs.array([2.0, 1.0, 1.0, 1.0]) vector = 1e-10 * gs.array([0.06, -51.0, 6.0, 5.0]) exp = self.metric.exp(tangent_vec=vector, base_point=base_point) result = self.metric.log(point=exp, base_point=base_point) expected = self.space.to_tangent(vector=vector, base_point=base_point) self.assertAllClose(result, expected) def test_scaled_inner_product(self): base_point_intrinsic = gs.array([1.0, 1.0, 1.0]) base_point = self.space.from_coordinates(base_point_intrinsic, "intrinsic") tangent_vec_a = gs.array([1.0, 2.0, 3.0, 4.0]) tangent_vec_b = gs.array([5.0, 6.0, 7.0, 8.0]) tangent_vec_a = self.space.to_tangent(tangent_vec_a, base_point) tangent_vec_b = self.space.to_tangent(tangent_vec_b, base_point) scale = 2 default_space = Hyperboloid(dim=self.dimension) scaled_space = Hyperboloid(dim=self.dimension, scale=2) inner_product_default_metric = default_space.metric.inner_product( tangent_vec_a, tangent_vec_b, base_point ) inner_product_scaled_metric = scaled_space.metric.inner_product( tangent_vec_a, tangent_vec_b, base_point ) result = inner_product_scaled_metric expected = scale**2 * inner_product_default_metric self.assertAllClose(result, expected) def test_scaled_squared_norm(self): base_point_intrinsic = gs.array([1.0, 1.0, 1.0]) base_point = self.space.from_coordinates(base_point_intrinsic, "intrinsic") tangent_vec = gs.array([1.0, 2.0, 3.0, 4.0]) tangent_vec = self.space.to_tangent(tangent_vec, base_point) scale = 2 default_space = Hyperboloid(dim=self.dimension) scaled_space = Hyperboloid(dim=self.dimension, scale=2) squared_norm_default_metric = default_space.metric.squared_norm( tangent_vec, base_point ) squared_norm_scaled_metric = scaled_space.metric.squared_norm( tangent_vec, base_point ) result = squared_norm_scaled_metric expected = scale**2 * squared_norm_default_metric self.assertAllClose(result, expected) def test_scaled_distance(self): point_a_intrinsic = gs.array([1.0, 2.0, 3.0]) point_b_intrinsic = gs.array([4.0, 5.0, 6.0]) point_a = self.space.from_coordinates(point_a_intrinsic, "intrinsic") point_b = self.space.from_coordinates(point_b_intrinsic, "intrinsic") scale = 2 scaled_space = Hyperboloid(dim=self.dimension, scale=2) distance_default_metric = self.space.metric.dist(point_a, point_b) distance_scaled_metric = scaled_space.metric.dist(point_a, point_b) result = distance_scaled_metric expected = scale * distance_default_metric self.assertAllClose(result, expected) def test_is_tangent(self): base_point = gs.array([4.0, 1.0, 3.0, math.sqrt(5.0)]) point = gs.array([2.0, 1.0, 1.0, 1.0]) log = self.metric.log(point=point, base_point=base_point) result = self.space.is_tangent(log, base_point) self.assertTrue(result) @geomstats.tests.np_autograd_and_tf_only def test_parallel_transport_vectorization(self): space = self.space shape = (4, space.dim + 1) metric = space.metric results = helper.test_parallel_transport(space, metric, shape) for res in results: self.assertTrue(res) def test_projection_and_belongs(self): shape = (self.n_samples, self.dimension + 1) result = helper.test_projection_and_belongs( self.space, shape, atol=gs.atol * 100 ) for res in result: self.assertTrue(res) point = gs.array([0.0, 1.0, 0.0, 0.0]) projected = self.space.projection(point) result = self.space.belongs(projected) self.assertTrue(result)
class TestHyperbolicCoords(geomstats.tests.TestCase): def setUp(self): gs.random.seed(1234) self.dimension = 2 self.extrinsic_manifold = Hyperboloid(dim=self.dimension) self.extrinsic_metric = self.extrinsic_manifold.metric self.ball_manifold = PoincareBall(dim=self.dimension) self.ball_metric = self.ball_manifold.metric self.intrinsic_manifold = Hyperboloid(dim=self.dimension, coords_type="intrinsic") self.intrinsic_metric = self.intrinsic_manifold.metric self.n_samples = 10 def test_extrinsic_ball_extrinsic(self): x_in = gs.array([0.5, 7]) x = self.intrinsic_manifold.to_coordinates(x_in, to_coords_type="extrinsic") x_b = self.extrinsic_manifold.to_coordinates(x, to_coords_type="ball") x2 = self.ball_manifold.to_coordinates(x_b, to_coords_type="extrinsic") self.assertAllClose(x, x2) def test_belongs_intrinsic(self): x_in = gs.array([0.5, 7]) is_in = self.intrinsic_manifold.belongs(x_in) self.assertTrue(is_in) def test_belongs_extrinsic(self): x_true = self.intrinsic_manifold.to_coordinates( gs.array([0.5, 7]), "extrinsic") x_false = gs.array([0.5, 7, 3.0]) is_in = self.extrinsic_manifold.belongs(x_true) self.assertTrue(is_in) is_out = self.extrinsic_manifold.belongs(x_false) self.assertFalse(is_out) def test_belongs_ball(self): x_true = gs.array([0.5, 0.5]) x_false = gs.array([0.8, 0.8]) is_in = self.ball_manifold.belongs(x_true) self.assertTrue(is_in) is_out = self.ball_manifold.belongs(x_false) self.assertFalse(is_out) def test_extrinsic_half_plane_extrinsic(self): x_in = gs.array([0.5, 7], dtype=gs.float64) x = self.intrinsic_manifold.to_coordinates(x_in, to_coords_type="extrinsic") x_up = self.extrinsic_manifold.to_coordinates( x, to_coords_type="half-space") x2 = Hyperbolic.change_coordinates_system(x_up, "half-space", "extrinsic") self.assertAllClose(x, x2) def test_intrinsic_extrinsic_intrinsic(self): x_intr = gs.array([0.5, 7]) x_extr = self.intrinsic_manifold.to_coordinates( x_intr, to_coords_type="extrinsic") x_intr2 = self.extrinsic_manifold.to_coordinates( x_extr, to_coords_type="intrinsic") self.assertAllClose(x_intr, x_intr2) def test_ball_extrinsic_ball(self): x = gs.array([0.5, 0.2]) x_e = self.ball_manifold.to_coordinates(x, to_coords_type="extrinsic") x2 = self.extrinsic_manifold.to_coordinates(x_e, to_coords_type="ball") self.assertAllClose(x, x2) def test_distance_ball_extrinsic_from_ball(self): x_ball = gs.array([0.7, 0.2]) y_ball = gs.array([0.2, 0.2]) x_extr = self.ball_manifold.to_coordinates(x_ball, to_coords_type="extrinsic") y_extr = self.ball_manifold.to_coordinates(y_ball, to_coords_type="extrinsic") dst_ball = self.ball_metric.dist(x_ball, y_ball) dst_extr = self.extrinsic_metric.dist(x_extr, y_extr) self.assertAllClose(dst_ball, dst_extr) def test_distance_ball_extrinsic_from_extr(self): x_int = gs.array([10, 0.2]) y_int = gs.array([1, 6.0]) x_extr = self.intrinsic_manifold.to_coordinates( x_int, to_coords_type="extrinsic") y_extr = self.intrinsic_manifold.to_coordinates( y_int, to_coords_type="extrinsic") x_ball = self.extrinsic_manifold.to_coordinates(x_extr, to_coords_type="ball") y_ball = self.extrinsic_manifold.to_coordinates(y_extr, to_coords_type="ball") dst_ball = self.ball_metric.dist(x_ball, y_ball) dst_extr = self.extrinsic_metric.dist(x_extr, y_extr) self.assertAllClose(dst_ball, dst_extr) def test_distance_ball_extrinsic_from_extr_4_dim(self): x_int = gs.array([10, 0.2, 3, 4]) y_int = gs.array([1, 6, 2.0, 1]) ball_manifold = PoincareBall(4) extrinsic_manifold = Hyperboloid(4) ball_metric = ball_manifold.metric extrinsic_metric = extrinsic_manifold.metric x_extr = extrinsic_manifold.from_coordinates( x_int, from_coords_type="intrinsic") y_extr = extrinsic_manifold.from_coordinates( y_int, from_coords_type="intrinsic") x_ball = extrinsic_manifold.to_coordinates(x_extr, to_coords_type="ball") y_ball = extrinsic_manifold.to_coordinates(y_extr, to_coords_type="ball") dst_ball = ball_metric.dist(x_ball, y_ball) dst_extr = extrinsic_metric.dist(x_extr, y_extr) self.assertAllClose(dst_ball, dst_extr) def test_log_exp_ball_extrinsic_from_extr(self): """Compare log exp in different parameterizations.""" x_int = gs.array([4.0, 0.2]) y_int = gs.array([3.0, 3]) x_extr = self.intrinsic_manifold.to_coordinates( x_int, to_coords_type="extrinsic") y_extr = self.intrinsic_manifold.to_coordinates( y_int, to_coords_type="extrinsic") x_ball = self.extrinsic_manifold.to_coordinates(x_extr, to_coords_type="ball") y_ball = self.extrinsic_manifold.to_coordinates(y_extr, to_coords_type="ball") x_ball_log_exp = self.ball_metric.exp( self.ball_metric.log(y_ball, x_ball), x_ball) x_extr_a = self.extrinsic_metric.exp( self.extrinsic_metric.log(y_extr, x_extr), x_extr) x_extr_b = self.extrinsic_manifold.from_coordinates( x_ball_log_exp, from_coords_type="ball") self.assertAllClose(x_extr_a, x_extr_b, atol=3e-4) def test_log_exp_ball(self): x = gs.array([0.1, 0.2]) y = gs.array([0.2, 0.5]) log = self.ball_metric.log(point=y, base_point=x) exp = self.ball_metric.exp(tangent_vec=log, base_point=x) self.assertAllClose(exp, y) def test_log_exp_ball_vectorization(self): x = gs.array([0.1, 0.2]) y = gs.array([[0.2, 0.5], [0.1, 0.7]]) log = self.ball_metric.log(y, x) exp = self.ball_metric.exp(log, x) self.assertAllClose(exp, y) def test_log_exp_ball_null_tangent(self): x = gs.array([[0.1, 0.2], [0.1, 0.2]]) tangent_vec = gs.array([[0.0, 0.0], [0.0, 0.0]]) exp = self.ball_metric.exp(tangent_vec, x) self.assertAllClose(exp, x)
class TestPoincareHalfSpace(geomstats.tests.TestCase): def setup_method(self): self.manifold = PoincareHalfSpace(2) self.metric = self.manifold.metric self.hyperboloid_manifold = Hyperboloid(2) self.hyperboloid_metric = self.hyperboloid_manifold.metric def test_belongs(self): point = gs.array([1.5, 2.3]) result = self.manifold.belongs(point) self.assertTrue(result) points = gs.array([[1.5, 2.0], [2.5, -0.3]]) result = self.manifold.belongs(points) expected = gs.array([True, False]) self.assertAllClose(result, expected) def test_inner_product_vectorization(self): tangent_vec = gs.array([[1.0, 2.0], [3.0, 4.0]]) base_point = gs.array([[0.0, 1.0], [0.0, 5.0]]) result = self.metric.inner_product(tangent_vec, tangent_vec, base_point) expected = gs.array([5.0, 1.0]) self.assertAllClose(result, expected) def test_half_space_to_ball_coordinates(self): point_half_space = gs.array([0.0, 1.0]) result = self.manifold.half_space_to_ball_coordinates(point_half_space) expected = gs.zeros(2) self.assertAllClose(result, expected) def test_half_space_to_ball_coordinates_vectorization(self): point_half_space = gs.array([[0.0, 1.0], [0.0, 2.0]]) point_ball = self.manifold.half_space_to_ball_coordinates(point_half_space) expected = gs.array([[0.0, 0.0], [0.0, 1.0 / 3.0]]) self.assertAllClose(point_ball, expected) def test_ball_to_half_space_coordinates(self): point_ball = gs.array([-0.3, 0.7]) point_half_space = self.manifold.ball_to_half_space_coordinates(point_ball) point_ext = self.hyperboloid_manifold.from_coordinates(point_ball, "ball") point_half_space_expected = self.hyperboloid_manifold.to_coordinates( point_ext, "half-space" ) self.assertAllClose(point_half_space, point_half_space_expected) def test_coordinates(self): point_half_space = gs.array([1.5, 2.3]) point_ball = self.manifold.half_space_to_ball_coordinates(point_half_space) result = self.manifold.ball_to_half_space_coordinates(point_ball) self.assertAllClose(result, point_half_space) def test_exp_and_coordinates_tangent(self): base_point = gs.array([1.5, 2.3]) tangent_vec = gs.array([0.0, 1.0]) end_point = self.metric.exp(tangent_vec, base_point) self.assertAllClose(base_point[0], end_point[0]) def test_ball_half_plane_are_inverse(self): base_point = gs.array([1.5, 2.3]) base_point_ball = self.manifold.half_space_to_ball_coordinates(base_point) result = self.manifold.ball_to_half_space_coordinates(base_point_ball) self.assertAllClose(result, base_point) def test_ball_half_plane_tangent_are_inverse(self): base_point = gs.array([1.5, 2.3]) tangent_vec = gs.array([0.5, 1.0]) tangent_vec_ball = self.manifold.half_space_to_ball_tangent( tangent_vec, base_point ) base_point_ball = self.manifold.half_space_to_ball_coordinates(base_point) result = self.manifold.ball_to_half_space_tangent( tangent_vec_ball, base_point_ball ) self.assertAllClose(result, tangent_vec) @geomstats.tests.np_and_autograd_only def test_exp(self): point = gs.array([1.0, 1.0]) tangent_vec = gs.array([2.0, 1.0]) end_point = self.metric.exp(tangent_vec, point) circle_center = point[0] + point[1] * tangent_vec[1] / tangent_vec[0] circle_radius = gs.sqrt((circle_center - point[0]) ** 2 + point[1] ** 2) moebius_d = 1 moebius_c = 1 / (2 * circle_radius) moebius_b = circle_center - circle_radius moebius_a = (circle_center + circle_radius) * moebius_c point_complex = point[0] + 1j * point[1] tangent_vec_complex = tangent_vec[0] + 1j * tangent_vec[1] point_moebius = ( 1j * (moebius_d * point_complex - moebius_b) / (moebius_c * point_complex - moebius_a) ) tangent_vec_moebius = ( -1j * tangent_vec_complex * (1j * moebius_c * point_moebius + moebius_d) ** 2 ) end_point_moebius = point_moebius * gs.exp(tangent_vec_moebius / point_moebius) end_point_complex = (moebius_a * 1j * end_point_moebius + moebius_b) / ( moebius_c * 1j * end_point_moebius + moebius_d ) end_point_expected = gs.hstack( [np.real(end_point_complex), np.imag(end_point_complex)] ) self.assertAllClose(end_point, end_point_expected) @geomstats.tests.np_and_autograd_only def test_exp_vectorization(self): point = gs.array([[1.0, 1.0], [1.0, 1.0]]) tangent_vec = gs.array([[2.0, 1.0], [2.0, 1.0]]) result = self.metric.exp(tangent_vec, point) point = point[0] tangent_vec = tangent_vec[0] circle_center = point[0] + point[1] * tangent_vec[1] / tangent_vec[0] circle_radius = gs.sqrt((circle_center - point[0]) ** 2 + point[1] ** 2) moebius_d = 1 moebius_c = 1 / (2 * circle_radius) moebius_b = circle_center - circle_radius moebius_a = (circle_center + circle_radius) * moebius_c point_complex = point[0] + 1j * point[1] tangent_vec_complex = tangent_vec[0] + 1j * tangent_vec[1] point_moebius = ( 1j * (moebius_d * point_complex - moebius_b) / (moebius_c * point_complex - moebius_a) ) tangent_vec_moebius = ( -1j * tangent_vec_complex * (1j * moebius_c * point_moebius + moebius_d) ** 2 ) end_point_moebius = point_moebius * gs.exp(tangent_vec_moebius / point_moebius) end_point_complex = (moebius_a * 1j * end_point_moebius + moebius_b) / ( moebius_c * 1j * end_point_moebius + moebius_d ) end_point_expected = gs.hstack( [np.real(end_point_complex), np.imag(end_point_complex)] ) expected = gs.stack([end_point_expected, end_point_expected]) self.assertAllClose(result, expected) def test_exp_and_log_are_inverse(self): points = gs.array([[1.0, 1.0], [1.0, 1.0]]) tangent_vecs = gs.array([[2.0, 1.0], [2.0, 1.0]]) end_points = self.metric.exp(tangent_vecs, points) result = self.metric.log(end_points, points) expected = tangent_vecs self.assertAllClose(result, expected) def test_projection(self): point = gs.array([[1.0, -1.0], [0.0, 1.0]]) projected = self.manifold.projection(point) result = self.manifold.belongs(projected) self.assertTrue(gs.all(result)) projected = self.manifold.projection(point[0]) result = self.manifold.belongs(projected) self.assertTrue(result)
class TestHyperbolicMethods(geomstats.tests.TestCase): def setUp(self): gs.random.seed(1234) self.dimension = 3 self.space = Hyperboloid(dim=self.dimension) self.metric = self.space.metric self.ball_manifold = PoincareBall(dim=2) self.n_samples = 10 def test_random_uniform_and_belongs(self): point = self.space.random_uniform() result = self.space.belongs(point) expected = True self.assertAllClose(result, expected) def test_random_uniform(self): result = self.space.random_uniform() self.assertAllClose(gs.shape(result), (self.dimension + 1,)) def test_projection_to_tangent_space(self): base_point = gs.array([1., 0., 0., 0.]) self.assertTrue(self.space.belongs(base_point)) tangent_vec = self.space.projection_to_tangent_space( vector=gs.array([1., 2., 1., 3.]), base_point=base_point) result = self.metric.inner_product(tangent_vec, base_point) expected = 0. self.assertAllClose(result, expected) result = self.space.projection_to_tangent_space( vector=gs.array([1., 2., 1., 3.]), base_point=base_point) expected = tangent_vec self.assertAllClose(result, expected) def test_intrinsic_and_extrinsic_coords(self): """ Test that the composition of intrinsic_to_extrinsic_coords and extrinsic_to_intrinsic_coords gives the identity. """ point_int = gs.ones(self.dimension) point_ext = self.space.from_coordinates(point_int, 'intrinsic') result = self.space.to_coordinates(point_ext, 'intrinsic') expected = point_int expected = helper.to_vector(expected) self.assertAllClose(result, expected) point_ext = gs.array([2.0, 1.0, 1.0, 1.0]) point_int = self.space.to_coordinates(point_ext, 'intrinsic') result = self.space.from_coordinates(point_int, 'intrinsic') expected = point_ext expected = helper.to_vector(expected) self.assertAllClose(result, expected) def test_intrinsic_and_extrinsic_coords_vectorization(self): """ Test that the composition of intrinsic_to_extrinsic_coords and extrinsic_to_intrinsic_coords gives the identity. """ point_int = gs.array([[.1, 0., 0., .1, 0., 0.], [.1, .1, .1, .4, .1, 0.], [.1, .3, 0., .1, 0., 0.], [-0.1, .1, -.4, .1, -.01, 0.], [0., 0., .1, .1, -0.08, -0.1], [.1, .1, .1, .1, 0., -0.5]]) point_ext = self.space.from_coordinates(point_int, 'intrinsic') result = self.space.to_coordinates(point_ext, 'intrinsic') expected = point_int expected = helper.to_vector(expected) self.assertAllClose(result, expected) point_ext = gs.array([[2., 1., 1., 1.], [4., 1., 3., math.sqrt(5.)], [3., 2., 0., 2.]]) point_int = self.space.to_coordinates(point_ext, 'intrinsic') result = self.space.from_coordinates(point_int, 'intrinsic') expected = point_ext expected = helper.to_vector(expected) self.assertAllClose(result, expected) def test_log_and_exp_general_case(self): """ Test that the Riemannian exponential and the Riemannian logarithm are inverse. Expect their composition to give the identity function. """ # Riemannian Log then Riemannian Exp # General case base_point = gs.array([4.0, 1., 3.0, math.sqrt(5.)]) point = gs.array([2.0, 1.0, 1.0, 1.0]) log = self.metric.log(point=point, base_point=base_point) result = self.metric.exp(tangent_vec=log, base_point=base_point) expected = point self.assertAllClose(result, expected) def test_log_and_exp_general_case_general_dim(self): """ Test that the Riemannian exponential and the Riemannian logarithm are inverse. Expect their composition to give the identity function. """ # Riemannian Log then Riemannian Exp dim = 5 n_samples = self.n_samples h5 = Hyperboloid(dim=dim) h5_metric = h5.metric base_point = h5.random_uniform() point = h5.random_uniform() one_log = h5_metric.log(point=point, base_point=base_point) result = h5_metric.exp(tangent_vec=one_log, base_point=base_point) expected = point self.assertAllClose(result, expected) # Test vectorization of log base_point = gs.stack([base_point] * n_samples, axis=0) point = gs.stack([point] * n_samples, axis=0) expected = gs.stack([one_log] * n_samples, axis=0) log = h5_metric.log(point=point, base_point=base_point) result = log self.assertAllClose(gs.shape(result), (n_samples, dim + 1)) self.assertAllClose(result, expected) result = h5_metric.exp(tangent_vec=log, base_point=base_point) expected = point self.assertAllClose(gs.shape(result), (n_samples, dim + 1)) self.assertAllClose(result, expected) # Test vectorization of exp tangent_vec = gs.stack([one_log] * n_samples, axis=0) exp = h5_metric.exp(tangent_vec=tangent_vec, base_point=base_point) result = exp expected = point self.assertAllClose(gs.shape(result), (n_samples, dim + 1)) self.assertAllClose(result, expected) def test_exp_and_belongs(self): H2 = Hyperboloid(dim=2) METRIC = H2.metric base_point = gs.array([1., 0., 0.]) self.assertTrue(H2.belongs(base_point)) tangent_vec = H2.projection_to_tangent_space( vector=gs.array([1., 2., 1.]), base_point=base_point) exp = METRIC.exp(tangent_vec=tangent_vec, base_point=base_point) self.assertTrue(H2.belongs(exp)) @geomstats.tests.np_and_pytorch_only def test_exp_vectorization(self): n_samples = 3 dim = self.dimension + 1 one_vec = gs.array([2.0, 1.0, 1.0, 1.0]) one_base_point = gs.array([4.0, 3., 1.0, math.sqrt(5)]) n_vecs = gs.array([[2., 1., 1., 1.], [4., 1., 3., math.sqrt(5.)], [3., 2., 0., 2.]]) n_base_points = gs.array([ [2.0, 0.0, 1.0, math.sqrt(2)], [5.0, math.sqrt(8), math.sqrt(8), math.sqrt(8)], [1.0, 0.0, 0.0, 0.0]]) one_tangent_vec = self.space.projection_to_tangent_space( one_vec, base_point=one_base_point) result = self.metric.exp(one_tangent_vec, one_base_point) self.assertAllClose(gs.shape(result), (dim,)) n_tangent_vecs = self.space.projection_to_tangent_space( n_vecs, base_point=one_base_point) result = self.metric.exp(n_tangent_vecs, one_base_point) self.assertAllClose(gs.shape(result), (n_samples, dim)) expected = gs.zeros((n_samples, dim)) for i in range(n_samples): expected[i] = self.metric.exp(n_tangent_vecs[i], one_base_point) expected = helper.to_vector(gs.array(expected)) self.assertAllClose(result, expected) one_tangent_vec = self.space.projection_to_tangent_space( one_vec, base_point=n_base_points) result = self.metric.exp(one_tangent_vec, n_base_points) self.assertAllClose(gs.shape(result), (n_samples, dim)) expected = gs.zeros((n_samples, dim)) for i in range(n_samples): expected[i] = self.metric.exp(one_tangent_vec[i], n_base_points[i]) expected = helper.to_vector(gs.array(expected)) self.assertAllClose(result, expected) n_tangent_vecs = self.space.projection_to_tangent_space( n_vecs, base_point=n_base_points) result = self.metric.exp(n_tangent_vecs, n_base_points) self.assertAllClose(gs.shape(result), (n_samples, dim)) expected = gs.zeros((n_samples, dim)) for i in range(n_samples): expected[i] = self.metric.exp(n_tangent_vecs[i], n_base_points[i]) expected = helper.to_vector(gs.array(expected)) self.assertAllClose(result, expected) def test_log_vectorization(self): n_samples = 3 dim = self.dimension + 1 one_point = gs.array([2.0, 1.0, 1.0, 1.0]) one_base_point = gs.array([4.0, 3., 1.0, math.sqrt(5)]) n_points = gs.array([[2.0, 1.0, 1.0, 1.0], [4.0, 1., 3.0, math.sqrt(5)], [3.0, 2.0, 0.0, 2.0]]) n_base_points = gs.array([ [2.0, 0.0, 1.0, math.sqrt(2)], [5.0, math.sqrt(8), math.sqrt(8), math.sqrt(8)], [1.0, 0.0, 0.0, 0.0]]) result = self.metric.log(one_point, one_base_point) self.assertAllClose(gs.shape(result), (dim,)) result = self.metric.log(n_points, one_base_point) self.assertAllClose(gs.shape(result), (n_samples, dim)) result = self.metric.log(one_point, n_base_points) self.assertAllClose(gs.shape(result), (n_samples, dim)) result = self.metric.log(n_points, n_base_points) self.assertAllClose(gs.shape(result), (n_samples, dim)) def test_inner_product(self): """ Test that the inner product between two tangent vectors is the Minkowski inner product. """ minkowski_space = Minkowski(self.dimension + 1) base_point = gs.array( [1.16563816, 0.36381045, -0.47000603, 0.07381469]) tangent_vec_a = self.space.projection_to_tangent_space( vector=gs.array([10., 200., 1., 1.]), base_point=base_point) tangent_vec_b = self.space.projection_to_tangent_space( vector=gs.array([11., 20., -21., 0.]), base_point=base_point) result = self.metric.inner_product( tangent_vec_a, tangent_vec_b, base_point) expected = minkowski_space.metric.inner_product( tangent_vec_a, tangent_vec_b, base_point) self.assertAllClose(result, expected) def test_squared_norm_and_squared_dist(self): """ Test that the squared distance between two points is the squared norm of their logarithm. """ point_a = gs.array([2.0, 1.0, 1.0, 1.0]) point_b = gs.array([4.0, 1., 3.0, math.sqrt(5)]) log = self.metric.log(point=point_a, base_point=point_b) result = self.metric.squared_norm(vector=log) expected = self.metric.squared_dist(point_a, point_b) self.assertAllClose(result, expected) def test_norm_and_dist(self): """ Test that the distance between two points is the norm of their logarithm. """ point_a = gs.array([2.0, 1.0, 1.0, 1.0]) point_b = gs.array([4.0, 1., 3.0, math.sqrt(5)]) log = self.metric.log(point=point_a, base_point=point_b) result = self.metric.norm(vector=log) expected = self.metric.dist(point_a, point_b) self.assertAllClose(result, expected) def test_log_and_exp_edge_case(self): """ Test that the Riemannian exponential and the Riemannian logarithm are inverse. Expect their composition to give the identity function. """ # Riemannian Log then Riemannian Exp # Edge case: two very close points, base_point_2 and point_2, # form an angle < epsilon base_point_intrinsic = gs.array([1., 2., 3.]) base_point =\ self.space.from_coordinates(base_point_intrinsic, 'intrinsic') point_intrinsic = (base_point_intrinsic + 1e-12 * gs.array([-1., -2., 1.])) point =\ self.space.from_coordinates(point_intrinsic, 'intrinsic') log = self.metric.log(point=point, base_point=base_point) result = self.metric.exp(tangent_vec=log, base_point=base_point) expected = point self.assertAllClose(result, expected) @geomstats.tests.np_and_tf_only def test_exp_and_log_and_projection_to_tangent_space_general_case(self): """ Test that the Riemannian exponential and the Riemannian logarithm are inverse. Expect their composition to give the identity function. """ # Riemannian Exp then Riemannian Log # General case base_point = gs.array([4.0, 1., 3.0, math.sqrt(5)]) vector = gs.array([2.0, 1.0, 1.0, 1.0]) vector = self.space.projection_to_tangent_space( vector=vector, base_point=base_point) exp = self.metric.exp(tangent_vec=vector, base_point=base_point) result = self.metric.log(point=exp, base_point=base_point) expected = vector self.assertAllClose(result, expected) def test_dist(self): # Distance between a point and itself is 0. point_a = gs.array([4.0, 1., 3.0, math.sqrt(5)]) point_b = point_a result = self.metric.dist(point_a, point_b) expected = 0 self.assertAllClose(result, expected) def test_exp_and_dist_and_projection_to_tangent_space(self): base_point = gs.array([4.0, 1., 3.0, math.sqrt(5)]) vector = gs.array([0.001, 0., -.00001, -.00003]) tangent_vec = self.space.projection_to_tangent_space( vector=vector, base_point=base_point) exp = self.metric.exp( tangent_vec=tangent_vec, base_point=base_point) result = self.metric.dist(base_point, exp) sq_norm = self.metric.embedding_metric.squared_norm( tangent_vec) expected = sq_norm self.assertAllClose(result, expected, atol=1e-2) def test_geodesic_and_belongs(self): # TODO(nina): Fix this tests, as it fails when geodesic goes "too far" initial_point = gs.array([4.0, 1., 3.0, math.sqrt(5)]) n_geodesic_points = 100 vector = gs.array([1., 0., 0., 0.]) initial_tangent_vec = self.space.projection_to_tangent_space( vector=vector, base_point=initial_point) geodesic = self.metric.geodesic( initial_point=initial_point, initial_tangent_vec=initial_tangent_vec) t = gs.linspace(start=0., stop=1., num=n_geodesic_points) points = geodesic(t) result = self.space.belongs(points) expected = n_geodesic_points * [True] self.assertAllClose(result, expected) def test_exp_and_log_and_projection_to_tangent_space_edge_case(self): """ Test that the Riemannian exponential and the Riemannian logarithm are inverse. Expect their composition to give the identity function. """ # Riemannian Exp then Riemannian Log # Edge case: tangent vector has norm < epsilon base_point = gs.array([2., 1., 1., 1.]) vector = 1e-10 * gs.array([.06, -51., 6., 5.]) exp = self.metric.exp(tangent_vec=vector, base_point=base_point) result = self.metric.log(point=exp, base_point=base_point) expected = self.space.projection_to_tangent_space( vector=vector, base_point=base_point) self.assertAllClose(result, expected, atol=1e-8) @geomstats.tests.np_only def test_scaled_inner_product(self): base_point_intrinsic = gs.array([1, 1, 1]) base_point = self.space.from_coordinates( base_point_intrinsic, "intrinsic") tangent_vec_a = gs.array([1, 2, 3, 4]) tangent_vec_b = gs.array([5, 6, 7, 8]) tangent_vec_a = self.space.projection_to_tangent_space( tangent_vec_a, base_point) tangent_vec_b = self.space.projection_to_tangent_space( tangent_vec_b, base_point) scale = 2 default_space = Hyperboloid(dim=self.dimension) scaled_space = Hyperboloid(dim=self.dimension, scale=2) inner_product_default_metric = \ default_space.metric.inner_product( tangent_vec_a, tangent_vec_b, base_point) inner_product_scaled_metric = \ scaled_space.metric.inner_product( tangent_vec_a, tangent_vec_b, base_point) result = inner_product_scaled_metric expected = scale ** 2 * inner_product_default_metric self.assertAllClose(result, expected) @geomstats.tests.np_only def test_scaled_squared_norm(self): base_point_intrinsic = gs.array([1, 1, 1]) base_point = self.space.from_coordinates(base_point_intrinsic, 'intrinsic') tangent_vec = gs.array([1, 2, 3, 4]) tangent_vec = self.space.projection_to_tangent_space( tangent_vec, base_point) scale = 2 default_space = Hyperboloid(dim=self.dimension) scaled_space = Hyperboloid(dim=self.dimension, scale=2) squared_norm_default_metric = default_space.metric.squared_norm( tangent_vec, base_point) squared_norm_scaled_metric = scaled_space.metric.squared_norm( tangent_vec, base_point) result = squared_norm_scaled_metric expected = scale ** 2 * squared_norm_default_metric self.assertAllClose(result, expected) @geomstats.tests.np_only def test_scaled_distance(self): point_a_intrinsic = gs.array([1, 2, 3]) point_b_intrinsic = gs.array([4, 5, 6]) point_a = self.space.from_coordinates(point_a_intrinsic, 'intrinsic') point_b = self.space.from_coordinates(point_b_intrinsic, 'intrinsic') scale = 2 scaled_space = Hyperboloid(dim=self.dimension, scale=2) distance_default_metric = self.space.metric.dist(point_a, point_b) distance_scaled_metric = scaled_space.metric.dist(point_a, point_b) result = distance_scaled_metric expected = scale * distance_default_metric self.assertAllClose(result, expected)
class TestHyperbolicMethods(geomstats.tests.TestCase): def setUp(self): gs.random.seed(1234) self.dimension = 2 self.extrinsic_manifold = Hyperboloid( dim=self.dimension) self.extrinsic_metric = self.extrinsic_manifold.metric self.ball_manifold = PoincareBall( dim=self.dimension) self.ball_metric = self.ball_manifold.metric self.intrinsic_manifold = Hyperboloid( dim=self.dimension, coords_type='intrinsic') self.intrinsic_metric = self.intrinsic_manifold.metric self.n_samples = 10 @geomstats.tests.np_and_pytorch_only def test_extrinsic_ball_extrinsic(self): x_in = gs.array([[0.5, 7]]) x = self.intrinsic_manifold.to_coordinates( x_in, to_coords_type='extrinsic') x_b = self.extrinsic_manifold.to_coordinates(x, to_coords_type='ball') x2 = self.ball_manifold.to_coordinates(x_b, to_coords_type='extrinsic') self.assertAllClose(x, x2, atol=1e-8) @geomstats.tests.np_and_pytorch_only def test_belongs_intrinsic(self): x_in = gs.array([[0.5, 7]]) is_in = self.intrinsic_manifold.belongs(x_in) self.assertTrue(is_in) @geomstats.tests.np_and_pytorch_only def test_belongs_extrinsic(self): x_true = self.intrinsic_manifold.to_coordinates(gs.array([[0.5, 7]]), 'extrinsic') x_false = gs.array([[0.5, 7, 3.]]) is_in = self.extrinsic_manifold.belongs(x_true) self.assertTrue(is_in) is_out = self.extrinsic_manifold.belongs(x_false) self.assertFalse(is_out) @geomstats.tests.np_and_pytorch_only def test_belongs_ball(self): x_true = gs.array([[0.5, 0.5]]) x_false = gs.array([[0.8, 0.8]]) is_in = self.ball_manifold.belongs(x_true) self.assertTrue(is_in) is_out = self.ball_manifold.belongs(x_false) self.assertFalse(is_out) @geomstats.tests.np_and_pytorch_only def test_extrinsic_half_plane_extrinsic(self): x_in = gs.array([[0.5, 7]]) x = self.intrinsic_manifold.to_coordinates( x_in, to_coords_type='extrinsic') x_up = self.extrinsic_manifold.to_coordinates( x, to_coords_type='half-plane') x2 = Hyperbolic.change_coordinates_system(x_up, "half-plane", "extrinsic") self.assertAllClose(x, x2, atol=1e-8) @geomstats.tests.np_and_pytorch_only def test_intrinsic_extrinsic_intrinsic(self): x_intr = gs.array([[0.5, 7]]) x_extr = self.intrinsic_manifold.to_coordinates( x_intr, to_coords_type='extrinsic') x_intr2 = self.extrinsic_manifold.to_coordinates( x_extr, to_coords_type='intrinsic') self.assertAllClose(x_intr, x_intr2, atol=1e-8) @geomstats.tests.np_and_pytorch_only def test_ball_extrinsic_ball(self): x = gs.array([[0.5, 0.2]]) x_e = self.ball_manifold.to_coordinates(x, to_coords_type='extrinsic') x2 = self.extrinsic_manifold.to_coordinates(x_e, to_coords_type='ball') self.assertAllClose(x, x2, atol=1e-10) @geomstats.tests.np_and_pytorch_only def test_distance_ball_extrinsic_from_ball(self): x_ball = gs.array([[0.7, 0.2]]) y_ball = gs.array([[0.2, 0.2]]) x_extr = self.ball_manifold.to_coordinates( x_ball, to_coords_type='extrinsic') y_extr = self.ball_manifold.to_coordinates( y_ball, to_coords_type='extrinsic') dst_ball = self.ball_metric.dist(x_ball, y_ball) dst_extr = self.extrinsic_metric.dist(x_extr, y_extr) # TODO(nmiolane): Remove this when ball is properly vectorized dst_extr = gs.to_ndarray(dst_extr, to_ndim=2, axis=1) self.assertAllClose(dst_ball, dst_extr) @geomstats.tests.np_and_pytorch_only def test_distance_ball_extrinsic_from_extr(self): x_int = gs.array([[10, 0.2]]) y_int = gs.array([[1, 6.]]) x_extr = self.intrinsic_manifold.to_coordinates( x_int, to_coords_type='extrinsic') y_extr = self.intrinsic_manifold.to_coordinates( y_int, to_coords_type='extrinsic') x_ball = self.extrinsic_manifold.to_coordinates( x_extr, to_coords_type='ball') y_ball = self.extrinsic_manifold.to_coordinates( y_extr, to_coords_type='ball') dst_ball = self.ball_metric.dist(x_ball, y_ball) dst_extr = self.extrinsic_metric.dist(x_extr, y_extr) # TODO(nmiolane): Remove this when ball is properly vectorized dst_extr = gs.to_ndarray(dst_extr, to_ndim=2, axis=1) self.assertAllClose(dst_ball, dst_extr) @geomstats.tests.np_and_pytorch_only def test_distance_ball_extrinsic_from_extr_4_dim(self): x_int = gs.array([[10, 0.2, 3, 4]]) y_int = gs.array([[1, 6, 2., 1]]) ball_manifold = PoincareBall(4) extrinsic_manifold = Hyperboloid(4) ball_metric = ball_manifold.metric extrinsic_metric = extrinsic_manifold.metric x_extr = extrinsic_manifold.from_coordinates( x_int, from_coords_type='intrinsic') y_extr = extrinsic_manifold.from_coordinates( y_int, from_coords_type='intrinsic') x_ball = extrinsic_manifold.to_coordinates( x_extr, to_coords_type='ball') y_ball = extrinsic_manifold.to_coordinates( y_extr, to_coords_type='ball') dst_ball = ball_metric.dist(x_ball, y_ball) dst_extr = extrinsic_metric.dist(x_extr, y_extr) # TODO(nmiolane): Remove this when ball is properly vectorized dst_extr = gs.to_ndarray(dst_extr, to_ndim=2, axis=1) self.assertAllClose(dst_ball, dst_extr) @geomstats.tests.np_and_pytorch_only def test_log_exp_ball_extrinsic_from_extr(self): """Compare log exp in different parameterizations.""" # TODO(Hazaatiti): Fix this test # x_int = gs.array([[4., 0.2]]) # y_int = gs.array([[3., 3]]) # x_extr = self.intrinsic_manifold.to_coordinates( # x_int, to_point_type='extrinsic') # y_extr = self.intrinsic_manifold.to_coordinates( # y_int, to_point_type='extrinsic') # x_ball = self.extrinsic_manifold.to_coordinates( # x_extr, to_point_type='ball') # y_ball = self.extrinsic_manifold.to_coordinates( # y_extr, to_point_type='ball') # x_ball_log_exp = self.ball_metric.exp( # self.ball_metric.log(y_ball, x_ball), x_ball) # x_extr_a = self.extrinsic_metric.exp( # self.extrinsic_metric.log(y_extr, x_extr), x_extr) # x_extr_b = self.extrinsic_manifold.from_coordinates( # x_ball_log_exp, from_point_type='ball') # self.assertAllClose(x_extr_a, x_extr_b, atol=1e-4) @geomstats.tests.np_only def test_log_exp_ball(self): x = gs.array([[0.1, 0.2]]) y = gs.array([[0.2, 0.5]]) log = self.ball_metric.log(point=y, base_point=x) exp = self.ball_metric.exp(tangent_vec=log, base_point=x) self.assertAllClose(exp, y, atol=1e-1) @geomstats.tests.np_only def test_log_exp_ball_vectorization(self): x = gs.array([[0.1, 0.2]]) y = gs.array([[0.2, 0.5], [0.1, 0.7]]) log = self.ball_metric.log(y, x) exp = self.ball_metric.exp(log, x) self.assertAllClose(exp, y, atol=1e-1) @geomstats.tests.np_only def test_log_exp_ball_null_tangent(self): x = gs.array([[0.1, 0.2], [0.1, 0.2]]) tangent_vec = gs.array([[0.0, 0.0], [0.0, 0.0]]) exp = self.ball_metric.exp(tangent_vec, x) self.assertAllClose(exp, x, atol=1e-10)