def inner_product(self, tangent_vec_a, tangent_vec_b, base_point):
        """Compute the inner-product of two tangent vectors at a base point.

        Inner product defined by the Riemannian metric at point `base_point`
        between tangent vectors `tangent_vec_a` and `tangent_vec_b`.

        Parameters
        ----------
        tangent_vec_a : array-like, shape=[..., n_copies, *base_shape]
            First tangent vector at base point.
        tangent_vec_b : array-like, shape=[..., n_copies, *base_shape]
            Second tangent vector at base point.
        base_point : array-like, shape=[..., n_copies, *base_shape]
            Point on the manifold.
            Optional, default: None.

        Returns
        -------
        inner_prod : array-like, shape=[...,]
            Inner-product of the two tangent vectors.
        """
        tangent_vec_a_, tangent_vec_b_, point_ = gs.broadcast_arrays(
            tangent_vec_a, tangent_vec_b, base_point
        )
        point_ = gs.reshape(point_, (-1, *self.base_shape))
        vector_a = gs.reshape(tangent_vec_a_, (-1, *self.base_shape))
        vector_b = gs.reshape(tangent_vec_b_, (-1, *self.base_shape))
        inner_each = self.base_metric.inner_product(vector_a, vector_b, point_)
        reshaped = gs.reshape(inner_each, (-1, self.n_copies))
        return gs.squeeze(gs.sum(reshaped, axis=-1))
Exemple #2
0
    def to_tangent(self, vector, base_point):
        """Project a vector to a tangent space of the manifold.

        The tangent space of the product manifold is the direct sum of
        tangent spaces.

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

        Returns
        -------
        tangent_vec : array-like, shape=[..., n_copies, *base_shape]
            Tangent vector at base point.
        """
        vector_, point_ = gs.broadcast_arrays(vector, base_point)
        point_ = gs.reshape(point_, (-1, *self.base_shape))
        vector_ = gs.reshape(vector_, (-1, *self.base_shape))
        each_tangent = self.base_manifold.to_tangent(vector_, point_)
        reshaped = gs.reshape(each_tangent,
                              (-1, self.n_copies) + self.base_shape)
        return gs.squeeze(reshaped)
Exemple #3
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 #4
0
    def inner_product(self, tangent_vec_a, tangent_vec_b, base_curve):
        """
        Inner product between two tangent vectors at a base curve.
        """
        assert tangent_vec_a.shape == tangent_vec_b.shape
        assert tangent_vec_a.shape == base_curve.shape
        tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=3)
        tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=3)
        base_curve = gs.to_ndarray(base_curve, to_ndim=3)

        n_curves, n_sampling_points, n_coords = tangent_vec_a.shape

        new_dim = n_curves * n_sampling_points
        tangent_vec_a = gs.reshape(tangent_vec_a, (new_dim, n_coords))
        tangent_vec_b = gs.reshape(tangent_vec_b, (new_dim, n_coords))
        base_curve = gs.reshape(base_curve, (new_dim, n_coords))

        inner_prod = self.embedding_metric.inner_product(
            tangent_vec_a, tangent_vec_b, base_curve)
        inner_prod = gs.reshape(inner_prod, (n_curves, n_sampling_points))
        inner_prod = gs.sum(inner_prod, -1)

        n_sampling_points_float = gs.array(n_sampling_points)
        n_sampling_points_float = gs.cast(n_sampling_points_float, gs.float32)
        inner_prod = inner_prod / n_sampling_points_float
        inner_prod = gs.to_ndarray(inner_prod, to_ndim=1)
        inner_prod = gs.to_ndarray(inner_prod, to_ndim=2, axis=1)

        return inner_prod
