Beispiel #1
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
Beispiel #2
0
    def inner_product_at_identity(self, tangent_vec_a, tangent_vec_b):
        """
        Inner product matrix at the tangent space at the identity.
        """
        assert self.group.point_representation in ('vector', 'matrix')

        if self.group.point_representation == 'vector':
            tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=2)
            tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=2)

            inner_prod = gs.einsum('ij,ijk,ik->i', tangent_vec_a,
                                   self.inner_product_mat_at_identity,
                                   tangent_vec_b)

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

        elif self.group.point_representation == 'matrix':
            logging.warning(
                'Only the canonical inner product -Frobenius inner product-'
                ' is implemented for Lie groups whose elements are represented'
                ' by matrices.')
            tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=3)
            tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=3)
            aux_prod = gs.matmul(gs.transpose(tangent_vec_a, axes=(0, 2, 1)),
                                 tangent_vec_b)
            inner_prod = gs.trace(aux_prod)

        return inner_prod
Beispiel #3
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
 def convert_to_planar_coordinates(self, points):
     """Convert polar coordinates to spherical one."""
     coords_r, coords_theta = self.convert_to_polar_coordinates(points)
     coords_x = coords_r * gs.cos(coords_theta)
     coords_y = coords_r * gs.sin(coords_theta)
     planar_coords = gs.transpose(gs.stack((coords_x, coords_y)))
     return planar_coords
