def skew_matrix_from_vector(vec): """ In 3D, compute the skew-symmetric matrix, known as the cross-product of a vector, associated to the vector vec. In nD, fill a skew-symmetric matrix with the values of the vector. """ vec = gs.to_ndarray(vec, to_ndim=2) n_vecs, vec_dim = vec.shape mat_dim = int((1 + gs.sqrt(1 + 8 * vec_dim)) / 2) skew_mat = gs.zeros((n_vecs, ) + (mat_dim, ) * 2) if vec_dim == 3: for i in range(n_vecs): skew_mat[i] = gs.cross(gs.eye(vec_dim), vec[i]) else: upper_triangle_indices = gs.triu_indices(mat_dim, k=1) for i in range(n_vecs): skew_mat[i][upper_triangle_indices] = vec[i] skew_mat[i] = skew_mat[i] - skew_mat[i].transpose() assert skew_mat.ndim == 3 return skew_mat
def from_vector(vec, dtype=gs.float32): """Convert a vector into a symmetric matrix. Parameters ---------- vec : array-like, shape=[..., n(n+1)/2] Vector. Returns ------- mat : array-like, shape=[..., n, n] Symmetric matrix. """ vec_dim = vec.shape[-1] mat_dim = (gs.sqrt(8. * vec_dim + 1) - 1) / 2 if mat_dim != int(mat_dim): raise ValueError('Invalid input dimension, it must be of the form' '(n_samples, n * (n + 1) / 2)') mat_dim = int(mat_dim) shape = (mat_dim, mat_dim) mask = 2 * gs.ones(shape) - gs.eye(mat_dim) indices = list(zip(*gs.triu_indices(mat_dim))) vec = gs.cast(vec, dtype) upper_triangular = gs.stack( [gs.array_from_sparse(indices, data, shape) for data in vec]) mat = Matrices.to_symmetric(upper_triangular) * mask return mat
def vector_from_symmetric_matrix(mat): """Convert the symmetric part of a symmetric matrix into a vector.""" if not gs.all(Matrices.is_symmetric(mat)): logging.warning('non-symmetric matrix encountered.') mat = Matrices.make_symmetric(mat) _, dim, _ = mat.shape i, j = gs.triu_indices(dim) vec = mat[:, i, j] return vec
def to_vector(mat): """Convert the symmetric part of a symmetric matrix into a vector.""" if not gs.all(Matrices.is_symmetric(mat)): logging.warning('non-symmetric matrix encountered.') mat = Matrices.to_symmetric(mat) _, dim, _ = mat.shape indices_i, indices_j = gs.triu_indices(dim) vec = [] for i, j in zip(indices_i, indices_j): vec.append(mat[:, i, j]) vec = gs.stack(vec, axis=1) return vec
def symmetric_matrix_from_vector(vec, dtype=gs.float32): """Convert a vector into a symmetric matrix.""" vec_dim = vec.shape[-1] mat_dim = (gs.sqrt(8. * vec_dim + 1) - 1) / 2 if mat_dim != int(mat_dim): raise ValueError('Invalid input dimension, it must be of the form' '(n_samples, n * (n - 1) / 2)') mat_dim = int(mat_dim) mask = 2 * gs.ones((mat_dim, mat_dim)) - gs.eye(mat_dim) indices = list(zip(*gs.triu_indices(3))) shape = (mat_dim, mat_dim) vec = gs.cast(vec, dtype) upper_triangular = gs.stack( [gs.array_from_sparse(indices, data, shape) for data in vec]) mat = Matrices.make_symmetric(upper_triangular) * mask return mat
def basis_representation(self, matrix_representation): """Calculate the coefficients of given matrix in the basis. Parameters ---------- matrix_representation: array-like, shape=[n_sample, n, n] Returns ------ basis_representation: array-like, shape=[n_sample, dimension] """ old_shape = gs.shape(matrix_representation) as_vector = gs.reshape(matrix_representation, (old_shape[0], -1)) upper_tri_indices = gs.reshape(gs.arange( 0, self.n**2), (self.n, self.n))[gs.triu_indices(self.n, k=1)] return as_vector[:, upper_tri_indices]
def basis_representation(self, matrix_representation): """Calculate the coefficients of given matrix in the basis. Compute a 1d-array that corresponds to the input matrix in the basis representation. Parameters ---------- matrix_representation : array-like, shape=[..., n, n] Returns ------- basis_representation : array-like, shape=[..., dim] """ old_shape = gs.shape(matrix_representation) as_vector = gs.reshape(matrix_representation, (old_shape[0], -1)) upper_tri_indices = gs.reshape(gs.arange( 0, self.n**2), (self.n, self.n))[gs.triu_indices(self.n, k=1)] return as_vector[:, upper_tri_indices]
def dist_pairwise(self, points, n_jobs=1, **joblib_kwargs): """Compute the pairwise distance between points. Parameters ---------- points : array-like, shape=[n_samples, dim] Set of points in the manifold. n_jobs : int Number of jobs to run in parallel, using joblib. Note that a higher number of jobs may not be beneficial when one computation of a geodesic distance is cheap. Optional. Default: 1. **joblib_kwargs : dict Keyword arguments to joblib.Parallel Returns ------- dist : array-like, shape=[n_samples, n_samples] Pairwise distance matrix between all the points. See Also -------- https://joblib.readthedocs.io/en/latest/ """ n_samples = points.shape[0] rows, cols = gs.triu_indices(n_samples) @joblib.delayed @joblib.wrap_non_picklable_objects def pickable_dist(x, y): """Wrap distance function to make it pickable.""" return self.dist(x, y) pool = joblib.Parallel(n_jobs=n_jobs, **joblib_kwargs) out = pool( pickable_dist(points[i], points[j]) for i, j in zip(rows, cols)) pairwise_dist = ( geometry.symmetric_matrices.SymmetricMatrices.from_vector( gs.array(out))) return pairwise_dist
def to_vector(mat): """Convert a symmetric matrix into a vector. Parameters ---------- mat : array-like, shape=[..., n, n] Matrix. Returns ------- vec : array-like, shape=[..., n(n+1)/2] Vector. """ if not gs.all(Matrices.is_symmetric(mat)): logging.warning('non-symmetric matrix encountered.') mat = Matrices.to_symmetric(mat) _, dim, _ = mat.shape indices_i, indices_j = gs.triu_indices(dim) vec = [] for i, j in zip(indices_i, indices_j): vec.append(mat[:, i, j]) vec = gs.stack(vec, axis=1) return vec
def skew_matrix_from_vector(self, vec): """Get the skew-symmetric matrix derived from the vector. In 3D, compute the skew-symmetric matrix,known as the cross-product of a vector, associated to the vector `vec`. In nD, fill a skew-symmetric matrix with the values of the vector. Parameters ---------- vec : array-like, shape=[..., dim] Returns ------- skew_mat : array-like, shape=[..., n, n] """ n_vecs, vec_dim = gs.shape(vec) if self.n == 2: vec = gs.tile(vec, [1, 2]) vec = gs.reshape(vec, (n_vecs, 2)) id_skew = gs.array(gs.tile([[[0., 1.], [-1., 0.]]], (n_vecs, 1, 1))) skew_mat = gs.einsum('...ij,...i->...ij', gs.cast(id_skew, gs.float32), vec) elif self.n == 3: levi_civita_symbol = gs.tile( [[[[0., 0., 0.], [0., 0., 1.], [0., -1., 0.]], [[0., 0., -1.], [0., 0., 0.], [1., 0., 0.]], [[0., 1., 0.], [-1., 0., 0.], [0., 0., 0.]]]], (n_vecs, 1, 1, 1)) levi_civita_symbol = gs.array(levi_civita_symbol) levi_civita_symbol += self.epsilon # This avoids dividing by 0. basis_vec_1 = gs.array(gs.tile([[1., 0., 0.]], (n_vecs, 1))) + self.epsilon basis_vec_2 = gs.array(gs.tile([[0., 1., 0.]], (n_vecs, 1))) + self.epsilon basis_vec_3 = gs.array(gs.tile([[0., 0., 1.]], (n_vecs, 1))) + self.epsilon cross_prod_1 = gs.einsum('nijk,ni,nj->nk', levi_civita_symbol, basis_vec_1, vec) cross_prod_2 = gs.einsum('nijk,ni,nj->nk', levi_civita_symbol, basis_vec_2, vec) cross_prod_3 = gs.einsum('nijk,ni,nj->nk', levi_civita_symbol, basis_vec_3, vec) cross_prod_1 = gs.to_ndarray(cross_prod_1, to_ndim=3, axis=1) cross_prod_2 = gs.to_ndarray(cross_prod_2, to_ndim=3, axis=1) cross_prod_3 = gs.to_ndarray(cross_prod_3, to_ndim=3, axis=1) skew_mat = gs.concatenate( [cross_prod_1, cross_prod_2, cross_prod_3], axis=1) else: # SO(n) mat_dim = gs.cast(((1. + gs.sqrt(1. + 8. * vec_dim)) / 2.), gs.int32) skew_mat = gs.zeros((n_vecs, ) + (self.n, ) * 2) upper_triangle_indices = gs.triu_indices(mat_dim, k=1) for i in range(n_vecs): skew_mat[i][upper_triangle_indices] = vec[i] skew_mat[i] = skew_mat[i] - gs.transpose(skew_mat[i]) return skew_mat