def random_gaussian_rotation_orbit_noisy(self, mean_spd=None, eigensummary=None, var_rotations=1., var_eigenvalues=None, n_samples=1): r""" Define a Gaussian-like random sample of SPD matrices. Formally speaking, sample an orbit for given rotations in the SPD manifold. For all means and purposes, it looks rather Gaussian. Parameters ---------- mean_spd : array-like, shape = [n, n] Mean SPD matrix. var_rotations : float Variance in rotation. var_eigenvalues : array-like, shape = [n,] Additional variance in eigenvalues. eigensummary : EigenSummary Represents the mean SPD matrix decomposed in eigenspace and eigenvalues. Notes ----- :math:'mean_spd' is the mean SPD matrix; :math:'var_rotations' is the scalar variance by which the mean is rotated: :math:'\Sigma_{mid} \sim \mathcal{N}(\Sigma_{in}, \sigma_{eig}'; :math:'X_{out} = R \Sigma_{in} R^T'. mean_spd and eigensummary are mutually exclusive; an error is thrown if both are not None, or if both are None. """ n = self.n if var_eigenvalues is None: var_eigenvalues = gs.ones(n) if mean_spd is not None and eigensummary is not None: raise NotImplementedError if mean_spd is None and eigensummary is None: raise NotImplementedError if eigensummary is None: eigenvalues, eigenspace = gs.linalg.eigh(mean_spd) eigenvalues = gs.diag(eigenvalues) if mean_spd is None: eigenvalues, eigenspace\ = eigensummary.eigenvalues, eigensummary.eigenspace rotations = SpecialOrthogonal(n).random_gaussian( eigenspace, var_rotations, n_samples=n_samples) eigenvalues =\ gs.abs(gs.diag(gs.random.multivariate_normal( gs.diag(eigenvalues), gs.diag(var_eigenvalues)))) spd_mat = Matrices.mul(rotations, eigenvalues, Matrices.transpose( rotations)) return spd_mat
def retraction(self, tangent_vec, base_point): """ Retraction map, based on QR-decomposion: P_x(V) = qf(X + V) """ tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=3) n_tangent_vecs, _, _ = tangent_vec.shape base_point = gs.to_ndarray(base_point, to_ndim=3) n_base_points, n, p = base_point.shape assert (n_tangent_vecs == n_base_points or n_tangent_vecs == 1 or n_base_points == 1) if n_base_points == 1: base_point = gs.tile(base_point, (n_tangent_vecs, 1, 1)) if n_tangent_vecs == 1: tangent_vec = gs.tile(tangent_vec, (n_base_points, 1, 1)) matrix_q, matrix_r = gs.linalg.qr(base_point+tangent_vec) diagonal = gs.diagonal(matrix_r, axis1=1, axis2=2) sign = gs.sign(gs.sign(diagonal) + 0.5) diag = gs.diag(sign) result = gs.einsum('nij,njk->nik', matrix_q, diag) return result
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 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 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 retraction(self, tangent_vec, base_point): """Compute the retraction of a tangent vector. This computation is based on the QR-decomposition. e.g. :math:`P_x(V) = qf(X + V)`. Parameters ---------- tangent_vec : array-like, shape=[n_samples, n, p] Tangent vector at a base point. base_point : array-like, shape=[n_samples, n, p] Point in the Stiefel manifold. Returns ------- exp : array-like, shape=[n_samples, n, p] Point in the Stiefel manifold equal to the retraction of tangent_vec at the base point. """ tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=3) n_tangent_vecs, _, _ = tangent_vec.shape base_point = gs.to_ndarray(base_point, to_ndim=3) n_base_points, n, p = base_point.shape assert (n_tangent_vecs == n_base_points or n_tangent_vecs == 1 or n_base_points == 1) if n_base_points == 1: base_point = gs.tile(base_point, (n_tangent_vecs, 1, 1)) if n_tangent_vecs == 1: tangent_vec = gs.tile(tangent_vec, (n_base_points, 1, 1)) matrix_q, matrix_r = gs.linalg.qr(base_point + tangent_vec) diagonal = gs.diagonal(matrix_r, axis1=1, axis2=2) sign = gs.sign(gs.sign(diagonal) + 0.5) diag = gs.diag(sign) result = gs.einsum('nij,njk->nik', matrix_q, diag) return result
def origin(self): return gs.diag(gs.repeat([1, 0], [self.k, self.n - self.k]))[0]
def closest(rot): d_coefs = gs.diagonal(rot) d_sign = gs.where(d_coefs >= 0, 1., -1.) return mul(rot, gs.diag(d_sign)[0])