Ejemplo n.º 1
0
 def test_horizontal_lift_and_tangent_riemannian_submersion(
         self, n, mat, vec):
     bundle = self.bundle(n)
     tangent_vec = Matrices.to_symmetric(vec)
     horizontal = bundle.horizontal_lift(tangent_vec, fiber_point=mat)
     result = bundle.tangent_riemannian_submersion(horizontal, mat)
     self.assertAllClose(result, tangent_vec, atol=1e-2)
Ejemplo n.º 2
0
 def setUp(self):
     self.dimension = 4
     self.dt = 0.1
     self.euclidean = Euclidean(self.dimension)
     self.matrices = Matrices(self.dimension, self.dimension)
     self.intercept = self.euclidean.random_point()
     self.slope = Matrices.to_symmetric(self.matrices.random_point())
Ejemplo n.º 3
0
    def test_integrability_tensor(self, n, mat, vec):
        bundle = self.bundle(n)
        point = bundle.riemannian_submersion(mat)
        tangent_vec = Matrices.to_symmetric(vec) / 20

        with pytest.raises(NotImplementedError):
            bundle.integrability_tensor(tangent_vec, tangent_vec, point)
Ejemplo n.º 4
0
    def test_integrability_tensor(self):
        mat = self.bundle.random_point()
        point = self.bundle.riemannian_submersion(mat)
        tangent_vec = Matrices.to_symmetric(self.bundle.random_point()) / 5

        with pytest.raises(NotImplementedError):
            self.bundle.integrability_tensor(tangent_vec, tangent_vec, point)
Ejemplo n.º 5
0
    def from_vector(vec, dtype=gs.float32):
        """Convert a vector into a symmetric matrix.

        Parameters
        ----------
        vec : array-like, shape=[..., n(n+1)/2]
            Vector.

        Returns
        -------
        mat : array-like, shape=[..., n, n]
            Symmetric matrix.
        """
        vec_dim = vec.shape[-1]
        mat_dim = (gs.sqrt(8. * vec_dim + 1) - 1) / 2
        if mat_dim != int(mat_dim):
            raise ValueError('Invalid input dimension, it must be of the form'
                             '(n_samples, n * (n + 1) / 2)')
        mat_dim = int(mat_dim)
        shape = (mat_dim, mat_dim)
        mask = 2 * gs.ones(shape) - gs.eye(mat_dim)
        indices = list(zip(*gs.triu_indices(mat_dim)))
        vec = gs.cast(vec, dtype)
        upper_triangular = gs.stack(
            [gs.array_from_sparse(indices, data, shape) for data in vec])
        mat = Matrices.to_symmetric(upper_triangular) * mask
        return mat
Ejemplo n.º 6
0
    def test_exp(self):
        mat = self.bundle.random_point()
        point = self.bundle.riemannian_submersion(mat)
        tangent_vec = Matrices.to_symmetric(self.bundle.random_point()) / 5

        result = self.quotient_metric.exp(tangent_vec, point)
        expected = self.base_metric.exp(tangent_vec, point)
        self.assertAllClose(result, expected)
Ejemplo n.º 7
0
 def test_inner_product(self):
     mat = self.bundle.random_point()
     point = self.bundle.riemannian_submersion(mat)
     tangent_vecs = Matrices.to_symmetric(self.bundle.random_point(2)) / 10
     result = self.quotient_metric.inner_product(tangent_vecs[0],
                                                 tangent_vecs[1],
                                                 fiber_point=mat)
     expected = self.base_metric.inner_product(tangent_vecs[0],
                                               tangent_vecs[1], point)
     self.assertAllClose(result, expected)
Ejemplo n.º 8
0
    def test_exp(self, n, mat, vec):
        bundle = self.bundle(n)
        quotient_metric = self.metric(bundle)
        base_metric = self.base_metric(n)
        point = bundle.riemannian_submersion(mat)
        tangent_vec = Matrices.to_symmetric(vec) / 40

        result = quotient_metric.exp(tangent_vec, point)
        expected = base_metric.exp(tangent_vec, point)
        self.assertAllClose(result, expected, atol=1e-1)