Exemple #5
0
    def dist(self, landmarks_a, landmarks_b):
        """Compute geodesic distance between two landmark sets.

        Parameters
        ----------
        landmarks_a
        landmarks_b

        Returns
        -------
        dist
        """
        if landmarks_a.shape != landmarks_b.shape:
            raise NotImplementedError

        n_landmark_sets, n_landmarks_per_set, n_coords = landmarks_a.shape

        landmarks_a = gs.reshape(
            landmarks_a, (n_landmark_sets * n_landmarks_per_set, n_coords))
        landmarks_b = gs.reshape(
            landmarks_b, (n_landmark_sets * n_landmarks_per_set, n_coords))

        dist = self.ambient_metric.dist(landmarks_a, landmarks_b)
        dist = gs.reshape(dist, (n_landmark_sets, n_landmarks_per_set))
        n_landmarks_per_set_float = gs.array(n_landmarks_per_set)
        n_landmarks_per_set_float = gs.cast(n_landmarks_per_set_float,
                                            gs.float32)
        dist = gs.sqrt(gs.sum(dist**2, -1) / n_landmarks_per_set_float)
        dist = gs.to_ndarray(dist, to_ndim=1)
        dist = gs.to_ndarray(dist, to_ndim=2, axis=1)

        return dist
    def jacobian_diffeomorphism(self, base_point):
        r"""Jacobian of the diffeomorphism at base point.

        Let :math:`f` be the diffeomorphism
        :math:`f: M \rightarrow N` of the manifold
        :math:`M` into the manifold `N`.

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

        Returns
        -------
        mat : array-like, shape=[..., *i_shape, *shape]
            Inner-product matrix.
        """
        rad = base_point.shape[:-len(self.shape)]
        base_point = gs.reshape(base_point, (-1, ) + self.shape)

        J = self.raw_jacobian_diffeomorphism(base_point)
        J = gs.moveaxis(
            gs.diagonal(J, axis1=0,
                        axis2=len(self.embedding_metric.shape) + 1),
            -1,
            0,
        )
        J = gs.reshape(J, rad + self.embedding_metric.shape + self.shape)

        return J
    def dist(self, landmarks_a, landmarks_b):
        """
        Geodesic distance between two landmark sets.
        """
        assert landmarks_a.shape == landmarks_b.shape
        landmarks_a = gs.to_ndarray(landmarks_a, to_ndim=3)
        landmarks_b = gs.to_ndarray(landmarks_b, to_ndim=3)

        n_landmark_sets, n_landmarks_per_set, n_coords = landmarks_a.shape

        landmarks_a = gs.reshape(
            landmarks_a, (n_landmark_sets * n_landmarks_per_set, n_coords))
        landmarks_b = gs.reshape(
            landmarks_b, (n_landmark_sets * n_landmarks_per_set, n_coords))

        dist = self.ambient_metric.dist(landmarks_a, landmarks_b)
        dist = gs.reshape(dist, (n_landmark_sets, n_landmarks_per_set))
        n_landmarks_per_set_float = gs.array(n_landmarks_per_set)
        n_landmarks_per_set_float = gs.cast(
            n_landmarks_per_set_float, gs.float32)
        dist = gs.sqrt(gs.sum(dist ** 2, -1) / n_landmarks_per_set_float)
        dist = gs.to_ndarray(dist, to_ndim=1)
        dist = gs.to_ndarray(dist, to_ndim=2, axis=1)

        return dist
Exemple #8
0
    def square_root_velocity(self, curve):
        """Compute the square root velocity representation of a curve.

        The velocity is computed using the log map. The case of several curves
        is handled through vectorization. In that case, an index selection
        procedure allows to get rid of the log between the end point of
        curve[k, :, :] and the starting point of curve[k + 1, :, :].

        Parameters
        ----------
        curve :

        Returns
        -------
        srv :
        """
        curve = gs.to_ndarray(curve, to_ndim=3)
        n_curves, n_sampling_points, n_coords = curve.shape
        srv_shape = (n_curves, n_sampling_points - 1, n_coords)

        curve = gs.reshape(curve, (n_curves * n_sampling_points, n_coords))
        coef = gs.cast(gs.array(n_sampling_points - 1), gs.float32)
        velocity = coef * self.ambient_metric.log(point=curve[1:, :],
                                                  base_point=curve[:-1, :])
        velocity_norm = self.ambient_metric.norm(velocity, curve[:-1, :])
        srv = velocity / gs.sqrt(velocity_norm)

        index = gs.arange(n_curves * n_sampling_points - 1)
        mask = ~gs.equal((index + 1) % n_sampling_points, 0)
        index_select = gs.gather(index, gs.squeeze(gs.where(mask)))
        srv = gs.reshape(gs.gather(srv, index_select), srv_shape)

        return srv
