def skew_matrix_from_vector(vec):
    """
    In 3D, compute the skew-symmetric matrix,
    known as the cross-product of a vector,
    associated to the vector vec.

    In nD, fill a skew-symmetric matrix with
    the values of the vector.
    """
    vec = gs.to_ndarray(vec, to_ndim=2)
    n_vecs, vec_dim = vec.shape

    mat_dim = int((1 + gs.sqrt(1 + 8 * vec_dim)) / 2)
    skew_mat = gs.zeros((n_vecs, ) + (mat_dim, ) * 2)

    if vec_dim == 3:
        for i in range(n_vecs):
            skew_mat[i] = gs.cross(gs.eye(vec_dim), vec[i])
    else:
        upper_triangle_indices = gs.triu_indices(mat_dim, k=1)
        for i in range(n_vecs):
            skew_mat[i][upper_triangle_indices] = vec[i]
            skew_mat[i] = skew_mat[i] - skew_mat[i].transpose()

    assert skew_mat.ndim == 3
    return skew_mat
Exemplo n.º 2
0
    def draw_vector(self, tangent_vec, base_point, tol=1e-03, **kwargs):
        """Draw one vector in the tangent space to disk at a base point."""
        r_bp, th_bp = self.convert_to_polar_coordinates(base_point)
        bp = gs.array([
            gs.cos(th_bp) * gs.sin(2 * r_bp),
            gs.sin(th_bp) * gs.sin(2 * r_bp),
            gs.cos(2 * r_bp)
        ])
        r_exp, th_exp = self.convert_to_polar_coordinates(
            METRIC_S33.exp(
                tol * tangent_vec / METRIC_S33.norm(tangent_vec, base_point),
                base_point))
        exp = gs.array([
            gs.cos(th_exp) * gs.sin(2 * r_exp),
            gs.sin(th_exp) * gs.sin(2 * r_exp),
            gs.cos(2 * r_exp)
        ])
        pole = gs.array([0., 0., 1.])

        tv = exp - gs.dot(exp, bp) * bp
        u_tv = tv / gs.linalg.norm(tv)
        u_r = (gs.dot(pole, bp) * bp -
               pole) / gs.linalg.norm(gs.dot(pole, bp) * bp - pole)
        u_th = gs.cross(bp, u_r)
        x_r, x_th = gs.dot(u_tv, u_r), gs.dot(u_tv, u_th)

        bp = self.convert_to_planar_coordinates(base_point)
        u_r = bp / gs.linalg.norm(bp)
        u_th = gs.array([[0., -1.], [1., 0.]]) @ u_r
        tv = METRIC_S33.norm(tangent_vec,
                             base_point) * (x_r * u_r + x_th * u_th)

        self.ax.quiver(bp[0], bp[1], tv[0], tv[1], **kwargs)
Exemplo n.º 3
0
    def lie_bracket(self, tangent_vector_a, tangent_vector_b, base_point=None):
        """Compute the lie bracket of two tangent vectors.

        For matrix Lie groups with tangent vectors A,B at the same base point P
        this is given by (translate to identity, compute commutator, go back)
        :math:`[A,B] = A_P^{-1}B - B_P^{-1}A`

        Parameters
        ----------
        tangent_vector_a : shape=[..., n, n]
        tangent_vector_b : shape=[..., n, n]
        base_point : array-like, shape=[..., n, n]

        Returns
        -------
        bracket : array-like, shape=[..., n, n]
        """
        return gs.cross(tangent_vector_a, tangent_vector_b)
Exemplo n.º 4
0
    def random_von_mises_fisher(self,
                                mu=None,
                                kappa=10,
                                n_samples=1,
                                max_iter=100):
        """Sample with the von Mises-Fisher distribution.

        This distribution corresponds to the maximum entropy distribution
        given a mean. In dimension 2, a closed form expression is available.
        In larger dimension, rejection sampling is used according to [Wood94]_

        References
        ----------
        https://en.wikipedia.org/wiki/Von_Mises-Fisher_distribution

        .. [Wood94]   Wood, Andrew T. A. “Simulation of the von Mises Fisher
                      Distribution.” Communications in Statistics - Simulation
                      and Computation, June 27, 2007.
                      https://doi.org/10.1080/03610919408813161.

        Parameters
        ----------
        mu : array-like, shape=[dim]
            Mean parameter of the distribution.
        kappa : float
            Kappa parameter of the von Mises distribution.
            Optional, default: 10.
        n_samples : int
            Number of samples.
            Optional, default: 1.

        Returns
        -------
        point : array-like, shape=[..., 3]
            Points sampled on the sphere in extrinsic coordinates
            in Euclidean space of dimension 3.
        """
        dim = self.dim

        if dim == 2:
            angle = 2. * gs.pi * gs.random.rand(n_samples)
            angle = gs.to_ndarray(angle, to_ndim=2, axis=1)
            unit_vector = gs.hstack((gs.cos(angle), gs.sin(angle)))
            scalar = gs.random.rand(n_samples)

            coord_z = 1. + 1. / kappa * gs.log(scalar + (1. - scalar) *
                                               gs.exp(gs.array(-2. * kappa)))
            coord_z = gs.to_ndarray(coord_z, to_ndim=2, axis=1)
            coord_xy = gs.sqrt(1. - coord_z**2) * unit_vector
            sample = gs.hstack((coord_xy, coord_z))

            if mu is not None:
                rot_vec = gs.cross(gs.array([0., 0., 1.]), mu)
                rot_vec *= gs.arccos(mu[-1]) / gs.linalg.norm(rot_vec)
                rot = SpecialOrthogonal(
                    3, 'vector').matrix_from_rotation_vector(rot_vec)
                sample = gs.matmul(sample, gs.transpose(rot))
        else:
            if mu is None:
                mu = gs.array([0.] * dim + [1.])
            # rejection sampling in the general case
            sqrt = gs.sqrt(4 * kappa**2. + dim**2)
            envelop_param = (-2 * kappa + sqrt) / dim
            node = (1. - envelop_param) / (1. + envelop_param)
            correction = kappa * node + dim * gs.log(1. - node**2)

            n_accepted, n_iter = 0, 0
            result = []
            while (n_accepted < n_samples) and (n_iter < max_iter):
                sym_beta = beta.rvs(dim / 2,
                                    dim / 2,
                                    size=n_samples - n_accepted)
                coord_z = (1 - (1 + envelop_param) * sym_beta) / (
                    1 - (1 - envelop_param) * sym_beta)
                accept_tol = gs.random.rand(n_samples - n_accepted)
                criterion = (kappa * coord_z + dim * gs.log(1 - node * coord_z)
                             - correction) > gs.log(accept_tol)
                result.append(coord_z[criterion])
                n_accepted += gs.sum(criterion)
                n_iter += 1
            if n_accepted < n_samples:
                logging.warning(
                    'Maximum number of iteration reached in rejection '
                    'sampling before n_samples were accepted.')
            coord_z = gs.concatenate(result)
            coord_rest = self.random_uniform(n_accepted)
            coord_rest = self.to_tangent(coord_rest, mu)
            coord_rest = self.projection(coord_rest)
            coord_rest = gs.einsum('...,...i->...i', gs.sqrt(1 - coord_z**2),
                                   coord_rest)
            sample = coord_rest + coord_z[:, None] * mu[None, :]

        return sample if n_samples > 1 else sample[0]