Ejemplo n.º 1
0
 def test_horizontal_projection(self):
     mat = self.bundle.random_point()
     vec = self.bundle.random_point()
     horizontal_vec = self.bundle.horizontal_projection(vec, mat)
     product = Matrices.mul(horizontal_vec, GeneralLinear.inverse(mat))
     is_horizontal = Matrices.is_symmetric(product)
     self.assertTrue(is_horizontal)
Ejemplo n.º 2
0
    def is_tangent(self, vector, base_point=None, atol=gs.atol):
        r"""Check if a vector is tangent to the manifold at the base point.

        Check if the (n,n)-matrix :math: `Y` is symmetric and verifies the
        relation :math: PY + YP = Y where :math: `P` represents the base
        point and :math: `Y` the vector.

        Parameters
        ----------
        vector : array-like, shape=[..., n, n]
            Matrix to be checked.
        base_point : array-like, shape=[..., n, n]
            Base point.
        atol : int
            Optional, default: 1e-5.

        Returns
        -------
        belongs : array-like, shape=[...,]
            Boolean evaluating if `vector` is tangent to the Grassmannian at
            `base_point`.
        """
        diff = Matrices.mul(base_point, vector) + Matrices.mul(
            vector, base_point) - vector
        is_close = gs.all(gs.isclose(diff, 0., atol=atol))
        return gs.logical_and(Matrices.is_symmetric(vector), is_close)
Ejemplo n.º 3
0
    def belongs(self, point, atol=gs.atol):
        """Check if the point belongs to the manifold.

        Check if an (n,n)-matrix is an orthogonal projector
        onto a subspace of rank k.

        Parameters
        ----------
        point : array-like, shape=[..., n, n]
            Point to be checked.
        atol : int
            Optional, default: 1e-5.

        Returns
        -------
        belongs : array-like, shape=[...,]
            Boolean evaluating if point belongs to the Grassmannian.
        """
        if not gs.all(self._check_square(point)):
            raise ValueError('all points must be square.')

        symm = Matrices.is_symmetric(point)
        idem = self._check_idempotent(point, atol)
        rank = self._check_rank(point, self.k, atol)

        belongs = gs.all(gs.stack([symm, idem, rank], axis=0), axis=0)

        return belongs
Ejemplo n.º 4
0
    def inverse_transform(self, X):
        """Low-dimensional reconstruction of X.

        The reconstruction will match X_original whose transform would be X
        if `n_components=min(n_samples, n_features)`.

        Parameters
        ----------
        X : array-like, shape=[n_samples, n_components]
            New data, where n_samples is the number of samples
            and n_components is the number of components.

        Returns
        -------
        X_original array-like, shape=[n_samples, n_features]
        """
        scores = self.mean_ + gs.matmul(X, self.components_)
        if self.point_type == 'matrix':
            if Matrices.is_symmetric(self.base_point_fit).all():
                scores = SymmetricMatrices(
                    self.base_point_fit.shape[-1]).from_vector(scores)
            else:
                dim = self.base_point_fit.shape[-1]
                scores = gs.reshape(scores, (len(scores), dim, dim))
        return self.metric.exp(scores, self.base_point_fit)
Ejemplo n.º 5
0
    def transform(self, X, y=None):
        """Project X on the principal components.

        Parameters
        ----------
        X : array-like, shape=[..., n_features]
            Data, where n_samples is the number of samples
            and n_features is the number of features.
        y : Ignored (Compliance with scikit-learn interface)

        Returns
        -------
        X_new : array-like, shape=[..., n_components]
            Projected data.
        """
        tangent_vecs = self.metric.log(X, base_point=self.base_point_fit)
        if self.point_type == "matrix":
            if Matrices.is_symmetric(tangent_vecs).all():
                X = SymmetricMatrices.to_vector(tangent_vecs)
            else:
                X = gs.reshape(tangent_vecs, (len(X), -1))
        else:
            X = tangent_vecs
        X = X - self.mean_
        X_transformed = gs.matmul(X, gs.transpose(self.components_))
        return X_transformed