Exemple #9
0
    def exp(self, tangent_vec, base_landmarks):
        """Compute Riemannian exponential of tan vector wrt base landmark set.

        Parameters
        ----------
        tangent_vec
        base_landmarks

        Returns
        -------
        exp
        """
        tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=3)
        base_landmarks = gs.to_ndarray(base_landmarks, to_ndim=3)

        n_landmark_sets, n_landmarks_per_set, n_coords = base_landmarks.shape
        n_tangent_vecs = tangent_vec.shape[0]

        new_dim = n_landmark_sets * n_landmarks_per_set
        new_base_landmarks = gs.reshape(base_landmarks, (new_dim, n_coords))
        new_tangent_vec = gs.reshape(tangent_vec, (new_dim, n_coords))

        exp = self.ambient_metric.exp(new_tangent_vec, new_base_landmarks)
        exp = gs.reshape(exp, (n_tangent_vecs, n_landmarks_per_set, n_coords))
        exp = gs.squeeze(exp)

        return exp
Exemple #10
0
    def log(self, point, base_point, **kwargs):
        """Compute the Riemannian logarithm of a point.

        Parameters
        ----------
        point : array-like, shape=[..., k_landmarks, m_ambient]
            Point on the pre-shape space.
        base_point : array-like, shape=[..., k_landmarks, m_ambient]
            Point on the pre-shape space.

        Returns
        -------
        log : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at the base point equal to the Riemannian logarithm
            of point at the base point.
        """
        flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1))
        flat_pt = gs.reshape(point, (-1, self.sphere_metric.dim + 1))
        flat_log = self.sphere_metric.log(flat_pt, flat_bp)
        try:
            log = gs.reshape(flat_log, base_point.shape)
        except (RuntimeError, check_tf_error(ValueError,
                                             "InvalidArgumentError")):
            log = gs.reshape(flat_log, point.shape)
        return log
Exemple #11
0
    def log(self, landmarks, base_landmarks):
        """Compute Riemannian log of a set of landmarks wrt base landmark set.

        Parameters
        ----------
        landmarks
        base_landmarks

        Returns
        -------
        log
        """
        assert landmarks.shape == base_landmarks.shape
        landmarks = gs.to_ndarray(landmarks, to_ndim=3)
        base_landmarks = gs.to_ndarray(base_landmarks, to_ndim=3)

        n_landmark_sets, n_landmarks_per_set, n_coords = landmarks.shape

        landmarks = gs.reshape(
            landmarks, (n_landmark_sets * n_landmarks_per_set, n_coords))
        base_landmarks = gs.reshape(
            base_landmarks, (n_landmark_sets * n_landmarks_per_set, n_coords))
        log = self.ambient_metric.log(landmarks, base_landmarks)
        log = gs.reshape(log, (n_landmark_sets, n_landmarks_per_set, n_coords))
        log = gs.squeeze(log)

        return log
    def is_tangent(self, vector, base_point, atol=gs.atol):
        """Check whether the vector is tangent at base_point.

        The tangent space of the product manifold is the direct sum of
        tangent spaces.

        Parameters
        ----------
        vector : array-like, shape=[..., n_copies, *base_shape]
            Vector.
        base_point : array-like, shape=[..., n_copies, *base_shape]
            Point on the manifold.
        atol : float
            Absolute tolerance.
            Optional, default: backend atol.

        Returns
        -------
        is_tangent : bool
            Boolean denoting if vector is a tangent vector at the base point.
        """
        vector_, point_ = gs.broadcast_arrays(vector, base_point)
        point_ = gs.reshape(point_, (-1, *self.base_shape))
        vector_ = gs.reshape(vector_, (-1, *self.base_shape))
        each_tangent = self.base_manifold.is_tangent(vector_, point_)
        reshaped = gs.reshape(each_tangent, (-1, self.n_copies))
        return gs.all(reshaped, axis=1)
