def setUp(self): self.n_samples = 10 self.SO3_GROUP = SpecialOrthogonal(n=3) self.SE3_GROUP = SpecialEuclidean(n=3) self.S1 = Hypersphere(dim=1) self.S2 = Hypersphere(dim=2) self.H2 = Hyperbolic(dim=2) plt.figure()
def setUp(self): self.n_samples = 10 self.SO3_GROUP = SpecialOrthogonalGroup(n=3) self.SE3_GROUP = SpecialEuclideanGroup(n=3) self.S1 = Hypersphere(dimension=1) self.S2 = Hypersphere(dimension=2) self.H2 = HyperbolicSpace(dimension=2) plt.figure()
def setUp(self): gs.random.seed(1234) self.space_matrix = ProductManifold( manifolds=[Hypersphere(dim=2), Hyperboloid(dim=2)], default_point_type='matrix') self.space_vector = ProductManifold( manifolds=[Hypersphere(dim=2), Hyperboloid(dim=5)], default_point_type='vector')
def empirical_frechet_var_bubble(n_samples, theta, dim, n_expectation=1000): """Variance of the empirical Fréchet mean for a bubble distribution. Draw n_sampless from a bubble distribution, computes its empirical Fréchet mean and the square distance to the asymptotic mean. This is repeated n_expectation times to compute an approximation of its expectation (i.e. its variance) by sampling. The bubble distribution is an isotropic distributions on a Riemannian hyper sub-sphere of radius 0 < theta < Pi around the north pole of the sphere of dimension dim. Parameters ---------- n_samples : int Number of samples to draw. theta: float Radius of the bubble distribution. dim : int Dimension of the sphere (embedded in R^{dim+1}). n_expectation: int, optional (defaults to 1000) Number of computations for approximating the expectation. Returns ------- tuple (variance, std-dev on the computed variance) """ if dim <= 1: raise ValueError( 'Dim > 1 needed to draw a uniform sample on sub-sphere.') var = [] sphere = Hypersphere(dim=dim) bubble = Hypersphere(dim=dim - 1) north_pole = gs.zeros(dim + 1) north_pole[dim] = 1.0 for _ in range(n_expectation): # Sample n points from the uniform distribution on a sub-sphere # of radius theta (i.e cos(theta) in ambient space) # TODO (nina): Add this code as a method of hypersphere data = gs.zeros((n_samples, dim + 1), dtype=gs.float64) directions = bubble.random_uniform(n_samples) directions = gs.to_ndarray(directions, to_ndim=2) for i in range(n_samples): for j in range(dim): data[i, j] = gs.sin(theta) * directions[i, j] data[i, dim] = gs.cos(theta) # TODO (nina): Use FrechetMean here current_mean = _adaptive_gradient_descent(data, metric=sphere.metric, max_iter=32, init_point=north_pole) var.append(sphere.metric.squared_dist(north_pole, current_mean)) return gs.mean(var), 2 * gs.std(var) / gs.sqrt(n_expectation)
def setUp(self): self.n_samples = 10 self.SO3_GROUP = SpecialOrthogonal(n=3, point_type='vector') self.SE3_GROUP = SpecialEuclidean(n=3, point_type='vector') self.S1 = Hypersphere(dim=1) self.S2 = Hypersphere(dim=2) self.H2 = Hyperbolic(dim=2) self.H2_half_plane = PoincareHalfSpace(dim=2) plt.figure()
def empirical_frechet_var_bubble(n_samples, theta, dim, n_expectation=1000): """Variance of the empirical Fréchet mean for a bubble distribution. Draw n_sampless from a bubble distribution, computes its empirical Fréchet mean and the square distance to the asymptotic mean. This is repeated n_expectation times to compute an approximation of its expectation (i.e. its variance) by sampling. The bubble distribution is an isotropic distributions on a Riemannian hyper sub-sphere of radius 0 < theta < Pi around the north pole of the sphere of dimension dim. Parameters ---------- n_samples : int Number of samples to draw. theta: float Radius of the bubble distribution. dim : int Dimension of the sphere (embedded in R^{dim+1}). n_expectation: int, optional (defaults to 1000) Number of computations for approximating the expectation. Returns ------- tuple (variance, std-dev on the computed variance) """ if dim <= 1: raise ValueError( "Dim > 1 needed to draw a uniform sample on sub-sphere.") var = [] sphere = Hypersphere(dim=dim) bubble = Hypersphere(dim=dim - 1) north_pole = gs.zeros(dim + 1) north_pole[dim] = 1.0 for _ in range(n_expectation): # Sample n points from the uniform distribution on a sub-sphere # of radius theta (i.e cos(theta) in ambient space) # TODO (nina): Add this code as a method of hypersphere last_col = gs.cos(theta) * gs.ones(n_samples) last_col = last_col[:, None] if (n_samples > 1) else last_col directions = bubble.random_uniform(n_samples) rest_col = gs.sin(theta) * directions data = gs.concatenate([rest_col, last_col], axis=-1) estimator = FrechetMean(sphere.metric, max_iter=32, method="adaptive", init_point=north_pole) estimator.fit(data) current_mean = estimator.estimate_ var.append(sphere.metric.squared_dist(north_pole, current_mean)) return gs.mean(var), 2 * gs.std(var) / gs.sqrt(n_expectation)
def setup_method(self): gs.random.seed(1234) self.space_matrix = ProductManifold( manifolds=[Hypersphere(dim=2), Hyperboloid(dim=2)], default_point_type="matrix", ) self.space_vector = ProductManifold( manifolds=[Hypersphere(dim=2), Hyperboloid(dim=3)], default_point_type="vector", )
def test_extrinsic_to_angle_inverse(self): space = Hypersphere(1) point = space.random_uniform() angle = space.extrinsic_to_angle(point) result = space.angle_to_extrinsic(angle) self.assertAllClose(result, point) space = Hypersphere(1, default_coords_type='intrinsic') angle = space.random_uniform() extrinsic = space.angle_to_extrinsic(angle) result = space.extrinsic_to_angle(extrinsic) self.assertAllClose(result, angle)
def setUp(self): gs.random.seed(1234) self.sphere = Hypersphere(dimension=2) self.hyperbolic = Hyperbolic(dimension=5) self.space = ProductManifold(manifolds=[self.sphere, self.hyperbolic])
def test_intrinsic_and_extrinsic_coords_vectorization(self): """Test change of coordinates. Test that the composition of intrinsic_to_extrinsic_coords and extrinsic_to_intrinsic_coords gives the identity. """ space = Hypersphere(dim=2) point_int = gs.array([ [0.1, 0.1], [0.1, 0.4], [0.1, 0.3], [0.0, 0.0], [0.1, 0.5], ]) point_ext = space.intrinsic_to_extrinsic_coords(point_int) result = space.extrinsic_to_intrinsic_coords(point_ext) expected = point_int self.assertAllClose(result, expected) point_int = space.extrinsic_to_intrinsic_coords(point_ext) result = space.intrinsic_to_extrinsic_coords(point_int) expected = point_ext self.assertAllClose(result, expected)
def test_random_von_mises_one_sample_belongs(self): for dim in [2, 9]: sphere = Hypersphere(dim) point = sphere.random_von_mises_fisher() self.assertAllClose(point.shape, (dim + 1, )) result = sphere.belongs(point) self.assertTrue(result)
def test_random_von_mises_kappa(self): # check concentration parameter for dispersed distribution kappa = 1.0 n_points = 100000 for dim in [2, 9]: sphere = Hypersphere(dim) points = sphere.random_von_mises_fisher(kappa=kappa, n_samples=n_points) sum_points = gs.sum(points, axis=0) mean_norm = gs.linalg.norm(sum_points) / n_points kappa_estimate = (mean_norm * (dim + 1.0 - mean_norm**2) / (1.0 - mean_norm**2)) kappa_estimate = gs.cast(kappa_estimate, gs.float64) p = dim + 1 n_steps = 100 for _ in range(n_steps): bessel_func_1 = scipy.special.iv(p / 2.0, kappa_estimate) bessel_func_2 = scipy.special.iv(p / 2.0 - 1.0, kappa_estimate) ratio = bessel_func_1 / bessel_func_2 denominator = 1.0 - ratio**2 - (p - 1.0) * ratio / kappa_estimate mean_norm = gs.cast(mean_norm, gs.float64) kappa_estimate = kappa_estimate - (ratio - mean_norm) / denominator result = kappa_estimate expected = kappa self.assertAllClose(result, expected, atol=KAPPA_ESTIMATION_TOL)
def setup_method(self): gs.random.seed(1234) self.n_samples = 20 # Set up for hypersphere self.dim_sphere = 2 self.shape_sphere = (self.dim_sphere + 1, ) self.sphere = Hypersphere(dim=self.dim_sphere) self.intercept_sphere_true = gs.array([0.0, -1.0, 0.0]) self.coef_sphere_true = gs.array([1.0, 0.0, 0.5]) # set up the prior self.prior = lambda x: self.sphere.metric.exp( x * self.coef_sphere_true, base_point=self.intercept_sphere_true, ) self.kernel = ConstantKernel(1.0, (1e-3, 1e3)) * RBF(10.0, (1e-2, 1e2)) # generate data X = gs.linspace(0.0, 1.5 * gs.pi, self.n_samples) self.X_sphere = gs.reshape((X - gs.mean(X)), (-1, 1)) # generate the geodesic y = self.prior(self.X_sphere) # Then add orthogonal sinusoidal oscillations o = (1.0 / 20.0) * gs.array([-0.5, 0.0, 1.0]) o = self.sphere.to_tangent(o, base_point=y) s = self.X_sphere * gs.sin(5.0 * gs.pi * self.X_sphere) self.y_sphere = self.sphere.metric.exp(s * o, base_point=y)
def load_cities(): """Load data from data/cities/cities.json. Returns ------- data : array-like, shape=[50, 2] Array with each row representing one sample, i. e. latitude and longitude of a city. Angles are in radians. name : list List of city names. """ with open(CITIES_PATH, encoding='utf-8') as json_file: data_file = json.load(json_file) names = [row['city'] for row in data_file] data = list( map( lambda row: [row[col_name] / 180 * gs.pi for col_name in ['lat', 'lng']], data_file, )) data = gs.array(data) colat = gs.pi / 2 - data[:, 0] colat = gs.expand_dims(colat, axis=1) lng = gs.expand_dims(data[:, 1] + gs.pi, axis=1) data = gs.concatenate([colat, lng], axis=1) sphere = Hypersphere(dim=2) data = sphere.spherical_to_extrinsic(data) return data, names
def setUp(self): gs.random.seed(1234) self.dimension = 4 self.space = Hypersphere(dimension=self.dimension) self.metric = self.space.metric self.n_samples = 10
def test_predict_hypersphere_distance(self): """Test the 'predict' class method using the hypersphere distance.""" dim = 2 space = Hypersphere(dim=dim) distance = space.metric.dist training_dataset = gs.array([ [1, 0, 0], [3**(1 / 2) / 2, 1 / 2, 0], [3**(1 / 2) / 2, -1 / 2, 0], [0, 0, 1], [0, 1 / 2, 3**(1 / 2) / 2], [0, -1 / 2, 3**(1 / 2) / 2], ]) labels = [0, 0, 0, 1, 1, 1] kde = KernelDensityEstimationClassifier(distance=distance) kde.fit(training_dataset, labels) target_dataset = gs.array([ [2**(1 / 2) / 2, 2**(1 / 2) / 2, 0], [0, 1 / 2, -(3**(1 / 2)) / 2], [0, -1 / 2, -(3**(1 / 2)) / 2], [-(3**(1 / 2)) / 2, 1 / 2, 0], [-(3**(1 / 2)) / 2, -1 / 2, 0], [0, 2**(1 / 2) / 2, 2**(1 / 2) / 2], ]) result = kde.predict(target_dataset) expected = [0, 0, 0, 1, 1, 1] self.assertAllClose(expected, result)
def test_single_cluster(self): gs.random.seed(10) sphere = Hypersphere(dim=2) metric = sphere.metric cluster = sphere.random_von_mises_fisher(kappa=100, n_samples=10) rms = riemannian_mean_shift( manifold=sphere, metric=metric, bandwidth=float("inf"), tol=1e-4, n_centers=1, max_iter=1, ) rms.fit(cluster) center = rms.predict(cluster) mean = FrechetMean(metric=metric, init_step_size=1.0) mean.fit(cluster) result = center[0] expected = mean.estimate_ self.assertAllClose(expected, result)
def test_fit_hypersphere_distance(self): """Test the 'fit' class method using the hypersphere distance.""" n_clusters = 2 dimension = 2 space = Hypersphere(dim=dimension) distance = space.metric.dist dataset = gs.array( [ [1, 0, 0], [3 ** (1 / 2) / 2, 1 / 2, 0], [3 ** (1 / 2) / 2, -1 / 2, 0], [0, 0, 1], [0, 1 / 2, 3 ** (1 / 2) / 2], [0, -1 / 2, 3 ** (1 / 2) / 2], ] ) clustering = AgglomerativeHierarchicalClustering( n_clusters=n_clusters, distance=distance ) clustering.fit(dataset) clustering_labels = clustering.labels_ result = (clustering_labels == gs.array([1, 1, 1, 0, 0, 0])).all() or ( clustering_labels == gs.array([0, 0, 0, 1, 1, 1]) ).all() expected = True self.assertAllClose(expected, result)
def main(): sphere = Hypersphere(dimension=2) data = sphere.random_von_mises_fisher(kappa=10, n_samples=1000) n_clusters = 4 clustering = OnlineKMeans(metric=sphere.metric, n_clusters=n_clusters) clustering = clustering.fit(data) plt.figure(0) ax = plt.subplot(111, projection="3d") visualization.plot(points=clustering.cluster_centers_, ax=ax, space='S2', c='r') plt.show() plt.figure(1) ax = plt.subplot(111, projection="3d") sphere_plot = visualization.Sphere() sphere_plot.draw(ax=ax) for i in range(n_clusters): cluster = data[clustering.labels_ == i, :] sphere_plot.draw_points(ax=ax, points=cluster) plt.show()
def test_parallel_transport(self, dim, n_samples): sphere = Hypersphere(dim) base_point = sphere.random_uniform(n_samples) tan_vec_a = sphere.to_tangent(gs.random.rand(n_samples, 3), base_point) tan_vec_b = sphere.to_tangent(gs.random.rand(n_samples, 3), base_point) expected = sphere.metric.parallel_transport(tan_vec_a, base_point, tan_vec_b) expected_point = sphere.metric.exp(tan_vec_b, base_point) base_point = gs.cast(base_point, gs.float64) base_point, tan_vec_a, tan_vec_b = gs.convert_to_wider_dtype( [base_point, tan_vec_a, tan_vec_b]) for step, alpha in zip(["pole", "schild"], [1, 2]): min_n = 1 if step == "pole" else 50 tol = 1e-5 if step == "pole" else 1e-2 for n_rungs in [min_n, 11]: ladder = sphere.metric.ladder_parallel_transport( tan_vec_a, base_point, tan_vec_b, n_rungs=n_rungs, scheme=step, alpha=alpha, ) result = ladder["transported_tangent_vec"] result_point = ladder["end_point"] self.assertAllClose(result, expected, rtol=tol, atol=tol) self.assertAllClose(result_point, expected_point)
def test_geodesic_vectorization(self): space = Hypersphere(2) metric = space.metric initial_point = space.random_uniform(2) vector = gs.random.rand(2, 3) initial_tangent_vec = space.to_tangent(vector=vector, base_point=initial_point) end_point = space.random_uniform(2) time = gs.linspace(0, 1, 10) geo = metric.geodesic(initial_point, initial_tangent_vec) path = geo(time) result = path.shape expected = (2, 10, 3) self.assertAllClose(result, expected) geo = metric.geodesic(initial_point, end_point=end_point) path = geo(time) result = path.shape expected = (2, 10, 3) self.assertAllClose(result, expected) geo = metric.geodesic(initial_point, end_point=end_point[0]) path = geo(time) result = path.shape expected = (2, 10, 3) self.assertAllClose(result, expected) initial_tangent_vec = space.to_tangent(vector=vector, base_point=initial_point[0]) geo = metric.geodesic(initial_point[0], initial_tangent_vec) path = geo(time) result = path.shape expected = (2, 10, 3) self.assertAllClose(result, expected)
def test_hypersphere_predict(self): gs.random.seed(1234) sphere = Hypersphere(dim=2) metric = sphere.metric cluster = sphere.random_von_mises_fisher(kappa=100, n_samples=10) rms = riemannian_mean_shift( manifold=sphere, metric=metric, bandwidth=0.6, tol=1e-4, n_centers=2, max_iter=100, ) rms.fit(cluster) result = rms.predict(cluster) closest_centers = [] for point in cluster: closest_center = metric.closest_neighbor_index(point, rms.centers) closest_centers.append(rms.centers[closest_center, :]) expected = gs.array(closest_centers) self.assertAllClose(expected, result)
def main(): """Plot an Agglomerative Hierarchical Clustering on the sphere.""" sphere = Hypersphere(dim=2) sphere_distance = sphere.metric.dist n_clusters = 2 n_samples_per_dataset = 50 dataset_1 = sphere.random_von_mises_fisher( kappa=10, n_samples=n_samples_per_dataset) dataset_2 = - sphere.random_von_mises_fisher( kappa=10, n_samples=n_samples_per_dataset) dataset = gs.concatenate((dataset_1, dataset_2), axis=0) clustering = AgglomerativeHierarchicalClustering( n_clusters=n_clusters, distance=sphere_distance) clustering.fit(dataset) clustering_labels = clustering.labels_ plt.figure(0) ax = plt.subplot(111, projection='3d') plt.title('Agglomerative Hierarchical Clustering') sphere_plot = visualization.Sphere() sphere_plot.draw(ax=ax) for i_label in range(n_clusters): points_label_i = dataset[clustering_labels == i_label, ...] sphere_plot.draw_points(ax=ax, points=points_label_i) plt.show()
def test_parallel_transport_trajectory(self, dim, n_samples): sphere = Hypersphere(dim) for step in ["pole", "schild"]: n_steps = 1 if step == "pole" else 50 tol = 1e-6 if step == "pole" else 1e-2 base_point = sphere.random_uniform(n_samples) tan_vec_a = sphere.to_tangent(gs.random.rand(n_samples, 3), base_point) tan_vec_b = sphere.to_tangent(gs.random.rand(n_samples, 3), base_point) expected = sphere.metric.parallel_transport( tan_vec_a, base_point, tan_vec_b) expected_point = sphere.metric.exp(tan_vec_b, base_point) ladder = sphere.metric.ladder_parallel_transport( tan_vec_a, base_point, tan_vec_b, n_rungs=n_steps, scheme=step, return_geodesics=True, ) result = ladder["transported_tangent_vec"] result_point = ladder["end_point"] self.assertAllClose(result, expected, rtol=tol, atol=tol) self.assertAllClose(result_point, expected_point)
def setUp(self): gs.random.seed(1234) self.dimension = 2 self.space = Hypersphere(dim=self.dimension) self.metric = self.space.metric self.data = self.space.random_von_mises_fisher(kappa=100, n_samples=50)
def test_parallel_transport_and_sphere_parallel_transport( self, dim, tangent_vec_a, tangent_vec_b, base_point): """Test consistency between sphere's parallel transports. The parallel transport of the class Hypersphere is defined in terms of extrinsic coordinates. The parallel transport of pullback_metric is defined in terms of the spherical coordinates. """ pullback_metric = PullbackMetric(dim=dim, embedding_dim=dim + 1, immersion=immersion) immersed_base_point = immersion(base_point) jac_immersion = pullback_metric.jacobian_immersion(base_point) immersed_tangent_vec_a = gs.matmul(jac_immersion, tangent_vec_a) immersed_tangent_vec_b = gs.matmul(jac_immersion, tangent_vec_b) result_dict = pullback_metric.ladder_parallel_transport( tangent_vec_a, base_point=base_point, direction=tangent_vec_b) result = result_dict["transported_tangent_vec"] end_point = result_dict["end_point"] result = pullback_metric.tangent_immersion(v=result, x=end_point) expected = Hypersphere(dim).metric.parallel_transport( immersed_tangent_vec_a, base_point=immersed_base_point, direction=immersed_tangent_vec_b, ) self.assertAllClose(result, expected, atol=1e-5)
def tangent_extrinsic_to_spherical_raises_test_data(self): smoke_data = [] dim_list = [2, 3] for dim in dim_list: space = Hypersphere(dim) base_point = space.random_point() tangent_vec = space.to_tangent(space.random_point(), base_point) if dim == 2: expected = does_not_raise() smoke_data.append( dict( dim=2, tangent_vec=tangent_vec, base_point=None, base_point_spherical=None, expected=pytest.raises(ValueError), ) ) else: expected = pytest.raises(NotImplementedError) smoke_data.append( dict( dim=dim, tangent_vec=tangent_vec, base_point=base_point, base_point_spherical=None, expected=expected, ) ) return self.generate_tests(smoke_data)
def test_inner_product_and_sphere_inner_product( self, dim, tangent_vec_a, tangent_vec_b, base_point, ): """Test consistency between sphere's inner-products. The inner-product of the class Hypersphere is defined in terms of extrinsic coordinates. The inner-product of pullback_metric is defined in terms of the spherical coordinates. """ pullback_metric = PullbackMetric(dim=dim, embedding_dim=dim + 1, immersion=immersion) immersed_base_point = immersion(base_point) jac_immersion = pullback_metric.jacobian_immersion(base_point) immersed_tangent_vec_a = gs.matmul(jac_immersion, tangent_vec_a) immersed_tangent_vec_b = gs.matmul(jac_immersion, tangent_vec_b) result = pullback_metric.inner_product(tangent_vec_a, tangent_vec_b, base_point=base_point) expected = Hypersphere(dim).metric.inner_product( immersed_tangent_vec_a, immersed_tangent_vec_b, base_point=immersed_base_point, ) self.assertAllClose(result, expected)
def setUp(self): self.n_samples = 10 self.SO3_GROUP = SpecialOrthogonal(n=3, point_type='vector') self.SE3_GROUP = SpecialEuclidean(n=3, point_type='vector') self.S1 = Hypersphere(dim=1) self.S2 = Hypersphere(dim=2) self.H2 = Hyperbolic(dim=2) self.H2_half_plane = PoincareHalfSpace(dim=2) self.M32 = Matrices(m=3, n=2) self.S32 = PreShapeSpace(k_landmarks=3, m_ambient=2) self.KS = visualization.KendallSphere() self.M33 = Matrices(m=3, n=3) self.S33 = PreShapeSpace(k_landmarks=3, m_ambient=3) self.KD = visualization.KendallDisk() plt.figure()
def setUp(self): s2 = Hypersphere(dimension=2) r3 = s2.embedding_manifold initial_point = [0., 0., 1.] initial_tangent_vec_a = [1., 0., 0.] initial_tangent_vec_b = [0., 1., 0.] initial_tangent_vec_c = [-1., 0., 0.] landmarks_a = s2.metric.geodesic(initial_point=initial_point, initial_tangent_vec=initial_tangent_vec_a) landmarks_b = s2.metric.geodesic(initial_point=initial_point, initial_tangent_vec=initial_tangent_vec_b) landmarks_c = s2.metric.geodesic(initial_point=initial_point, initial_tangent_vec=initial_tangent_vec_c) self.n_sampling_points = 10 sampling_times = gs.linspace(0., 1., self.n_sampling_points) landmark_set_a = landmarks_a(sampling_times) landmark_set_b = landmarks_b(sampling_times) landmark_set_c = landmarks_c(sampling_times) self.n_landmark_sets = 5 self.times = gs.linspace(0., 1., self.n_landmark_sets) self.atol = 1e-6 gs.random.seed(1234) self.space_landmarks_in_euclidean_3d = LandmarksSpace( ambient_manifold=r3) self.space_landmarks_in_sphere_2d = LandmarksSpace( ambient_manifold=s2) self.l2_metric_s2 = self.space_landmarks_in_sphere_2d.l2_metric self.l2_metric_r3 = self.space_landmarks_in_euclidean_3d.l2_metric self.landmarks_a = landmark_set_a self.landmarks_b = landmark_set_b self.landmarks_c = landmark_set_c