Ejemplo n.º 6
0
 def test_alignment_is_symmetric(self, k_landmarks, m_ambient, point,
                                 base_point):
     space = self.space(k_landmarks, m_ambient)
     aligned = space.align(point, base_point)
     alignment = gs.matmul(Matrices.transpose(aligned), base_point)
     result = gs.all(Matrices.is_symmetric(alignment))
     self.assertTrue(result)
Ejemplo n.º 7
0
    def belongs(self, mat, atol=TOLERANCE):
        """Check if a matrix belongs to the manifold.

        Check if a matrix belongs to the manifold of symmetric positive
        definite matrices.
        """
        return Matrices.is_symmetric(mat)
Ejemplo n.º 8
0
    def transform(self, X, y=None):
        """Project X on the principal components.

        Parameters
        ----------
        X : array-like, shape=[n_samples, n_features]
            Data, where n_samples is the number of samples
            and n_features is the number of features.
        y : Ignored (Compliance with scikit-learn interface)

        Returns
        -------
        X_new : array-like, shape=[n_samples, n_components]
        """
        tangent_vecs = self.metric.log(X, base_point=self.base_point_fit)
        if self.point_type == 'matrix':
            if Matrices.is_symmetric(tangent_vecs).all():
                X = SymmetricMatrices.vector_from_symmetric_matrix(
                    tangent_vecs)
            else:
                X = gs.reshape(tangent_vecs, (len(X), -1))
        else:
            X = tangent_vecs

        return super(TangentPCA, self).transform(X)
Ejemplo n.º 9
0
 def test_dist_pairwise_parallel(self):
     n_samples = 15
     points = self.space.random_uniform(n_samples)
     result = self.metric.dist_pairwise(points, n_jobs=2, prefer="threads")
     is_sym = Matrices.is_symmetric(result)
     belongs = Matrices(n_samples, n_samples).belongs(result)
     self.assertTrue(is_sym)
     self.assertTrue(belongs)
Ejemplo n.º 10
0
 def belongs(mat, atol=TOLERANCE):
     """Check if a matrix is symmetric and invertible."""
     # TODO (opeltre): check positivity, implying invertibility.
     #
     # note : vectorized "and" on numpy works with:
     #       [bool] * [bool] -> bool
     # but does not on tf.
     return Matrices.is_symmetric(mat)
Ejemplo n.º 11
0
 def vector_from_symmetric_matrix(mat):
     """Convert the symmetric part of a symmetric matrix into a vector."""
     if not gs.all(Matrices.is_symmetric(mat)):
         logging.warning('non-symmetric matrix encountered.')
     mat = Matrices.make_symmetric(mat)
     _, dim, _ = mat.shape
     i, j = gs.triu_indices(dim)
     vec = mat[:, i, j]
     return vec
Ejemplo n.º 12
0
 def test_dist_pairwise_parallel(self):
     gs.random.seed(0)
     n_samples = 2
     group = self.matrix_so3
     metric = InvariantMetric(group=group)
     points = group.random_uniform(n_samples)
     result = metric.dist_pairwise(points, n_jobs=2)
     is_sym = Matrices.is_symmetric(result)
     belongs = Matrices(n_samples, n_samples).belongs(result)
     self.assertTrue(is_sym)
     self.assertTrue(belongs)
Ejemplo n.º 13
0
    def to_vector(mat):
        """Convert the symmetric part of a symmetric matrix into a vector."""
        if not gs.all(Matrices.is_symmetric(mat)):
            logging.warning('non-symmetric matrix encountered.')
        mat = Matrices.to_symmetric(mat)
        _, dim, _ = mat.shape
        indices_i, indices_j = gs.triu_indices(dim)
        vec = []
        for i, j in zip(indices_i, indices_j):
            vec.append(mat[:, i, j])
        vec = gs.stack(vec, axis=1)

        return vec
