Example #1
0
    def _update_medoid_indexes(self, distances, labels, medoid_indices):

        for cluster in range(self.n_clusters):

            cluster_index = gs.where(labels == cluster)[0]

            if len(cluster_index) == 0:
                logging.warning('One cluster is empty.')
                continue

            in_cluster_distances = distances[
                cluster_index,
                gs.expand_dims(cluster_index, axis=-1)]

            in_cluster_all_costs = gs.sum(in_cluster_distances, axis=1)

            min_cost_index = gs.argmin(in_cluster_all_costs)

            min_cost = in_cluster_all_costs[min_cost_index]

            current_cost = in_cluster_all_costs[gs.argmax(
                cluster_index == medoid_indices[cluster])]

            if min_cost < current_cost:
                medoid_indices[cluster] = cluster_index[min_cost_index]
Example #2
0
    def sample(self, point, n_samples=1):
        """Sample from the categorical distribution.

        Sample from the categorical distribution with parameters provided by
        point. This gives samples in the simplex.

        Parameters
        ----------
        point : array-like, shape=[..., dim + 1]
            Parameters of a categorical distribution, i.e. probabilities
            associated to dim + 1 outcomes.
        n_samples : int
            Number of points to sample with each set of parameters in point.
            Optional, default: 1.

        Returns
        -------
        samples : array-like, shape=[..., n_samples]
            Samples from categorical distributions.
        """
        geomstats.errors.check_belongs(point, self)
        point = gs.to_ndarray(point, to_ndim=2)
        samples = []
        for param in point:
            counts = multinomial.rvs(1, param, size=n_samples)
            samples.append(gs.argmax(counts, axis=-1))
        return samples[0] if len(point) == 1 else gs.stack(samples)
Example #3
0
    def split_data_in_classes(X, y, c):
        """Split a labelled dataset in sub-datasets of each label.

        Parameters
        ----------
        X : array-like, shape=[n_samples, dim]
                              if point_type='vector'
                              shape=[n_samples, n, n]
                              if point_type='matrix'
            Labelled dataset, where n_samples is the number of samples
            and n_features is the number of features.
        y : array-like, shape=[n_samples, n_classes]
            Labels, where n_classes is the number of classes.
        c : int
            Class index

        Returns
        -------
        X_split : array-like, shape=[n_samples_in_class, dim]
                             if point_type='vector'
                             shape=[n_samples_in_class, n, n]
                             if point_type='matrix'
            Labelled dataset,
            Where n_samples_in_class is the number of samples in class c
        """
        return X[gs.argmax(y, axis=1) == c]
    problem = Problem(manifold=sphere, cost=cost, egrad=egrad)
    solver = SteepestDescent()
    return solver.solve(problem)


if __name__ == '__main__':
    if os.environ.get('GEOMSTATS_BACKEND') != 'numpy':
        raise SystemExit(
            'This example currently only supports the numpy backend')

    ambient_dim = 128
    mat = gs.random.normal(size=(ambient_dim, ambient_dim))
    mat = 0.5 * (mat + mat.T)

    eigenvalues, eigenvectors = gs.linalg.eig(mat)
    dominant_eigenvector = eigenvectors[:, gs.argmax(eigenvalues)]

    dominant_eigenvector_estimate = estimate_dominant_eigenvector(mat)
    if (gs.sign(dominant_eigenvector[0]) !=
            gs.sign(dominant_eigenvector_estimate[0])):
        dominant_eigenvector_estimate = -dominant_eigenvector_estimate

    logging.info(
        'l2-norm of dominant eigenvector: %s',
        gs.linalg.norm(dominant_eigenvector))
    logging.info(
        'l2-norm of dominant eigenvector estimate: %s',
        gs.linalg.norm(dominant_eigenvector_estimate))
    error_norm = gs.linalg.norm(
        dominant_eigenvector - dominant_eigenvector_estimate)
    logging.info('l2-norm of difference vector: %s', error_norm)
    def rotation_vector_from_matrix(self, rot_mat):
        """
        In 3D, convert rotation matrix to rotation vector
        (axis-angle representation).

        Get the angle through the trace of the rotation matrix:
        The eigenvalues are:
        1, cos(angle) + i sin(angle), cos(angle) - i sin(angle)
        so that: trace = 1 + 2 cos(angle), -1 <= trace <= 3

        Get the rotation vector through the formula:
        S_r = angle / ( 2 * sin(angle) ) (R - R^T)

        For the edge case where the angle is close to pi,
        the formulation is derived by going from rotation matrix to unit
        quaternion to axis-angle:
         r = angle * v / |v|, where (w, v) is a unit quaternion.

        In nD, the rotation vector stores the n(n-1)/2 values of the
        skew-symmetric matrix representing the rotation.
        """
        rot_mat = gs.to_ndarray(rot_mat, to_ndim=3)
        n_rot_mats, mat_dim_1, mat_dim_2 = rot_mat.shape
        assert mat_dim_1 == mat_dim_2 == self.n

        rot_mat = closest_rotation_matrix(rot_mat)

        if self.n == 3:
            trace = gs.trace(rot_mat, axis1=1, axis2=2)
            trace = gs.to_ndarray(trace, to_ndim=2, axis=1)
            assert trace.shape == (n_rot_mats, 1), trace.shape

            cos_angle = .5 * (trace - 1)
            cos_angle = gs.clip(cos_angle, -1, 1)
            angle = gs.arccos(cos_angle)

            rot_mat_transpose = gs.transpose(rot_mat, axes=(0, 2, 1))
            rot_vec = vector_from_skew_matrix(rot_mat - rot_mat_transpose)

            mask_0 = gs.isclose(angle, 0)
            mask_0 = gs.squeeze(mask_0, axis=1)
            rot_vec[mask_0] = (rot_vec[mask_0] * (.5 -
                                                  (trace[mask_0] - 3.) / 12.))

            mask_pi = gs.isclose(angle, gs.pi)
            mask_pi = gs.squeeze(mask_pi, axis=1)

            # choose the largest diagonal element
            # to avoid a square root of a negative number
            a = 0
            if gs.any(mask_pi):
                a = gs.argmax(gs.diagonal(rot_mat[mask_pi], axis1=1, axis2=2))
            b = gs.mod(a + 1, 3)
            c = gs.mod(a + 2, 3)

            # compute the axis vector
            sq_root = gs.sqrt(
                (rot_mat[mask_pi, a, a] - rot_mat[mask_pi, b, b] -
                 rot_mat[mask_pi, c, c] + 1.))
            rot_vec_pi = gs.zeros((sum(mask_pi), self.dimension))
            rot_vec_pi[:, a] = sq_root / 2.
            rot_vec_pi[:, b] = (
                (rot_mat[mask_pi, b, a] + rot_mat[mask_pi, a, b]) /
                (2. * sq_root))
            rot_vec_pi[:, c] = (
                (rot_mat[mask_pi, c, a] + rot_mat[mask_pi, a, c]) /
                (2. * sq_root))

            rot_vec[mask_pi] = (angle[mask_pi] * rot_vec_pi /
                                gs.linalg.norm(rot_vec_pi))

            mask_else = ~mask_0 & ~mask_pi
            rot_vec[mask_else] = (angle[mask_else] /
                                  (2. * gs.sin(angle[mask_else])) *
                                  rot_vec[mask_else])
        else:
            skew_mat = self.embedding_manifold.group_log_from_identity(rot_mat)
            rot_vec = vector_from_skew_matrix(skew_mat)

        return self.regularize(rot_vec)
