Exemple #1
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
Exemple #2
0
    def exp(self, tangent_vec, base_point):
        """
        Compute the Riemannian exponential at point base_point
        of tangent vector tangent_vec wrt the metric
        defined in inner_product.

        This gives a symmetric positive definite matrix.
        """
        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, mat_dim, _ = base_point.shape

        assert (n_tangent_vecs == n_base_points or n_tangent_vecs == 1
                or n_base_points == 1)

        if n_tangent_vecs == 1:
            tangent_vec = gs.tile(tangent_vec, (n_base_points, 1, 1))
        if n_base_points == 1:
            base_point = gs.tile(base_point, (n_tangent_vecs, 1, 1))

        sqrt_base_point = gs.linalg.sqrtm(base_point)

        inv_sqrt_base_point = gs.linalg.inv(sqrt_base_point)

        tangent_vec_at_id = gs.matmul(inv_sqrt_base_point, tangent_vec)
        tangent_vec_at_id = gs.matmul(tangent_vec_at_id, inv_sqrt_base_point)
        exp_from_id = gs.linalg.expm(tangent_vec_at_id)

        exp = gs.matmul(exp_from_id, sqrt_base_point)
        exp = gs.matmul(sqrt_base_point, exp)

        return exp
Exemple #3
0
    def test_lifting_vectorization_shape(self):
        n_samples = self.n_samples
        n = self.n
        p = self.p

        one_point = self.point_a
        one_base_point = self.point_b
        n_points = gs.tile(
            gs.to_ndarray(self.point_a, to_ndim=3),
            (n_samples, 1, 1))
        n_base_points = gs.tile(
            gs.to_ndarray(self.point_b, to_ndim=3),
            (n_samples, 1, 1))

        result = self.metric.lifting(one_point, one_base_point)
        self.assertAllClose(gs.shape(result), (n, p))

        result = self.metric.lifting(n_points, one_base_point)
        self.assertAllClose(gs.shape(result), (n_samples, n, p))

        result = self.metric.lifting(one_point, n_base_points)
        self.assertAllClose(gs.shape(result), (n_samples, n, p))

        result = self.metric.lifting(n_points, n_base_points)
        self.assertAllClose(gs.shape(result), (n_samples, n, p))
Exemple #4
0
    def log(self, point, base_point):
        """
        Compute the Riemannian logarithm at point base_point,
        of point wrt the metric defined in inner_product.

        This gives a tangent vector at point base_point.
        """
        point = gs.to_ndarray(point, to_ndim=3)
        n_points, _, _ = point.shape

        base_point = gs.to_ndarray(base_point, to_ndim=3)
        n_base_points, mat_dim, _ = base_point.shape

        assert (n_points == n_base_points or n_points == 1
                or n_base_points == 1)

        if n_points == 1:
            point = gs.tile(point, (n_base_points, 1, 1))
        if n_base_points == 1:
            base_point = gs.tile(base_point, (n_points, 1, 1))

        sqrt_base_point = gs.zeros((n_base_points, ) + (mat_dim, ) * 2)
        sqrt_base_point = gs.linalg.sqrtm(base_point)

        inv_sqrt_base_point = gs.linalg.inv(sqrt_base_point)
        point_near_id = gs.matmul(inv_sqrt_base_point, point)
        point_near_id = gs.matmul(point_near_id, inv_sqrt_base_point)
        log_at_id = gs.linalg.logm(point_near_id)

        log = gs.matmul(sqrt_base_point, log_at_id)
        log = gs.matmul(log, sqrt_base_point)

        return log
Exemple #5
0
    def inner_product(self, tangent_vec_a, tangent_vec_b, base_point):
        """
        Compute the inner product of tangent_vec_a and tangent_vec_b
        at point base_point using the affine invariant Riemannian metric.
        """
        power_affine = self.power_affine
        tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=3)
        n_tangent_vecs_a, _, _ = tangent_vec_a.shape
        tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=3)
        n_tangent_vecs_b, _, _ = tangent_vec_b.shape

        base_point = gs.to_ndarray(base_point, to_ndim=3)
        n_base_points, _, _ = base_point.shape

        spd_space = self.space

        assert (n_tangent_vecs_a == n_tangent_vecs_b == n_base_points
                or n_tangent_vecs_a == n_tangent_vecs_b and n_base_points == 1
                or n_base_points == n_tangent_vecs_a and n_tangent_vecs_b == 1
                or n_base_points == n_tangent_vecs_b and n_tangent_vecs_a == 1
                or n_tangent_vecs_a == 1 and n_tangent_vecs_b == 1
                or n_base_points == 1 and n_tangent_vecs_a == 1
                or n_base_points == 1 and n_tangent_vecs_b == 1)

        if n_tangent_vecs_a == 1:
            tangent_vec_a = gs.tile(
                tangent_vec_a,
                (gs.maximum(n_base_points, n_tangent_vecs_b), 1, 1))

        if n_tangent_vecs_b == 1:
            tangent_vec_b = gs.tile(
                tangent_vec_b,
                (gs.maximum(n_base_points, n_tangent_vecs_a), 1, 1))

        if n_base_points == 1:
            base_point = gs.tile(
                base_point,
                (gs.maximum(n_tangent_vecs_a, n_tangent_vecs_b), 1, 1))

        if power_affine == 1:
            inv_base_point = gs.linalg.inv(base_point)
            inner_product = self._aux_inner_product(tangent_vec_a,
                                                    tangent_vec_b,
                                                    inv_base_point)
        else:
            modified_tangent_vec_a =\
                spd_space.differential_power(power_affine, tangent_vec_a,
                                             base_point)
            modified_tangent_vec_b =\
                spd_space.differential_power(power_affine, tangent_vec_b,
                                             base_point)
            power_inv_base_point = gs.linalg.powerm(base_point, -power_affine)
            inner_product = self._aux_inner_product(modified_tangent_vec_a,
                                                    modified_tangent_vec_b,
                                                    power_inv_base_point)
            inner_product = inner_product / (power_affine**2)

        inner_product = gs.to_ndarray(inner_product, to_ndim=2, axis=1)

        return inner_product
