def projection(self, mat): """ Project a matrix on SO(n), using the Frobenius norm. """ # TODO(nina): projection when the point_type is not 'matrix'? mat = gs.to_ndarray(mat, to_ndim=3) n_mats, mat_dim_1, mat_dim_2 = mat.shape assert mat_dim_1 == mat_dim_2 == self.n if self.n == 3: mat_unitary_u, diag_s, mat_unitary_v = gs.linalg.svd(mat) rot_mat = gs.matmul(mat_unitary_u, mat_unitary_v) mask = gs.nonzero(gs.linalg.det(rot_mat) < 0) diag = gs.array([1, 1, -1]) new_mat_diag_s = gs.tile(gs.diag(diag), len(mask)) rot_mat[mask] = gs.matmul( gs.matmul(mat_unitary_u[mask], new_mat_diag_s), mat_unitary_v[mask]) else: aux_mat = gs.matmul(gs.transpose(mat, axes=(0, 2, 1)), mat) inv_sqrt_mat = gs.zeros_like(mat) for i in range(n_mats): sym_mat = aux_mat[i] assert spd_matrices_space.is_symmetric(sym_mat) inv_sqrt_mat[i] = gs.linalg.inv( spd_matrices_space.sqrtm(sym_mat)) rot_mat = gs.matmul(mat, inv_sqrt_mat) assert gs.ndim(rot_mat) == 3 return rot_mat
def closest_rotation_matrix(mat): """ Compute the closest rotation matrix of a given matrix mat, in terms of the Frobenius norm. """ mat = gs.to_ndarray(mat, to_ndim=3) n_mats, mat_dim_1, mat_dim_2 = mat.shape assert mat_dim_1 == mat_dim_2 if mat_dim_1 == 3: mat_unitary_u, diag_s, mat_unitary_v = gs.linalg.svd(mat) rot_mat = gs.matmul(mat_unitary_u, mat_unitary_v) mask = gs.where(gs.linalg.det(rot_mat) < 0) new_mat_diag_s = gs.tile(gs.diag([1, 1, -1]), len(mask)) rot_mat[mask] = gs.matmul( gs.matmul(mat_unitary_u[mask], new_mat_diag_s), mat_unitary_v[mask]) else: aux_mat = gs.matmul(gs.transpose(mat, axes=(0, 2, 1)), mat) inv_sqrt_mat = gs.zeros_like(mat) for i in range(n_mats): sym_mat = aux_mat[i] assert spd_matrices_space.is_symmetric(sym_mat) inv_sqrt_mat[i] = gs.linalg.inv(spd_matrices_space.sqrtm(sym_mat)) rot_mat = gs.matmul(mat, inv_sqrt_mat) assert rot_mat.ndim == 3 return rot_mat
def left_exp_from_identity(self, tangent_vec): """ Compute the *left* Riemannian exponential from the identity of the Lie group of tangent vector tangent_vec. The left Riemannian exponential has a special role since the left Riemannian exponential of the canonical metric parameterizes the points. Note: In the case where the method is called by a right-invariant metric, it used the left-invariant metric associated to the same inner-product at the identity. """ import geomstats.spd_matrices_space as spd_matrices_space tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=2) tangent_vec = self.group.regularize_tangent_vec_at_identity( tangent_vec=tangent_vec, metric=self) sqrt_inner_product_mat = spd_matrices_space.sqrtm( self.inner_product_mat_at_identity) mat = gs.transpose(sqrt_inner_product_mat, axes=(0, 2, 1)) exp = gs.matmul(tangent_vec, mat) exp = gs.squeeze(exp, axis=0) exp = self.group.regularize(exp) return exp
def test_sqrtm(self): n_samples = self.n_samples points = self.space.random_uniform(n_samples=n_samples) result = spd_matrices_space.sqrtm(points) expected = gs.zeros((n_samples, self.n, self.n)) for i in range(n_samples): expected[i] = scipy.linalg.sqrtm(points[i]) self.assertTrue(gs.allclose(result, expected))
def left_exp_from_identity(self, tangent_vec): """ Riemannian exponential of a tangent vector wrt the identity associated to the left-invariant metric. If the method is called by a right-invariant metric, it uses the left-invariant metric associated to the same inner-product matrix at the identity. """ import geomstats.spd_matrices_space as spd_matrices_space tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=2) tangent_vec = self.group.regularize_tangent_vec_at_identity( tangent_vec=tangent_vec, metric=self) sqrt_inner_product_mat = spd_matrices_space.sqrtm( self.inner_product_mat_at_identity) mat = gs.transpose(sqrt_inner_product_mat, axes=(0, 2, 1)) exp = gs.einsum('ni,nij->nj', tangent_vec, mat) exp = self.group.regularize(exp) return exp
def left_log_from_identity(self, point): """ Riemannian logarithm of a point wrt the identity associated to the left-invariant metric. If the method is called by a right-invariant metric, it uses the left-invariant metric associated to the same inner-product matrix at the identity. """ import geomstats.spd_matrices_space as spd_matrices_space point = self.group.regularize(point) inner_prod_mat = self.inner_product_mat_at_identity inv_inner_prod_mat = gs.linalg.inv(inner_prod_mat) sqrt_inv_inner_prod_mat = spd_matrices_space.sqrtm(inv_inner_prod_mat) assert sqrt_inv_inner_prod_mat.shape == ((1, ) + (self.group.dimension, ) * 2) aux = gs.squeeze(sqrt_inv_inner_prod_mat, axis=0) log = gs.matmul(point, aux) log = self.group.regularize_tangent_vec_at_identity(tangent_vec=log, metric=self) assert log.ndim == 2 return log
def left_log_from_identity(self, point): """ Compute the *left* Riemannian logarithm from the identity of the Lie group of tangent vector tangent_vec. The left Riemannian logarithm has a special role since the left Riemannian logarithm of the canonical metric parameterizes the points. """ import geomstats.spd_matrices_space as spd_matrices_space point = self.group.regularize(point) inner_prod_mat = self.inner_product_mat_at_identity inv_inner_prod_mat = gs.linalg.inv(inner_prod_mat) sqrt_inv_inner_prod_mat = spd_matrices_space.sqrtm(inv_inner_prod_mat) assert sqrt_inv_inner_prod_mat.shape == ((1, ) + (self.group.dimension, ) * 2) aux = gs.squeeze(sqrt_inv_inner_prod_mat, axis=0) log = gs.matmul(point, aux) log = self.group.regularize_tangent_vec_at_identity(tangent_vec=log, metric=self) assert log.ndim == 2 return log