Пример #1
0
    def regularize(self, point):
        """Regularize a point to the canonical representation.

        Regularize a point to the canonical representation chosen
        for the Hyperbolic space, to avoid numerical issues.

        Parameters
        ----------
        point : array-like, shape=[n_samples, dimension + 1]
                Input points. TODO: confusing: singular or plural

        Returns
        -------
        projected_point : array-like, shape=[n_samples, dimension + 1]
        """
        point = gs.to_ndarray(point, to_ndim=2)

        sq_norm = self.embedding_metric.squared_norm(point)
        real_norm = gs.sqrt(gs.abs(sq_norm))

        mask_0 = gs.isclose(real_norm, 0.)
        mask_not_0 = ~mask_0
        mask_not_0_float = gs.cast(mask_not_0, gs.float32)
        projected_point = point

        projected_point = mask_not_0_float * (point / real_norm)
        return projected_point
Пример #2
0
    def reshape_metric_matrix(self, metric_mat):
        """Reshape diagonal metric matrix to a symmetric matrix of size n.

        Reshape a diagonal metric matrix of size `dim x dim` into a symmetric
        matrix of size `n x n` where :math: `dim= n (n -1) / 2` is the
        dimension of the space of skew symmetric matrices. The
        non-diagonal coefficients in the output matrix correspond to the
        basis matrices of this space. The diagonal is filled with ones.
        This useful to compute a matrix inner product.

        Parameters
        ----------
        metric_mat : array-like, shape=[dim, dim]
            Diagonal metric matrix.

        Returns
        -------
        symmetric_matrix : array-like, shape=[n, n]
            Symmetric matrix.
        """
        if Matrices.is_diagonal(metric_mat):
            metric_coeffs = gs.diagonal(metric_mat)
            metric_mat = gs.abs(self.matrix_representation(metric_coeffs))
            return metric_mat
        raise ValueError('This is only possible for a diagonal matrix')