Example #6
0
    def rotation_vector_from_matrix(self, rot_mat):
        r"""Convert rotation matrix (in 3D) to rotation vector (axis-angle).

        Get the angle through the trace of the rotation matrix:
        The eigenvalues are:
        :math:`\{1, \cos(angle) + i \sin(angle), \cos(angle) - i \sin(angle)\}`
        so that:
        :math:`trace = 1 + 2 \cos(angle), \{-1 \leq trace \leq 3\}`

        Get the rotation vector through the formula:
        :math:`S_r = \frac{angle}{(2 * \sin(angle) ) (R - R^T)}`

        For the edge case where the angle is close to pi,
        the formulation is derived by using the following equality (see the
        Axis-angle representation on Wikipedia):
        :math:`outer(r, r) = \frac{1}{2} (R + I_3)`
        In nD, the rotation vector stores the :math:`n(n-1)/2` values
        of the skew-symmetric matrix representing the rotation.

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

        Returns
        -------
        regularized_rot_vec : array-like, shape=[..., 3]
        """
        n_rot_mats, _, _ = rot_mat.shape

        trace = gs.trace(rot_mat, axis1=1, axis2=2)
        trace = gs.to_ndarray(trace, to_ndim=2, axis=1)
        trace_num = gs.clip(trace, -1, 3)
        angle = gs.arccos(0.5 * (trace_num - 1))
        rot_mat_transpose = gs.transpose(rot_mat, axes=(0, 2, 1))
        rot_vec_not_pi = self.vector_from_skew_matrix(rot_mat -
                                                      rot_mat_transpose)
        mask_0 = gs.cast(gs.isclose(angle, 0.), gs.float32)
        mask_pi = gs.cast(gs.isclose(angle, gs.pi, atol=1e-2), gs.float32)
        mask_else = (1 - mask_0) * (1 - mask_pi)

        numerator = 0.5 * mask_0 + angle * mask_else
        denominator = (1 - angle**2 /
                       6) * mask_0 + 2 * gs.sin(angle) * mask_else + mask_pi

        rot_vec_not_pi = rot_vec_not_pi * numerator / denominator

        vector_outer = 0.5 * (gs.eye(3) + rot_mat)
        gs.set_diag(
            vector_outer,
            gs.maximum(0., gs.diagonal(vector_outer, axis1=1, axis2=2)))
        squared_diag_comp = gs.diagonal(vector_outer, axis1=1, axis2=2)
        diag_comp = gs.sqrt(squared_diag_comp)
        norm_line = gs.linalg.norm(vector_outer, axis=2)
        max_line_index = gs.argmax(norm_line, axis=1)
        selected_line = gs.get_slice(vector_outer,
                                     (range(n_rot_mats), max_line_index))
        signs = gs.sign(selected_line)
        rot_vec_pi = angle * signs * diag_comp

        rot_vec = rot_vec_not_pi + mask_pi * rot_vec_pi

        return self.regularize(rot_vec)