Exemple #6
0
 def test_exp_diagonal(self, dim, param, param_list):
     """Check that the diagonal x1 = ... = xn is totally geodesic."""
     base_point = param * gs.ones(dim)
     initial_vectors = gs.transpose(gs.tile(param_list, (dim, 1)))
     result = self.metric(dim).exp(initial_vectors, base_point)
     expected = gs.squeeze(gs.transpose(gs.tile(result[..., 0], (dim, 1))))
     return self.assertAllClose(expected, result)
Exemple #7
0
    def inner_product(self, tangent_vec_a, tangent_vec_b, base_point=None):
        """
        Inner product between two tangent vectors at a base point.
        """
        tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=2)
        tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=2)
        n_tangent_vec_a = gs.shape(tangent_vec_a)[0]
        n_tangent_vec_b = gs.shape(tangent_vec_b)[0]

        inner_prod_mat = self.inner_product_matrix(base_point)
        inner_prod_mat = gs.to_ndarray(inner_prod_mat, to_ndim=3)
        n_mats = gs.shape(inner_prod_mat)[0]

        n_inner_prod = gs.maximum(n_tangent_vec_a, n_tangent_vec_b)
        n_inner_prod = gs.maximum(n_inner_prod, n_mats)

        n_tiles_a = gs.divide(n_inner_prod, n_tangent_vec_a)
        n_tiles_a = gs.cast(n_tiles_a, gs.int32)
        tangent_vec_a = gs.tile(tangent_vec_a, [n_tiles_a, 1])

        n_tiles_b = gs.divide(n_inner_prod, n_tangent_vec_b)
        n_tiles_b = gs.cast(n_tiles_b, gs.int32)
        tangent_vec_b = gs.tile(tangent_vec_b, [n_tiles_b, 1])

        n_tiles_mat = gs.divide(n_inner_prod, n_mats)
        n_tiles_mat = gs.cast(n_tiles_mat, gs.int32)
        inner_prod_mat = gs.tile(inner_prod_mat, [n_tiles_mat, 1, 1])

        aux = gs.einsum('nj,njk->nk', tangent_vec_a, inner_prod_mat)
        inner_prod = gs.einsum('nk,nk->n', aux, tangent_vec_b)
        inner_prod = gs.to_ndarray(inner_prod, to_ndim=2, axis=1)

        assert gs.ndim(inner_prod) == 2, inner_prod.shape
        return inner_prod
Exemple #8
0
    def test_exp_vectorization_shape(self):
        n_samples = self.n_samples
        n = self.n
        p = self.p

        one_base_point = self.point_a
        one_tangent_vec = self.tangent_vector_1

        n_base_points = gs.tile(
            gs.to_ndarray(self.point_a, to_ndim=3),
            (n_samples, 1, 1))
        n_tangent_vecs = gs.tile(
            gs.to_ndarray(self.tangent_vector_2, to_ndim=3),
            (n_samples, 1, 1))

        # With single tangent vec and base point
        result = self.metric.exp(one_tangent_vec, one_base_point)
        self.assertAllClose(gs.shape(result), (n, p))

        # With n_samples tangent vecs and base points
        result = self.metric.exp(n_tangent_vecs, one_base_point)
        self.assertAllClose(gs.shape(result), (n_samples, n, p))

        result = self.metric.exp(one_tangent_vec, n_base_points)
        self.assertAllClose(gs.shape(result), (n_samples, n, p))
    def inner_product(self, tangent_vec_a, tangent_vec_b, base_point=None):
        """
        Inner product defined by the Riemannian metric at point base_point
        between tangent vectors tangent_vec_a and tangent_vec_b.
        """
        tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=2)
        tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=2)
        n_tangent_vec_a, _ = tangent_vec_a.shape
        n_tangent_vec_b, _ = tangent_vec_b.shape

        inner_prod_mat = self.inner_product_matrix(base_point)
        inner_prod_mat = gs.to_ndarray(inner_prod_mat, to_ndim=3)
        n_mats, _, _ = inner_prod_mat.shape

        n_inner_prod = gs.maximum(n_tangent_vec_a, n_tangent_vec_b)
        n_inner_prod = gs.maximum(n_inner_prod, n_mats)

        n_tiles_a = gs.divide(n_inner_prod, n_tangent_vec_a)
        n_tiles_a = gs.cast(n_tiles_a, gs.int32)
        tangent_vec_a = gs.tile(tangent_vec_a, [n_tiles_a, 1])
        n_tiles_b = gs.divide(n_inner_prod, n_tangent_vec_b)
        n_tiles_b = gs.cast(n_tiles_b, gs.int32)
        tangent_vec_b = gs.tile(tangent_vec_b, [n_tiles_b, 1])

        n_tiles_mat = gs.divide(n_inner_prod, n_mats)
        n_tiles_mat = gs.cast(n_tiles_mat, gs.int32)
        inner_prod_mat = gs.tile(inner_prod_mat, [n_tiles_mat, 1, 1])

        aux = gs.einsum('nj,njk->nk', tangent_vec_a, inner_prod_mat)
        inner_prod = gs.einsum('nk,nk->n', aux, tangent_vec_b)
        inner_prod = gs.to_ndarray(inner_prod, to_ndim=2, axis=1)

        assert gs.ndim(inner_prod) == 2, inner_prod.shape
        return inner_prod