Пример #3
0
    def belongs(self, point, tolerance=TOLERANCE):
        """Test if a point belongs to the hyperbolic space.

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

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

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

        sq_norm = self.embedding_metric.squared_norm(point)
        euclidean_sq_norm = gs.linalg.norm(point, axis=-1)**2
        diff = gs.abs(sq_norm + 1)
        belongs = diff < tolerance * euclidean_sq_norm
        return belongs
Пример #4
0
    def regularize(self, point):
        """Regularize a point to the canonical representation.

        Regularize a point to the canonical representation chosen
        for the hyperbolic space, to avoid numerical issues.

        Parameters
        ----------
        point : array-like, shape=[..., dim + 1]
            Point.

        Returns
        -------
        projected_point : array-like, shape=[..., dim + 1]
            Point in hyperbolic space in canonical representation
            in extrinsic coordinates.
        """
        if self.coords_type == 'intrinsic':
            point = self.intrinsic_to_extrinsic_coords(point)

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

        sq_norm = self.embedding_metric.squared_norm(point)
        real_norm = gs.sqrt(gs.abs(sq_norm))

        mask_0 = gs.isclose(real_norm, 0.)
        mask_not_0 = ~mask_0
        mask_not_0_float = gs.cast(mask_not_0, gs.float32)
        projected_point = point

        normalized_point = gs.einsum('...,...i->...i', 1. / real_norm, point)
        projected_point = gs.einsum('...,...i->...i', mask_not_0_float,
                                    normalized_point)
        return projected_point
Пример #5
0
    def regularize(self, point):
        """Regularize a point to the canonical representation.

        Regularize a point to the canonical representation chosen
        for the hyperbolic space, to avoid numerical issues.

        Parameters
        ----------
        point : array-like, shape=[..., dim + 1]
            Point.

        Returns
        -------
        projected_point : array-like, shape=[..., dim + 1]
            Point in hyperbolic space in canonical representation
            in extrinsic coordinates.
        """
        if self.coords_type == 'intrinsic':
            point = self.intrinsic_to_extrinsic_coords(point)

        sq_norm = self.embedding_metric.squared_norm(point)
        if not gs.all(sq_norm):
            raise ValueError('Cannot project a vector of norm 0. in the '
                             'Minkowski space to the hyperboloid')
        real_norm = gs.sqrt(gs.abs(sq_norm))
        projected_point = gs.einsum('...i,...->...i', point, 1. / real_norm)

        return projected_point
Пример #6
0
    def belongs(self, point, tolerance=TOLERANCE):
        """Test if a point belongs to the hypersphere.

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

        Parameters
        ----------
        point : array-like, shape=[n_samples, dimension + 1]
            Points in Euclidean space.
        tolerance : float, optional
            Tolerance at which to evaluate norm == 1 (default: TOLERANCE).

        Returns
        -------
        belongs : array-like, shape=[n_samples, 1]
            Array of booleans evaluating if each point belongs to
            the hypersphere.
        """
        point = gs.asarray(point)
        point_dim = point.shape[-1]
        if point_dim != self.dimension + 1:
            if point_dim is self.dimension:
                logging.warning('Use the extrinsic coordinates to '
                                'represent points on the hypersphere.')
            return gs.array([[False]])
        sq_norm = self.embedding_metric.squared_norm(point)
        diff = gs.abs(sq_norm - 1)
        return gs.less_equal(diff, tolerance)
Пример #7
0
    def belongs(self, point, tolerance=TOLERANCE):
        """Test if a point belongs to the hypersphere.

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

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

        Returns
        -------
        belongs : array-like, shape=[...,]
            Boolean evaluating if point belongs to the hypersphere.
        """
        point_dim = gs.shape(point)[-1]
        if point_dim != self.dim + 1:
            if point_dim is self.dim:
                logging.warning('Use the extrinsic coordinates to '
                                'represent points on the hypersphere.')
            belongs = False
            if gs.ndim(point) == 2:
                belongs = gs.tile([belongs], (point.shape[0], ))
            return belongs
        sq_norm = self.embedding_metric.squared_norm(point)
        diff = gs.abs(sq_norm - 1)
        return gs.less_equal(diff, tolerance)
Пример #8
0
def find_normalization_factor(variances, variances_range,
                              normalization_factor_var):
    """Find the normalization factor given some variances.

    Parameters
    ----------
    variances : array-like, shape=[n_gaussians,]
        Array of standard deviations for each component
        of some GMM.
    variances_range : array-like, shape=[n_variances,]
        Array of standard deviations.
    normalization_factor_var : array-like, shape=[n_variances,]
        Array of computed normalization factor.

    Returns
    -------
    norm_factor : array-like, shape=[n_gaussians,]
        Array of normalization factors for the given
        variances.
    """
    n_gaussians, precision = variances.shape[0], variances_range.shape[0]

    ref = gs.expand_dims(variances_range, 0)
    ref = gs.repeat(ref, n_gaussians, axis=0)
    val = gs.expand_dims(variances, 1)
    val = gs.repeat(val, precision, axis=1)

    difference = gs.abs(ref - val)

    index = gs.argmin(difference, axis=-1)
    norm_factor = normalization_factor_var[index]

    return norm_factor
Пример #9
0
    def random_gaussian_rotation_orbit_noisy(self, mean_spd=None,
                                             eigensummary=None,
                                             var_rotations=1.,
                                             var_eigenvalues=None,
                                             n_samples=1):
        r"""
        Define a Gaussian-like random sample of SPD matrices.

        Formally speaking, sample an orbit for given rotations in the SPD
        manifold. For all means and purposes, it looks rather Gaussian.

        Parameters
        ----------
        mean_spd : array-like, shape = [n, n]
            Mean SPD matrix.
        var_rotations : float
            Variance in rotation.
        var_eigenvalues : array-like, shape = [n,]
            Additional variance in eigenvalues.
        eigensummary : EigenSummary
            Represents the mean SPD matrix decomposed in eigenspace and
            eigenvalues.

        Notes
        -----
        :math:'mean_spd' is the mean SPD matrix; :math:'var_rotations' is the
        scalar variance by which the mean is rotated:
        :math:'\Sigma_{mid} \sim \mathcal{N}(\Sigma_{in}, \sigma_{eig}';
        :math:'X_{out} = R \Sigma_{in} R^T'.
        mean_spd and eigensummary are mutually exclusive; an error is thrown
        if both are not None, or if both are None.
        """
        n = self.n
        if var_eigenvalues is None:
            var_eigenvalues = gs.ones(n)

        if mean_spd is not None and eigensummary is not None:
            raise NotImplementedError
        if mean_spd is None and eigensummary is None:
            raise NotImplementedError
        if eigensummary is None:
            eigenvalues, eigenspace = gs.linalg.eigh(mean_spd)
            eigenvalues = gs.diag(eigenvalues)
        if mean_spd is None:
            eigenvalues, eigenspace\
                = eigensummary.eigenvalues, eigensummary.eigenspace
        rotations = SpecialOrthogonal(n).random_gaussian(
            eigenspace, var_rotations, n_samples=n_samples)

        eigenvalues =\
            gs.abs(gs.diag(gs.random.multivariate_normal(
                gs.diag(eigenvalues), gs.diag(var_eigenvalues))))
        spd_mat = Matrices.mul(rotations, eigenvalues, Matrices.transpose(
            rotations))

        return spd_mat
Пример #10
0
 def plot(self, points, ax=None, space=None, **point_draw_kwargs):
     if space == "SE3_GROUP":
         ax_s = AX_SCALE * gs.amax(gs.abs(points[:, 3:6]))
     elif space == "SO3_GROUP":
         ax_s = AX_SCALE * gs.amax(gs.abs(points[:, :3]))
     ax_s = float(ax_s)
     bounds = (-ax_s, ax_s)
     plt.setp(
         ax,
         xlim=bounds,
         ylim=bounds,
         zlim=bounds,
         xlabel="X",
         ylabel="Y",
         zlabel="Z",
     )
     trihedrons = convert_to_trihedron(points, space=space)
     for t in trihedrons:
         t.draw(ax, **point_draw_kwargs)
Пример #11
0
    def regularize(self, point):
        assert gs.all(self.belongs(point))
        point = gs.to_ndarray(point, to_ndim=2)

        sq_norm = self.embedding_metric.squared_norm(point)
        real_norm = gs.sqrt(gs.abs(sq_norm))

        mask_0 = gs.isclose(real_norm, 0)
        mask_0 = gs.squeeze(mask_0, axis=1)
        mask_not_0 = ~mask_0
        projected_point = point

        projected_point[mask_not_0] = (point[mask_not_0] /
                                       real_norm[mask_not_0])
        return projected_point
Пример #12
0
 def belongs(self, point, tolerance=TOLERANCE):
     """
     Evaluate if a point belongs to the Hypersphere,
     i.e. evaluate if its squared norm in the Euclidean space is 1.
     """
     point = gs.asarray(point)
     point_dim = point.shape[-1]
     if point_dim != self.dimension + 1:
         if point_dim is self.dimension:
             logging.warning('Use the extrinsic coordinates to '
                             'represent points on the hypersphere.')
         return gs.array([[False]])
     sq_norm = self.embedding_metric.squared_norm(point)
     diff = gs.abs(sq_norm - 1)
     return gs.less_equal(diff, tolerance)
Пример #13
0
 def belongs(self, point, tolerance=TOLERANCE):
     """
     By definition, a point on the Hypersphere has squared norm 1
     in the embedding Euclidean space.
     Note: point must be given in extrinsic coordinates.
     """
     point = gs.asarray(point)
     point_dim = point.shape[-1]
     if point_dim != self.dimension + 1:
         if point_dim is self.dimension:
             logging.warning('Use the extrinsic coordinates to '
                             'represent points on the hypersphere.')
         return False
     sq_norm = self.embedding_metric.squared_norm(point)
     diff = gs.abs(sq_norm - 1)
     return gs.less_equal(diff, tolerance)
Пример #14
0
def plot(points, ax=None, space=None, **point_draw_kwargs):
    """Plot trihedrons."""
    ax_s = AX_SCALE * gs.amax(gs.abs(points[:, :3]))
    ax_s = float(ax_s)
    bounds = (-ax_s, ax_s)
    plt.setp(
        ax,
        xlim=bounds,
        ylim=bounds,
        zlim=bounds,
        xlabel="X",
        ylabel="Y",
        zlabel="Z",
    )
    trihedrons = convert_to_trihedron(points, space=space)
    for t in trihedrons:
        t.draw(ax, **point_draw_kwargs)
Пример #15
0
    def regularize(self, point):
        """
        Regularize a point to the canonical representation
        chosen for the Hyperbolic space, to avoid numerical issues.
        """
        point = gs.to_ndarray(point, to_ndim=2)

        sq_norm = self.embedding_metric.squared_norm(point)
        real_norm = gs.sqrt(gs.abs(sq_norm))

        mask_0 = gs.isclose(real_norm, 0.)
        mask_not_0 = ~mask_0
        mask_not_0_float = gs.cast(mask_not_0, gs.float32)
        projected_point = point

        projected_point = mask_not_0_float * (point / real_norm)
        return projected_point
Пример #16
0
    def regularize(self, point):
        """
        Regularize a point to the canonical representation
        chosen for the Hyperbolic space, to avoid numerical issues.
        """
        assert gs.all(self.belongs(point))
        point = gs.to_ndarray(point, to_ndim=2)

        sq_norm = self.embedding_metric.squared_norm(point)
        real_norm = gs.sqrt(gs.abs(sq_norm))

        mask_0 = gs.isclose(real_norm, 0)
        mask_0 = gs.squeeze(mask_0, axis=1)
        mask_not_0 = ~mask_0
        projected_point = point

        projected_point[mask_not_0] = (point[mask_not_0] /
                                       real_norm[mask_not_0])
        return projected_point
Пример #17
0
    def find_variance_from_index(weighted_distances, variances_range,
                                 phi_inv_var):
        r"""Return the variance given weighted distances.

        Parameters
        ----------
        weighted_distances : array-like, shape=[n_gaussians,]
            Mean of the weighted distances between training data
            and current barycentres. The weights of each data sample
            corresponds to the probability of belonging to a component
            of the Gaussian mixture model.
        variances_range : array-like, shape=[n_variances,]
            Array of standard deviations.
        phi_inv_var : array-like, shape=[n_variances,]
            Array of the computed inverse of a function phi
            whose expression is closed-form
            :math:`\sigma\mapsto \sigma^3 \times \frac{d  }
            {\mathstrut d\sigma}\log \zeta_m(\sigma)'
            where :math:'\sigma' denotes the variance
            and :math:'\zeta' the normalization coefficient
            and :math:'m' the dimension.

        Returns
        -------
        var : array-like, shape=[n_gaussians,]
            Estimated variances for each component of the GMM.
        """
        n_gaussians, precision = \
            weighted_distances.shape[0], variances_range.shape[0]

        ref = gs.expand_dims(phi_inv_var, 0)
        ref = gs.repeat(ref, n_gaussians, axis=0)

        val = gs.expand_dims(weighted_distances, 1)
        val = gs.repeat(val, precision, axis=1)

        abs_difference = gs.abs(ref - val)

        index = gs.argmin(abs_difference, -1)

        var = variances_range[index]

        return var
Пример #18
0
    def belongs(self, point, tolerance=TOLERANCE):
        """
        Evaluate if a point belongs to the Hyperbolic space,
        i.e. evaluate if its squared norm in the Minkowski space is -1.
        """
        point = gs.to_ndarray(point, to_ndim=2)
        _, point_dim = point.shape
        if point_dim is not self.dimension + 1:
            if point_dim is self.dimension:
                logging.warning('Use the extrinsic coordinates to '
                                'represent points on the hyperbolic space.')
                return gs.array([[False]])

        sq_norm = self.embedding_metric.squared_norm(point)
        euclidean_sq_norm = gs.linalg.norm(point, axis=-1)**2
        euclidean_sq_norm = gs.to_ndarray(euclidean_sq_norm, to_ndim=2, axis=1)
        diff = gs.abs(sq_norm + 1)
        belongs = diff < tolerance * euclidean_sq_norm
        return belongs
Пример #19
0
def gradient_descent(
    start, loss, grad, manifold, lr=0.01, max_iter=256, precision=1e-5
):
    """Operate a gradient descent on a given manifold.

    Until either max_iter or a given precision is reached.
    """
    x = start
    for i in range(max_iter):
        x_prev = x
        euclidean_grad = -lr * grad(x)
        tangent_vec = manifold.to_tangent(vector=euclidean_grad, base_point=x)
        x = manifold.metric.exp(base_point=x, tangent_vec=tangent_vec)
        if gs.abs(loss(x, use_gs=True) - loss(x_prev, use_gs=True)) <= precision:
            logging.info("x: %s", x)
            logging.info("reached precision %s", precision)
            logging.info("iterations: %d", i)
            break
        yield x, loss(x)
Пример #20
0
    def belongs(self, point, atol=gs.atol):
        """Check if a matrix is invertible and of the right shape.

        Parameters
        ----------
        point : array-like, shape=[..., n, n]
            Matrix to be checked.
        atol : float
            Tolerance threshold for the determinant.

        Returns
        -------
        belongs : array-like, shape=[...,]
            Boolean denoting if point is in GL(n).
        """
        has_right_size = self.ambient_space.belongs(point)
        if gs.all(has_right_size):
            det = gs.linalg.det(point)
            return det > atol if self.positive_det else gs.abs(det) > atol
        return has_right_size
Пример #21
0
    def belongs(self, point, tolerance=TOLERANCE):
        """Test if a point belongs to the hyperbolic space.

        Test if a point belongs to the hyperbolic space according
        to the current representation.

        Parameters
        ----------
        point : array-like, shape=[n_samples, dimension]
                            or shape=[n_samples, dimension + 1]
            Point.
        tolerance : float, optional
            Tolerance at which to evaluate how close is the squared norm
            compared to the reference value.

        Returns
        -------
        belongs : array-like, shape=[n_samples, 1]
            Array of booleans evaluating if the corresponding points
            belong to the hyperbolic space.
        """
        if self.point_type == 'ball':
            return self.belongs_to[self.point_type](point, tolerance=tolerance)
        else:
            point = gs.to_ndarray(point, to_ndim=2)
            _, point_dim = point.shape
            if point_dim is not self.dimension + 1:
                if point_dim is self.dimension:
                    logging.warning(
                        'Use the extrinsic coordinates to '
                        'represent points in the hyperbolic space.')
                    return gs.array([[False]])

            sq_norm = self.embedding_metric.squared_norm(point)
            euclidean_sq_norm = gs.linalg.norm(point, axis=-1)**2
            euclidean_sq_norm = gs.to_ndarray(euclidean_sq_norm,
                                              to_ndim=2,
                                              axis=1)
            diff = gs.abs(sq_norm + 1)
            belongs = diff < tolerance * euclidean_sq_norm
            return belongs
    def projection(self, point):
        """Project a matrix to the Cholesksy space.

        First it is projected to space lower triangular matrices
        and then diagonal elements are exponentiated to make it positive.

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

        Returns
        -------
        projected: array-like, shape=[..., n, n]
            SPD matrix.
        """
        vec_diag = gs.abs(Matrices.diagonal(point) - 0.1) + 0.1
        diag = gs.vec_to_diag(vec_diag)
        strictly_lower_triangular = Matrices.to_lower_triangular(point)
        projection = diag + strictly_lower_triangular
        return projection
Пример #23
0
    def belongs(self, point, tolerance=TOLERANCE):
        """Evaluate if a point belongs to the Hyperbolic space.

        Evaluate if a point belongs to the Hyperbolic space according
        to the current representation

        Parameters
        ----------
        point : array-like, shape=[n_samples, dimension] or
                shape=[n_samples, dimension + 1] for extrinsic
                coordinates
                Input points.
        tolerance : float, optional

        Returns
        -------
        belongs : array-like, shape=[n_samples, 1]
        """
        if self.point_type == 'ball':
            return self.belongs_to[self.point_type](point, tolerance=tolerance)
        else:
            point = gs.to_ndarray(point, to_ndim=2)
            _, point_dim = point.shape
            if point_dim is not self.dimension + 1:
                if point_dim is self.dimension:
                    logging.warning(
                        'Use the extrinsic coordinates to '
                        'represent points on the hyperbolic space.')
                    return gs.array([[False]])

            sq_norm = self.embedding_metric.squared_norm(point)
            euclidean_sq_norm = gs.linalg.norm(point, axis=-1)**2
            euclidean_sq_norm = gs.to_ndarray(euclidean_sq_norm,
                                              to_ndim=2,
                                              axis=1)
            diff = gs.abs(sq_norm + 1)
            belongs = diff < tolerance * euclidean_sq_norm
            return belongs
Пример #24
0
def gradient_descent(start,
                     loss,
                     grad,
                     manifold,
                     lr=0.01,
                     max_iter=256,
                     precision=1e-5):
    """Operate a gradient descent on a given manifold until either max_iter or
    a given precision is reached."""
    x = start
    for i in range(max_iter):
        x_prev = x
        euclidean_grad = - lr * grad(x)
        tangent_vec = manifold.projection_to_tangent_space(
                vector=euclidean_grad, base_point=x)
        x = manifold.metric.exp(base_point=x, tangent_vec=tangent_vec)[0]
        if (gs.abs(loss(x, use_gs=True) - loss(x_prev, use_gs=True))
                <= precision):
            print('x: %s' % x)
            print('reached precision %s' % precision)
            print('iterations: %d' % i)
            break
        yield x, loss(x)
Пример #25
0
    def random_point(self, n_samples=1):
        r"""Compute a random point of the spider set.

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

        Returns
        -------
        samples : list of SpiderPoint, shape=[...]
            List of SpiderPoints randomly sampled from the Spider.
        """
        if self.n_rays != 0:
            s = gs.random.randint(low=0, high=self.n_rays, size=n_samples)
            x = gs.abs(gs.random.normal(loc=10, scale=1, size=n_samples))
            x[s == 0] = 0
            return [
                SpiderPoint(stratum=s[k], stratum_coord=x[k])
                for k in range(n_samples)
            ]
        return [SpiderPoint(stratum=0, stratum_coord=0)] * n_samples
Пример #26
0
def from_vector_to_diagonal_matrix(vector, num_diag=0):
    """Create diagonal matrices from rows of a matrix.

    Parameters
    ----------
    vector : array-like, shape=[m, n]
    num_diag : int
        number of diagonal in result matrix. If 0, the result matrix is a
        diagonal matrix; if positive, the result matrix has an upper-right
        non-zero diagonal; if negative, the result matrix has a lower-left
        non-zero diagonal.
        Optional, Default: 0.

    Returns
    -------
    diagonals : array-like, shape=[m, n, n]
        3-dimensional array where the `i`-th n-by-n array `diagonals[i, :, :]`
        is a diagonal matrix containing the `i`-th row of `vector`.
    """
    num_columns = gs.shape(vector)[-1]
    identity = gs.eye(num_columns)
    identity = gs.cast(identity, vector.dtype)
    diagonals = gs.einsum("...i,ij->...ij", vector, identity)
    diagonals = gs.to_ndarray(diagonals, to_ndim=3)
    num_lines = diagonals.shape[0]
    if num_diag > 0:
        left_zeros = gs.zeros((num_lines, num_columns, num_diag))
        lower_zeros = gs.zeros((num_lines, num_diag, num_columns + num_diag))
        diagonals = gs.concatenate((left_zeros, diagonals), axis=2)
        diagonals = gs.concatenate((diagonals, lower_zeros), axis=1)
    elif num_diag < 0:
        num_diag = gs.abs(num_diag)
        right_zeros = gs.zeros((num_lines, num_columns, num_diag))
        upper_zeros = gs.zeros((num_lines, num_diag, num_columns + num_diag))
        diagonals = gs.concatenate((diagonals, right_zeros), axis=2)
        diagonals = gs.concatenate((upper_zeros, diagonals), axis=1)
    return gs.squeeze(diagonals) if gs.ndim(vector) == 1 else diagonals
Пример #27
0
    def belongs(self, point, tolerance=TOLERANCE):
        """
        By definition, a point on the Hyperbolic space
        has Minkowski squared norm -1.

        We use a tolerance relative to the Euclidean norm of
        the point.

        Note: point must be given in extrinsic coordinates.
        """
        point = gs.to_ndarray(point, to_ndim=2)
        _, point_dim = point.shape
        if point_dim is not self.dimension + 1:
            if point_dim is self.dimension:
                logging.warning('Use the extrinsic coordinates to '
                                'represent points on the hyperbolic space.')
            return False

        sq_norm = self.embedding_metric.squared_norm(point)
        euclidean_sq_norm = gs.linalg.norm(point, axis=-1)**2
        euclidean_sq_norm = gs.to_ndarray(euclidean_sq_norm, to_ndim=2, axis=1)
        diff = gs.abs(sq_norm + 1)
        belongs = diff < tolerance * euclidean_sq_norm
        return belongs
Пример #28
0
    def belongs(self, point, atol=TOLERANCE):
        """Test if a point belongs to the pre-shape space.

        This tests whether the point is centered and whether the point's
        Frobenius norm is 1.

        Parameters
        ----------
        point : array-like, shape=[..., k_landmarks, m_ambient]
            Point in Matrices space.
        atol : float
            Tolerance at which to evaluate norm == 1 and mean == 0.
            Optional, default: 1e-6.

        Returns
        -------
        belongs : array-like, shape=[...,]
            Boolean evaluating if point belongs to the pre-shape space.
        """
        shape = point.shape[-2:] == (self.k_landmarks, self.m_ambient)
        frob_norm = self.ambient_metric.norm(point)
        diff = gs.abs(frob_norm - 1)
        is_centered = gs.logical_and(self.is_centered(point, atol), shape)
        return gs.logical_and(gs.less_equal(diff, atol), is_centered)
Пример #29
0
def plot(points,
         ax=None,
         space=None,
         point_type='extrinsic',
         **point_draw_kwargs):
    """Plot points in the 3D Special Euclidean Group.

    Plot points in the 3D Special Euclidean Group,
    by showing them as trihedrons.
    """
    if space not in IMPLEMENTED:
        raise NotImplementedError(
            'The plot function is not implemented'
            ' for space {}. The spaces available for visualization'
            ' are: {}.'.format(space, IMPLEMENTED))

    if points is None:
        raise ValueError("No points given for plotting.")

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

    if space in ('SO3_GROUP', 'SE3_GROUP'):
        if ax is None:
            ax = plt.subplot(111, projection='3d')
        if space == 'SE3_GROUP':
            ax_s = AX_SCALE * gs.amax(gs.abs(points[:, 3:6]))
        elif space == 'SO3_GROUP':
            ax_s = AX_SCALE * gs.amax(gs.abs(points[:, :3]))
        plt.setp(ax,
                 xlim=(-ax_s, ax_s),
                 ylim=(-ax_s, ax_s),
                 zlim=(-ax_s, ax_s),
                 xlabel='X',
                 ylabel='Y',
                 zlabel='Z')
        trihedrons = convert_to_trihedron(points, space=space)
        for t in trihedrons:
            t.draw(ax, **point_draw_kwargs)

    elif space == 'S1':
        circle = Circle()
        ax = circle.set_ax(ax=ax)
        circle.add_points(points)
        circle.draw(ax, **point_draw_kwargs)

    elif space == 'S2':
        sphere = Sphere()
        ax = sphere.set_ax(ax=ax)
        sphere.add_points(points)
        sphere.draw(ax, **point_draw_kwargs)

    elif space == 'H2_poincare_disk':
        poincare_disk = PoincareDisk(point_type=point_type)
        ax = poincare_disk.set_ax(ax=ax)
        poincare_disk.add_points(points)
        poincare_disk.draw(ax, **point_draw_kwargs)

    elif space == 'poincare_polydisk':
        n_disks = points.shape[1]
        poincare_poly_disk = PoincarePolyDisk(point_type=point_type,
                                              n_disks=n_disks)
        n_columns = gs.ceil(n_disks**0.5)
        n_rows = gs.ceil(n_disks / n_columns)

        axis_list = []

        for i_disk in range(n_disks):
            axis_list.append(ax.add_subplot(n_rows, n_columns, i_disk + 1))

        for i_disk, ax in enumerate(axis_list):
            ax = poincare_poly_disk.set_ax(ax=ax)
            poincare_poly_disk.clear_points()
            poincare_poly_disk.add_points(points[:, i_disk, ...])
            poincare_poly_disk.draw(ax, **point_draw_kwargs)

    elif space == 'H2_poincare_half_plane':
        poincare_half_plane = PoincareHalfPlane()
        ax = poincare_half_plane.set_ax(ax=ax)
        poincare_half_plane.add_points(points)
        poincare_half_plane.draw(ax, **point_draw_kwargs)

    elif space == 'H2_klein_disk':
        klein_disk = KleinDisk()
        ax = klein_disk.set_ax(ax=ax)
        klein_disk.add_points(points)
        klein_disk.draw(ax, **point_draw_kwargs)

    return ax
Пример #30
0
def plot(points, ax=None, space=None, point_type=None, **point_draw_kwargs):
    """Plot points in one of the implemented manifolds.

    The implemented manifolds are:
    - the special orthogonal group SO(3)
    - the special Euclidean group SE(3)
    - the circle S1 and the sphere S2
    - the hyperbolic plane (the Poincare disk, the Poincare
      half plane and the Klein disk)
    - the Poincare polydisk
    - the Kendall shape space of 2D triangles
    - the Kendall shape space of 3D triangles

    Parameters
    ----------
    points : array-like, shape=[..., dim]
        Points to be plotted.
    space: str, optional, {'SO3_GROUP', 'SE3_GROUP', 'S1', 'S2',
        'H2_poincare_disk', 'H2_poincare_half_plane', 'H2_klein_disk',
        'poincare_polydisk', 'S32', 'M32', 'S33', 'M33'}
    point_type: str, optional, {'extrinsic', 'ball', 'half-space', 'pre-shape'}
    """
    if space not in IMPLEMENTED:
        raise NotImplementedError(
            'The plot function is not implemented'
            ' for space {}. The spaces available for visualization'
            ' are: {}.'.format(space, IMPLEMENTED))

    if points is None:
        raise ValueError("No points given for plotting.")

    if points.ndim < 2:
        points = gs.expand_dims(points, 0)

    if space in ('SO3_GROUP', 'SE3_GROUP'):
        if ax is None:
            ax = plt.subplot(111, projection='3d')
        if space == 'SE3_GROUP':
            ax_s = AX_SCALE * gs.amax(gs.abs(points[:, 3:6]))
        elif space == 'SO3_GROUP':
            ax_s = AX_SCALE * gs.amax(gs.abs(points[:, :3]))
        ax_s = float(ax_s)
        bounds = (-ax_s, ax_s)
        plt.setp(ax,
                 xlim=bounds,
                 ylim=bounds,
                 zlim=bounds,
                 xlabel='X',
                 ylabel='Y',
                 zlabel='Z')
        trihedrons = convert_to_trihedron(points, space=space)
        for t in trihedrons:
            t.draw(ax, **point_draw_kwargs)

    elif space == 'S1':
        circle = Circle()
        ax = circle.set_ax(ax=ax)
        circle.add_points(points)
        circle.draw(ax, **point_draw_kwargs)

    elif space == 'S2':
        sphere = Sphere()
        ax = sphere.set_ax(ax=ax)
        sphere.add_points(points)
        sphere.draw(ax, **point_draw_kwargs)

    elif space == 'H2_poincare_disk':
        if point_type is None:
            point_type = 'extrinsic'
        poincare_disk = PoincareDisk(point_type=point_type)
        ax = poincare_disk.set_ax(ax=ax)
        poincare_disk.add_points(points)
        poincare_disk.draw(ax, **point_draw_kwargs)
        plt.axis('off')

    elif space == 'poincare_polydisk':
        if point_type is None:
            point_type = 'extrinsic'
        n_disks = points.shape[1]
        poincare_poly_disk = PoincarePolyDisk(point_type=point_type,
                                              n_disks=n_disks)
        n_columns = int(gs.ceil(n_disks**0.5))
        n_rows = int(gs.ceil(n_disks / n_columns))

        axis_list = []

        for i_disk in range(n_disks):
            axis_list.append(ax.add_subplot(n_rows, n_columns, i_disk + 1))

        for i_disk, one_ax in enumerate(axis_list):
            ax = poincare_poly_disk.set_ax(ax=one_ax)
            poincare_poly_disk.clear_points()
            poincare_poly_disk.add_points(points[:, i_disk, ...])
            poincare_poly_disk.draw(ax, **point_draw_kwargs)

    elif space == 'H2_poincare_half_plane':
        if point_type is None:
            point_type = 'half-space'
        poincare_half_plane = PoincareHalfPlane(point_type=point_type)
        ax = poincare_half_plane.set_ax(points=points, ax=ax)
        poincare_half_plane.add_points(points)
        poincare_half_plane.draw(ax, **point_draw_kwargs)

    elif space == 'H2_klein_disk':
        klein_disk = KleinDisk()
        ax = klein_disk.set_ax(ax=ax)
        klein_disk.add_points(points)
        klein_disk.draw(ax, **point_draw_kwargs)

    elif space == 'SE2_GROUP':
        plane = SpecialEuclidean2()
        ax = plane.set_ax(ax=ax)
        plane.add_points(points)
        plane.draw(ax, **point_draw_kwargs)

    elif space == 'S32':
        sphere = KendallSphere()
        sphere.add_points(points)
        sphere.draw()
        sphere.draw_points()
        ax = sphere.ax

    elif space == 'M32':
        sphere = KendallSphere(point_type='extrinsic')
        sphere.add_points(points)
        sphere.draw()
        sphere.draw_points()
        ax = sphere.ax

    elif space == 'S33':
        disk = KendallDisk()
        disk.add_points(points)
        disk.draw()
        disk.draw_points()
        ax = disk.ax

    elif space == 'M33':
        disk = KendallDisk(point_type='extrinsic')
        disk.add_points(points)
        disk.draw()
        disk.draw_points()
        ax = disk.ax

    return ax