Exemple #13
0
    def parallel_transport(self, tangent_vec_a, tangent_vec_b, base_point):
        """Compute the Riemannian parallel transport of a tangent vector.

        Parameters
        ----------
        tangent_vec_a : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at a base point.
        tangent_vec_b : array-like, shape=[..., k_landmarks, m_ambient]
            Tangent vector at a base point.
        base_point : array-like, shape=[..., k_landmarks, m_ambient]
            Point on the pre-shape space.

        Returns
        -------
        transported : array-like, shape=[..., k_landmarks, m_ambient]
            Point on the pre-shape space equal to the Riemannian exponential
            of tangent_vec at the base point.
        """
        max_shape = (tangent_vec_a.shape
                     if tangent_vec_a.ndim == 3 else tangent_vec_b.shape)

        flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1))
        flat_tan_a = gs.reshape(tangent_vec_a,
                                (-1, self.sphere_metric.dim + 1))
        flat_tan_b = gs.reshape(tangent_vec_b,
                                (-1, self.sphere_metric.dim + 1))

        flat_transport = self.sphere_metric.parallel_transport(
            flat_tan_a, flat_tan_b, flat_bp)
        return gs.reshape(flat_transport, max_shape)
    def _fit_extrinsic(self, X, y, weights=None, compute_training_score=False):
        """Estimate the parameters using the extrinsic gradient descent.

        Estimate the intercept and the coefficient defining the
        geodesic regression model, using the extrinsic gradient.

        Parameters
        ----------
        X : {array-like, sparse matrix}, shape=[...,}]
            Training input samples.
        y : array-like, shape=[..., {dim, [n,n]}]
            Training target values.
        weights : array-like, shape=[...,]
            Weights associated to the points.
            Optional, default: None.
        compute_training_score : bool
            Whether to compute R^2.
            Optional, default: False.

        Returns
        -------
        self : object
            Returns self.
        """
        shape = (
            y.shape[-1:] if self.space.default_point_type == "vector" else y.shape[-2:]
        )

        intercept_init, coef_init = self.initialize_parameters(y)
        intercept_hat = self.space.projection(intercept_init)
        coef_hat = self.space.to_tangent(coef_init, intercept_hat)
        initial_guess = gs.vstack([gs.flatten(intercept_hat), gs.flatten(coef_hat)])

        objective_with_grad = gs.autodiff.value_and_grad(
            lambda param: self._loss(X, y, param, shape, weights), to_numpy=True
        )

        res = minimize(
            objective_with_grad,
            initial_guess,
            method="CG",
            jac=True,
            options={"disp": self.verbose, "maxiter": self.max_iter},
            tol=self.tol,
        )

        intercept_hat, coef_hat = gs.split(gs.array(res.x), 2)
        intercept_hat = gs.reshape(intercept_hat, shape)
        intercept_hat = gs.cast(intercept_hat, dtype=y.dtype)
        coef_hat = gs.reshape(coef_hat, shape)
        coef_hat = gs.cast(coef_hat, dtype=y.dtype)

        self.intercept_ = self.space.projection(intercept_hat)
        self.coef_ = self.space.to_tangent(coef_hat, self.intercept_)

        if compute_training_score:
            variance = gs.sum(self.metric.squared_dist(y, self.intercept_))
            self.training_score_ = 1 - 2 * res.fun / variance

        return self
    def square_root_velocity(self, curve):
        """Compute the square root velocity representation of a curve.

        The velocity is computed using the log map. In the case of several
        curves, an index selection procedure allows to get rid of the log
        between the end point of curve[k, :, :] and the starting point of
        curve[k + 1, :, :].

        Parameters
        ----------
        curve : array-like, shape=[..., n_sampling_points, ambient_dim]
            Discrete curve.

        Returns
        -------
        srv : array-like, shape=[..., n_sampling_points - 1, ambient_dim]
            Square-root velocity representation of a discrete curve.
        """
        curve = gs.to_ndarray(curve, to_ndim=3)
        n_curves, n_sampling_points, n_coords = curve.shape
        srv_shape = (n_curves, n_sampling_points - 1, n_coords)

        curve = gs.reshape(curve, (n_curves * n_sampling_points, n_coords))
        coef = gs.cast(gs.array(n_sampling_points - 1), gs.float32)
        velocity = coef * self.ambient_metric.log(point=curve[1:, :],
                                                  base_point=curve[:-1, :])
        velocity_norm = self.ambient_metric.norm(velocity, curve[:-1, :])
        srv = gs.einsum(
            '...i,...->...i', velocity, 1. / gs.sqrt(velocity_norm))

        index = gs.arange(n_curves * n_sampling_points - 1)
        mask = ~((index + 1) % n_sampling_points == 0)
        srv = gs.reshape(srv[mask], srv_shape)

        return srv