Ejemplo n.º 14
0
    def belongs(self, mat, atol=TOLERANCE):
        """Check if mat belongs to the vector space of symmetric matrices.

        Parameters
        ----------
        mat : array-like, shape=[..., n, n]
            Matrix to check.

        Returns
        -------
        belongs : array-like, shape=[...,]
            Boolean evaluating if mat is a symmetric matrix.
        """
        check_shape = self.embedding_manifold.belongs(mat)
        return gs.logical_and(check_shape, Matrices.is_symmetric(mat, atol))
Ejemplo n.º 15
0
    def to_vector(mat):
        """Convert a symmetric matrix into a vector.

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

        Returns
        -------
        vec : array-like, shape=[..., n(n+1)/2]
            Vector.
        """
        if not gs.all(Matrices.is_symmetric(mat)):
            logging.warning("non-symmetric matrix encountered.")
        mat = Matrices.to_symmetric(mat)
        return gs.triu_to_vec(mat)
Ejemplo n.º 16
0
    def transform(self, X, base_point=None):
        """Lift data to a tangent space.

        Compute the logs of all data point and reshapes them to
        1d vectors if necessary. By default the logs are taken at the mean
        but any other base point can be passed. Any machine learning
        algorithm can then be used with the output array.

        Parameters
        ----------
        X : array-like, shape=[..., {dim, [n, n]}]
            Data to transform.
        y : Ignored (Compliance with scikit-learn interface)
        base_point : array-like, shape={dim, [n,n]}, optional (mean)
            Point on the manifold, the returned samples will be tangent
            vectors at the base point.

        Returns
        -------
        X_new : array-like, shape=[..., dim]
            Lifted data.
        """
        if base_point is None:
            base_point = self.estimator.estimate_

            if self.estimator.estimate_ is None:
                raise RuntimeError(
                    "fit needs to be called first or a " "base_point passed."
                )

        tangent_vecs = self._used_geometry.log(X, base_point=base_point)

        if self.point_type == "vector":
            return tangent_vecs

        if gs.all(Matrices.is_symmetric(tangent_vecs)):
            X = SymmetricMatrices.to_vector(tangent_vecs)
        elif gs.all(Matrices.is_skew_symmetric(tangent_vecs)):
            X = SkewSymmetricMatrices(tangent_vecs.shape[-1]).basis_representation(
                tangent_vecs
            )
        else:
            X = gs.reshape(tangent_vecs, (len(X), -1))

        return X
Ejemplo n.º 17
0
    def inverse_transform(self, X, base_point=None):
        """Reconstruction of X.

        The reconstruction will match X_original whose transform would be X.

        Parameters
        ----------
        X : array-like, shape=[..., dim]
            New data, where dim is the dimension of the manifold data belong
            to.
        base_point : array-like, shape={dim, [n,n]}, optional (mean)
            Point on the manifold, where the input samples are tangent
            vectors.

        Returns
        -------
        X_original : array-like, shape=[..., {dim, [n, n]}
            Data lying on the manifold.
        """
        if base_point is None:
            base_point = self.estimator.estimate_

            if self.estimator.estimate_ is None:
                raise RuntimeError('fit needs to be called first or a '
                                   'base_point passed.')

        if self.point_type == 'matrix':
            n_base_point = base_point.shape[-1]
            n_vecs = X.shape[-1]
            dim_sym = int(n_base_point * (n_base_point + 1) / 2)
            dim_skew = int(n_base_point * (n_base_point - 1) / 2)

            if gs.all(Matrices.is_symmetric(base_point)) and dim_sym == n_vecs:
                tangent_vecs = SymmetricMatrices(
                    base_point.shape[-1]).from_vector(X)
            elif dim_skew == n_vecs:
                tangent_vecs = SkewSymmetricMatrices(
                    dim_skew).matrix_representation(X)
            else:
                dim = base_point.shape[-1]
                tangent_vecs = gs.reshape(X, (len(X), dim, dim))
        else:
            tangent_vecs = X
        return self._used_geometry.exp(tangent_vecs, base_point)