Ejemplo n.º 9
0
 def test_inner_product(self, n, mat, vec_a, vec_b):
     bundle = self.bundle(n)
     quotient_metric = self.metric(bundle)
     base_metric = self.base_metric(n)
     point = bundle.riemannian_submersion(mat)
     tangent_vecs = Matrices.to_symmetric(gs.array([vec_a, vec_b])) / 40
     result = quotient_metric.inner_product(
         tangent_vecs[0], tangent_vecs[1], fiber_point=mat
     )
     expected = base_metric.inner_product(tangent_vecs[0], tangent_vecs[1], point)
     self.assertAllClose(result, expected, atol=1e-1)
Ejemplo n.º 10
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.º 11
0
    def projection(self, point):
        """Make a matrix symmetric, by averaging with its transpose.

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

        Returns
        -------
        sym : array-like, shape=[..., n, n]
            Symmetric matrix.
        """
        return Matrices.to_symmetric(point)
Ejemplo n.º 12
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.º 13
0
    def random_point(self, n_samples=1, bound=1.):
        """Sample from a uniform distribution.

        Parameters
        ----------
        n_samples : int
            Number of samples.
            Optional, default: 1.
        bound : float
            Bound of the interval in which to sample each entry.
            Optional, default: 1.

        Returns
        -------
        point : array-like, shape=[m, n] or [n_samples, m, n]
            Sample.
        """
        return Matrices.to_symmetric(Matrices.random_point(n_samples, bound))
Ejemplo n.º 14
0
    def random_point(self, n_samples=1, bound=1.0):
        """Sample a symmetric matrix with a uniform distribution in a box.

        Parameters
        ----------
        n_samples : int
            Number of samples.
            Optional, default: 1.
        bound : float
            Side of hypercube support of the uniform distribution.
            Optional, default: 1.0

        Returns
        -------
        point : array-like, shape=[..., n, n]
           Sample.
        """
        sample = super(SymmetricMatrices, self).random_point(n_samples, bound)
        return Matrices.to_symmetric(sample)
Ejemplo n.º 15
0
    def _aux_log(point, sqrt_base_point, inv_sqrt_base_point):
        """Compute the log (auxiliary function).

        Parameters
        ----------
        point : array-like, shape=[..., n, n]
        sqrt_base_point : array-like, shape=[..., n, n]
        inv_sqrt_base_point : array-like, shape=[.., n, n]

        Returns
        -------
        log : array-like, shape=[..., n, n]
        """
        point_near_id = Matrices.mul(inv_sqrt_base_point, point, inv_sqrt_base_point)
        point_near_id = Matrices.to_symmetric(point_near_id)

        log_at_id = SPDMatrices.logm(point_near_id)
        log = Matrices.mul(sqrt_base_point, log_at_id, sqrt_base_point)
        return log
Ejemplo n.º 16
0
    def to_tangent(self, vector, base_point):
        """Project a vector to a tangent space of the manifold.

        Compute the bracket (commutator) of the base_point with
        the skew-symmetric part of vector.

        Parameters
        ----------
        vector : array-like, shape=[..., n, n]
            Vector.
        base_point : array-like, shape=[..., n, n]
            Point on the manifold.

        Returns
        -------
        tangent_vec : array-like, shape=[..., n, n]
            Tangent vector at base point.
        """
        sym = Matrices.to_symmetric(vector)
        return Matrices.bracket(base_point, Matrices.bracket(base_point, sym))
Ejemplo n.º 17
0
    def __init__(self, n, k):
        geomstats.errors.check_integer(k, "k")
        geomstats.errors.check_integer(n, "n")
        if k > n:
            raise ValueError(
                "k < n is required: k-dimensional subspaces in n dimensions.")

        self.n = n
        self.k = k

        dim = int(k * (n - k))
        super(Grassmannian, self).__init__(
            dim=dim,
            embedding_space=SymmetricMatrices(n),
            submersion=lambda x: submersion(x, k),
            value=gs.zeros((n, n)),
            tangent_submersion=lambda v, x: 2 * Matrices.to_symmetric(
                Matrices.mul(x, v)) - v,
            metric=GrassmannianCanonicalMetric(n, k),
        )