Exemple #16
0
    def log(self, landmarks, base_landmarks):
        """Compute Riemannian log of a set of landmarks wrt base landmark set.

        Parameters
        ----------
        landmarks
        base_landmarks

        Returns
        -------
        log
        """
        if landmarks.shape != base_landmarks.shape:
            raise NotImplementedError

        n_landmark_sets, n_landmarks_per_set, n_coords = landmarks.shape

        landmarks = gs.reshape(
            landmarks, (n_landmark_sets * n_landmarks_per_set, n_coords))
        base_landmarks = gs.reshape(
            base_landmarks, (n_landmark_sets * n_landmarks_per_set, n_coords))
        log = self.ambient_metric.log(landmarks, base_landmarks)
        log = gs.reshape(log, (n_landmark_sets, n_landmarks_per_set, n_coords))
        log = gs.squeeze(log)

        return log
    def inner_product(self, tangent_vec_a, tangent_vec_b, base_landmarks):
        """
        Inner product between two tangent vectors at a base landmark set.
        """
        assert tangent_vec_a.shape == tangent_vec_b.shape
        assert tangent_vec_a.shape == base_landmarks.shape
        tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=3)
        tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=3)
        base_landmarks = gs.to_ndarray(base_landmarks, to_ndim=3)

        n_landmark_sets, n_landmarks_per_set, n_coords = tangent_vec_a.shape

        new_dim = n_landmark_sets * n_landmarks_per_set
        tangent_vec_a = gs.reshape(tangent_vec_a, (new_dim, n_coords))
        tangent_vec_b = gs.reshape(tangent_vec_b, (new_dim, n_coords))
        base_landmarks = gs.reshape(base_landmarks, (new_dim, n_coords))

        inner_prod = self.ambient_metric.inner_product(
                tangent_vec_a, tangent_vec_b, base_landmarks)
        inner_prod = gs.reshape(
            inner_prod, (n_landmark_sets, n_landmarks_per_set))
        inner_prod = gs.sum(inner_prod, -1)

        n_landmarks_per_set_float = gs.array(n_landmarks_per_set)
        n_landmarks_per_set_float = gs.cast(
            n_landmarks_per_set_float, gs.float32)
        inner_prod = inner_prod / n_landmarks_per_set_float
        inner_prod = gs.to_ndarray(inner_prod, to_ndim=1)
        inner_prod = gs.to_ndarray(inner_prod, to_ndim=2, axis=1)

        return inner_prod
    def sample_y(self, X, n_samples=1, random_state=0):
        """Draw samples from Wrapped Gaussian process and evaluate at X.

        A fitted Wrapped Gaussian process can be use to sample
        values through the following steps:

            - Use the stored Gaussian process regression on the dataset
                to sample tangent values
            - Compute the base-points using the prior
            - Flatten (and repeat if needed) both the base-points and the
                tangent samples to benefit from vectorized computation.
            - Map the tangent samples on the manifold via the metric's exp with the
                flattened and repeated base-points yielded by the prior

        Parameters
        ----------
        X : array-like of shape (n_samples_X, n_features) or list of object
            Query points where the WGP is evaluated.
        n_samples : int, default=1
            Number of samples drawn from the Wrapped Gaussian process per query point.
        random_state : int, RandomState instance or None, default=0
            Determines random number generation to randomly draw samples.
            Pass an int for reproducible results across multiple function
            calls.

        Returns
        -------
        y_samples : ndarray of shape (n_samples_X, n_samples), or \
            (n_samples_X, n_targets, n_samples)
            Values of n_samples samples drawn from wrapped Gaussian process and
            evaluated at query points.
        """
        tangent_samples = self._euclidean_gpr.sample_y(X, n_samples,
                                                       random_state)
        tangent_samples = gs.cast(tangent_samples, dtype=X.dtype)
        # flatten the samples
        tangent_samples = gs.reshape(gs.transpose(tangent_samples, [0, 2, 1]),
                                     (-1, *self.y_train_shape_))

        # generate the base_points
        base_points = self.prior(X)
        # repeat the base points in order to match the tangent samples
        base_points = gs.repeat(gs.expand_dims(base_points, 2),
                                n_samples,
                                axis=2)
        # flatten the base_points
        base_points = gs.reshape(gs.transpose(base_points, [0, 2, 1]),
                                 (-1, *self.y_train_shape_))

        # get the flattened samples
        y_samples = self.metric.exp(tangent_samples, base_point=base_points)
        y_samples = gs.transpose(
            gs.reshape(y_samples,
                       (X.shape[0], n_samples, *self.y_train_shape_)),
            [0, 2, 1],
        )

        return y_samples
