def align(self, point, base_point, **kwargs): """Align point to base_point. Find the optimal rotation R in SO(m) such that the base point and R.point are well positioned. Parameters ---------- point : array-like, shape=[..., k_landmarks, m_ambient] Point on the manifold. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the manifold. Returns ------- aligned : array-like, shape=[..., k_landmarks, m_ambient] R.point. """ mat = gs.matmul(Matrices.transpose(point), base_point) left, singular_values, right = gs.linalg.svd(mat) det = gs.linalg.det(mat) conditioning = ((singular_values[..., -2] + gs.sign(det) * singular_values[..., -1]) / singular_values[..., 0]) if gs.any(conditioning < 5e-4): logging.warning(f'Singularity close, ill-conditioned matrix ' f'encountered: {conditioning}') if gs.any(gs.isclose(conditioning, 0.)): logging.warning("Alignment matrix is not unique.") flipped = flip_determinant(Matrices.transpose(right), det) return Matrices.mul(point, left, Matrices.transpose(flipped))
def align_matrices(cls, point, base_point): """Align matrices. Find the optimal rotation R in SO(m) such that the base point and R.point are well positioned. Parameters ---------- point : array-like, shape=[..., m, n] Point on the manifold. base_point : array-like, shape=[..., m, n] Point on the manifold. Returns ------- aligned : array-like, shape=[..., m, n] R.point. """ mat = gs.matmul(cls.transpose(point), base_point) left, singular_values, right = gs.linalg.svd(mat) det = gs.linalg.det(mat) conditioning = ( singular_values[..., -2] + gs.sign(det) * singular_values[..., -1] ) / singular_values[..., 0] if gs.any(conditioning < gs.atol): logging.warning( f"Singularity close, ill-conditioned matrix " f"encountered: " f"{conditioning[conditioning < 1e-10]}" ) if gs.any(gs.isclose(conditioning, 0.0)): logging.warning("Alignment matrix is not unique.") flipped = flip_determinant(cls.transpose(right), det) return Matrices.mul(point, left, cls.transpose(flipped))
def projection(self, point): r"""Project a matrix to the general linear group. As GL(n) is dense in the space of matrices, this is not a projection per se, but a regularization if the matrix is not already invertible: :math:`X + \epsilon I_n` is returned where :math:`\epsilon=gs.atol` is returned for an input X. Parameters ---------- point : array-like, shape=[..., dim_embedding] Point in embedding manifold. Returns ------- projected : array-like, shape=[..., dim_embedding] Projected point. """ belongs = self.belongs(point) regularization = gs.einsum("...,ij->...ij", gs.where(~belongs, gs.atol, 0.0), self.identity) projected = point + regularization if self.positive_det: det = gs.linalg.det(point) return utils.flip_determinant(projected, det) return projected