Beispiel #5
0
    def exp(self, tangent_vec, base_point=None):
        """
        Riemannian exponential of a tangent vector wrt to a 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)

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

        jacobian = self.group.jacobian_translation(
            point=base_point, left_or_right=self.left_or_right)
        assert jacobian.ndim == 3
        inv_jacobian = gs.linalg.inv(jacobian)
        inv_jacobian_transposed = gs.transpose(inv_jacobian, axes=(0, 2, 1))

        tangent_vec_at_id = gs.einsum('ij,ijk->ik', 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
 def process_function(return_dict, ip=ip, ep=ep):
     solution = solve_bvp(
         bvp, bc, t_int, initialize(ip, ep), fun_jac=fun_jac)
     solution_at_t = solution.sol(t)
     geodesic = solution_at_t[:self.dim, :]
     geod.append(gs.squeeze(gs.transpose(geodesic)))
     return_dict[0] = geod
Beispiel #7
0
    def metric_matrix(self, base_point=None):
        """Compute the inner-product matrix.

        Compute the inner-product matrix of the Fisher information metric
        at the tangent space at base point.

        Parameters
        ----------
        base_point : array-like, shape=[..., 2]
            Base point.

        Returns
        -------
        mat : array-like, shape=[..., 2, 2]
            Inner-product matrix.
        """
        if base_point is None:
            raise ValueError("A base point must be given to compute the "
                             "metric matrix")
        base_point = gs.to_ndarray(base_point, to_ndim=2)

        kappa, gamma = base_point[:, 0], base_point[:, 1]

        mat_diag = gs.transpose(
            gs.array([gs.polygamma(1, kappa) - 1 / kappa, kappa / gamma**2]))
        mat = from_vector_to_diagonal_matrix(mat_diag)
        return gs.squeeze(mat)
    def test_squared_dist_vectorization(self):
        n_samples = self.n_samples

        one_point_a = gs.array([0., 1.])
        one_point_b = gs.array([2., 10.])
        n_points_a = gs.array([
            [2., 1.],
            [-2., -4.],
            [-5., 1.]])
        n_points_b = gs.array([
            [2., 10.],
            [8., -1.],
            [-3., 6.]])

        result = self.metric.squared_dist(one_point_a, one_point_b)
        vec = one_point_a - one_point_b
        expected = gs.dot(vec, gs.transpose(vec))
        self.assertAllClose(result, expected)

        result = self.metric.squared_dist(n_points_a, one_point_b)
        self.assertAllClose(gs.shape(result), (n_samples,))

        result = self.metric.squared_dist(one_point_a, n_points_b)
        self.assertAllClose(gs.shape(result), (n_samples,))

        result = self.metric.squared_dist(n_points_a, n_points_b)
        expected = gs.array([81., 109., 29.])
        self.assertAllClose(gs.shape(result), (n_samples,))
        self.assertAllClose(result, expected)
Beispiel #9
0
    def belongs(self, point, tolerance=TOLERANCE):
        """
        Check if an (n,n)-matrix is an orthogonal projector
        onto a subspace of rank k.
        """
        point = gs.to_ndarray(point, to_ndim=3)
        n_points, n, k = point.shape

        if (n, k) != (self.n, self.k):
            return gs.array([[False]] * n_points)

        point_transpose = gs.transpose(point, axes=(0, 2, 1))
        identity = gs.to_ndarray(gs.eye(k), to_ndim=3)
        identity = gs.tile(identity, (n_points, 1, 1))
        diff = gs.einsum('nij,njk->nik', point_transpose, point) - identity

        diff_norm = gs.linalg.norm(diff, axis=(1, 2))
        belongs = gs.less_equal(diff_norm, tolerance)

        belongs = gs.to_ndarray(belongs, to_ndim=1)
        belongs = gs.to_ndarray(belongs, to_ndim=2, axis=1)
        return belongs

        raise NotImplementedError(
            'The Grassmann `belongs` is not implemented.'
            'It shall test whether p*=p, p^2 = p and rank(p) = k.')
    def inverse(self, point):
        """
        Compute the group inverse in SE(3).

        Formula:
        (R, t)^{-1} = (R^{-1}, R^{-1}.(-t))

        :param point: 6d vector element in SE(3)
        :returns inverse_point: 6d vector inverse of point
        """
        rotations = self.rotations
        dim_rotations = rotations.dimension

        point = self.regularize(point)
        n_points, _ = point.shape

        rot_vec = point[:, :dim_rotations]
        translation = point[:, dim_rotations:]

        inverse_point = gs.zeros_like(point)
        inverse_rotation = -rot_vec

        inv_rot_mat = rotations.matrix_from_rotation_vector(inverse_rotation)

        inverse_translation = gs.zeros((n_points, self.n))
        for i in range(n_points):
            inverse_translation[i] = gs.dot(-translation[i],
                                            gs.transpose(inv_rot_mat[i]))

        inverse_point[:, :dim_rotations] = inverse_rotation
        inverse_point[:, dim_rotations:] = inverse_translation

        inverse_point = self.regularize(inverse_point)
        return inverse_point
Beispiel #11
0
    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)
Beispiel #12
0
    def left_exp_from_identity(self, tangent_vec):
        """
        Compute the *left* Riemannian exponential from the identity of the
        Lie group of tangent vector tangent_vec.

        The left Riemannian exponential has a special role since the
        left Riemannian exponential of the canonical metric parameterizes
        the points.

        Note: In the case where the method is called by a right-invariant
        metric, it used the left-invariant metric associated to the same
        inner-product at the identity.
        """
        import geomstats.spd_matrices_space as spd_matrices_space
        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 = spd_matrices_space.sqrtm(
            self.inner_product_mat_at_identity)
        mat = gs.transpose(sqrt_inner_product_mat, axes=(0, 2, 1))
        exp = gs.matmul(tangent_vec, mat)
        exp = gs.squeeze(exp, axis=0)

        exp = self.group.regularize(exp)
        return exp
Beispiel #13
0
    def random_tangent_vec_uniform(self, n_samples=1, base_point=None):
        """Define a uniform random sample of tangent vectors."""
        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
Beispiel #14
0
    def random_uniform(self, n_samples=1):
        """Define a log-uniform random sample of SPD matrices."""
        mat = 2 * gs.random.rand(n_samples, self.n, self.n) - 1

        spd_mat = self.embedding_manifold.exp(
            mat + gs.transpose(mat, axes=(0, 2, 1)))
        return spd_mat
    def test_aux_differential_square_root_velocity(self):
        """Test differential of square root velocity transform.

        Check that its value at (curve, tangent_vec) coincides
        with the derivative at zero of the square root velocity
        transform of a path of curves starting at curve with
        initial derivative tangent_vec.
        """
        dim = 3
        n_sampling_points = 2000
        sampling_times = gs.linspace(0.0, 1.0, n_sampling_points)
        curve_a = self.curve_fun_a(sampling_times)
        tangent_vec = gs.transpose(
            gs.tile(gs.linspace(1.0, 2.0, n_sampling_points), (dim, 1))
        )
        result = self.srv_metric_r3.aux_differential_square_root_velocity(
            tangent_vec, curve_a
        )

        n_curves = 2000
        times = gs.linspace(0.0, 1.0, n_curves)
        path_of_curves = curve_a + gs.einsum("i,jk->ijk", times, tangent_vec)
        srv_path = self.srv_metric_r3.square_root_velocity(path_of_curves)
        expected = n_curves * (srv_path[1] - srv_path[0])
        self.assertAllClose(result, expected, atol=1e-3, rtol=1e-3)
 def make_symmetric(matrix):
     """Make a matrix fully symmetric to avoid numerical issues."""
     matrix = gs.to_ndarray(matrix, to_ndim=3)
     n_mats, m, n = matrix.shape
     assert m == n
     matrix = gs.to_ndarray(matrix, to_ndim=3)
     return (matrix + gs.transpose(matrix, axes=(0, 2, 1))) / 2
    def test_horizontal_geodesic(self):
        """Test horizontal geodesic.

        Check that the time derivative of the geodesic is
        horizontal at all time.
        """
        curve_b = gs.transpose(
            gs.stack(
                (
                    gs.zeros(self.n_sampling_points),
                    gs.zeros(self.n_sampling_points),
                    gs.linspace(1.0, 0.5, self.n_sampling_points),
                )
            )
        )
        horizontal_geod_fun = self.quotient_srv_metric_r3.horizontal_geodesic(
            self.curve_a, curve_b
        )
        n_times = 20
        times = gs.linspace(0.0, 1.0, n_times)
        horizontal_geod = horizontal_geod_fun(times)
        velocity_vec = n_times * (horizontal_geod[1:] - horizontal_geod[:-1])
        _, _, vertical_norms = self.quotient_srv_metric_r3.split_horizontal_vertical(
            velocity_vec, horizontal_geod[:-1]
        )
        result = gs.sum(vertical_norms ** 2, axis=1) ** (1 / 2)
        expected = gs.zeros(n_times - 1)
        self.assertAllClose(result, expected, atol=1e-3)
Beispiel #18
0
    def test_dist_vectorization(self):
        n_samples = self.n_samples

        one_point_a = gs.array([0., 1.])
        one_point_b = gs.array([2., 10.])
        n_points_a = gs.array([[2., 1.], [-2., -4.], [-5., 1.]])
        n_points_b = gs.array([[2., 10.], [8., -1.], [-3., 6.]])

        result = self.metric.dist(one_point_a, one_point_b)
        vec = one_point_a - one_point_b
        expected = gs.sqrt(gs.dot(vec, gs.transpose(vec)))
        expected = helper.to_scalar(expected)
        self.assertAllClose(result, expected)

        result = self.metric.dist(n_points_a, one_point_b)
        self.assertAllClose(gs.shape(result), (n_samples, 1))

        result = self.metric.dist(one_point_a, n_points_b)
        self.assertAllClose(gs.shape(result), (n_samples, 1))

        result = self.metric.dist(n_points_a, n_points_b)
        expected = gs.array([[9.], [gs.sqrt(109.)], [gs.sqrt(29.)]])

        self.assertAllClose(gs.shape(result), (n_samples, 1))
        self.assertAllClose(result, expected)
Beispiel #19
0
    def tangent_natural_to_standard(self, vec, base_point):
        """Convert tangent vector from natural coordinates to standard coordinates.

        The change of variable is symmetric.

        Parameters
        ----------
        base_point : array-like, shape=[..., 2]
            Point of the Gamma manifold, given in natural coordinates.
        vec : array-like, shape=[..., 2]
            Tangent vector at base_point, given in natural coordinates.

        Returns
        -------
        vec : array-like, shape=[..., 2]
            Tangent vector at base_point, given in standard coordinates.
        """
        vec = gs.to_ndarray(vec, to_ndim=2)
        base_point = gs.broadcast_to(base_point, vec.shape)

        n_points = base_point.shape[0]

        kappa, scale = (
            base_point[..., 0],
            base_point[..., 1],
        )

        jac = gs.array([[gs.ones(n_points),
                         gs.zeros(n_points)], [1 / scale, -kappa / scale**2]])
        jac = gs.transpose(jac, [2, 0, 1])

        vec = gs.einsum("...jk,...k->...j", jac, vec)

        return gs.squeeze(vec)
Beispiel #20
0
    def random_uniform(self, n_samples=1, tol=1e-6):
        """Sample in SE(n) from the uniform distribution.

        Parameters
        ----------
        n_samples : int
            Number of samples.
            Optional, default: 1.
        tol : unused

        Returns
        -------
        samples : array-like, shape=[..., n + 1, n + 1]
            Sample in SE(n).
        """
        random_translation = self.translations.random_uniform(n_samples)
        random_rotation = self.rotations.random_uniform(n_samples)
        random_rotation = gs.to_ndarray(random_rotation, to_ndim=3)

        random_translation = gs.to_ndarray(random_translation, to_ndim=2)
        random_translation = gs.transpose(
            gs.to_ndarray(random_translation, to_ndim=3, axis=1), (0, 2, 1))

        random_point = gs.concatenate((random_rotation, random_translation),
                                      axis=2)
        last_line = gs.zeros((n_samples, 1, self.n + 1))
        random_point = gs.concatenate((random_point, last_line), axis=1)
        random_point = gs.assignment(random_point, 1, (-1, -1), axis=0)
        if gs.shape(random_point)[0] == 1:
            random_point = gs.squeeze(random_point, axis=0)
        return random_point
Beispiel #21
0
    def basis_representation(self, matrix_representation):
        """Calculate the coefficients of given matrix in the basis.

        Compute a 1d-array that corresponds to the input matrix in the basis
        representation.

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

        Returns
        -------
        basis_representation : array-like, shape=[..., dim]
            Representation in the basis.
        """
        if self.n == 2:
            return matrix_representation[..., 1, 0][..., None]
        if self.n == 3:
            vec = gs.stack([
                matrix_representation[..., 2, 1],
                matrix_representation[..., 0, 2],
                matrix_representation[..., 1, 0]])
            return gs.transpose(vec)

        return gs.triu_to_vec(matrix_representation, k=1)
Beispiel #22
0
    def square_root_velocity_inverse(self, srv, starting_point):
        """
        Retreive a curve from its square root velocity representation
        and starting point.
        """
        if not isinstance(self.embedding_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(np.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.embedding_metric.norm(srv)
        delta_points = 1 / n_sampling_points_minus_one * srv_norm * srv
        delta_points = gs.reshape(delta_points, srv_shape)
        curve = np.concatenate((starting_point, delta_points), -2)
        curve = np.cumsum(curve, -2)

        return curve
Beispiel #23
0
    def inner_product_matrix(self, base_point=None):
        """
        Inner product matrix at the tangent space at a base point.
        """
        if self.group.point_representation == 'matrix':
            raise NotImplementedError(
                'inner_product_matrix not implemented for Lie groups'
                ' whose elements are represented as matrices.')

        if base_point is None:
            base_point = self.group.identity
        base_point = self.group.regularize(base_point)

        jacobian = self.group.jacobian_translation(
            point=base_point, left_or_right=self.left_or_right)
        assert jacobian.ndim == 3
        inv_jacobian = gs.linalg.inv(jacobian)
        inv_jacobian_transposed = gs.transpose(inv_jacobian, axes=(0, 2, 1))

        inner_product_mat_at_id = self.inner_product_mat_at_identity
        inner_product_mat_at_id = gs.to_ndarray(inner_product_mat_at_id,
                                                to_ndim=3)

        metric_mat = gs.matmul(inv_jacobian_transposed,
                               inner_product_mat_at_id)
        metric_mat = gs.matmul(metric_mat, inv_jacobian)
        return metric_mat
Beispiel #24
0
    def projection(self, mat):
        """
        Project a matrix on SO(n), using the Frobenius norm.
        """
        # TODO(nina): projection when the point_type is not 'matrix'?
        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 == self.n

        if self.n == 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.nonzero(gs.linalg.det(rot_mat) < 0)
            diag = gs.array([1, 1, -1])
            new_mat_diag_s = gs.tile(gs.diag(diag), 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 gs.ndim(rot_mat) == 3
        return rot_mat
Beispiel #25
0
    def log(self, point, base_point=None):
        """
        Riemannian logarithm of a point wrt a 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)

        log = gs.einsum('ij,ijk->ik', log_from_id,
                        gs.transpose(jacobian, axes=(0, 2, 1)))
        assert log.ndim == 2
        return log
    def closest_neighbor_index(self, point, neighbors):
        """Closest neighbor of point among neighbors.

        Parameters
        ----------
        point : array-like, shape=[..., dim]
            Point.
        neighbors : array-like, shape=[n_neighbors, dim]
            Neighbors.

        Returns
        -------
        closest_neighbor_index : int
            Index of closest neighbor.
        """
        n_points = point.shape[0] if gs.ndim(point) == gs.ndim(
            neighbors) else 1
        n_neighbors = neighbors.shape[0]

        if n_points > 1 and n_neighbors > 1:
            neighbors = gs.repeat(neighbors, n_points, axis=0)

            point = gs.concatenate([point for _ in range(n_neighbors)])

        closest_neighbor_index = gs.argmin(
            gs.transpose(
                gs.reshape(self.dist(point, neighbors),
                           (n_neighbors, n_points)), ),
            axis=1,
        )

        if n_points == 1:
            return closest_neighbor_index[0]

        return closest_neighbor_index
