def __init__(self, dim, scale=1.0): super(PoincareHalfSpaceMetric, self).__init__(dim=dim, signature=(dim, 0)) self.coords_type = PoincareHalfSpace.default_coords_type self.point_type = PoincareHalfSpace.default_point_type self.scale = scale self.poincare_ball = PoincareBall(dim=dim, scale=scale)
def test_coordinate(self, dim, point_a, point_b): metric = self.metric(dim) point_a_h = PoincareBall(dim).to_coordinates(gs.array(point_a), "extrinsic") point_b_h = PoincareBall(dim).to_coordinates(gs.array(point_b), "extrinsic") dist_in_ball = metric.dist(gs.array(point_a), gs.array(point_b)) dist_in_hype = Hyperboloid(dim).metric.dist(point_a_h, point_b_h) self.assertAllClose(dist_in_ball, dist_in_hype)
def test_distance_ball_extrinsic_from_ball(self, dim, x_ball, y_ball): ball_manifold = PoincareBall(dim) space = Hyperboloid(dim) x_extr = ball_manifold.to_coordinates(x_ball, to_coords_type="extrinsic") y_extr = ball_manifold.to_coordinates(y_ball, to_coords_type="extrinsic") dst_ball = ball_manifold.metric.dist(x_ball, y_ball) dst_extr = space.metric.dist(x_extr, y_extr) self.assertAllClose(dst_ball, dst_extr)
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_composition(self, dim, point_intrinsic): x = Hyperboloid(dim, coords_type="intrinsic").to_coordinates( point_intrinsic, to_coords_type="extrinsic" ) x_b = Hyperboloid(dim).to_coordinates(x, to_coords_type="ball") x2 = PoincareBall(dim).to_coordinates(x_b, to_coords_type="extrinsic") self.assertAllClose(x, x2)
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 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 expectation_maximisation_poincare_ball(): """Apply EM algorithm on three random data clusters.""" dim = 2 n_samples = 5 cluster_1 = gs.random.uniform(low=0.2, high=0.6, size=(n_samples, dim)) cluster_2 = gs.random.uniform(low=-0.6, high=-0.2, size=(n_samples, dim)) cluster_3 = gs.random.uniform(low=-0.3, high=0, size=(n_samples, dim)) cluster_3[:, 0] = -cluster_3[:, 0] data = gs.concatenate((cluster_1, cluster_2, cluster_3), axis=0) n_clusters = 3 manifold = PoincareBall(dim=2) metric = manifold.metric EM = RiemannianEM(n_gaussians=n_clusters, metric=metric, initialisation_method='random') means, variances, mixture_coefficients = EM.fit(data=data) # Plot result plot = plot_gaussian_mixture_distribution(data, mixture_coefficients, means, variances, plot_precision=100, save_path='result.png', metric=metric) return plot
def test_predict_poincare_ball_distance(self): """Test the 'predict' class method using the Poincare ball distance.""" dim = 2 space = PoincareBall(dim=dim) distance = space.metric.dist training_dataset = gs.array([ [1 / 2, 1 / 4], [1 / 2, 0], [1 / 2, -1 / 4], [-1 / 2, 1 / 4], [-1 / 2, 0], [-1 / 2, -1 / 4], ]) labels = [0, 0, 0, 1, 1, 1] kde = KernelDensityEstimationClassifier(distance=distance, kernel="distance") kde.fit(training_dataset, labels) target_dataset = gs.array([ [1 / 2, 1 / 5], [1 / 2, 0], [1 / 2, -1 / 5], [-1 / 2, 1 / 5], [-1 / 2, 0], [-1 / 2, -1 / 5], ]) result = kde.predict(target_dataset) expected = [0, 0, 0, 1, 1, 1] self.assertAllClose(expected, result)
def __init__( self, dim=2, max_epochs=100, lr=.05, n_context=1, n_negative=2): self.manifold = PoincareBall(dim) self.max_epochs = max_epochs self.lr = lr self.n_context = n_context self.n_negative = n_negative
def __new__(cls, *args, default_coords_type='extrinsic', **kwargs): """Instantiate class that corresponds to the default_coords_type.""" errors.check_parameter_accepted_values( default_coords_type, 'default_coords_type', ['extrinsic', 'ball', 'half-space']) if default_coords_type == 'extrinsic': return Hyperboloid(*args, **kwargs) if default_coords_type == 'ball': return PoincareBall(*args, **kwargs) return PoincareHalfSpace(*args, **kwargs)
def setup_method(self): """Set manifold, data and EM parameters.""" self.n_samples = 5 self.dim = 2 self.space = PoincareBall(dim=self.dim) self.metric = self.space.metric self.initialisation_method = "random" self.mean_method = "batch" cluster_1 = gs.random.uniform( low=0.2, high=0.6, size=(self.n_samples, self.dim) ) cluster_2 = gs.random.uniform( low=-0.6, high=-0.2, size=(self.n_samples, self.dim) ) cluster_3 = gs.random.uniform(low=-0.3, high=0, size=(self.n_samples, self.dim)) cluster_3 = cluster_3 * gs.array([-1.0, 1.0]) self.n_gaussian = 3 self.data = gs.concatenate((cluster_1, cluster_2, cluster_3), axis=0)
def __new__(cls, *args, default_coords_type="extrinsic", **kwargs): """Instantiate class that corresponds to the default_coords_type.""" errors.check_parameter_accepted_values( default_coords_type, "default_coords_type", ["extrinsic", "ball", "half-space"], ) if default_coords_type == "extrinsic": return Hyperboloid(*args, **kwargs) if default_coords_type == "ball": return PoincareBall(*args, **kwargs) return PoincareHalfSpace(*args, **kwargs)
def kmean_poincare_ball(): """Run K-means on the Poincare ball.""" n_samples = 20 dim = 2 n_clusters = 2 manifold = PoincareBall(dim=dim) metric = manifold.metric cluster_1 = gs.random.uniform(low=0.5, high=0.6, size=(n_samples, dim)) cluster_2 = gs.random.uniform(low=0, high=-0.2, size=(n_samples, dim)) data = gs.concatenate((cluster_1, cluster_2), axis=0) kmeans = RiemannianKMeans(metric=metric, n_clusters=n_clusters, init='random', mean_method='frechet-poincare-ball' ) centroids = kmeans.fit(X=data, max_iter=100) labels = kmeans.predict(X=data) plt.figure(1) colors = ['red', 'blue'] ax = visualization.plot( data, space='H2_poincare_disk', marker='.', color='black', point_type=manifold.point_type) for i in range(n_clusters): ax = visualization.plot( data[labels == i], ax=ax, space='H2_poincare_disk', marker='.', color=colors[i], point_type=manifold.point_type) ax = visualization.plot( centroids, ax=ax, space='H2_poincare_disk', marker='*', color='green', s=100, point_type=manifold.point_type) ax.set_title('Kmeans on Poincaré Ball Manifold') return plt
def setUp(self): """Set manifold, data and EM parameters.""" self.n_samples = 5 self.dim = 2 self.space = PoincareBall(dim=self.dim) self.metric = self.space.metric self.initialisation_method = 'random' self.mean_method = 'frechet-poincare-ball' cluster_1 = gs.random.uniform(low=0.2, high=0.6, size=(self.n_samples, self.dim)) cluster_2 = gs.random.uniform(low=-0.6, high=-0.2, size=(self.n_samples, self.dim)) cluster_3 = gs.random.uniform(low=-0.3, high=0, size=(self.n_samples, self.dim)) cluster_3[:, 0] = -cluster_3[:, 0] self.n_gaussian = 3 self.data = gs.concatenate((cluster_1, cluster_2, cluster_3), axis=0)
def grad_squared_distance(point_a, point_b): """Gradient of squared hyperbolic distance. Gradient of the squared distance based on the Ball representation according to point_a Parameters ---------- point_a : array-like, shape=[n_samples, dim] First point in hyperbolic space. point_b : array-like, shape=[n_samples, dim] Second point in hyperbolic space. Returns ------- dist : array-like, shape=[n_samples, 1] Geodesic squared distance between the two points. """ hyperbolic_metric = PoincareBall(2).metric log_map = hyperbolic_metric.log(point_b, point_a) return -2 * log_map
def kmean_poincare_ball(): """Run K-means on the Poincare ball.""" n_samples = 20 dim = 2 n_clusters = 2 manifold = PoincareBall(dim=dim) metric = manifold.metric cluster_1 = gs.random.uniform(low=0.5, high=0.6, size=(n_samples, dim)) cluster_2 = gs.random.uniform(low=0, high=-0.2, size=(n_samples, dim)) data = gs.concatenate((cluster_1, cluster_2), axis=0) kmeans = RiemannianKMeans(metric=metric, n_clusters=n_clusters, init="random") centroids = kmeans.fit(X=data) labels = kmeans.predict(X=data) plt.figure(1) colors = ["red", "blue"] ax = visualization.plot( data, space="H2_poincare_disk", marker=".", color="black", point_type=manifold.point_type, ) for i in range(n_clusters): ax = visualization.plot( data[labels == i], ax=ax, space="H2_poincare_disk", marker=".", color=colors[i], point_type=manifold.point_type, ) ax = visualization.plot( centroids, ax=ax, space="H2_poincare_disk", marker="*", color="green", s=100, point_type=manifold.point_type, ) ax.set_title("Kmeans on Poincaré Ball Manifold") return plt
def _expectation(self, data): """Update the posterior probabilities. Parameters ---------- data : array-like, shape=[n_samples, n_features] Training data, where n_samples is the number of samples and n_features is the number of features. """ probability_distribution_function = \ PoincareBall.gmm_pdf( data, self.means, self.variances, norm_func=self.metric.find_normalization_factor, metric=self.metric, variances_range=self.variances_range, norm_func_var=self.normalization_factor_var) if gs.isnan(probability_distribution_function.mean()): logging.warning('EXPECTATION : Probability distribution function' 'contain elements that are not numbers') num_normalized_pdf = gs.einsum('j,...j->...j', self.mixture_coefficients, probability_distribution_function) valid_pdf_condition = gs.amin(gs.sum(num_normalized_pdf, -1)) if valid_pdf_condition <= PDF_TOL: num_normalized_pdf[gs.sum(num_normalized_pdf, -1) <= PDF_TOL] = 1 sum_pdf = gs.sum(num_normalized_pdf, -1) posterior_probabilities =\ gs.einsum('...i,...->...i', num_normalized_pdf, 1 / sum_pdf) if gs.any(gs.mean(posterior_probabilities)) is None: logging.warning('EXPECTATION : posterior probabilities ' 'contain elements that are not numbers.') if 1 - SUM_CHECK_PDF >= gs.mean(gs.sum(posterior_probabilities, 1)) >= 1 + SUM_CHECK_PDF: logging.warning('EXPECTATION : posterior probabilities ' 'do not sum to 1.') if gs.any(gs.sum(posterior_probabilities, 0) < PDF_TOL): logging.warning('EXPECTATION : Gaussian got no elements ' '(precision error) reinitialize') posterior_probabilities[posterior_probabilities == 0] = PDF_TOL return posterior_probabilities
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)
def setUp(self): self.manifold = PoincareBall(2) self.metric = self.manifold.metric self.hyperboloid_manifold = Hyperboloid(2) self.hyperboloid_metric = self.hyperboloid_manifold.metric
class TestPoincareBallMethods(geomstats.tests.TestCase): def setUp(self): self.manifold = PoincareBall(2) self.metric = self.manifold.metric self.hyperboloid_manifold = Hyperboloid(2) self.hyperboloid_metric = self.hyperboloid_manifold.metric def test_squared_dist(self): point_a = gs.array([-0.3, 0.7]) point_b = gs.array([0.2, 0.5]) distance_a_b = self.metric.dist(point_a, point_b) squared_distance = self.metric.squared_dist(point_a, point_b) # TODO(nmiolane): # Remove this line when poincare ball is properly vectorized squared_distance = gs.to_ndarray(squared_distance, to_ndim=2, axis=1) self.assertAllClose(distance_a_b**2, squared_distance, atol=1e-8) @geomstats.tests.np_and_pytorch_only def test_coordinates(self): point_a = gs.array([-0.3, 0.7]) point_b = gs.array([0.2, 0.5]) point_a_h =\ self.manifold.to_coordinates(point_a, 'extrinsic') point_b_h =\ self.manifold.to_coordinates(point_b, 'extrinsic') dist_in_ball =\ self.metric.dist(point_a, point_b) dist_in_hype =\ self.hyperboloid_metric.dist(point_a_h, point_b_h) # TODO(ninamiolane): Remove this to_ndarray when poincare_ball # is properly vectorized dist_in_hype = gs.to_ndarray(dist_in_hype, to_ndim=2, axis=1) self.assertAllClose(dist_in_ball, dist_in_hype, atol=1e-8) def test_dist_poincare(self): point_a = gs.array([0.5, 0.5]) point_b = gs.array([0.5, -0.5]) dist_a_b =\ self.manifold.metric.dist(point_a, point_b) result = dist_a_b expected = gs.array([[2.887270927429199]]) self.assertAllClose(result, expected) def test_dist_vectorization(self): point_a = gs.array([0.2, 0.5]) point_b = gs.array([[0.3, -0.5], [0.2, 0.2]]) dist_a_b =\ self.manifold.metric.dist(point_a, point_b) result_vect = dist_a_b result =\ [self.manifold.metric.dist(point_a, point_b[i]) for i in range(len(point_b))] result = gs.concatenate(result, axis=0) self.assertAllClose(result_vect, result) def test_mobius_vectorization(self): point_a = gs.array([0.5, 0.5]) point_b = gs.array([[0.5, -0.3], [0.3, 0.4]]) dist_a_b =\ self.manifold.metric.mobius_add(point_a, point_b) result_vect = dist_a_b result =\ [self.manifold.metric.mobius_add(point_a, point_b[i]) for i in range(len(point_b))] result = gs.concatenate(result, axis=0) self.assertAllClose(result_vect, result) dist_a_b =\ self.manifold.metric.mobius_add(point_b, point_a) result_vect = dist_a_b result =\ [self.manifold.metric.mobius_add(point_b[i], point_a) for i in range(len(point_b))] result = gs.concatenate(result, axis=0) self.assertAllClose(result_vect, result) def test_log_vectorization(self): point_a = gs.array([0.5, 0.5]) point_b = gs.array([[0.5, -0.5], [0.4, 0.4]]) dist_a_b =\ self.manifold.metric.log(point_a, point_b) result_vect = dist_a_b result =\ [self.manifold.metric.log(point_a, point_b[i]) for i in range(len(point_b))] result = gs.concatenate(result, axis=0) self.assertAllClose(result_vect, result) dist_a_b =\ self.manifold.metric.log(point_b, point_a) result_vect = dist_a_b result =\ [self.manifold.metric.log(point_b[i], point_a) for i in range(len(point_b))] result = gs.concatenate(result, axis=0) self.assertAllClose(result_vect, result) def test_exp_vectorization(self): point_a = gs.array([0.5, 0.5]) point_b = gs.array([[0.5, -0.5], [0.4, 0.4]]) dist_a_b =\ self.manifold.metric.exp(point_a, point_b) result_vect = dist_a_b result =\ [self.manifold.metric.exp(point_a, point_b[i]) for i in range(len(point_b))] result = gs.concatenate(result, axis=0) self.assertAllClose(result_vect, result) dist_a_b =\ self.manifold.metric.exp(point_b, point_a) result_vect = dist_a_b result =\ [self.manifold.metric.exp(point_b[i], point_a) for i in range(len(point_b))] result = gs.concatenate(result, axis=0) self.assertAllClose(result_vect, result) def test_log_poincare(self): point = gs.array([[0.3, 0.5]]) base_point = gs.array([[0.3, 0.3]]) result = self.manifold.metric.log(point, base_point) expected = gs.array([[-0.01733576, 0.21958634]]) self.manifold.metric.coords_type = 'extrinsic' self.assertAllClose(result, expected) def test_belong_true_poincare(self): point = gs.array([[0.3, 0.5]]) belong = self.manifold.belongs(point) self.assertTrue(belong) def test_belong_false_poincare(self): point = gs.array([[1.2, 0.5]]) belong = self.manifold.belongs(point) self.assertFalse(belong) def test_exp_poincare(self): point = gs.array([[0.3, 0.5]]) base_point = gs.array([[0.3, 0.3]]) tangent_vec = self.manifold.metric.log(point, base_point) result = self.manifold.metric.exp(tangent_vec, base_point) self.manifold.metric.coords_type = 'extrinsic' self.assertAllClose(result, point) def test_ball_retraction(self): x = gs.array([[0.5, 0.6], [0.2, -0.1], [0.2, -0.4]]) y = gs.array([[0.3, 0.5], [0.3, -0.6], [0.3, -0.3]]) ball_metric = self.manifold.metric tangent_vec = ball_metric.log(y, x) ball_metric.retraction(tangent_vec, x)
class TestPoincareBall(geomstats.tests.TestCase): def setUp(self): self.manifold = PoincareBall(2) self.metric = self.manifold.metric self.hyperboloid_manifold = Hyperboloid(2) self.hyperboloid_metric = self.hyperboloid_manifold.metric def test_squared_dist(self): point_a = gs.array([-0.3, 0.7]) point_b = gs.array([0.2, 0.5]) distance_a_b = self.metric.dist(point_a, point_b) squared_distance = self.metric.squared_dist(point_a, point_b) self.assertAllClose(distance_a_b**2, squared_distance, atol=1e-8) @geomstats.tests.np_and_pytorch_only def test_coordinates(self): point_a = gs.array([-0.3, 0.7]) point_b = gs.array([0.2, 0.5]) point_a_h =\ self.manifold.to_coordinates(point_a, 'extrinsic') point_b_h =\ self.manifold.to_coordinates(point_b, 'extrinsic') dist_in_ball =\ self.metric.dist(point_a, point_b) dist_in_hype =\ self.hyperboloid_metric.dist(point_a_h, point_b_h) self.assertAllClose(dist_in_ball, dist_in_hype, atol=1e-8) def test_dist_poincare(self): point_a = gs.array([0.5, 0.5]) point_b = gs.array([0.5, -0.5]) dist_a_b =\ self.manifold.metric.dist(point_a, point_b) result = dist_a_b expected = 2.887270927429199 self.assertAllClose(result, expected) def test_dist_vectorization(self): point_a = gs.array([0.2, 0.5]) point_b = gs.array([[0.3, -0.5], [0.2, 0.2]]) dist_a_b =\ self.manifold.metric.dist(point_a, point_b) result_vect = dist_a_b result =\ [self.manifold.metric.dist(point_a, point_b[i]) for i in range(len(point_b))] result = gs.stack(result, axis=0) self.assertAllClose(result_vect, result) def test_dist_broadcast(self): point_a = gs.array([[0.2, 0.5], [0.3, 0.1]]) point_b = gs.array([[0.3, -0.5], [0.2, 0.2]]) point_c = gs.array([[0.2, 0.3], [0.5, 0.5], [-0.4, 0.1]]) point_d = gs.array([0.1, 0.2, 0.3]) dist_a_b =\ self.manifold.metric.dist_broadcast(point_a, point_b) dist_b_c = gs.flatten( self.manifold.metric.dist_broadcast(point_b, point_c)) result_vect = gs.concatenate((dist_a_b, dist_b_c), axis=0) result_a_b =\ [self.manifold.metric.dist_broadcast(point_a[i], point_b[i]) for i in range(len(point_b))] result_b_c = \ [self.manifold.metric.dist_broadcast(point_b[i], point_c[j]) for i in range(len(point_b)) for j in range(len(point_c)) ] result = result_a_b + result_b_c result = gs.stack(result, axis=0) self.assertAllClose(result_vect, result) with self.assertRaises(ValueError): self.manifold.metric.dist_broadcast(point_a, point_d) @geomstats.tests.np_and_pytorch_only def test_dist_pairwise(self): point = gs.array([[0.1, 0.2], [0.3, 0.4], [0.5, 0.5]]) result = self.manifold.metric.dist_pairwise(point) expected = gs.array([[0., 0.65821943, 1.34682524], [0.65821943, 0., 0.71497076], [1.34682524, 0.71497076, 0.]]) self.assertAllClose(result, expected, rtol=1e-3) def test_mobius_vectorization(self): point_a = gs.array([0.5, 0.5]) point_b = gs.array([[0.5, -0.3], [0.3, 0.4]]) dist_a_b =\ self.manifold.metric.mobius_add(point_a, point_b) result_vect = dist_a_b result =\ [self.manifold.metric.mobius_add(point_a, point_b[i]) for i in range(len(point_b))] result = gs.stack(result, axis=0) self.assertAllClose(result_vect, result) dist_a_b =\ self.manifold.metric.mobius_add(point_b, point_a) result_vect = dist_a_b result =\ [self.manifold.metric.mobius_add(point_b[i], point_a) for i in range(len(point_b))] result = gs.stack(result, axis=0) self.assertAllClose(result_vect, result) def test_log_vectorization(self): point_a = gs.array([0.5, 0.5]) point_b = gs.array([[0.5, -0.5], [0.4, 0.4]]) dist_a_b =\ self.manifold.metric.log(point_a, point_b) result_vect = dist_a_b result =\ [self.manifold.metric.log(point_a, point_b[i]) for i in range(len(point_b))] result = gs.stack(result, axis=0) self.assertAllClose(result_vect, result) dist_a_b =\ self.manifold.metric.log(point_b, point_a) result_vect = dist_a_b result =\ [self.manifold.metric.log(point_b[i], point_a) for i in range(len(point_b))] result = gs.stack(result, axis=0) self.assertAllClose(result_vect, result) def test_exp_vectorization(self): point_a = gs.array([0.5, 0.5]) point_b = gs.array([[0.0, 0.0], [0.5, -0.5], [0.4, 0.4]]) dist_a_b =\ self.manifold.metric.exp(point_a, point_b) result_vect = dist_a_b result =\ [self.manifold.metric.exp(point_a, point_b[i]) for i in range(len(point_b))] result = gs.stack(result, axis=0) self.assertAllClose(result_vect, result) dist_a_b =\ self.manifold.metric.exp(point_b, point_a) result_vect = dist_a_b result =\ [self.manifold.metric.exp(point_b[i], point_a) for i in range(len(point_b))] result = gs.stack(result, axis=0) self.assertAllClose(result_vect, result) def test_log_poincare(self): point = gs.array([0.3, 0.5]) base_point = gs.array([0.3, 0.3]) result = self.manifold.metric.log(point, base_point) expected = gs.array([-0.01733576, 0.21958634]) self.manifold.metric.coords_type = 'extrinsic' self.assertAllClose(result, expected) def test_belong_true_poincare(self): point = gs.array([0.3, 0.5]) belong = self.manifold.belongs(point) self.assertTrue(belong) def test_belong_false_poincare(self): point = gs.array([1.2, 0.5]) belong = self.manifold.belongs(point) self.assertFalse(belong) def test_projection(self): point = gs.array([1.2, 0.5]) projected_point = self.manifold.projection(point) self.assertTrue(gs.sum(projected_point * projected_point) < 1.) def test_exp_poincare(self): point = gs.array([0.3, 0.5]) base_point = gs.array([0.3, 0.3]) tangent_vec = self.manifold.metric.log(point, base_point) result = self.manifold.metric.exp(tangent_vec, base_point) self.manifold.metric.coords_type = 'extrinsic' self.assertAllClose(result, point) def test_ball_retraction(self): x = gs.array([[0.5, 0.6], [0.2, -0.1], [0.2, -0.4]]) y = gs.array([[0.3, 0.5], [0.3, -0.6], [0.3, -0.3]]) ball_metric = self.manifold.metric tangent_vec = ball_metric.log(y, x) ball_metric.retraction(tangent_vec, x)
class TestEM(geomstats.tests.TestCase): """Class for testing Expectation Maximization.""" def setUp(self): """Set manifold, data and EM parameters.""" self.n_samples = 5 self.dim = 2 self.space = PoincareBall(dim=self.dim) self.metric = self.space.metric self.initialisation_method = 'random' self.mean_method = 'batch' cluster_1 = gs.random.uniform(low=0.2, high=0.6, size=(self.n_samples, self.dim)) cluster_2 = gs.random.uniform(low=-0.6, high=-0.2, size=(self.n_samples, self.dim)) cluster_3 = gs.random.uniform(low=-0.3, high=0, size=(self.n_samples, self.dim)) cluster_3 = cluster_3 * gs.array([-1., 1.]) self.n_gaussian = 3 self.data = gs.concatenate((cluster_1, cluster_2, cluster_3), axis=0) @geomstats.tests.np_only def test_fit_init_kmeans(self): """Test fitting data into a GMM.""" gmm_learning = RiemannianEM( metric=self.metric, n_gaussians=self.n_gaussian, initialisation_method=self.initialisation_method) means, variances, coefficients = gmm_learning.fit(self.data) self.assertTrue((coefficients < 1).all() and (coefficients > 0).all()) self.assertTrue((variances < 1).all() and (variances > 0).all()) self.assertTrue(self.space.belongs(means).all()) gmm_learning = RiemannianEM(metric=self.metric, n_gaussians=self.n_gaussian, initialisation_method='kmeans') means, variances, coefficients = gmm_learning.fit(self.data) self.assertTrue((coefficients < 1).all() and (coefficients > 0).all()) self.assertTrue((variances < 1).all() and (variances > 0).all()) self.assertTrue(self.space.belongs(means).all()) @geomstats.tests.np_only def test_fit_init_random(self): """Test fitting data into a GMM.""" gmm_learning = RiemannianEM( metric=self.metric, n_gaussians=self.n_gaussian, initialisation_method=self.initialisation_method) means, variances, coefficients = gmm_learning.fit(self.data) self.assertTrue((coefficients < 1).all() and (coefficients > 0).all()) self.assertTrue((variances < 1).all() and (variances > 0).all()) self.assertTrue(self.space.belongs(means).all()) gmm_learning = RiemannianEM(metric=self.metric, n_gaussians=self.n_gaussian, initialisation_method='random') means, variances, coefficients = gmm_learning.fit(self.data) self.assertTrue((coefficients < 1).all() and (coefficients > 0).all()) self.assertTrue((variances < 1).all() and (variances > 0).all()) self.assertTrue(self.space.belongs(means).all()) def test_weighted_frechet_mean(self): """Test for weighted mean.""" data = gs.array([[0.1, 0.2], [0.25, 0.35]]) weights = gs.array([3., 1.]) mean_o = FrechetMean(metric=self.metric, point_type='vector', lr=1.) mean_o.fit(data, weights=weights) result = mean_o.estimate_ expected = self.metric.exp( weights[1] / gs.sum(weights) * self.metric.log(data[1], data[0]), data[0]) self.assertAllClose(result, expected) @geomstats.tests.np_and_pytorch_only def test_normalization_factor(self): """Test for Gaussian distribution normalization factor.""" gmm = RiemannianEM(self.metric) variances_range, normalization_factor_var, phi_inv_var = \ gmm.normalization_factor_init( gs.arange(ZETA_LOWER_BOUND, ZETA_UPPER_BOUND, ZETA_STEP)) self.assertAllClose(normalization_factor_var[4], 0.00291884, TOLERANCE) self.assertAllClose(phi_inv_var[3], 0.00562326, TOLERANCE) variances_test = gs.array([0.8, 1.2]) norm_factor_test = find_normalization_factor(variances_test, variances_range, normalization_factor_var) norm_factor_verdict = gs.array([0.79577319, 2.3791778]) self.assertAllClose(norm_factor_test, norm_factor_verdict, TOLERANCE) norm_factor_test2 = self.metric.normalization_factor(variances_test) self.assertAllClose(norm_factor_test2, norm_factor_verdict, TOLERANCE) norm_factor_test3, norm_factor_gradient_test = \ self.metric.norm_factor_gradient(variances_test) norm_factor_gradient_verdict = gs.array([3.0553115709, 2.53770926]) self.assertAllClose(norm_factor_test3, norm_factor_verdict, TOLERANCE) self.assertAllClose(norm_factor_gradient_test, norm_factor_gradient_verdict, TOLERANCE) find_var_test = find_variance_from_index( gs.array([0.5, 0.4, 0.3, 0.2]), variances_range, phi_inv_var) find_var_verdict = gs.array([0.481, 0.434, 0.378, 0.311]) self.assertAllClose(find_var_test, find_var_verdict, TOLERANCE) @geomstats.tests.np_only def test_fit_init_random_sphere(self): """Test fitting data into a GMM.""" space = Hypersphere(2) gmm_learning = RiemannianEM( metric=space.metric, n_gaussians=2, initialisation_method=self.initialisation_method) means = space.random_uniform(2) cluster_1 = space.random_von_mises_fisher(mu=means[0], kappa=20, n_samples=140) cluster_2 = space.random_von_mises_fisher(mu=means[1], kappa=20, n_samples=140) data = gs.concatenate((cluster_1, cluster_2), axis=0) means, variances, coefficients = gmm_learning.fit(data) self.assertTrue((coefficients < 1).all() and (coefficients > 0).all()) self.assertTrue((variances < 1).all() and (variances > 0).all()) self.assertTrue(space.belongs(means).all())
def test_ball_extrinsic_ball(self, dim, x_ball): x_extrinsic = PoincareBall(dim).to_coordinates( x_ball, to_coords_type="extrinsic" ) result = self.space(dim).to_coordinates(x_extrinsic, to_coords_type="ball") self.assertAllClose(result, x_ball)
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 PoincareHalfSpaceMetric(RiemannianMetric): """Class for the metric of the n-dimensional hyperbolic space. Class for the metric of the n-dimensional hyperbolic space as embedded in the Poincaré half space model. Parameters ---------- dim : int Dimension of the hyperbolic space. scale : int Scale of the hyperbolic space, defined as the set of points in Minkowski space whose squared norm is equal to -scale. Optional, default: 1. """ default_point_type = "vector" default_coords_type = "half-space" def __init__(self, dim, scale=1.0): super(PoincareHalfSpaceMetric, self).__init__(dim=dim, signature=(dim, 0)) self.coords_type = PoincareHalfSpace.default_coords_type self.point_type = PoincareHalfSpace.default_point_type self.scale = scale self.poincare_ball = PoincareBall(dim=dim, scale=scale) def inner_product(self, tangent_vec_a, tangent_vec_b, base_point): """Compute the inner-product of two tangent vectors at a base point. Parameters ---------- tangent_vec_a : array-like, shape=[..., dim + 1] First tangent vector at base point. tangent_vec_b : array-like, shape=[..., dim + 1] Second tangent vector at base point. base_point : array-like, shape=[..., dim + 1] Point in hyperbolic space. Returns ------- inner_prod : array-like, shape=[..., 1] Inner-product of the two tangent vectors. """ inner_prod = gs.sum(tangent_vec_a * tangent_vec_b, axis=-1) inner_prod = inner_prod / base_point[..., -1]**2 return inner_prod def exp(self, tangent_vec, base_point, **kwargs): """Compute the Riemannian exponential. Parameters ---------- tangent_vec : array-like, shape=[...,n] Tangent vector at the base point in the Poincare half space. base_point : array-like, shape=[...,n] Point in the Poincare half space. Returns ------- end_point : array-like, shape=[...,n] Point in the Poincare half space, reached by the geodesic starting from `base_point` with initial velocity `tangent_vec` """ base_point_ball = self.poincare_ball.half_space_to_ball_coordinates( base_point) tangent_vec_ball = self.poincare_ball.half_space_to_ball_tangent( tangent_vec, base_point) end_point_ball = self.poincare_ball.metric.exp(tangent_vec_ball, base_point_ball) end_point = self.poincare_ball.ball_to_half_space_coordinates( end_point_ball) return end_point def log(self, point, base_point, **kwargs): """Compute Riemannian logarithm of a point wrt a base point. Parameters ---------- point : array-like, shape=[..., dim] Point in hyperbolic space. base_point : array-like, shape=[..., dim] Point in hyperbolic space. Returns ------- log : array-like, shape=[..., dim] Tangent vector at the base point equal to the Riemannian logarithm of point at the base point. """ point_ball = self.poincare_ball.half_space_to_ball_coordinates(point) base_point_ball = self.poincare_ball.half_space_to_ball_coordinates( base_point) log_ball = self.poincare_ball.metric.log(point_ball, base_point_ball) log = self.poincare_ball.ball_to_half_space_tangent( log_ball, base_point_ball) return log
class TestEM(geomstats.tests.TestCase): """Class for testing Expectation Maximization.""" @geomstats.tests.np_and_pytorch_only def setUp(self): """Set manifold, data and EM parameters.""" self.n_samples = 5 self.dim = 2 self.space = PoincareBall(dim=self.dim) self.metric = self.space.metric self.initialisation_method = 'random' self.mean_method = 'frechet-poincare-ball' cluster_1 = gs.random.uniform(low=0.2, high=0.6, size=(self.n_samples, self.dim)) cluster_2 = gs.random.uniform(low=-0.6, high=-0.2, size=(self.n_samples, self.dim)) cluster_3 = gs.random.uniform(low=-0.3, high=0, size=(self.n_samples, self.dim)) cluster_3[:, 0] = -cluster_3[:, 0] self.n_gaussian = 3 self.data = gs.concatenate((cluster_1, cluster_2, cluster_3), axis=0) @geomstats.tests.np_only def test_fit(self): """Test fitting data into a GMM.""" gmm_learning = RiemannianEM( riemannian_metric=self.metric, n_gaussians=self.n_gaussian, initialisation_method=self.initialisation_method, mean_method=self.mean_method) means, variances, coefficients = gmm_learning.fit(self.data) self.assertTrue((coefficients < 1).all() and (coefficients > 0).all()) self.assertTrue((variances < 1).all() and (variances > 0).all()) self.assertTrue(self.space.belongs(means).all()) @geomstats.tests.np_only def test_weighted_frechet_mean(self): """Test for weighted mean.""" data = gs.array([[0.1, 0.2], [0.25, 0.35], [-0.1, -0.2], [-0.4, 0.3]]) weights = gs.repeat([0.5], data.shape[0]) mean_o = FrechetMean(metric=self.metric, point_type='vector') mean_o.fit(data, weights) mean = mean_o.estimate_ mean_verdict = [-0.03857, 0.15922] self.assertAllClose(mean, mean_verdict, TOLERANCE) @geomstats.tests.np_and_pytorch_only def test_normalization_factor(self): """Test for Gaussian distribution normalization factor.""" variances_range,\ normalization_factor_var,\ phi_inv_var = \ self.metric.normalization_factor_init(gs.arange( ZETA_LOWER_BOUND, ZETA_UPPER_BOUND, ZETA_STEP)) self.assertAllClose(normalization_factor_var[4], 0.00291884, TOLERANCE) self.assertAllClose(phi_inv_var[3], 0.00562326, TOLERANCE) variances_test = gs.array([0.8, 1.2]) norm_factor_test = self.metric.find_normalization_factor( variances_test, variances_range, normalization_factor_var) norm_factor_verdict = gs.array([0.79577319, 2.3791778]) self.assertAllClose(norm_factor_test, norm_factor_verdict, TOLERANCE) norm_factor_test2 = self.metric.normalization_factor(variances_test) self.assertAllClose(norm_factor_test2, norm_factor_verdict, TOLERANCE) norm_factor_test3, norm_factor_gradient_test = \ self.metric.norm_factor_gradient(variances_test) norm_factor_gradient_verdict = gs.array([3.0553115709, 2.53770926]) self.assertAllClose(norm_factor_test3, norm_factor_verdict, TOLERANCE) self.assertAllClose(norm_factor_gradient_test, norm_factor_gradient_verdict, TOLERANCE) find_var_test = self.metric.find_variance_from_index( gs.array([0.5, 0.4, 0.3, 0.2]), variances_range, phi_inv_var) find_var_verdict = gs.array([0.481, 0.434, 0.378, 0.311]) self.assertAllClose(find_var_test, find_var_verdict, TOLERANCE)
def main(): """Learning Poincaré graph embedding. Learns Poincaré Ball embedding by using Riemannian gradient descent algorithm. """ gs.random.seed(1234) dim = 2 max_epochs = 100 lr = .05 n_negative = 2 context_size = 1 karate_graph = gdp.Graph( graph_matrix_path='examples/data/graph_karate/karate.txt', labels_path='examples/data/graph_karate/karate_labels.txt') nb_vertices_by_edges =\ [len(e_2) for _, e_2 in karate_graph.edges.items()] logging.info('Number of edges: %s', len(karate_graph.edges)) logging.info('Mean vertices by edges: %s', (sum(nb_vertices_by_edges, 0) / len(karate_graph.edges))) negative_table_parameter = 5 negative_sampling_table = [] for i, nb_v in enumerate(nb_vertices_by_edges): negative_sampling_table +=\ ([i] * int((nb_v**(3. / 4.))) * negative_table_parameter) negative_sampling_table = gs.array(negative_sampling_table) random_walks = karate_graph.random_walk() embeddings = gs.random.normal(size=(karate_graph.n_nodes, dim)) embeddings = embeddings * 0.2 hyperbolic_manifold = PoincareBall(2) colors = {1: 'b', 2: 'r'} for epoch in range(max_epochs): total_loss = [] for path in random_walks: for example_index, one_path in enumerate(path): context_index = path[max(0, example_index - context_size ):min(example_index + context_size, len(path))] negative_index =\ gs.random.randint(negative_sampling_table.shape[0], size=(len(context_index), n_negative)) negative_index = negative_sampling_table[negative_index] example_embedding = embeddings[one_path] for one_context_i, one_negative_i in zip( context_index, negative_index): context_embedding = embeddings[one_context_i] negative_embedding = embeddings[one_negative_i] l, g_ex = loss(example_embedding, context_embedding, negative_embedding, hyperbolic_manifold) total_loss.append(l) example_to_update = embeddings[one_path] embeddings[one_path] = hyperbolic_manifold.metric.exp( -lr * g_ex, example_to_update) logging.info('iteration %d loss_value %f', epoch, sum(total_loss, 0) / len(total_loss)) circle = visualization.PoincareDisk(point_type='ball') plt.figure() ax = plt.subplot(111) circle.add_points(gs.array([[0, 0]])) circle.set_ax(ax) circle.draw(ax=ax) for i_embedding, embedding in enumerate(embeddings): plt.scatter(embedding[0], embedding[1], c=colors[karate_graph.labels[i_embedding][0]]) plt.show()