예제 #1
0
    def retraction(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, p]
            Tangent vector at a base point.
        base_point : array-like, shape=[..., n, p]
            Point in the Stiefel manifold.

        Returns
        -------
        exp : array-like, shape=[..., n, p]
            Point in the Stiefel manifold equal to the retraction
            of tangent_vec at the base point.
        """
        n_tangent_vecs, _, _ = tangent_vec.shape
        n_base_points, _, _ = base_point.shape

        if not (n_tangent_vecs == n_base_points or n_tangent_vecs == 1
                or n_base_points == 1):
            raise NotImplementedError

        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 = algebra_utils.from_vector_to_diagonal_matrix(sign)
        result = gs.einsum('nij,njk->nik', matrix_q, diag)

        return result
예제 #2
0
    def retraction(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, p]
            Tangent vector at a base point.
        base_point : array-like, shape=[..., n, p]
            Point in the Stiefel manifold.

        Returns
        -------
        exp : array-like, shape=[..., n, p]
            Point in the Stiefel manifold equal to the retraction
            of tangent_vec at the base point.
        """
        matrix_q, matrix_r = gs.linalg.qr(base_point + tangent_vec)

        diagonal = gs.diagonal(matrix_r, axis1=-2, axis2=-1)
        sign = gs.sign(gs.sign(diagonal) + 0.5)
        diag = algebra_utils.from_vector_to_diagonal_matrix(sign)
        result = Matrices.mul(matrix_q, diag)

        return result
예제 #3
0
    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
예제 #4
0
    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))
예제 #5
0
    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))
예제 #6
0
    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
예제 #7
0
    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.")
        if gs.any(det < 0):
            ones = gs.ones(self.m_ambient)
            reflection_vec = gs.concatenate(
                [ones[:-1], gs.array([-1.])], axis=0)
            mask = gs.cast(det < 0, gs.float32)
            sign = (mask[..., None] * reflection_vec
                    + (1. - mask)[..., None] * ones)
            j_matrix = from_vector_to_diagonal_matrix(sign)
            rotation = Matrices.mul(
                Matrices.transpose(right), j_matrix, Matrices.transpose(left))
        else:
            rotation = gs.matmul(
                Matrices.transpose(right), Matrices.transpose(left))
        return gs.matmul(point, Matrices.transpose(rotation))
    return solver.solve(problem)


if __name__ == '__main__':
    if os.environ.get('GEOMSTATS_BACKEND') != 'numpy':
        raise SystemExit(
            'This example currently only supports the numpy backend')

    ambient_dim = 128
    mat = gs.random.normal(size=(ambient_dim, ambient_dim))
    mat = 0.5 * (mat + mat.T)

    eigenvalues, eigenvectors = gs.linalg.eig(mat)
    dominant_eigenvector = eigenvectors[:, gs.argmax(eigenvalues)]

    dominant_eigenvector_estimate = estimate_dominant_eigenvector(mat)
    if (gs.sign(dominant_eigenvector[0]) !=
            gs.sign(dominant_eigenvector_estimate[0])):
        dominant_eigenvector_estimate = -dominant_eigenvector_estimate

    logging.info(
        'l2-norm of dominant eigenvector: %s',
        gs.linalg.norm(dominant_eigenvector))
    logging.info(
        'l2-norm of dominant eigenvector estimate: %s',
        gs.linalg.norm(dominant_eigenvector_estimate))
    error_norm = gs.linalg.norm(
        dominant_eigenvector - dominant_eigenvector_estimate)
    logging.info('l2-norm of difference vector: %s', error_norm)
    logging.info('solution found: %s', gs.isclose(error_norm, 0.0, atol=1e-3))
예제 #9
0
    def rotation_vector_from_matrix(self, rot_mat):
        r"""Convert rotation matrix (in 3D) to rotation vector (axis-angle).

        Get the angle through the trace of the rotation matrix:
        The eigenvalues are:
        :math:`\{1, \cos(angle) + i \sin(angle), \cos(angle) - i \sin(angle)\}`
        so that:
        :math:`trace = 1 + 2 \cos(angle), \{-1 \leq trace \leq 3\}`

        Get the rotation vector through the formula:
        :math:`S_r = \frac{angle}{(2 * \sin(angle) ) (R - R^T)}`

        For the edge case where the angle is close to pi,
        the formulation is derived by using the following equality (see the
        Axis-angle representation on Wikipedia):
        :math:`outer(r, r) = \frac{1}{2} (R + I_3)`
        In nD, the rotation vector stores the :math:`n(n-1)/2` values
        of the skew-symmetric matrix representing the rotation.

        Parameters
        ----------
        rot_mat : array-like, shape=[..., n, n]

        Returns
        -------
        regularized_rot_vec : array-like, shape=[..., 3]
        """
        n_rot_mats, _, _ = rot_mat.shape

        trace = gs.trace(rot_mat, axis1=1, axis2=2)
        trace = gs.to_ndarray(trace, to_ndim=2, axis=1)
        trace_num = gs.clip(trace, -1, 3)
        angle = gs.arccos(0.5 * (trace_num - 1))
        rot_mat_transpose = gs.transpose(rot_mat, axes=(0, 2, 1))
        rot_vec_not_pi = self.vector_from_skew_matrix(rot_mat -
                                                      rot_mat_transpose)
        mask_0 = gs.cast(gs.isclose(angle, 0.), gs.float32)
        mask_pi = gs.cast(gs.isclose(angle, gs.pi, atol=1e-2), gs.float32)
        mask_else = (1 - mask_0) * (1 - mask_pi)

        numerator = 0.5 * mask_0 + angle * mask_else
        denominator = (1 - angle**2 /
                       6) * mask_0 + 2 * gs.sin(angle) * mask_else + mask_pi

        rot_vec_not_pi = rot_vec_not_pi * numerator / denominator

        vector_outer = 0.5 * (gs.eye(3) + rot_mat)
        gs.set_diag(
            vector_outer,
            gs.maximum(0., gs.diagonal(vector_outer, axis1=1, axis2=2)))
        squared_diag_comp = gs.diagonal(vector_outer, axis1=1, axis2=2)
        diag_comp = gs.sqrt(squared_diag_comp)
        norm_line = gs.linalg.norm(vector_outer, axis=2)
        max_line_index = gs.argmax(norm_line, axis=1)
        selected_line = gs.get_slice(vector_outer,
                                     (range(n_rot_mats), max_line_index))
        signs = gs.sign(selected_line)
        rot_vec_pi = angle * signs * diag_comp

        rot_vec = rot_vec_not_pi + mask_pi * rot_vec_pi

        return self.regularize(rot_vec)