Ejemplo n.º 18
0
    def to_tangent(self, vector, base_point):
        """Project a vector to a tangent space of the manifold.

        Inspired by the method of Pymanopt.

        Parameters
        ----------
        vector : array-like, shape=[..., n, p]
            Vector.
        base_point : array-like, shape=[..., n, p]
            Point on the manifold.

        Returns
        -------
        tangent_vec : array-like, shape=[..., n, p]
            Tangent vector at base point.
        """
        aux = Matrices.mul(Matrices.transpose(base_point), vector)
        sym_aux = Matrices.to_symmetric(aux)
        return vector - Matrices.mul(base_point, sym_aux)
Ejemplo n.º 19
0
    def projection(self, point):
        """Project a matrix to the Grassmann manifold.

        An eigenvalue decomposition of (the symmetric part of) point is used.

        Parameters
        ----------
        point : array-like, shape=[..., n, n]
            Point in embedding manifold.

        Returns
        -------
        projected : array-like, shape=[..., n, n]
            Projected point.
        """
        mat = Matrices.to_symmetric(point)
        _, eigvecs = gs.linalg.eigh(mat)
        diagonal = gs.array([0.0] * (self.n - self.k) + [1.0] * self.k)
        p_d = gs.einsum("...ij,...j->...ij", eigvecs, diagonal)
        return Matrices.mul(p_d, Matrices.transpose(eigvecs))
Ejemplo n.º 20
0
    def __init__(self, n, p):
        geomstats.errors.check_integer(n, 'n')
        geomstats.errors.check_integer(p, 'p')
        if p > n:
            raise ValueError('p needs to be smaller than n.')

        dim = int(p * n - (p * (p + 1) / 2))
        matrices = Matrices(n, p)
        super(Stiefel, self).__init__(
            dim=dim,
            embedding_space=matrices,
            submersion=lambda x: matrices.mul(matrices.transpose(x), x),
            value=gs.eye(p),
            tangent_submersion=lambda v, x: 2 * matrices.to_symmetric(
                matrices.mul(matrices.transpose(x), v)),
            metric=StiefelCanonicalMetric(n, p))

        self.n = n
        self.p = p
        self.canonical_metric = self.metric
Ejemplo n.º 21
0
    def _aux_exp(tangent_vec, sqrt_base_point, inv_sqrt_base_point):
        """Compute the exponential map (auxiliary function).

        Parameters
        ----------
        tangent_vec : array-like, shape=[..., n, n]
        sqrt_base_point : array-like, shape=[..., n, n]
        inv_sqrt_base_point : array-like, shape=[..., n, n]

        Returns
        -------
        exp : array-like, shape=[..., n, n]
        """
        tangent_vec_at_id = Matrices.mul(inv_sqrt_base_point, tangent_vec,
                                         inv_sqrt_base_point)

        tangent_vec_at_id = Matrices.to_symmetric(tangent_vec_at_id)
        exp_from_id = SymmetricMatrices.expm(tangent_vec_at_id)

        exp = Matrices.mul(sqrt_base_point, exp_from_id, sqrt_base_point)
        return exp
Ejemplo n.º 22
0
    def __init__(self, n, p, **kwargs):
        geomstats.errors.check_integer(n, "n")
        geomstats.errors.check_integer(p, "p")
        if p > n:
            raise ValueError("p needs to be smaller than n.")

        dim = int(p * n - (p * (p + 1) / 2))
        matrices = Matrices(n, p)
        canonical_metric = StiefelCanonicalMetric(n, p)
        kwargs.setdefault("metric", canonical_metric)
        super(Stiefel, self).__init__(
            dim=dim,
            embedding_space=matrices,
            submersion=lambda x: matrices.mul(matrices.transpose(x), x),
            value=gs.eye(p),
            tangent_submersion=lambda v, x: 2 * matrices.to_symmetric(
                matrices.mul(matrices.transpose(x), v)),
            **kwargs)
        self.canonical_metric = canonical_metric
        self.n = n
        self.p = p