Exemple #19
0
    def dist_broadcast(self, point_a, point_b):
        """Compute the geodesic distance between points.

        If n_samples_a == n_samples_b then dist is the element-wise
        distance result of a point in points_a with the point from
        points_b of the same index. If n_samples_a not equal to
        n_samples_b then dist is the result of applying geodesic
        distance for each point from points_a to all points from
        points_b.

        Parameters
        ----------
        point_a : array-like, shape=[n_samples_a, dim]
            Set of points in hyperbolic space.
        point_b : array-like, shape=[n_samples_b, dim]
            Second set of points in hyperbolic space.

        Returns
        -------
        dist : array-like,
            shape=[n_samples_a, dim] or [n_samples_a, n_samples_b, dim]
            Geodesic distance between the two points.
        """
        if point_a.shape[-1] != point_b.shape[-1]:
            raise ValueError('Manifold dimensions not equal')

        if point_a.shape[0] != point_b.shape[0]:

            point_a_broadcast, point_b_broadcast = gs.broadcast_arrays(
                point_a[:, None], point_b[None, ...])

            point_a_flatten = gs.reshape(point_a_broadcast,
                                         (-1, point_a_broadcast.shape[-1]))
            point_b_flatten = gs.reshape(point_b_broadcast,
                                         (-1, point_b_broadcast.shape[-1]))

            point_a_norm = gs.clip(gs.sum(point_a_flatten**2, -1), 0.,
                                   1 - EPSILON)
            point_b_norm = gs.clip(gs.sum(point_b_flatten**2, -1), 0.,
                                   1 - EPSILON)

            square_diff = (point_a_flatten - point_b_flatten)**2

            diff_norm = gs.sum(square_diff, -1)
            norm_function = 1 + 2 * \
                diff_norm / ((1 - point_a_norm) * (1 - point_b_norm))

            dist = gs.log(norm_function + gs.sqrt(norm_function**2 - 1))
            dist *= self.scale
            dist = gs.reshape(dist, (point_a.shape[0], point_b.shape[0]))
            dist = gs.squeeze(dist)

        elif point_a.shape == point_b.shape:
            dist = self.dist(point_a, point_b)

        return dist
