def _replace_values(self, samples, new_samples, indcs): replaced_indices = [ i for i, is_replaced in enumerate(indcs) if is_replaced ] value_indices = list( product(replaced_indices, range(self.n), range(self.n))) return gs.assignment(samples, gs.flatten(new_samples), value_indices)
def setup_data(self): """Generate the un-shuffled dataset. Returns ------- X : array-like, shape = [n_samples * n_classes, n_features, n_features] Data. y : array-like, shape = [n_samples * n_classes, n_classes] Labels. """ mean_covariance_eigenvalues = gs.random.uniform( 0.1, 5., (self.n_classes, self.n_features)) var = 1. base_rotations = SpecialOrthogonal(n=self.n_features).random_gaussian( gs.eye(self.n_features), var, n_samples=self.n_classes) var_rotations = gs.random.uniform(.5, .75, (self.n_classes)) y = gs.zeros((self.n_classes * self.n_samples, self.n_classes)) X = [] for i in range(self.n_classes): value_x = self.make_data(base_rotations[i], gs.diag(mean_covariance_eigenvalues[i]), var_rotations[i]) value_y = 1 idx_y = [ (j, i) for j in range(i * self.n_samples, (i + 1) * self.n_samples) ] y = gs.assignment(y, value_y, idx_y) X.append(value_x) return gs.concatenate(X, axis=0), y
def random_uniform(self, n_samples=1, tol=1e-6): """Sample in SE(n) from the uniform distribution. Parameters ---------- n_samples : int Number of samples. Optional, default: 1. tol : unused Returns ------- samples : array-like, shape=[..., n + 1, n + 1] Sample in SE(n). """ random_translation = self.translations.random_uniform(n_samples) random_rotation = self.rotations.random_uniform(n_samples) random_rotation = gs.to_ndarray(random_rotation, to_ndim=3) random_translation = gs.to_ndarray(random_translation, to_ndim=2) random_translation = gs.transpose(gs.to_ndarray( random_translation, to_ndim=3, axis=1), (0, 2, 1)) random_point = gs.concatenate( (random_rotation, random_translation), axis=2) last_line = gs.zeros((n_samples, 1, self.n + 1)) random_point = gs.concatenate( (random_point, last_line), axis=1) random_point = gs.assignment(random_point, 1, (-1, -1), axis=0) if gs.shape(random_point)[0] == 1: random_point = gs.squeeze(random_point, axis=0) return random_point
def test_assignment(self): gs_array_1 = gs.ones(3) with pytest.raises(ValueError): gs.assignment(gs_array_1, [0.1, 2.0, 1.0], [0, 1]) np_array_1 = _np.ones(3) gs_array_1 = gs.ones_like(gs.array(np_array_1)) np_array_1[2] = 1.5 gs_result = gs.assignment(gs_array_1, 1.5, 2) self.assertAllCloseToNp(gs_result, np_array_1) np_array_1_list = _np.ones(3) gs_array_1_list = gs.ones_like(gs.array(np_array_1_list)) indices = [1, 2] np_array_1_list[indices] = 1.5 gs_result = gs.assignment(gs_array_1_list, 1.5, indices) self.assertAllCloseToNp(gs_result, np_array_1_list) np_array_2 = _np.zeros((3, 2)) gs_array_2 = gs.zeros_like(gs.array(np_array_2)) np_array_2[0, :] = 1 gs_result = gs.assignment(gs_array_2, 1, 0, axis=1) self.assertAllCloseToNp(gs_result, np_array_2) np_array_3 = _np.zeros((3, 3)) gs_array_3 = gs.zeros_like(gs.array(np_array_3)) np_array_3[0, 1] = 1 gs_result = gs.assignment(gs_array_3, 1, (0, 1)) self.assertAllCloseToNp(gs_result, np_array_3) np_array_4 = _np.zeros((3, 3, 2)) gs_array_4 = gs.zeros_like(gs.array(np_array_4)) np_array_4[0, :, 1] = 1 gs_result = gs.assignment(gs_array_4, 1, (0, 1), axis=1) self.assertAllCloseToNp(gs_result, np_array_4) gs_array_4_arr = gs.zeros_like(gs.array(np_array_4)) gs_result = gs.assignment(gs_array_4_arr, 1, gs.array((0, 1)), axis=1) self.assertAllCloseToNp(gs_result, np_array_4) np_array_4_list = _np.zeros((3, 3, 2)) gs_array_4_list = gs.zeros_like(gs.array(np_array_4_list)) np_array_4_list[(0, 1), :, (1, 1)] = 1 gs_result = gs.assignment(gs_array_4_list, 1, [(0, 1), (1, 1)], axis=1) self.assertAllCloseToNp(gs_result, np_array_4_list)
def log(self, point, base_point): """Compute Riemannian logarithm of a point wrt a base point. If point_type = 'poincare' then base_point belongs to the Poincare ball and point is a vector in the Euclidean space of the same dimension as the ball. 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. """ add_base_point = self.mobius_add(-base_point, point) norm_add =\ gs.expand_dims(gs.linalg.norm( add_base_point, axis=-1), axis=-1) norm_base_point =\ gs.expand_dims(gs.linalg.norm( base_point, axis=-1), axis=-1) log = (1 - norm_base_point**2) * gs.arctanh(norm_add) mask_0 = gs.isclose(gs.squeeze(norm_add, axis=-1), 0.) mask_non0 = ~mask_0 add_base_point = gs.assignment( add_base_point, gs.zeros_like(add_base_point[mask_0]), mask_0) add_base_point = gs.assignment( add_base_point, add_base_point[mask_non0] / norm_add[mask_non0], mask_non0) log = gs.einsum( '...i,...j->...j', log, add_base_point) return log
def exp(self, tangent_vec, base_point): """Compute the Riemannian exponential of a tangent vector. Parameters ---------- tangent_vec : array-like, shape=[..., dim] Tangent vector at a base point. base_point : array-like, shape=[..., dim] Point in hyperbolic space. Returns ------- exp : array-like, shape=[..., dim] Point in hyperbolic space equal to the Riemannian exponential of tangent_vec at the base point. """ norm_base_point = gs.linalg.norm(base_point, axis=-1) norm_tan = gs.linalg.norm(tangent_vec, axis=-1) den = 1 - norm_base_point ** 2 lambda_base_point = 1 / den zero_tan = gs.isclose(gs.sum(tangent_vec ** 2, axis=-1), 0.) if gs.any(zero_tan): norm_tan = gs.assignment(norm_tan, EPSILON, zero_tan) direction = gs.einsum('...i,...->...i', tangent_vec, 1 / norm_tan) factor = gs.tanh( gs.einsum('...,...->...', lambda_base_point, norm_tan)) exp = self.mobius_add( base_point, gs.einsum('...i,...->...i', direction, factor)) if gs.any(zero_tan): exp = gs.assignment( exp, base_point[zero_tan], zero_tan) return exp
def test_assignment_with_matrices(self): np_array = _np.zeros((2, 3, 3)) gs_array = gs.zeros((2, 3, 3)) np_array[:, 0, 1] = 44.0 gs_array = gs.assignment(gs_array, 44.0, (0, 1), axis=0) self.assertAllCloseToNp(gs_array, np_array) n_samples = 3 theta = _np.random.rand(5) phi = _np.random.rand(5) np_array = _np.zeros((n_samples, 5, 4)) gs_array = gs.array(np_array) np_array[0, :, 0] = gs.cos(theta) * gs.cos(phi) np_array[0, :, 1] = -gs.sin(theta) * gs.sin(phi) gs_array = gs.assignment(gs_array, gs.cos(theta) * gs.cos(phi), (0, 0), axis=1) gs_array = gs.assignment(gs_array, -gs.sin(theta) * gs.sin(phi), (0, 1), axis=1) self.assertAllCloseToNp(gs_array, np_array)
def random_uniform(self, n_samples=1, point_type=None): """Sample in SE(n) with the uniform distribution. Parameters ---------- n_samples: int, optional default: 1 point_type: str, {'vector', 'matrix'}, optional default: self.default_point_type Returns ------- random_point: array-like, shape=[n_samples, {dim, [n + 1, n + 1]}] An array of random elements in SE(n) having the given point_type. """ if point_type is None: point_type = self.default_point_type random_translation = self.translations.random_uniform(n_samples) if point_type == 'vector': random_rot_vec = self.rotations.random_uniform( n_samples, point_type=point_type) return gs.concatenate([random_rot_vec, random_translation], axis=-1) if point_type == 'matrix': random_rotation = self.rotations.random_uniform( n_samples, point_type=point_type) random_rotation = gs.to_ndarray(random_rotation, to_ndim=3) random_translation = gs.to_ndarray(random_translation, to_ndim=2) random_translation = gs.transpose( gs.to_ndarray(random_translation, to_ndim=3, axis=1), (0, 2, 1)) random_point = gs.concatenate( (random_rotation, random_translation), axis=2) last_line = gs.zeros((n_samples, 1, self.n + 1)) random_point = gs.concatenate((random_point, last_line), axis=1) random_point = gs.assignment(random_point, 1, (-1, -1), axis=0) if gs.shape(random_point)[0] == 1: random_point = gs.squeeze(random_point, axis=0) return random_point raise ValueError('Invalid point_type, expected \'vector\' or ' '\'matrix\'.')
def projection(self, point): r"""Project a matrix to the space of PSD matrices of rank k. The nearest symmetric positive semidefinite matrix in the Frobenius norm to an arbitrary real matrix A is shown to be (B + H)/2, where H is the symmetric polar factor of B=(A + A')/2. As [Higham1988] is turning the matrix into a PSD, the rank is then forced to be k. Parameters ---------- point : array-like, shape=[..., n, n] Matrix to project. Returns ------- projected: array-like, shape=[..., n, n] PSD matrix rank k. References ---------- [Higham1988]_ Highamm, N. J. “Computing a nearest symmetric positive semidefinite matrix.” Linear Algebra and Its Applications 103 (May 1, 1988): 103-118. https://doi.org/10.1016/0024-3795(88)90223-6 """ sym = Matrices.to_symmetric(point) _, s, v = gs.linalg.svd(sym) h = gs.matmul(Matrices.transpose(v), s[..., None] * v) sym_proj = (sym + h) / 2 eigvals, eigvecs = gs.linalg.eigh(sym_proj) i = gs.array([0] * (self.n - self.rank) + [2 * gs.atol] * self.rank) regularized = ( gs.assignment(eigvals, 0, gs.arange((self.n - self.rank)), axis=0) + i ) reconstruction = gs.einsum("...ij,...j->...ij", eigvecs, regularized) return Matrices.mul(reconstruction, Matrices.transpose(eigvecs))
def embed(self, graph): """Compute embedding. Optimize a loss function to obtain a representable embedding. Parameters ---------- graph : object An instance of the Graph class. Returns ------- embeddings : array-like, shape=[n_samples, dim] Return the embedding of the data. Each data sample is represented as a point belonging to the manifold. """ nb_vertices_by_edges = [len(e_2) for _, e_2 in graph.edges.items()] logging.info("Number of edges: %s", len(graph.edges)) logging.info( "Mean vertices by edges: %s", (sum(nb_vertices_by_edges, 0) / len(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.0 / 4.0))) * negative_table_parameter) negative_sampling_table = gs.array(negative_sampling_table) random_walks = graph.random_walk() embeddings = gs.random.normal(size=(graph.n_nodes, self.manifold.dim)) embeddings = embeddings * 0.2 for epoch in range(self.max_epochs): total_loss = [] for path in random_walks: for example_index, one_path in enumerate(path): context_index = path[ max(0, example_index - self.n_context):min(example_index + self.n_context, len(path))] negative_index = gs.random.randint( negative_sampling_table.shape[0], size=(len(context_index), self.n_negative), ) negative_index = gs.expand_dims(gs.flatten(negative_index), axis=-1) negative_index = gs.get_slice(negative_sampling_table, negative_index) example_embedding = embeddings[gs.cast(one_path, dtype=gs.int64)] for one_context_i, one_negative_i in zip( context_index, negative_index): context_embedding = embeddings[one_context_i] negative_embedding = gs.get_slice( embeddings, gs.squeeze(gs.cast(one_negative_i, dtype=gs.int64)), ) l, g_ex = self.loss(example_embedding, context_embedding, negative_embedding) total_loss.append(l) example_to_update = embeddings[one_path] valeur = self.manifold.metric.exp( -self.lr * g_ex, example_to_update) embeddings = gs.assignment( embeddings, valeur, gs.to_ndarray(one_path, to_ndim=1), axis=1, ) logging.info( "iteration %d loss_value %f", epoch, sum(total_loss, 0) / len(total_loss), ) return embeddings
def test_assignment_with_booleans_many_indices(self): np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([True, False, True]) gs_mask = gs.array([True, False, True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * _np.ones_like(np_array[~np_mask]) np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs.ones_like(gs_array[~gs_mask]), ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([False, True, True]) gs_mask = gs.array([False, True, True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * _np.ones_like(np_array[~np_mask]) np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs.ones_like(gs_array[~gs_mask]), ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([True, True, True]) gs_mask = gs.array([True, True, True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * _np.ones_like(np_array[~np_mask]) np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs.ones_like(gs_array[~gs_mask]), ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([True, True, True]) gs_mask = gs.array([True, True, True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([False, False, False]) gs_mask = gs.array([False, False, False]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([[False, False], [False, True], [True, True]]) gs_mask = gs.array([[False, False], [False, True], [True, True]]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result)
def test_assignment_with_booleans_single_index(self): np_array = _np.array([[2., 5.]]) gs_array = gs.array([[2., 5.]]) np_mask = _np.array([True]) gs_mask = gs.array([True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * _np.ones_like(np_array[~np_mask]) np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment( gs_array, values_mask, gs_mask) gs_result = gs.assignment( gs_result, 4 * gs.ones_like(gs_array[~gs_mask]), ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[2., 5.]]) gs_array = gs.array([[2., 5.]]) np_mask = _np.array([True]) gs_mask = gs.array([True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment( gs_array, values_mask, gs_mask) gs_result = gs.assignment( gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[2., 5.]]) gs_array = gs.array([[2., 5.]]) np_mask = _np.array([False]) gs_mask = gs.array([False]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * _np.ones_like(np_array[~np_mask]) np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment( gs_array, values_mask, gs_mask) gs_result = gs.assignment( gs_result, 4 * gs.ones_like(gs_array[~gs_mask]), ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[2., 5.]]) gs_array = gs.array([[2., 5.]]) np_mask = _np.array([False]) gs_mask = gs.array([False]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment( gs_array, values_mask, gs_mask) gs_result = gs.assignment( gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result)