Exemple #10
0
    def test_retraction_vectorization_shape(self):
        n_samples = self.n_samples
        n = self.n
        p = self.p

        one_point = self.point_a
        n_points = gs.tile(
            gs.to_ndarray(one_point, to_ndim=3),
            (n_samples, 1, 1))
        one_tangent_vec = self.tangent_vector_1
        n_tangent_vecs = gs.tile(
            gs.to_ndarray(self.tangent_vector_2, to_ndim=3),
            (n_samples, 1, 1))

        result = self.metric.retraction(one_tangent_vec, one_point)
        self.assertAllClose(gs.shape(result), (n, p))

        result = self.metric.retraction(n_tangent_vecs, one_point)
        self.assertAllClose(gs.shape(result), (n_samples, n, p))

        result = self.metric.retraction(one_tangent_vec, n_points)
        self.assertAllClose(gs.shape(result), (n_samples, n, p))

        result = self.metric.retraction(n_tangent_vecs, n_points)
        self.assertAllClose(gs.shape(result), (n_samples, n, p))
Exemple #11
0
    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.
        """
        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 = gs.linalg.sqrtm(
            self.inner_product_mat_at_identity)
        mat = gs.transpose(sqrt_inner_product_mat, axes=(0, 2, 1))

        n_tangent_vecs, _ = tangent_vec.shape
        n_mats, _, _ = mat.shape

        if n_mats == 1:
            mat = gs.tile(mat, (n_tangent_vecs, 1, 1))
        if n_tangent_vecs == 1:
            tangent_vec = gs.tile(tangent_vec, (n_mats, 1))
        exp = gs.einsum('ni,nij->nj', tangent_vec, mat)

        exp = self.group.regularize(exp)
        return exp
Exemple #12
0
    def inner_product(self, tangent_vec_a, tangent_vec_b, base_point):
        """Compute the Log-Euclidean inner product.

        Compute the inner product of tangent_vec_a and tangent_vec_b
        at point base_point using the log-Euclidean metric.

        Parameters
        ----------
        tangent_vec_a : array-like, shape=[n_samples, n, n]
        tangent_vec_b : array-like, shape=[n_samples, n, n]
        base_point : array-like, shape=[n_samples, n, n]

        Returns
        -------
        inner_product : float
        """
        tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=3)
        n_tangent_vecs_a, _, _ = tangent_vec_a.shape
        tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=3)
        n_tangent_vecs_b, _, _ = tangent_vec_b.shape

        base_point = gs.to_ndarray(base_point, to_ndim=3)
        n_base_points, _, _ = base_point.shape

        spd_space = self.space

        assert (n_tangent_vecs_a == n_tangent_vecs_b == n_base_points
                or n_tangent_vecs_a == n_tangent_vecs_b and n_base_points == 1
                or n_base_points == n_tangent_vecs_a and n_tangent_vecs_b == 1
                or n_base_points == n_tangent_vecs_b and n_tangent_vecs_a == 1
                or n_tangent_vecs_a == 1 and n_tangent_vecs_b == 1
                or n_base_points == 1 and n_tangent_vecs_a == 1
                or n_base_points == 1 and n_tangent_vecs_b == 1)

        if n_tangent_vecs_a == 1:
            tangent_vec_a = gs.tile(
                tangent_vec_a,
                (gs.maximum(n_base_points, n_tangent_vecs_b), 1, 1))

        if n_tangent_vecs_b == 1:
            tangent_vec_b = gs.tile(
                tangent_vec_b,
                (gs.maximum(n_base_points, n_tangent_vecs_a), 1, 1))

        if n_base_points == 1:
            base_point = gs.tile(
                base_point,
                (gs.maximum(n_tangent_vecs_a, n_tangent_vecs_b), 1, 1))

        modified_tangent_vec_a = spd_space.differential_log(
            tangent_vec_a, base_point)
        modified_tangent_vec_b = spd_space.differential_log(
            tangent_vec_b, base_point)
        product = gs.matmul(modified_tangent_vec_a, modified_tangent_vec_b)
        inner_product = gs.trace(product, axis1=1, axis2=2)

        inner_product = gs.to_ndarray(inner_product, to_ndim=2, axis=1)

        return inner_product
Exemple #13
0
    def log(self, point, base_point=None):
        """Compute Riemannian logarithm of a point from a base point.

        Parameters
        ----------
        point : array-like, shape=[n_samples, dimension]
            Point in the group.
        base_point : array-like, shape=[n_samples, dimension], optional
            Point in the group, from which to compute the log,
            (the default is identity).

        Returns
        -------
        log : array-like, shape=[n_samples, dimension]
            Tangent vector at the base point equal to the Riemannian logarithm
            of point at the base point.
        """
        if base_point is None:
            base_point = self.group.identity
        base_point = self.group.regularize(base_point)
        if base_point is self.group.identity:
            return self.log_from_identity(point)

        point = self.group.regularize(point)

        n_points, _ = point.shape
        n_base_points, _ = base_point.shape

        if self.left_or_right == 'left':
            point_near_id = self.group.compose(
                self.group.inverse(base_point), point)

        else:
            point_near_id = self.group.compose(
                point, self.group.inverse(base_point))

        log_from_id = self.log_from_identity(point_near_id)

        jacobian = self.group.jacobian_translation(
            base_point, left_or_right=self.left_or_right)

        n_logs, _ = log_from_id.shape
        n_jacobians, _, _ = jacobian.shape

        if n_logs == 1:
            log_from_id = gs.tile(log_from_id, (n_jacobians, 1))
        if n_jacobians == 1:
            jacobian = gs.tile(jacobian, (n_logs, 1, 1))
        log = gs.einsum(
            'ij,ijk->ik',
            log_from_id,
            gs.transpose(jacobian, axes=(0, 2, 1)))
        assert gs.ndim(log) == 2
        return log
Exemple #14
0
    def inner_product(self, tangent_vec_a, tangent_vec_b, base_point):
        """Compute the Procrustes inner product.

        Compute the inner product of tangent_vec_a and tangent_vec_b
        at point base_point using the Procrustes Riemannian metric.

        Parameters
        ----------
        tangent_vec_a : array-like, shape=[n_samples, n, n]
        tangent_vec_b : array-like, shape=[n_samples, n, n]
        base_point : array-like, shape=[n_samples, n, n]

        Returns
        -------
        inner_product : float
        """
        tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=3)
        n_tangent_vecs_a, _, _ = tangent_vec_a.shape
        tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=3)
        n_tangent_vecs_b, _, _ = tangent_vec_b.shape

        base_point = gs.to_ndarray(base_point, to_ndim=3)
        n_base_points, _, _ = base_point.shape

        assert (n_tangent_vecs_a == n_tangent_vecs_b == n_base_points
                or n_tangent_vecs_a == n_tangent_vecs_b and n_base_points == 1
                or n_base_points == n_tangent_vecs_a and n_tangent_vecs_b == 1
                or n_base_points == n_tangent_vecs_b and n_tangent_vecs_a == 1
                or n_tangent_vecs_a == 1 and n_tangent_vecs_b == 1
                or n_base_points == 1 and n_tangent_vecs_a == 1
                or n_base_points == 1 and n_tangent_vecs_b == 1)

        if n_tangent_vecs_a == 1:
            tangent_vec_a = gs.tile(
                tangent_vec_a,
                (gs.maximum(n_base_points, n_tangent_vecs_b), 1, 1))

        if n_tangent_vecs_b == 1:
            tangent_vec_b = gs.tile(
                tangent_vec_b,
                (gs.maximum(n_base_points, n_tangent_vecs_a), 1, 1))

        if n_base_points == 1:
            base_point = gs.tile(
                base_point,
                (gs.maximum(n_tangent_vecs_a, n_tangent_vecs_b), 1, 1))

        spd_space = self.space
        modified_tangent_vec_a =\
            spd_space.inverse_differential_power(2, tangent_vec_a, base_point)
        product = gs.matmul(modified_tangent_vec_a, tangent_vec_b)
        result = gs.trace(product, axis1=1, axis2=2) / 2
        return result
Exemple #15
0
    def exp(self, tangent_vec, base_point):
        """Compute the Riemannian exponential of a tangent vector.

        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 Riemannian exponential
            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_tangent_vecs == 1:
            tangent_vec = gs.tile(tangent_vec, (n_base_points, 1, 1))

        if n_base_points == 1:
            base_point = gs.tile(base_point, (n_tangent_vecs, 1, 1))

        matrix_a = gs.einsum('nij, njk->nik',
                             gs.transpose(base_point, axes=(0, 2, 1)),
                             tangent_vec)
        matrix_k = (tangent_vec -
                    gs.einsum('nij,njk->nik', base_point, matrix_a))

        matrix_q, matrix_r = gs.linalg.qr(matrix_k)

        matrix_ar = gs.concatenate(
            [matrix_a, -gs.transpose(matrix_r, axes=(0, 2, 1))], axis=2)

        zeros = gs.zeros((gs.maximum(n_base_points, n_tangent_vecs), p, p))

        matrix_rz = gs.concatenate([matrix_r, zeros], axis=2)
        block = gs.concatenate([matrix_ar, matrix_rz], axis=1)
        matrix_mn_e = gs.linalg.expm(block)

        exp = gs.einsum('nij,njk->nik',
                        gs.concatenate([base_point, matrix_q], axis=2),
                        matrix_mn_e[:, :, 0:p])

        return exp