Exemple #20
0
    def test_loss_minimization_extrinsic_se2(self):
        gr = GeodesicRegression(
            self.se2,
            metric=self.metric_se2,
            center_X=False,
            method="extrinsic",
            max_iter=50,
            init_step_size=0.1,
            verbose=True,
        )

        def loss_of_param(param):
            return gr._loss(self.X_se2, self.y_se2, param, self.shape_se2)

        objective_with_grad = gs.autodiff.value_and_grad(loss_of_param,
                                                         to_numpy=True)

        res = minimize(
            objective_with_grad,
            gs.flatten(self.param_se2_guess),
            method="CG",
            jac=True,
            options={
                "disp": True,
                "maxiter": 50
            },
        )
        self.assertAllClose(gs.array(res.x).shape, (18, ))

        self.assertAllClose(res.fun, 0.0, atol=1e-6)

        # Cast required because minimization happens in scipy in float64
        param_hat = gs.cast(gs.array(res.x), self.param_se2_true.dtype)

        intercept_hat, coef_hat = gs.split(param_hat, 2)
        intercept_hat = gs.reshape(intercept_hat, self.shape_se2)
        coef_hat = gs.reshape(coef_hat, self.shape_se2)

        intercept_hat = self.se2.projection(intercept_hat)
        coef_hat = self.se2.to_tangent(coef_hat, intercept_hat)
        self.assertAllClose(intercept_hat, self.intercept_se2_true, atol=1e-4)

        tangent_vec_of_transport = self.se2.metric.log(
            self.intercept_se2_true, base_point=intercept_hat)

        transported_coef_hat = self.se2.metric.parallel_transport(
            tangent_vec=coef_hat,
            base_point=intercept_hat,
            direction=tangent_vec_of_transport,
        )

        self.assertAllClose(transported_coef_hat, self.coef_se2_true, atol=0.6)
Exemple #21
0
    def gmm_pdf(
            data, means, variances, norm_func,
            metric, variances_range, norm_func_var):
        """Return the separate probability density function of GMM.

        The probability density function is computed for
        each component of the GMM separately (i.e., mixture coefficients
        are not taken into account).

        Parameters
        ----------
        data : array-like, shape=[n_samples, dim]
            Points at which the GMM probability density is computed.
        means : array-like, shape=[n_gaussians, dim]
            Means of each component of the GMM.
        variances : array-like, shape=[n_gaussians,]
            Variances of each component of the GMM.
        norm_func : function
            Normalisation factor function.
        metric : function
            Distance function associated with the used metric.

        Returns
        -------
        pdf : array-like, shape=[n_samples, n_gaussians,]
            Probability density function computed at each data
            sample and for each component of the GMM.
        """
        data_length, _, _ = data.shape + (means.shape[0],)

        variances_expanded = gs.expand_dims(variances, 0)
        variances_expanded = gs.repeat(variances_expanded, data_length, 0)

        variances_flatten = variances_expanded.flatten()

        distances = -(metric.dist_broadcast(data, means) ** 2)
        distances = gs.reshape(distances, (data.shape[0] * variances.shape[0]))

        num = gs.exp(
            distances / (2 * variances_flatten ** 2))

        den = norm_func(variances, variances_range, norm_func_var)

        den = gs.expand_dims(den, 0)
        den = gs.repeat(den, data_length, axis=0).flatten()

        pdf = num / den
        pdf = gs.reshape(
            pdf, (data.shape[0], means.shape[0]))

        return pdf
Exemple #22
0
 def objective(velocity):
     """Define the objective function."""
     velocity = gs.array(velocity)
     velocity = gs.cast(velocity, dtype=base_point.dtype)
     velocity = gs.reshape(velocity, max_shape)
     delta = self.exp(velocity, base_point, n_steps, step) - point
     return gs.sum(delta**2)
    def setup_method(self):
        gs.random.seed(1234)
        self.n_samples = 20

        # Set up for hypersphere
        self.dim_sphere = 2
        self.shape_sphere = (self.dim_sphere + 1, )
        self.sphere = Hypersphere(dim=self.dim_sphere)

        self.intercept_sphere_true = gs.array([0.0, -1.0, 0.0])
        self.coef_sphere_true = gs.array([1.0, 0.0, 0.5])

        # set up the prior
        self.prior = lambda x: self.sphere.metric.exp(
            x * self.coef_sphere_true,
            base_point=self.intercept_sphere_true,
        )

        self.kernel = ConstantKernel(1.0, (1e-3, 1e3)) * RBF(10.0, (1e-2, 1e2))

        # generate data
        X = gs.linspace(0.0, 1.5 * gs.pi, self.n_samples)
        self.X_sphere = gs.reshape((X - gs.mean(X)), (-1, 1))
        # generate the geodesic
        y = self.prior(self.X_sphere)
        # Then add orthogonal sinusoidal oscillations

        o = (1.0 / 20.0) * gs.array([-0.5, 0.0, 1.0])
        o = self.sphere.to_tangent(o, base_point=y)
        s = self.X_sphere * gs.sin(5.0 * gs.pi * self.X_sphere)
        self.y_sphere = self.sphere.metric.exp(s * o, base_point=y)