Ejemplo n.º 18
0
    def belongs(self, point, atol=gs.atol):
        """Evaluate if a matrix is symmetric.

        Parameters
        ----------
        point : array-like, shape=[.., n, n]
            Point to test.
        atol : float
            Tolerance to evaluate equality with the transpose.

        Returns
        -------
        belongs : array-like, shape=[...,]
            Boolean evaluating if point belongs to the space.
        """
        belongs = super(SymmetricMatrices, self).belongs(point)
        if gs.any(belongs):
            is_symmetric = Matrices.is_symmetric(point, atol)
            return gs.logical_and(belongs, is_symmetric)
        return belongs
Ejemplo n.º 19
0
    def is_horizontal(self, tangent_vec, base_point, atol=gs.atol):
        """Check whether the tangent vector is horizontal at base_point.

        Parameters
        ----------
        tangent_vec : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector.
        base_point : array-like, shape=[..., k_landmarks, m_ambient]
            Point on the manifold.
            Optional, default: none.
        atol : float
            Absolute tolerance.
            Optional, default: backend atol.

        Returns
        -------
        is_tangent : bool
            Boolean denoting if tangent vector is horizontal.
        """
        product = gs.matmul(Matrices.transpose(tangent_vec), base_point)
        is_tangent = self.is_tangent(tangent_vec, base_point, atol)
        is_symmetric = Matrices.is_symmetric(product, atol)
        return gs.logical_and(is_tangent, is_symmetric)
Ejemplo n.º 20
0
    def to_vector(mat):
        """Convert a symmetric matrix into a vector.

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

        Returns
        -------
        vec : array-like, shape=[..., n(n+1)/2]
            Vector.
        """
        if not gs.all(Matrices.is_symmetric(mat)):
            logging.warning('non-symmetric matrix encountered.')
        mat = Matrices.to_symmetric(mat)
        _, dim, _ = mat.shape
        indices_i, indices_j = gs.triu_indices(dim)
        vec = []
        for i, j in zip(indices_i, indices_j):
            vec.append(mat[:, i, j])
        vec = gs.stack(vec, axis=1)

        return vec