Ejemplo n.º 23
0
    def projection(self, point):
        """Project a matrix to the space of SPD matrices.

        First the symmetric part of point is computed, then the eigenvalues
        are floored to gs.atol.

        Parameters
        ----------
        point : array-like, shape=[..., n, n]
            Matrix to project.

        Returns
        -------
        projected: array-like, shape=[..., n, n]
            SPD matrix.
        """
        sym = Matrices.to_symmetric(point)
        eigvals, eigvecs = gs.linalg.eigh(sym)
        regularized = gs.where(eigvals < gs.atol, gs.atol, eigvals)
        reconstruction = gs.einsum("...ij,...j->...ij", eigvecs, regularized)
        return Matrices.mul(reconstruction, Matrices.transpose(eigvecs))
Ejemplo n.º 24
0
    def random_point(self, n_samples=1, bound=1.0):
        r"""Sample in PSD(n,k) from the log-uniform distribution.

        Parameters
        ----------
        n_samples : int
            Number of samples.
            Optional, default: 1.
        bound : float
            Bound of the interval in which to sample in the tangent space.
            Optional, default: 1.

        Returns
        -------
        samples : array-like, shape=[..., n, n]
            Points sampled in PSD(n,k).
        """
        n = self.n
        size = (n_samples, n, n) if n_samples != 1 else (n, n)
        mat = bound * (2 * gs.random.rand(*size) - 1)
        spd_mat = GeneralLinear.exp(Matrices.to_symmetric(mat))
        return self.projection(spd_mat)
Ejemplo n.º 25
0
    def projection(self, point):
        r"""Project a matrix to the space of PSD matrices of rank k.

        The nearest symmetric positive semidefinite matrix in the
        Frobenius norm to an arbitrary real matrix A is shown to be (B + H)/2,
        where H is the symmetric polar factor of B=(A + A')/2.
        As [Higham1988] is turning the matrix into a PSD, the rank
        is then forced to be k.

        Parameters
        ----------
        point : array-like, shape=[..., n, n]
            Matrix to project.

        Returns
        -------
        projected: array-like, shape=[..., n, n]
            PSD matrix rank k.

        References
        ----------
        [Higham1988]_    Highamm, N. J.
                        “Computing a nearest symmetric positive semidefinite matrix.”
                        Linear Algebra and Its Applications 103 (May 1, 1988):
                        103-118. https://doi.org/10.1016/0024-3795(88)90223-6
        """
        sym = Matrices.to_symmetric(point)
        _, s, v = gs.linalg.svd(sym)
        h = gs.matmul(Matrices.transpose(v), s[..., None] * v)
        sym_proj = (sym + h) / 2
        eigvals, eigvecs = gs.linalg.eigh(sym_proj)
        i = gs.array([0] * (self.n - self.rank) + [2 * gs.atol] * self.rank)
        regularized = (
            gs.assignment(eigvals, 0, gs.arange((self.n - self.rank)), axis=0) + i
        )
        reconstruction = gs.einsum("...ij,...j->...ij", eigvecs, regularized)

        return Matrices.mul(reconstruction, Matrices.transpose(eigvecs))
Ejemplo n.º 26
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.º 27
0
 def test_horizontal_lift_and_tangent_riemannian_submersion(self):
     mat = self.bundle.random_point()
     tangent_vec = Matrices.to_symmetric(self.bundle.random_point())
     horizontal = self.bundle.horizontal_lift(tangent_vec, fiber_point=mat)
     result = self.bundle.tangent_riemannian_submersion(horizontal, mat)
     self.assertAllClose(result, tangent_vec)
Ejemplo n.º 28
0
 def tangent_riemannian_submersion(self, tangent_vec, base_point):
     product = Matrices.mul(base_point, Matrices.transpose(tangent_vec))
     return 2 * Matrices.to_symmetric(product)
Ejemplo n.º 29
0
 def test_is_horizontal(self):
     mat = self.bundle.random_point()
     tangent_vec = Matrices.to_symmetric(self.bundle.random_point())
     horizontal = self.bundle.horizontal_lift(tangent_vec, fiber_point=mat)
     result = self.bundle.is_horizontal(horizontal, mat)
     self.assertTrue(result)
Ejemplo n.º 30
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)