Exemple #24
0
    def test_svd(self):
        gs_point = gs.reshape(gs.arange(12), (4, 3))
        gs_point = gs.cast(gs_point, gs.float64)
        np_point = _np.arange(12).reshape(4, 3)
        reconstruction = gs.array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.],
                                   [0., 0., 0.]])
        u, s, v = _np.linalg.svd(np_point)
        u_r, s_r, v_r = gs.linalg.svd(gs_point)
        s_r_reconstructed = gs.einsum('kl,l->kl', reconstruction, s_r)
        gs_a_approx = gs.matmul(gs.matmul(u_r, s_r_reconstructed), v_r)
        s_reconstructed = _np.einsum('kl,l->kl', reconstruction, s)
        np_a_approx = _np.dot(u, _np.dot(s_reconstructed, v))
        self.assertAllClose(gs_a_approx, np_a_approx)

        full_matrices = False
        u, s, v = _np.linalg.svd(np_point, full_matrices=full_matrices)
        u_r, s_r, v_r = gs.linalg.svd(gs_point, full_matrices)
        reconstruction = gs.eye(3)
        s_r_reconstructed = gs.einsum('kl,l->kl', reconstruction, s_r)
        gs_a_approx = gs.matmul(gs.matmul(u_r, s_r_reconstructed), v_r)
        s_reconstructed = _np.einsum('kl,l->kl', reconstruction, s)
        np_a_approx = _np.dot(u, _np.dot(s_reconstructed, v))
        self.assertAllClose(gs_a_approx, np_a_approx)

        compute_uv = False
        s = _np.linalg.svd(np_point, compute_uv=compute_uv)
        s_r = gs.linalg.svd(gs_point, compute_uv=compute_uv)
        self.assertAllClose(s, s_r)
Exemple #25
0
    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)
Exemple #26
0
 def test_basis_and_matrix_representation(self):
     n_samples = 2
     expected = gs.random.rand(n_samples * self.dim)
     expected = gs.reshape(expected, (n_samples, self.dim))
     mat = self.algebra.matrix_representation(expected)
     result = self.algebra.basis_representation(mat)
     self.assertAllClose(result, expected)
Exemple #27
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
Exemple #28
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_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_features]
            Original data.
        """
        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)
 def vector_from_matrix(self, matrix):
     """
     Conversion function from (_, m, n) to (_, mn).
     """
     matrix = gs.to_ndarray(matrix, to_ndim=3)
     n_mats, m, n = matrix.shape
     return gs.reshape(matrix, (n_mats, m * n))
    def matrix_from_vector(self, vec):
        """Convert point in vector point-type to matrix.

        Parameters
        ----------
        vec : array-like, shape=[..., dimension]
            Vector.

        Returns
        -------
        mat : array-like, shape=[..., n+1, n+1]
            Matrix.
        """
        vec = self.regularize(vec)
        n_vecs, _ = vec.shape

        rot_vec = vec[:, :self.rotations.dim]
        trans_vec = vec[:, self.rotations.dim:]

        rot_mat = self.rotations.matrix_from_rotation_vector(rot_vec)
        trans_vec = gs.reshape(trans_vec, (n_vecs, self.n, 1))
        mat = gs.concatenate((rot_mat, trans_vec), axis=2)
        last_lines = gs.array(gs.get_mask_i_float(self.n, self.n + 1))
        last_lines = gs.to_ndarray(last_lines, to_ndim=2)
        last_lines = gs.to_ndarray(last_lines, to_ndim=3)
        mat = gs.concatenate((mat, last_lines), axis=1)

        return mat