Exemple #16
0
    def exp(self, tangent_vec, base_point):
        """
        Riemannian exponential of a tangent vector wrt to a 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_tangent_vecs == 1:
            tangent_vec = gs.tile(tangent_vec, (n_base_points, 1, 1))

        if n_base_points == 1:
            base_point = gs.tile(base_point, (n_tangent_vecs, 1, 1))

        matrix_a = gs.einsum(
            'nij, njk->nik',
            gs.transpose(base_point, axes=(0, 2, 1)), tangent_vec)
        matrix_k = (tangent_vec
                    - gs.einsum('nij,njk->nik', base_point, matrix_a))

        matrix_q, matrix_r = gs.linalg.qr(matrix_k)

        matrix_ar = gs.concatenate(
            [matrix_a,
             -gs.transpose(matrix_r, axes=(0, 2, 1))],
            axis=2)

        zeros = gs.zeros(
            (gs.maximum(n_base_points, n_tangent_vecs), p, p))

        matrix_rz = gs.concatenate(
            [matrix_r,
             zeros],
            axis=2)
        block = gs.concatenate([matrix_ar, matrix_rz], axis=1)
        matrix_mn_e = gs.linalg.expm(block)

        exp = gs.einsum(
            'nij,njk->nik',
            gs.concatenate(
                [base_point,
                 matrix_q],
                axis=2),
            matrix_mn_e[:, :, 0:p])

        return exp
Exemple #17
0
    def exp(self, tangent_vec, base_point):
        """Compute the affine-invariant exponential map.

        Compute the Riemannian exponential at point base_point
        of tangent vector tangent_vec wrt the metric defined in inner_product.
        This gives a symmetric positive definite matrix.

        Parameters
        ----------
        tangent_vec : array-like, shape=[n_samples, n, n]
        base_point : array-like, shape=[n_samples, n, n]

        Returns
        -------
        exp : array-like, shape=[n_samples, n, n]
        """
        power_affine = self.power_affine
        ndim = gs.maximum(gs.ndim(tangent_vec), gs.ndim(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, mat_dim, _ = base_point.shape

        assert (n_tangent_vecs == n_base_points
                or n_tangent_vecs == 1
                or n_base_points == 1)

        if n_tangent_vecs == 1:
            tangent_vec = gs.tile(tangent_vec, (n_base_points, 1, 1))
        if n_base_points == 1:
            base_point = gs.tile(base_point, (n_tangent_vecs, 1, 1))

        if power_affine == 1:
            sqrt_base_point = gs.linalg.sqrtm(base_point)
            inv_sqrt_base_point = gs.linalg.inv(sqrt_base_point)
            exp = self._aux_exp(tangent_vec, sqrt_base_point,
                                inv_sqrt_base_point)
        else:
            modified_tangent_vec = self.space.differential_power(power_affine,
                                                                 tangent_vec,
                                                                 base_point)
            power_sqrt_base_point = gs.linalg.powerm(base_point,
                                                     power_affine / 2)
            power_inv_sqrt_base_point = gs.linalg.inv(power_sqrt_base_point)
            exp = self._aux_exp(modified_tangent_vec, power_sqrt_base_point,
                                power_inv_sqrt_base_point)
            exp = gs.linalg.powerm(exp, 1 / power_affine)

        if ndim == 2:
            return exp[0]
        return exp
Exemple #18
0
    def log(self, point, base_point):
        """Compute the affine-invariant logarithm map.

        Compute the Riemannian logarithm at point base_point,
        of point wrt the metric defined in inner_product.
        This gives a tangent vector at point base_point.

        Parameters
        ----------
        point : array-like, shape=[n_samples, n, n]
        base_point : array-like, shape=[n_samples, n, n]

        Returns
        -------
        log : array-like, shape=[n_samples, n, n]
        """
        power_affine = self.power_affine
        ndim = gs.maximum(gs.ndim(point), gs.ndim(base_point))
        point = gs.to_ndarray(point, to_ndim=3)
        n_points, _, _ = point.shape

        base_point = gs.to_ndarray(base_point, to_ndim=3)
        n_base_points, mat_dim, _ = base_point.shape

        assert (n_points == n_base_points
                or n_points == 1
                or n_base_points == 1)

        if n_points == 1:
            point = gs.tile(point, (n_base_points, 1, 1))
        if n_base_points == 1:
            base_point = gs.tile(base_point, (n_points, 1, 1))

        if power_affine == 1:
            sqrt_base_point = gs.linalg.sqrtm(base_point)
            inv_sqrt_base_point = gs.linalg.inv(sqrt_base_point)
            log = self._aux_log(point, sqrt_base_point, inv_sqrt_base_point)
        else:
            power_point = gs.linalg.powerm(point, power_affine)
            power_sqrt_base_point = gs.linalg.powerm(
                base_point, power_affine / 2)
            power_inv_sqrt_base_point = gs.linalg.inv(power_sqrt_base_point)
            log = self._aux_log(
                power_point,
                power_sqrt_base_point,
                power_inv_sqrt_base_point)
            log = self.space.inverse_differential_power(power_affine, log,
                                                        base_point)

        if ndim == 2:
            return log[0]
        return log
Exemple #19
0
    def exp(self, tangent_vec, base_point=None):
        """Compute Riemannian exponential of tan. vector wrt to base point.

        Parameters
        ----------
        tangent_vec : array-like, shape=[n_samples, dimension]
            Tangent vector at a base point.
        base_point : array-like, shape=[n_samples, dimension]
            Point in the group.

        Returns
        -------
        exp : array-like, shape=[n_samples, dimension]
            Point in the group equal to the Riemannian exponential
            of tangent_vec at the base point.
        """
        if base_point is None:
            base_point = self.group.identity
        base_point = self.group.regularize(base_point)
        if base_point is self.group.identity:
            return self.exp_from_identity(tangent_vec)

        tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=2)
        assert gs.ndim(tangent_vec) == 2

        n_tangent_vecs, _ = tangent_vec.shape
        n_base_points, _ = base_point.shape

        if n_tangent_vecs == 1:
            tangent_vec = gs.tile(tangent_vec, (n_base_points, 1))
        if n_base_points == 1:
            base_point = gs.tile(base_point, (n_tangent_vecs, 1))

        jacobian = self.group.jacobian_translation(
            point=base_point,
            left_or_right=self.left_or_right)
        assert gs.ndim(jacobian) == 3
        inv_jacobian = gs.linalg.inv(jacobian)
        inv_jacobian_transposed = gs.transpose(inv_jacobian, axes=(0, 2, 1))
        tangent_vec_at_id = gs.einsum(
            'ni,nij->nj', tangent_vec, inv_jacobian_transposed)
        exp_from_id = self.exp_from_identity(tangent_vec_at_id)

        if self.left_or_right == 'left':
            exp = self.group.compose(base_point, exp_from_id)

        else:
            exp = self.group.compose(exp_from_id, base_point)

        exp = self.group.regularize(exp)

        return exp
Exemple #20
0
    def jacobian_translation(self,
                             point,
                             left_or_right='left',
                             point_type=None):
        """
        Compute the jacobian matrix of the differential
        of the left/right translations from the identity to point in SE(n).
        """
        if point_type is None:
            point_type = self.default_point_type

        assert left_or_right in ('left', 'right')

        dim = self.dimension
        rotations = self.rotations
        translations = self.translations
        dim_rotations = rotations.dimension
        dim_translations = translations.dimension

        point = self.regularize(point, point_type=point_type)

        if point_type == 'vector':
            n_points, _ = point.shape

            rot_vec = point[:, :dim_rotations]

            jacobian = gs.zeros((n_points, ) + (dim, ) * 2)
            jacobian_rot = self.rotations.jacobian_translation(
                point=rot_vec,
                left_or_right=left_or_right,
                point_type=point_type)
            block_zeros_1 = gs.zeros(
                (n_points, dim_rotations, dim_translations))
            jacobian_block_line_1 = gs.concatenate(
                [jacobian_rot, block_zeros_1], axis=2)

            if left_or_right == 'left':
                rot_mat = self.rotations.matrix_from_rotation_vector(rot_vec)
                jacobian_trans = rot_mat
                block_zeros_2 = gs.zeros(
                    (n_points, dim_translations, dim_rotations))
                jacobian_block_line_2 = gs.concatenate(
                    [block_zeros_2, jacobian_trans], axis=2)

            else:
                inv_skew_mat = -self.rotations.skew_matrix_from_vector(rot_vec)
                eye = gs.to_ndarray(gs.eye(self.n), to_ndim=3)
                eye = gs.tile(eye, [n_points, 1, 1])
                jacobian_block_line_2 = gs.concatenate([inv_skew_mat, eye],
                                                       axis=2)

            jacobian = gs.concatenate(
                [jacobian_block_line_1, jacobian_block_line_2], axis=1)

            assert gs.ndim(jacobian) == 3

        elif point_type == 'matrix':
            raise NotImplementedError()

        return jacobian
Exemple #21
0
        def coefficients(ind_k):
            """Christoffel symbols for contravariant index ind_k."""
            param_k = base_point[..., ind_k]
            param_sum = gs.sum(base_point, -1)
            c1 = (
                1
                / gs.polygamma(1, param_k)
                / (
                    1 / gs.polygamma(1, param_sum)
                    - gs.sum(1 / gs.polygamma(1, base_point), -1)
                )
            )
            c2 = -c1 * gs.polygamma(2, param_sum) / gs.polygamma(1, param_sum)

            mat_ones = gs.ones((n_points, self.dim, self.dim))
            mat_diag = from_vector_to_diagonal_matrix(
                -gs.polygamma(2, base_point) / gs.polygamma(1, base_point)
            )
            arrays = [
                gs.zeros((1, ind_k)),
                gs.ones((1, 1)),
                gs.zeros((1, self.dim - ind_k - 1)),
            ]
            vec_k = gs.tile(gs.hstack(arrays), (n_points, 1))
            val_k = gs.polygamma(2, param_k) / gs.polygamma(1, param_k)
            vec_k = gs.einsum("i,ij->ij", val_k, vec_k)
            mat_k = from_vector_to_diagonal_matrix(vec_k)

            mat = (
                gs.einsum("i,ijk->ijk", c2, mat_ones)
                - gs.einsum("i,ijk->ijk", c1, mat_diag)
                + mat_k
            )

            return 1 / 2 * mat
Exemple #22
0
    def is_tangent(self, vector, base_point=None, atol=gs.atol):
        """Check whether vector is tangent to the manifold at base_point.

        In what follows, the ellipsis ... indicates either nothing
        or any number n of elements, i.e. shape=[..., dim] means
        shape=[dim] or shape=[n, dim] for any n.
        All functions/methods of geomstats should work for any number
        of inputs. In the case of the function `is_tangent`, it means:
        for any number of input vectors.

        Parameters
        ----------
        vector : array-like, shape=[..., dim]
            Vector.
        base_point : array-like, shape=[..., dim]
            Point on the manifold.
            Optional, default: None.
        atol : float
            Absolute tolerance threshold

        Returns
        -------
        is_tangent : bool
            Boolean denoting if vector is a tangent vector at the base point.
        """
        # Perform operations to determine if vector is a tangent vector,
        # for example:
        is_tangent = gs.shape(vector)[-1] == self.dim
        if gs.ndim(vector) == 2:
            is_tangent = gs.tile([is_tangent], (vector.shape[0], ))
        return is_tangent
Exemple #23
0
    def belongs(self, point, atol=gs.atol):
        """Evaluate if the point belongs to the vector space.

        This method checks the shape of the input point.

        Parameters
        ----------
        point : array-like, shape=[.., {dim, [n, n]}]
            Point to test.
        atol : float
            Unused here.

        Returns
        -------
        belongs : array-like, shape=[...,]
            Boolean evaluating if point belongs to the space.
        """
        if self.default_point_type == 'vector':
            point_shape = point.shape[-1:]
            minimal_ndim = 1
        else:
            point_shape = point.shape[-2:]
            minimal_ndim = 2
        belongs = point_shape == self.shape
        if point.ndim == minimal_ndim:
            return belongs
        return gs.tile(gs.array([belongs]), [point.shape[0]])
Exemple #24
0
    def random_tangent_vec_uniform(self, n_samples=1, base_point=None):
        if base_point is None:
            base_point = gs.eye(self.n)

        base_point = gs.to_ndarray(base_point, to_ndim=3)
        n_base_points, _, _ = base_point.shape

        assert n_base_points == n_samples or n_base_points == 1
        if n_base_points == 1:
            base_point = gs.tile(base_point, (n_samples, 1, 1))

        sqrt_base_point = gs.linalg.sqrtm(base_point)

        tangent_vec_at_id = (2 * gs.random.rand(n_samples,
                                                self.n,
                                                self.n)
                             - 1)
        tangent_vec_at_id = (tangent_vec_at_id
                             + gs.transpose(tangent_vec_at_id,
                                            axes=(0, 2, 1)))

        tangent_vec = gs.matmul(sqrt_base_point, tangent_vec_at_id)
        tangent_vec = gs.matmul(tangent_vec, sqrt_base_point)

        return tangent_vec
Exemple #25
0
    def square_root_velocity_inverse(self, srv, starting_point):
        """Retrieve a curve from sqrt velocity rep and starting point.

        Parameters
        ----------
        srv :
        starting_point :

        Returns
        -------
        curve :
        """
        if not isinstance(self.ambient_metric, EuclideanMetric):
            raise AssertionError('The square root velocity inverse is only '
                                 'implemented for dicretized curves embedded '
                                 'in a Euclidean space.')
        if gs.ndim(srv) != gs.ndim(starting_point):
            starting_point = gs.transpose(gs.tile(starting_point, (1, 1, 1)),
                                          axes=(1, 0, 2))
        srv_shape = srv.shape
        srv = gs.to_ndarray(srv, to_ndim=3)
        n_curves, n_sampling_points_minus_one, n_coords = srv.shape

        srv = gs.reshape(srv,
                         (n_curves * n_sampling_points_minus_one, n_coords))
        srv_norm = self.ambient_metric.norm(srv)
        delta_points = 1 / n_sampling_points_minus_one * srv_norm * srv
        delta_points = gs.reshape(delta_points, srv_shape)
        curve = gs.concatenate((starting_point, delta_points), -2)
        curve = gs.cumsum(curve, -2)

        return curve
Exemple #26
0
    def belongs(self, point, atol=gs.atol):
        """Test if a point belongs to the hyperbolic space.

        Test if a point belongs to the hyperbolic space in
        its hyperboloid representation.

        Parameters
        ----------
        point : array-like, shape=[..., dim]
            Point to be tested.
        atol : float, optional
            Tolerance at which to evaluate how close the squared norm
            is to the reference value.
            Optional, default: backend atol.

        Returns
        -------
        belongs : array-like, shape=[...,]
            Array of booleans indicating whether the corresponding points
            belong to the hyperbolic space.
        """
        point_dim = point.shape[-1]
        if point_dim is not self.dim + 1:
            belongs = False
            if point_dim is self.dim and self.coords_type == "intrinsic":
                belongs = True
            if gs.ndim(point) == 2:
                belongs = gs.tile([belongs], (point.shape[0], ))
            return belongs

        return super(Hyperboloid, self).belongs(point, atol)
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
Exemple #28
0
        def coefficients(ind_k):
            param_k = base_point[..., ind_k]
            param_sum = gs.sum(base_point, -1)
            c1 = 1 / gs.polygamma(
                1, param_k) / (1 / gs.polygamma(1, param_sum) -
                               gs.sum(1 / gs.polygamma(1, base_point), -1))
            c2 = -c1 * gs.polygamma(2, param_sum) / gs.polygamma(1, param_sum)

            mat_ones = gs.ones((n_points, self.dim, self.dim))
            mat_diag = from_vector_to_diagonal_matrix(
                -gs.polygamma(2, base_point) / gs.polygamma(1, base_point))
            arrays = [
                gs.zeros((1, ind_k)),
                gs.ones((1, 1)),
                gs.zeros((1, self.dim - ind_k - 1))
            ]
            vec_k = gs.tile(gs.hstack(arrays), (n_points, 1))
            val_k = gs.polygamma(2, param_k) / gs.polygamma(1, param_k)
            vec_k = gs.einsum('i,ij->ij', val_k, vec_k)
            mat_k = from_vector_to_diagonal_matrix(vec_k)

            mat = gs.einsum('i,ijk->ijk', c2, mat_ones)\
                - gs.einsum('i,ijk->ijk', c1, mat_diag) + mat_k

            return 1 / 2 * mat
    def belongs(self, point, atol=gs.atol):
        """Test if a point belongs to the hypersphere.

        This tests whether the point's squared norm in Euclidean space is 1.

        Parameters
        ----------
        point : array-like, shape=[..., dim + 1]
            Point in Euclidean space.
        atol : float
            Tolerance at which to evaluate norm == 1.
            Optional, default: backend atol.

        Returns
        -------
        belongs : array-like, shape=[...,]
            Boolean evaluating if point belongs to the hypersphere.
        """
        point_dim = gs.shape(point)[-1]
        if point_dim != self.dim + 1:
            if point_dim is self.dim:
                logging.warning(
                    'Use the extrinsic coordinates to '
                    'represent points on the hypersphere.')
            belongs = False
            if gs.ndim(point) == 2:
                belongs = gs.tile([belongs], (point.shape[0],))
            return belongs
        sq_norm = gs.sum(point ** 2, axis=-1)
        diff = gs.abs(sq_norm - 1)
        return gs.less_equal(diff, atol)
Exemple #30
0
    def belongs(self, point, tolerance=TOLERANCE):
        """Test if a point belongs to the hyperbolic space.

        Test if a point belongs to the hyperbolic space in
        its hyperboloid representation.

        Parameters
        ----------
        point : array-like, shape=[..., dim]
            Point to be tested.
        tolerance : float, optional
            Tolerance at which to evaluate how close the squared norm
            is to the reference value.
            Optional, default: 1e-6.

        Returns
        -------
        belongs : array-like, shape=[...,]
            Array of booleans indicating whether the corresponding points
            belong to the hyperbolic space.
        """
        point_dim = point.shape[-1]
        if point_dim is not self.dim + 1:
            belongs = False
            if point_dim is self.dim and self.coords_type == 'intrinsic':
                belongs = True
            if gs.ndim(point) == 2:
                belongs = gs.tile([belongs], (point.shape[0], ))
            return belongs

        sq_norm = self.embedding_metric.squared_norm(point)
        euclidean_sq_norm = gs.sum(point**2, axis=-1)
        diff = gs.abs(sq_norm + 1)
        belongs = diff < tolerance * euclidean_sq_norm
        return belongs