def grad(y_pred, y_true, metric):
    """Closed-form for the gradient of the loss function.

    Parameters
    ----------
    y_pred : array-like, shape=[..., dim]
        Prediction.
    y_true : array-like, shape=[..., dim]
        Ground-truth.
    metric : RiemannianMetric
        Metric.

    Returns
    -------
    loss_grad : array-like, shape=[...,]
        Gradient of the loss.

    """
    tangent_vec = metric.log(base_point=y_pred, point=y_true)
    grad_vec = - 2. * tangent_vec

    inner_prod_mat = metric.metric_matrix(base_point=y_pred)
    is_vectorized = inner_prod_mat.ndim == 3
    axes = (0, 2, 1) if is_vectorized else (1, 0)

    loss_grad = gs.einsum(
        '...i,...ij->...i',
        grad_vec,
        gs.transpose(inner_prod_mat, axes=axes))

    return loss_grad
    def test_inner_product_vectorization(self):
        n_samples = 3
        one_point_a = gs.array([[-1., 0.]])
        one_point_b = gs.array([[1.0, 0.]])

        n_points_a = gs.array([[-1., 0.], [1., 0.], [2., math.sqrt(3)]])
        n_points_b = gs.array([[2., -math.sqrt(3)], [4.0, math.sqrt(15)],
                               [-4.0, math.sqrt(15)]])

        result = self.metric.inner_product(one_point_a, one_point_b)
        expected = gs.dot(one_point_a, gs.transpose(one_point_b))
        expected -= (2 * one_point_a[:, self.time_like_dim] *
                     one_point_b[:, self.time_like_dim])
        expected = helper.to_scalar(expected)

        result_no = self.metric.inner_product(n_points_a, one_point_b)
        result_on = self.metric.inner_product(one_point_a, n_points_b)

        result_nn = self.metric.inner_product(n_points_a, n_points_b)

        self.assertAllClose(result, expected)
        self.assertAllClose(gs.shape(result_no), (n_samples, 1))
        self.assertAllClose(gs.shape(result_on), (n_samples, 1))
        self.assertAllClose(gs.shape(result_nn), (n_samples, 1))

        with self.session():
            expected = np.zeros(n_samples)
            for i in range(n_samples):
                expected[i] = gs.eval(gs.dot(n_points_a[i], n_points_b[i]))
                expected[i] -= (2 *
                                gs.eval(n_points_a[i, self.time_like_dim]) *
                                gs.eval(n_points_b[i, self.time_like_dim]))
            expected = helper.to_scalar(gs.array(expected))

            self.assertAllClose(result_nn, expected)
    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 = gs.einsum(
            '...,...i->...i', 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
Beispiel #30
0
    def group_exp(self, tangent_vec, base_point=None):
        """
        Compute the group exponential at point base_point
        of tangent vector tangent_vec.
        """
        if base_point is None:
            base_point = self.identity
        base_point = self.regularize(base_point)
        if base_point is self.identity:
            return self.group_exp_from_identity(tangent_vec)

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

        jacobian = self.jacobian_translation(point=base_point,
                                             left_or_right='left')
        inv_jacobian = gs.linalg.inv(jacobian)

        tangent_vec_at_id = gs.einsum(
            'ij,ijk->ik', tangent_vec,
            gs.transpose(inv_jacobian, axes=(0, 2, 1)))

        group_exp_from_identity = self.group_exp_from_identity(
            tangent_vec=tangent_vec_at_id)
        group_exp = self.compose(base_point, group_exp_from_identity)
        group_exp = self.regularize(group_exp)
        return group_exp