Ejemplo n.º 21
0
class TestMatrices(geomstats.tests.TestCase):
    def setUp(self):
        gs.random.seed(1234)

        self.m = 2
        self.n = 3
        self.space = Matrices(m=self.n, n=self.n)
        self.space_nonsquare = Matrices(m=self.m, n=self.n)
        self.metric = self.space.metric
        self.n_samples = 2

    @geomstats.tests.np_only
    def test_mul(self):
        a = gs.eye(3, 3, 1)
        b = gs.eye(3, 3, -1)
        c = gs.array([
            [1., 0., 0.],
            [0., 1., 0.],
            [0., 0., 0.]])
        d = gs.array([
            [0., 0., 0.],
            [0., 1., 0.],
            [0., 0., 1.]])
        result = self.space.mul([a, b], [b, a])
        expected = gs.array([c, d])
        self.assertAllClose(result, expected)

        result = self.space.mul(a, [a, b])
        expected = gs.array([gs.eye(3, 3, 2), c])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_only
    def test_bracket(self):
        x = gs.array([
            [0., 0., 0.],
            [0., 0., -1.],
            [0., 1., 0.]])
        y = gs.array([
            [0., 0., 1.],
            [0., 0., 0.],
            [-1., 0., 0.]])
        z = gs.array([
            [0., -1., 0.],
            [1., 0., 0.],
            [0., 0., 0.]])
        result = self.space.bracket([x, y], [y, z])
        expected = gs.array([z, x])
        self.assertAllClose(result, expected)

        result = self.space.bracket(x, [x, y, z])
        expected = gs.array([gs.zeros((3, 3)), z, -y])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_only
    def test_transpose(self):
        tr = self.space.transpose
        ar = gs.array
        a = gs.eye(3, 3, 1)
        b = gs.eye(3, 3, -1)
        self.assertAllClose(tr(a), b)
        self.assertAllClose(tr(ar([a, b])), ar([b, a]))

    def test_is_symmetric(self):
        not_squared = gs.array([[1., 2.], [2., 1.], [3., 1.]])
        result = self.space.is_symmetric(not_squared)
        expected = False
        self.assertAllClose(result, expected)

        sym_mat = gs.array([[1., 2.], [2., 1.]])
        result = self.space.is_symmetric(sym_mat)
        expected = gs.array(True)
        self.assertAllClose(result, expected)

        not_a_sym_mat = gs.array([[1., 0.6, -3.],
                                  [6., -7., 0.],
                                  [0., 7., 8.]])
        result = self.space.is_symmetric(not_a_sym_mat)
        expected = gs.array(False)
        self.assertAllClose(result, expected)

    @geomstats.tests.np_only
    def test_is_skew_symmetric(self):
        skew_mat = gs.array([[0, - 2.],
                            [2., 0]])
        result = self.space.is_skew_symmetric(skew_mat)
        expected = gs.array(True)
        self.assertAllClose(result, expected)

        not_a_sym_mat = gs.array([[1., 0.6, -3.],
                                  [6., -7., 0.],
                                  [0., 7., 8.]])
        result = self.space.is_skew_symmetric(not_a_sym_mat)
        expected = gs.array(False)
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_is_symmetric_vectorization(self):
        points = gs.array([
            [[1., 2.],
             [2., 1.]],
            [[3., 4.],
             [4., 5.]],
            [[1., 2.],
             [3., 4.]]])
        result = self.space.is_symmetric(points)
        expected = [True, True, False]
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_pytorch_only
    def test_make_symmetric(self):
        sym_mat = gs.array([[1., 2.],
                            [2., 1.]])
        result = self.space.to_symmetric(sym_mat)
        expected = sym_mat
        self.assertAllClose(result, expected)

        mat = gs.array([[1., 2., 3.],
                        [0., 0., 0.],
                        [3., 1., 1.]])
        result = self.space.to_symmetric(mat)
        expected = gs.array([[1., 1., 3.],
                             [1., 0., 0.5],
                             [3., 0.5, 1.]])
        self.assertAllClose(result, expected)

        mat = gs.array([[1e100, 1e-100, 1e100],
                        [1e100, 1e-100, 1e100],
                        [1e-100, 1e-100, 1e100]])
        result = self.space.to_symmetric(mat)

        res = 0.5 * (1e100 + 1e-100)

        expected = gs.array([[1e100, res, res],
                             [res, 1e-100, res],
                             [res, res, 1e100]])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_make_symmetric_and_is_symmetric_vectorization(self):
        points = gs.array([
            [[1., 2.],
             [3., 4.]],
            [[5., 6.],
             [4., 9.]]])

        sym_points = self.space.to_symmetric(points)
        result = gs.all(self.space.is_symmetric(sym_points))
        expected = True
        self.assertAllClose(result, expected)

    def test_inner_product(self):
        base_point = gs.array([
            [1., 2., 3.],
            [0., 0., 0.],
            [3., 1., 1.]])

        tangent_vector_1 = gs.array([
            [1., 2., 3.],
            [0., -10., 0.],
            [30., 1., 1.]])

        tangent_vector_2 = gs.array([
            [1., 4., 3.],
            [5., 0., 0.],
            [3., 1., 1.]])

        result = self.metric.inner_product(
            tangent_vector_1,
            tangent_vector_2,
            base_point=base_point)

        expected = gs.trace(
            gs.matmul(
                gs.transpose(tangent_vector_1),
                tangent_vector_2))

        self.assertAllClose(result, expected)

    def test_cong(self):
        base_point = gs.array([
            [1., 2., 3.],
            [0., 0., 0.],
            [3., 1., 1.]])

        tangent_vector = gs.array([
            [1., 2., 3.],
            [0., -10., 0.],
            [30., 1., 1.]])

        result = self.space.congruent(tangent_vector, base_point)
        expected = gs.matmul(
            tangent_vector, gs.transpose(base_point))
        expected = gs.matmul(base_point, expected)

        self.assertAllClose(result, expected)

    def test_belongs(self):
        base_point_square = gs.zeros((self.n, self.n))
        base_point_nonsquare = gs.zeros((self.m, self.n))

        result = self.space.belongs(base_point_square)
        expected = True
        self.assertAllClose(result, expected)
        result = self.space_nonsquare.belongs(base_point_square)
        expected = False
        self.assertAllClose(result, expected)

        result = self.space.belongs(base_point_nonsquare)
        expected = False
        self.assertAllClose(result, expected)
        result = self.space_nonsquare.belongs(base_point_nonsquare)
        expected = True
        self.assertAllClose(result, expected)

        result = self.space.belongs(gs.zeros((2, 2, 3)))
        self.assertFalse(gs.all(result))

        result = self.space.belongs(gs.zeros((2, 3, 3)))
        self.assertTrue(gs.all(result))

    def test_is_diagonal(self):
        base_point = gs.array([
            [1., 2., 3.],
            [0., 0., 0.],
            [3., 1., 1.]])
        result = self.space.is_diagonal(base_point)
        expected = False
        self.assertAllClose(result, expected)

        diagonal = gs.eye(3)
        result = self.space.is_diagonal(diagonal)
        self.assertTrue(result)

        base_point = gs.stack([base_point, diagonal])
        result = self.space.is_diagonal(base_point)
        expected = gs.array([False, True])
        self.assertAllClose(result, expected)

        base_point = gs.stack([diagonal] * 2)
        result = self.space.is_diagonal(base_point)
        self.assertTrue(gs.all(result))

        base_point = gs.reshape(gs.arange(6), (2, 3))
        result = self.space.is_diagonal(base_point)
        self.assertTrue(~result)

    def test_norm(self):
        for n_samples in [1, 2]:
            mat = self.space.random_point(n_samples)
            result = self.metric.norm(mat)
            expected = self.space.frobenius_product(mat, mat) ** .5
            self.assertAllClose(result, expected)
