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
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)
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)
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]