Ejemplo n.º 22
0
 def test_horizontal_projection(self, n, mat, vec):
     bundle = self.bundle(n)
     horizontal_vec = bundle.horizontal_projection(vec, mat)
     product = Matrices.mul(horizontal_vec, GeneralLinear.inverse(mat))
     is_horizontal = Matrices.is_symmetric(product)
     self.assertTrue(is_horizontal)
Ejemplo n.º 23
0
 def test_align(self):
     point, base_point = self.space.random_point(2)
     aligned = self.space.align(point, base_point)
     alignment = gs.matmul(Matrices.transpose(aligned), base_point)
     result = Matrices.is_symmetric(alignment)
     self.assertTrue(result)
Ejemplo n.º 24
0
 def belongs(self, mat, atol=TOLERANCE):
     """Check if mat belongs to the vector space of symmetric matrices."""
     check_shape = self.embedding_manifold.belongs(mat)
     return gs.logical_and(check_shape, Matrices.is_symmetric(mat, atol))
Ejemplo n.º 25
0
class TestMatricesMethods(geomstats.tests.TestCase):
    def setUp(self):
        gs.random.seed(1234)

        self.n = 3
        self.space = Matrices(m=self.n, n=self.n)
        self.metric = self.space.metric
        self.n_samples = 2

    @geomstats.tests.np_only
    def test_mul(self):
        a = gs.eye(3, 3, 1)
        b = gs.eye(3, 3, -1)
        c = gs.array([[1., 0., 0.], [0., 1., 0.], [0., 0., 0.]])
        d = gs.array([[0., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
        result = self.space.mul([a, b], [b, a])
        expected = gs.array([c, d])
        self.assertAllClose(result, expected)

        result = self.space.mul(a, [a, b])
        expected = gs.array([gs.eye(3, 3, 2), c])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_only
    def test_bracket(self):
        x = gs.array([[0., 0., 0.], [0., 0., -1.], [0., 1., 0.]])
        y = gs.array([[0., 0., 1.], [0., 0., 0.], [-1., 0., 0.]])
        z = gs.array([[0., -1., 0.], [1., 0., 0.], [0., 0., 0.]])
        result = self.space.bracket([x, y], [y, z])
        expected = gs.array([z, x])
        self.assertAllClose(result, expected)

        result = self.space.bracket(x, [x, y, z])
        expected = gs.array([gs.zeros((3, 3)), z, -y])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_only
    def test_transpose(self):
        tr = self.space.transpose
        ar = gs.array
        a = gs.eye(3, 3, 1)
        b = gs.eye(3, 3, -1)
        self.assertAllClose(tr(a), b)
        self.assertAllClose(tr(ar([a, b])), ar([b, a]))

    @geomstats.tests.np_only
    def test_is_symmetric(self):
        sym_mat = gs.array([[1., 2.], [2., 1.]])
        result = self.space.is_symmetric(sym_mat)
        expected = gs.array(True)
        self.assertAllClose(result, expected)

        not_a_sym_mat = gs.array([[1., 0.6, -3.], [6., -7., 0.], [0., 7., 8.]])
        result = self.space.is_symmetric(not_a_sym_mat)
        expected = gs.array(False)
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_is_symmetric_vectorization(self):
        points = gs.array([[[1., 2.], [2., 1.]], [[3., 4.], [4., 5.]]])
        result = gs.all(self.space.is_symmetric(points))
        expected = True
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_pytorch_only
    def test_make_symmetric(self):
        sym_mat = gs.array([[1., 2.], [2., 1.]])
        result = self.space.make_symmetric(sym_mat)
        expected = sym_mat
        self.assertAllClose(result, expected)

        mat = gs.array([[1., 2., 3.], [0., 0., 0.], [3., 1., 1.]])
        result = self.space.make_symmetric(mat)
        expected = gs.array([[1., 1., 3.], [1., 0., 0.5], [3., 0.5, 1.]])
        self.assertAllClose(result, expected)

        mat = gs.array([[1e100, 1e-100, 1e100], [1e100, 1e-100, 1e100],
                        [1e-100, 1e-100, 1e100]])
        result = self.space.make_symmetric(mat)

        res = 0.5 * (1e100 + 1e-100)

        expected = gs.array([[1e100, res, res], [res, 1e-100, res],
                             [res, res, 1e100]])
        self.assertAllClose(result, expected)

    @geomstats.tests.np_and_tf_only
    def test_make_symmetric_and_is_symmetric_vectorization(self):
        points = gs.array([[[1., 2.], [3., 4.]], [[5., 6.], [4., 9.]]])

        sym_points = self.space.make_symmetric(points)
        result = gs.all(self.space.is_symmetric(sym_points))
        expected = True
        self.assertAllClose(result, expected)

    def test_inner_product(self):
        base_point = gs.array([[1., 2., 3.], [0., 0., 0.], [3., 1., 1.]])

        tangent_vector_1 = gs.array([[1., 2., 3.], [0., -10., 0.],
                                     [30., 1., 1.]])

        tangent_vector_2 = gs.array([[1., 4., 3.], [5., 0., 0.], [3., 1., 1.]])

        result = self.metric.inner_product(tangent_vector_1,
                                           tangent_vector_2,
                                           base_point=base_point)

        expected = gs.trace(
            gs.matmul(gs.transpose(tangent_vector_1), tangent_vector_2))
        expected = helper.to_scalar(expected)

        self.assertAllClose(result, expected)
Ejemplo n.º 26
0
    def _fit(self, X, base_point=None):
        """Fit the model by computing full SVD on X.

        Parameters
        ----------
        X : array-like, shape=[..., n_features]
            Training data, where n_samples is the number of samples
            and n_features is the number of features.
        y : Ignored (Compliance with scikit-learn interface)
        base_point : array-like, shape=[..., n_features]
            Point at which to perform the tangent PCA.
            Optional, default to Frechet mean if None.

        Returns
        -------
        U, S, V : array-like
            Matrices of the SVD decomposition
        """
        if base_point is None:
            mean = FrechetMean(metric=self.metric, point_type=self.point_type)
            mean.fit(X)
            base_point = mean.estimate_

        tangent_vecs = self.metric.log(X, base_point=base_point)

        if self.point_type == 'matrix':
            if Matrices.is_symmetric(tangent_vecs).all():
                X = SymmetricMatrices.to_vector(tangent_vecs)
            else:
                X = gs.reshape(tangent_vecs, (len(X), -1))
        else:
            X = tangent_vecs

        if self.n_components is None:
            n_components = min(X.shape)
        else:
            n_components = self.n_components
        n_samples, n_features = X.shape

        if n_components == 'mle':
            if n_samples < n_features:
                raise ValueError("n_components='mle' is only supported "
                                 "if n_samples >= n_features")
        elif not 0 <= n_components <= min(n_samples, n_features):
            raise ValueError("n_components=%r must be between 0 and "
                             "min(n_samples, n_features)=%r with "
                             "svd_solver='full'" %
                             (n_components, min(n_samples, n_features)))
        elif n_components >= 1:
            if not isinstance(n_components, numbers.Integral):
                raise ValueError("n_components=%r must be of type int "
                                 "when greater than or equal to 1, "
                                 "was of type=%r" %
                                 (n_components, type(n_components)))

        # Center data - the mean should be 0 if base_point is the Frechet mean
        self.mean_ = gs.mean(X, axis=0)
        X -= self.mean_

        U, S, V = gs.linalg.svd(X, full_matrices=False)
        # flip eigenvectors' sign to enforce deterministic output
        U, V = svd_flip(U, V)

        components_ = V

        # Get variance explained by singular values
        explained_variance_ = (S**2) / (n_samples - 1)
        total_var = explained_variance_.sum()
        explained_variance_ratio_ = explained_variance_ / total_var
        singular_values_ = gs.copy(S)  # Store the singular values.

        # Postprocess the number of components required
        if n_components == 'mle':
            n_components = \
                _infer_dimension_(explained_variance_, n_samples, n_features)
        elif 0 < n_components < 1.0:
            # number of components for which the cumulated explained
            # variance percentage is superior to the desired threshold
            ratio_cumsum = stable_cumsum(explained_variance_ratio_)
            n_components = gs.searchsorted(ratio_cumsum, n_components) + 1

        # Compute noise covariance using Probabilistic PCA model
        # The sigma2 maximum likelihood (cf. eq. 12.46)
        if n_components < min(n_features, n_samples):
            self.noise_variance_ = explained_variance_[n_components:].mean()
        else:
            self.noise_variance_ = 0.

        self.base_point_fit = base_point
        self.n_samples_, self.n_features_ = n_samples, n_features
        self.components_ = components_[:n_components]
        self.n_components_ = int(n_components)
        self.explained_variance_ = explained_variance_[:n_components]
        self.explained_variance_ratio_ = \
            explained_variance_ratio_[:n_components]
        self.singular_values_ = singular_values_[:n_components]

        return U, S, V