def test_assignment_with_booleans_single_index(self): np_array = _np.array([[2., 5.]]) gs_array = gs.array([[2., 5.]]) np_mask = _np.array([True]) gs_mask = gs.array([True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * _np.ones_like(np_array[~np_mask]) np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs.ones_like(gs_array[~gs_mask]), ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[2., 5.]]) gs_array = gs.array([[2., 5.]]) np_mask = _np.array([True]) gs_mask = gs.array([True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[2., 5.]]) gs_array = gs.array([[2., 5.]]) np_mask = _np.array([False]) gs_mask = gs.array([False]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * _np.ones_like(np_array[~np_mask]) np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs.ones_like(gs_array[~gs_mask]), ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[2., 5.]]) gs_array = gs.array([[2., 5.]]) np_mask = _np.array([False]) gs_mask = gs.array([False]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result)
def aux_differential_power(power, tangent_vec, base_point): """Compute the differential of the matrix power. Auxiliary function to the functions differential_power and inverse_differential_power. Parameters ---------- power : float Power function to differentiate. tangent_vec : array_like, shape=[..., n, n] Tangent vector at base point. base_point : array_like, shape=[..., n, n] Base point. Returns ------- eigvectors : array-like, shape=[..., n, n] transp_eigvectors : array-like, shape=[..., n, n] numerator : array-like, shape=[..., n, n] denominator : array-like, shape=[..., n, n] temp_result : array-like, shape=[..., n, n] """ eigvalues, eigvectors = gs.linalg.eigh(base_point) if power == 0: powered_eigvalues = gs.log(eigvalues) elif power == math.inf: powered_eigvalues = gs.exp(eigvalues) else: powered_eigvalues = eigvalues ** power denominator = eigvalues[..., :, None] - eigvalues[..., None, :] numerator = powered_eigvalues[..., :, None] - powered_eigvalues[..., None, :] if power == 0: numerator = gs.where(denominator == 0, gs.ones_like(numerator), numerator) denominator = gs.where( denominator == 0, eigvalues[..., :, None], denominator ) elif power == math.inf: numerator = gs.where( denominator == 0, powered_eigvalues[..., :, None], numerator ) denominator = gs.where( denominator == 0, gs.ones_like(numerator), denominator ) else: numerator = gs.where( denominator == 0, power * powered_eigvalues[..., :, None], numerator ) denominator = gs.where( denominator == 0, eigvalues[..., :, None], denominator ) transp_eigvectors = Matrices.transpose(eigvectors) temp_result = Matrices.mul(transp_eigvectors, tangent_vec, eigvectors) return (eigvectors, transp_eigvectors, numerator, denominator, temp_result)
def _log_translation_transform(self, rot_vec): """Compute matrix associated to rot_vec for the translation part in log. Parameters ---------- rot_vec : array-like, shape=[..., 3] Returns ------- transform : array-like, shape=[..., 3, 3] Matrix to be applied to the translation part in log """ n_samples = rot_vec.shape[0] angle = gs.linalg.norm(rot_vec, axis=1) angle = gs.to_ndarray(angle, to_ndim=2, axis=1) skew_mat = self.rotations.skew_matrix_from_vector(rot_vec) sq_skew_mat = gs.matmul(skew_mat, skew_mat) mask_close_0 = gs.isclose(angle, 0.0) mask_close_pi = gs.isclose(angle, gs.pi) mask_else = ~mask_close_0 & ~mask_close_pi mask_close_0_float = gs.cast(mask_close_0, gs.float32) mask_close_pi_float = gs.cast(mask_close_pi, gs.float32) mask_else_float = gs.cast(mask_else, gs.float32) mask_0 = gs.isclose(angle, 0.0, atol=1e-7) mask_0_float = gs.cast(mask_0, gs.float32) angle += mask_0_float * gs.ones_like(angle) coef_1 = -0.5 * gs.ones_like(angle) coef_2 = gs.zeros_like(angle) coef_2 += mask_close_0_float * (1.0 / 12.0 + angle**2 / 720.0 + angle**4 / 30240.0 + angle**6 / 1209600.0) delta_angle = angle - gs.pi coef_2 += mask_close_pi_float * ( 1.0 / PI2 + (PI2 - 8.0) * delta_angle / (4.0 * PI3) - ((PI2 - 12.0) * delta_angle**2 / (4.0 * PI4)) + ((-192.0 + 12.0 * PI2 + PI4) * delta_angle**3 / (48.0 * PI5)) - ((-240.0 + 12.0 * PI2 + PI4) * delta_angle**4 / (48.0 * PI6)) + ((-2880.0 + 120.0 * PI2 + 10.0 * PI4 + PI6) * delta_angle**5 / (480.0 * PI7)) - ((-3360 + 120.0 * PI2 + 10.0 * PI4 + PI6) * delta_angle**6 / (480.0 * PI8))) psi = 0.5 * angle * gs.sin(angle) / (1 - gs.cos(angle)) coef_2 += mask_else_float * (1 - psi) / (angle**2) term_1 = gs.einsum("...i,...ij->...ij", coef_1, skew_mat) term_2 = gs.einsum("...i,...ij->...ij", coef_2, sq_skew_mat) term_id = gs.array([gs.eye(3)] * n_samples) transform = term_id + term_1 + term_2 return transform
def _exp_translation_transform(self, rot_vec): """Compute matrix associated to rot_vec for the translation part in exp. Parameters ---------- rot_vec : array-like, shape=[..., 3] Returns ------- transform : array-like, shape=[..., 3, 3] Matrix to be applied to the translation part in exp. """ n_samples = rot_vec.shape[0] angle = gs.linalg.norm(rot_vec, axis=-1) angle = gs.to_ndarray(angle, to_ndim=2, axis=1) skew_mat = self.rotations.skew_matrix_from_vector(rot_vec) sq_skew_mat = gs.matmul(skew_mat, skew_mat) mask_0 = gs.equal(angle, 0.) mask_close_0 = gs.isclose(angle, 0.) & ~mask_0 mask_else = ~mask_0 & ~mask_close_0 mask_0_float = gs.cast(mask_0, gs.float32) mask_close_0_float = gs.cast(mask_close_0, gs.float32) mask_else_float = gs.cast(mask_else, gs.float32) coef_1 = gs.zeros_like(angle) coef_2 = gs.zeros_like(angle) coef_1 += mask_0_float * 1. / 2. * gs.ones_like(angle) coef_2 += mask_0_float * 1. / 6. * gs.ones_like(angle) coef_1 += mask_close_0_float * ( TAYLOR_COEFFS_1_AT_0[0] + TAYLOR_COEFFS_1_AT_0[2] * angle ** 2 + TAYLOR_COEFFS_1_AT_0[4] * angle ** 4 + TAYLOR_COEFFS_1_AT_0[6] * angle ** 6) coef_2 += mask_close_0_float * ( TAYLOR_COEFFS_2_AT_0[0] + TAYLOR_COEFFS_2_AT_0[2] * angle ** 2 + TAYLOR_COEFFS_2_AT_0[4] * angle ** 4 + TAYLOR_COEFFS_2_AT_0[6] * angle ** 6) angle += mask_0_float * 1. coef_1 += mask_else_float * ((1. - gs.cos(angle)) / angle ** 2) coef_2 += mask_else_float * ((angle - gs.sin(angle)) / angle ** 3) term_1 = gs.einsum('...i,...ij->...ij', coef_1, skew_mat) term_2 = gs.einsum('...i,...ij->...ij', coef_2, sq_skew_mat) term_id = gs.array([gs.eye(3)] * n_samples) transform = term_id + term_1 + term_2 return transform
def test_assignment(self): gs_array_1 = gs.ones(3) self.assertRaises(ValueError, gs.assignment, gs_array_1, [.1, 2., 1.], [0, 1]) np_array_1 = _np.ones(3) gs_array_1 = gs.ones_like(gs.array(np_array_1)) np_array_1[2] = 1.5 gs_result = gs.assignment(gs_array_1, 1.5, 2) self.assertAllCloseToNp(gs_result, np_array_1) np_array_1_list = _np.ones(3) gs_array_1_list = gs.ones_like(gs.array(np_array_1_list)) indices = [1, 2] np_array_1_list[indices] = 1.5 gs_result = gs.assignment(gs_array_1_list, 1.5, indices) self.assertAllCloseToNp(gs_result, np_array_1_list) np_array_2 = _np.zeros((3, 2)) gs_array_2 = gs.zeros_like(gs.array(np_array_2)) np_array_2[0, :] = 1 gs_result = gs.assignment(gs_array_2, 1, 0, axis=1) self.assertAllCloseToNp(gs_result, np_array_2) np_array_3 = _np.zeros((3, 3)) gs_array_3 = gs.zeros_like(gs.array(np_array_3)) np_array_3[0, 1] = 1 gs_result = gs.assignment(gs_array_3, 1, (0, 1)) self.assertAllCloseToNp(gs_result, np_array_3) np_array_4 = _np.zeros((3, 3, 2)) gs_array_4 = gs.zeros_like(gs.array(np_array_4)) np_array_4[0, :, 1] = 1 gs_result = gs.assignment(gs_array_4, 1, (0, 1), axis=1) self.assertAllCloseToNp(gs_result, np_array_4) gs_array_4_arr = gs.zeros_like(gs.array(np_array_4)) gs_result = gs.assignment(gs_array_4_arr, 1, gs.array((0, 1)), axis=1) self.assertAllCloseToNp(gs_result, np_array_4) np_array_4_list = _np.zeros((3, 3, 2)) gs_array_4_list = gs.zeros_like(gs.array(np_array_4_list)) np_array_4_list[(0, 1), :, (1, 1)] = 1 gs_result = gs.assignment(gs_array_4_list, 1, [(0, 1), (1, 1)], axis=1) self.assertAllCloseToNp(gs_result, np_array_4_list)
def test_assignment_by_sum(self): np_array_1 = _np.ones(3) gs_array_1 = gs.ones_like(gs.array(np_array_1)) np_array_1[2] += 1.5 gs_result = gs.assignment_by_sum(gs_array_1, 1.5, 2) self.assertAllCloseToNp(gs_result, np_array_1) np_array_1_list = _np.ones(3) gs_array_1_list = gs.ones_like(gs.array(np_array_1_list)) indices = [1, 2] np_array_1_list[indices] += 1.5 gs_result = gs.assignment_by_sum(gs_array_1_list, 1.5, indices) self.assertAllCloseToNp(gs_result, np_array_1) np_array_2 = _np.zeros((3, 2)) gs_array_2 = gs.zeros_like(gs.array(np_array_2)) np_array_2[0, :] += 1 gs_result = gs.assignment_by_sum(gs_array_2, 1, 0, axis=0) self.assertAllCloseToNp(gs_result, np_array_2) np_array_3 = _np.zeros((3, 3)) gs_array_3 = gs.zeros_like(gs.array(np_array_3)) np_array_3[0, 1] += 1 gs_result = gs.assignment_by_sum(gs_array_3, 1, (0, 1)) self.assertAllCloseToNp(gs_result, np_array_3) np_array_4 = _np.zeros((3, 3, 2)) gs_array_4 = gs.zeros_like(gs.array(np_array_4)) np_array_4[0, :, 1] += 1 gs_result = gs.assignment_by_sum(gs_array_4, 1, (0, 1), axis=1) self.assertAllCloseToNp(gs_result, np_array_4) np_array_4_list = _np.zeros((3, 3, 2)) gs_array_4_list = gs.zeros_like(gs.array(np_array_4_list)) np_array_4_list[(0, 1), :, (1, 1)] += 1 gs_result = gs.assignment_by_sum( gs_array_4_list, 1, [(0, 1), (1, 1)], axis=1) self.assertAllCloseToNp(gs_result, np_array_4)
def draw(self, n_theta=25, n_phi=13, scale=0.05, elev=60.0, azim=0.0): """Draw the sphere regularly sampled with corresponding triangles.""" self.set_ax() self.set_view(elev=elev, azim=azim) self.ax.set_axis_off() plt.tight_layout() coords_theta = gs.linspace(0.0, 2.0 * gs.pi, n_theta) coords_phi = gs.linspace(0.0, gs.pi, n_phi) coords_x = gs.to_numpy(0.5 * gs.outer(gs.sin(coords_phi), gs.cos(coords_theta))) coords_y = gs.to_numpy(0.5 * gs.outer(gs.sin(coords_phi), gs.sin(coords_theta))) coords_z = gs.to_numpy( 0.5 * gs.outer(gs.cos(coords_phi), gs.ones_like(coords_theta)) ) self.ax.plot_surface( coords_x, coords_y, coords_z, rstride=1, cstride=1, color="grey", linewidth=0, alpha=0.1, zorder=-1, ) self.ax.plot_wireframe( coords_x, coords_y, coords_z, linewidths=0.6, color="grey", alpha=0.6, zorder=-1, ) def lim(theta): return ( gs.pi - self.elev + (2.0 * self.elev - gs.pi) / gs.pi * abs(self.azim - theta) ) for theta in gs.linspace(0.0, 2.0 * gs.pi, n_theta // 2 + 1): for phi in gs.linspace(0.0, gs.pi, n_phi): if theta <= self.azim + gs.pi and phi <= lim(theta): self.draw_triangle(theta, phi, scale) if theta > self.azim + gs.pi and phi < lim( 2.0 * self.azim + 2.0 * gs.pi - theta ): self.draw_triangle(theta, phi, scale)
def homogeneous_representation(rotation, translation, output_shape, constant=1.0): r"""Embed rotation, translation couples into n+1 square matrices. Construct a block matrix of size :math: `n + 1 \times n + 1` of the form .. math:: \matvec{cc}{R & t\\ 0&c} where :math: `R` is a square matrix, :math: `t` a vector of size :math: `n`, and :math: `c` a constant (either 0 or 1 should be used). Parameters ---------- rotation : array-like, shape=[..., n, n] Square Matrix. translation : array-like, shape=[..., n] Vector. output_shape : tuple of int Desired output shape. This is need for vectorization. constant : float or array-like of shape [...] Constant to use at the last line and column of the square matrix. Optional, default: 1. Returns ------- mat: array-like, shape=[..., n + 1, n + 1] Square Matrix of size n + 1. It can represent an element of the special euclidean group or its Lie algebra. """ mat = gs.concatenate((rotation, translation[..., None]), axis=-1) last_line = gs.zeros(output_shape)[..., -1] if isinstance(constant, float): last_col = constant * gs.ones_like(translation)[..., None, -1] else: last_col = constant[..., None] last_line = gs.concatenate([last_line[..., :-1], last_col], axis=-1) mat = gs.concatenate((mat, last_line[..., None, :]), axis=-2) return mat
def to_tangent(self, vector, base_point): """Project a matrix to the tangent space at a base point. The tangent space to the space of correlation matrices is the space of symmetric matrices with null diagonal. Parameters ---------- vector : array-like, shape=[..., n, n] Matrix to project base_point : array-like, shape=[..., n, n] Correlation matrix. Returns ------- tangent_vec : array-like, shape=[..., n, n] Symmetric matrix with 0 diagonal. """ sym = self.embedding_space.to_tangent(vector, base_point) mask_diag = gs.ones_like(vector) - gs.eye(self.n) return sym * mask_diag
def exp_from_identity(self, tangent_vec, point_type=None): """Compute group exponential of the tangent vector at the identity. Parameters ---------- tangent_vec: array-like, shape=[n_samples, {dim, [n + 1, n + 1]}] point_type: str, {'vector', 'matrix'}, optional default: self.default_point_type Returns ------- group_exp: array-like, shape=[n_samples, {dim, [n + 1, n + 1]}] the group exponential of the tangent vectors calculated at the identity """ if point_type == 'vector': rotations = self.rotations dim_rotations = rotations.dim rot_vec = tangent_vec[:, :dim_rotations] rot_vec = self.rotations.regularize(rot_vec, point_type=point_type) translation = tangent_vec[:, dim_rotations:] angle = gs.linalg.norm(rot_vec, axis=1) angle = gs.to_ndarray(angle, to_ndim=2, axis=1) skew_mat = self.rotations.skew_matrix_from_vector(rot_vec) sq_skew_mat = gs.matmul(skew_mat, skew_mat) mask_0 = gs.equal(angle, 0.) mask_close_0 = gs.isclose(angle, 0.) & ~mask_0 mask_else = ~mask_0 & ~mask_close_0 mask_0_float = gs.cast(mask_0, gs.float32) mask_close_0_float = gs.cast(mask_close_0, gs.float32) mask_else_float = gs.cast(mask_else, gs.float32) angle += mask_0_float * gs.ones_like(angle) coef_1 = gs.zeros_like(angle) coef_2 = gs.zeros_like(angle) coef_1 += mask_0_float * 1. / 2. * gs.ones_like(angle) coef_2 += mask_0_float * 1. / 6. * gs.ones_like(angle) coef_1 += mask_close_0_float * ( TAYLOR_COEFFS_1_AT_0[0] + TAYLOR_COEFFS_1_AT_0[2] * angle**2 + TAYLOR_COEFFS_1_AT_0[4] * angle**4 + TAYLOR_COEFFS_1_AT_0[6] * angle**6) coef_2 += mask_close_0_float * ( TAYLOR_COEFFS_2_AT_0[0] + TAYLOR_COEFFS_2_AT_0[2] * angle**2 + TAYLOR_COEFFS_2_AT_0[4] * angle**4 + TAYLOR_COEFFS_2_AT_0[6] * angle**6) coef_1 += mask_else_float * ((1. - gs.cos(angle)) / angle**2) coef_2 += mask_else_float * ((angle - gs.sin(angle)) / angle**3) n_tangent_vecs, _ = tangent_vec.shape exp_translation = gs.zeros((n_tangent_vecs, self.n)) for i in range(n_tangent_vecs): translation_i = translation[i] term_1_i = coef_1[i] * gs.dot(translation_i, gs.transpose(skew_mat[i])) term_2_i = coef_2[i] * gs.dot(translation_i, gs.transpose(sq_skew_mat[i])) mask_i_float = gs.get_mask_i_float(i, n_tangent_vecs) exp_translation += gs.outer( mask_i_float, translation_i + term_1_i + term_2_i) group_exp = gs.concatenate([rot_vec, exp_translation], axis=1) group_exp = self.regularize(group_exp, point_type=point_type) return group_exp if point_type == 'matrix': return GeneralLinear.exp(tangent_vec) raise ValueError('Invalid point_type, expected \'vector\' or ' '\'matrix\'.')
def group_log_from_identity(self, point): """ Compute the group logarithm of the point at the identity. """ assert self.belongs(point) point = self.regularize(point) rotations = self.rotations dim_rotations = rotations.dimension rot_vec = point[:, :dim_rotations] angle = gs.linalg.norm(rot_vec, axis=1) angle = gs.to_ndarray(angle, to_ndim=2, axis=1) translation = point[:, dim_rotations:] group_log = gs.zeros_like(point) group_log[:, :dim_rotations] = rot_vec skew_rot_vec = so_group.skew_matrix_from_vector(rot_vec) sq_skew_rot_vec = gs.matmul(skew_rot_vec, skew_rot_vec) mask_close_0 = gs.isclose(angle, 0) mask_close_0 = gs.squeeze(mask_close_0, axis=1) mask_close_pi = gs.isclose(angle, gs.pi) mask_close_pi = gs.squeeze(mask_close_pi, axis=1) mask_else = ~mask_close_0 & ~mask_close_pi coef_1 = - 0.5 * gs.ones_like(angle) coef_2 = gs.zeros_like(angle) coef_2[mask_close_0] = (1. / 12. + angle[mask_close_0] ** 2 / 720. + angle[mask_close_0] ** 4 / 30240. + angle[mask_close_0] ** 6 / 1209600.) delta_angle = angle[mask_close_pi] - gs.pi coef_2[mask_close_pi] = (1. / PI2 + (PI2 - 8.) * delta_angle / (4. * PI3) - ((PI2 - 12.) * delta_angle ** 2 / (4. * PI4)) + ((-192. + 12. * PI2 + PI4) * delta_angle ** 3 / (48. * PI5)) - ((-240. + 12. * PI2 + PI4) * delta_angle ** 4 / (48. * PI6)) + ((-2880. + 120. * PI2 + 10. * PI4 + PI6) * delta_angle ** 5 / (480. * PI7)) - ((-3360 + 120. * PI2 + 10. * PI4 + PI6) * delta_angle ** 6 / (480. * PI8))) psi = (0.5 * angle[mask_else] * gs.sin(angle[mask_else]) / (1 - gs.cos(angle[mask_else]))) coef_2[mask_else] = (1 - psi) / (angle[mask_else] ** 2) n_points, _ = point.shape group_log_translation = gs.zeros((n_points, self.n)) for i in range(n_points): translation_i = translation[i] term_1_i = coef_1[i] * gs.dot(translation_i, gs.transpose(skew_rot_vec[i])) term_2_i = coef_2[i] * gs.dot(translation_i, gs.transpose(sq_skew_rot_vec[i])) group_log_translation[i] = translation_i + term_1_i + term_2_i group_log[:, dim_rotations:] = group_log_translation assert group_log.ndim == 2 return group_log
def test_assignment_by_sum(self): gs_array_1 = gs.ones(3) self.assertRaises(ValueError, gs.assignment_by_sum, gs_array_1, [.1, 2., 1.], [0, 1]) np_array_1 = _np.ones(3) gs_array_1 = gs.ones_like(gs.array(np_array_1)) np_array_1[2] += 1.5 gs_result = gs.assignment_by_sum(gs_array_1, 1.5, 2) self.assertAllCloseToNp(gs_result, np_array_1) gs_result_list = gs.assignment_by_sum(gs_array_1, [2., 1.5], [0, 2]) np_array_1[0] += 2. self.assertAllCloseToNp(gs_result_list, np_array_1) np_array_1_list = _np.ones(3) gs_array_1_list = gs.ones_like(gs.array(np_array_1_list)) indices = [1, 2] np_array_1_list[indices] += 1.5 gs_result = gs.assignment_by_sum(gs_array_1_list, 1.5, indices) self.assertAllCloseToNp(gs_result, np_array_1_list) np_array_2 = _np.zeros((3, 2)) gs_array_2 = gs.zeros_like(gs.array(np_array_2)) np_array_2[0, :] += 1 gs_result = gs.assignment_by_sum(gs_array_2, 1, 0, axis=1) self.assertAllCloseToNp(gs_result, np_array_2) np_array_3 = _np.zeros((3, 3)) gs_array_3 = gs.zeros_like(gs.array(np_array_3)) np_array_3[0, 1] += 1 gs_result = gs.assignment_by_sum(gs_array_3, 1, (0, 1)) self.assertAllCloseToNp(gs_result, np_array_3) np_array_4 = _np.zeros((3, 3, 2)) gs_array_4 = gs.zeros_like(gs.array(np_array_4)) np_array_4[0, :, 1] += 1 gs_result = gs.assignment_by_sum(gs_array_4, 1, (0, 1), axis=1) self.assertAllCloseToNp(gs_result, np_array_4) np_array_4_list = _np.zeros((3, 3, 2)) gs_array_4_list = gs.zeros_like(gs.array(np_array_4_list)) np_array_4_list[(0, 1), :, (1, 1)] += 1 gs_result = gs.assignment_by_sum(gs_array_4_list, 1, [(0, 1), (1, 1)], axis=1) self.assertAllCloseToNp(gs_result, np_array_4_list) n_samples = 3 theta = _np.array([0.1, 0.2, 0.3, 0.4, 5.5]) phi = _np.array([0.11, 0.22, 0.33, 0.44, -.55]) np_array = _np.ones((n_samples, 5, 4)) gs_array = gs.array(np_array) gs_array = gs.assignment_by_sum(gs_array, gs.cos(theta) * gs.cos(phi), (0, 0), axis=1) gs_array = gs.assignment_by_sum(gs_array, -gs.sin(theta) * gs.sin(phi), (0, 1), axis=1) np_array[0, :, 0] += _np.cos(theta) * _np.cos(phi) np_array[0, :, 1] -= _np.sin(theta) * _np.sin(phi) # TODO (ninamiolane): This test fails 15% of the time, # when gs and _np computations are in the reverse order. # We should investigate this. self.assertAllCloseToNp(gs_array, np_array) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([[False, False], [False, True], [True, True]]) gs_mask = gs.array([[False, False], [False, True], [True, True]]) np_array[np_mask] += _np.zeros_like(np_array[np_mask]) np_array[~np_mask] += 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment_by_sum(gs_array, values_mask, gs_mask) gs_result = gs.assignment_by_sum(gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result)
def exp_from_identity(self, tangent_vec): """Compute group exponential of the tangent vector at the identity. Parameters ---------- tangent_vec: array-like, shape=[..., 3] Returns ------- group_exp: array-like, shape=[..., 3] The group exponential of the tangent vectors calculated at the identity. """ rotations = self.rotations dim_rotations = rotations.dim rot_vec = tangent_vec[..., :dim_rotations] rot_vec = self.rotations.regularize(rot_vec) translation = tangent_vec[..., dim_rotations:] angle = gs.linalg.norm(rot_vec, axis=-1) angle = gs.to_ndarray(angle, to_ndim=2, axis=1) skew_mat = self.rotations.skew_matrix_from_vector(rot_vec) sq_skew_mat = gs.matmul(skew_mat, skew_mat) mask_0 = gs.equal(angle, 0.) mask_close_0 = gs.isclose(angle, 0.) & ~mask_0 mask_else = ~mask_0 & ~mask_close_0 mask_0_float = gs.cast(mask_0, gs.float32) mask_close_0_float = gs.cast(mask_close_0, gs.float32) mask_else_float = gs.cast(mask_else, gs.float32) angle += mask_0_float * gs.ones_like(angle) coef_1 = gs.zeros_like(angle) coef_2 = gs.zeros_like(angle) coef_1 += mask_0_float * 1. / 2. * gs.ones_like(angle) coef_2 += mask_0_float * 1. / 6. * gs.ones_like(angle) coef_1 += mask_close_0_float * (TAYLOR_COEFFS_1_AT_0[0] + TAYLOR_COEFFS_1_AT_0[2] * angle**2 + TAYLOR_COEFFS_1_AT_0[4] * angle**4 + TAYLOR_COEFFS_1_AT_0[6] * angle**6) coef_2 += mask_close_0_float * (TAYLOR_COEFFS_2_AT_0[0] + TAYLOR_COEFFS_2_AT_0[2] * angle**2 + TAYLOR_COEFFS_2_AT_0[4] * angle**4 + TAYLOR_COEFFS_2_AT_0[6] * angle**6) coef_1 += mask_else_float * ((1. - gs.cos(angle)) / angle**2) coef_2 += mask_else_float * ((angle - gs.sin(angle)) / angle**3) n_tangent_vecs, _ = tangent_vec.shape exp_translation = gs.zeros((n_tangent_vecs, self.n)) for i in range(n_tangent_vecs): translation_i = translation[i] term_1_i = coef_1[i] * gs.dot(translation_i, gs.transpose(skew_mat[i])) term_2_i = coef_2[i] * gs.dot(translation_i, gs.transpose(sq_skew_mat[i])) mask_i_float = gs.get_mask_i_float(i, n_tangent_vecs) exp_translation += gs.outer(mask_i_float, translation_i + term_1_i + term_2_i) group_exp = gs.concatenate([rot_vec, exp_translation], axis=1) group_exp = self.regularize(group_exp) return group_exp
def test_assignment_with_booleans_many_indices(self): np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([True, False, True]) gs_mask = gs.array([True, False, True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * _np.ones_like(np_array[~np_mask]) np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs.ones_like(gs_array[~gs_mask]), ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([False, True, True]) gs_mask = gs.array([False, True, True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * _np.ones_like(np_array[~np_mask]) np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs.ones_like(gs_array[~gs_mask]), ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([True, True, True]) gs_mask = gs.array([True, True, True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * _np.ones_like(np_array[~np_mask]) np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs.ones_like(gs_array[~gs_mask]), ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([True, True, True]) gs_mask = gs.array([True, True, True]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([False, False, False]) gs_mask = gs.array([False, False, False]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result) np_array = _np.array([[22., 55.], [33., 88.], [77., 99.]]) gs_array = gs.array([[22., 55.], [33., 88.], [77., 99.]]) np_mask = _np.array([[False, False], [False, True], [True, True]]) gs_mask = gs.array([[False, False], [False, True], [True, True]]) np_array[np_mask] = _np.zeros_like(np_array[np_mask]) np_array[~np_mask] = 4 * np_array[~np_mask] np_result = np_array values_mask = gs.zeros_like(gs_array[gs_mask]) gs_result = gs.assignment(gs_array, values_mask, gs_mask) gs_result = gs.assignment(gs_result, 4 * gs_array[~gs_mask], ~gs_mask) self.assertAllCloseToNp(gs_result, np_result)
def log_from_identity(self, point): """Compute the group logarithm of the point at the identity. Parameters ---------- point: array-like, shape=[..., 3] Returns ------- group_log: array-like, shape=[..., 3] the group logarithm in the Lie algbra """ point = self.regularize(point) rotations = self.rotations dim_rotations = rotations.dim rot_vec = point[:, :dim_rotations] angle = gs.linalg.norm(rot_vec, axis=1) angle = gs.to_ndarray(angle, to_ndim=2, axis=1) translation = point[:, dim_rotations:] skew_rot_vec = rotations.skew_matrix_from_vector(rot_vec) sq_skew_rot_vec = gs.matmul(skew_rot_vec, skew_rot_vec) mask_close_0 = gs.isclose(angle, 0.) mask_close_pi = gs.isclose(angle, gs.pi) mask_else = ~mask_close_0 & ~mask_close_pi mask_close_0_float = gs.cast(mask_close_0, gs.float32) mask_close_pi_float = gs.cast(mask_close_pi, gs.float32) mask_else_float = gs.cast(mask_else, gs.float32) mask_0 = gs.isclose(angle, 0., atol=1e-6) mask_0_float = gs.cast(mask_0, gs.float32) angle += mask_0_float * gs.ones_like(angle) coef_1 = -0.5 * gs.ones_like(angle) coef_2 = gs.zeros_like(angle) coef_2 += mask_close_0_float * (1. / 12. + angle**2 / 720. + angle**4 / 30240. + angle**6 / 1209600.) delta_angle = angle - gs.pi coef_2 += mask_close_pi_float * ( 1. / PI2 + (PI2 - 8.) * delta_angle / (4. * PI3) - ((PI2 - 12.) * delta_angle**2 / (4. * PI4)) + ((-192. + 12. * PI2 + PI4) * delta_angle**3 / (48. * PI5)) - ((-240. + 12. * PI2 + PI4) * delta_angle**4 / (48. * PI6)) + ((-2880. + 120. * PI2 + 10. * PI4 + PI6) * delta_angle**5 / (480. * PI7)) - ((-3360 + 120. * PI2 + 10. * PI4 + PI6) * delta_angle**6 / (480. * PI8))) psi = 0.5 * angle * gs.sin(angle) / (1 - gs.cos(angle)) coef_2 += mask_else_float * (1 - psi) / (angle**2) n_points, _ = point.shape log_translation = gs.zeros((n_points, self.n)) for i in range(n_points): translation_i = translation[i] term_1_i = coef_1[i] * gs.dot(translation_i, gs.transpose(skew_rot_vec[i])) term_2_i = coef_2[i] * gs.dot(translation_i, gs.transpose(sq_skew_rot_vec[i])) mask_i_float = gs.get_mask_i_float(i, n_points) log_translation += gs.outer(mask_i_float, translation_i + term_1_i + term_2_i) return gs.concatenate([rot_vec, log_translation], axis=1)
def group_exp_from_identity(self, tangent_vec, point_type=None): """ Compute the group exponential of the tangent vector at the identity. """ if point_type is None: point_type = self.default_point_type if point_type == 'vector': tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=2) rotations = self.rotations dim_rotations = rotations.dimension rot_vec = tangent_vec[:, :dim_rotations] rot_vec = self.rotations.regularize(rot_vec, point_type=point_type) translation = tangent_vec[:, dim_rotations:] angle = gs.linalg.norm(rot_vec, axis=1) angle = gs.to_ndarray(angle, to_ndim=2, axis=1) skew_mat = self.rotations.skew_matrix_from_vector(rot_vec) sq_skew_mat = gs.matmul(skew_mat, skew_mat) mask_0 = gs.equal(angle, 0.) mask_close_0 = gs.isclose(angle, 0.) & ~mask_0 mask_else = ~mask_0 & ~mask_close_0 mask_0_float = gs.cast(mask_0, gs.float32) mask_close_0_float = gs.cast(mask_close_0, gs.float32) mask_else_float = gs.cast(mask_else, gs.float32) angle += mask_0_float * gs.ones_like(angle) coef_1 = gs.zeros_like(angle) coef_2 = gs.zeros_like(angle) coef_1 += mask_0_float * 1. / 2. * gs.ones_like(angle) coef_2 += mask_0_float * 1. / 6. * gs.ones_like(angle) coef_1 += mask_close_0_float * ( TAYLOR_COEFFS_1_AT_0[0] + TAYLOR_COEFFS_1_AT_0[2] * angle**2 + TAYLOR_COEFFS_1_AT_0[4] * angle**4 + TAYLOR_COEFFS_1_AT_0[6] * angle**6) coef_2 += mask_close_0_float * ( TAYLOR_COEFFS_2_AT_0[0] + TAYLOR_COEFFS_2_AT_0[2] * angle**2 + TAYLOR_COEFFS_2_AT_0[4] * angle**4 + TAYLOR_COEFFS_2_AT_0[6] * angle**6) coef_1 += mask_else_float * ((1. - gs.cos(angle)) / angle**2) coef_2 += mask_else_float * ((angle - gs.sin(angle)) / angle**3) n_tangent_vecs, _ = tangent_vec.shape group_exp_translation = gs.zeros((n_tangent_vecs, self.n)) for i in range(n_tangent_vecs): translation_i = translation[i] term_1_i = coef_1[i] * gs.dot(translation_i, gs.transpose(skew_mat[i])) term_2_i = coef_2[i] * gs.dot(translation_i, gs.transpose(sq_skew_mat[i])) mask_i_float = gs.get_mask_i_float(i, n_tangent_vecs) group_exp_translation += mask_i_float * (translation_i + term_1_i + term_2_i) group_exp = gs.concatenate([rot_vec, group_exp_translation], axis=1) group_exp = self.regularize(group_exp, point_type=point_type) return group_exp elif point_type == 'matrix': raise NotImplementedError()
def fit(self, x, y=None): """Fit centers in all the input points. Parameters ---------- x : array-like, shape=[..., n_features] Clusters of points. """ @joblib.delayed @joblib.wrap_non_picklable_objects def pickable_mean(points, weights): """Frechet Mean of all points weighted by weights. Parameters ---------- points : array-like, shape=[..., n_features] Clusters of points. weights : array-like, Weight associated with each point in cluster. """ return self.mean.fit(points, weights=weights).estimate_ if self.init_centers == "from_points": n_points = x.shape[0] centers = x[gs.random.randint(n_points, size=( self.n_centers, )), :] elif self.init_centers == "random_uniform": centers = self.manifold.random_uniform(n_samples=self.n_centers) for _ in range(self.max_iter): dists = self.dist_intersets(centers, x) if self.kernel == "flat": weights = gs.ones_like(dists) weights[dists > self.bandwidth] = 0.0 weights = weights / gs.sum(weights, axis=1, keepdims=True) points_to_average, nonzero_weights = [], [] for j in range(self.n_centers): indexes = gs.where(weights[j] > 0) nonzero_weights += [ weights[j][indexes], ] points_to_average += [ x[indexes], ] pool = joblib.Parallel(n_jobs=self.n_jobs) out = pool( pickable_mean(points_to_average[j], nonzero_weights[j]) for j in range(self.n_centers)) new_centers = gs.array(out) displacements = [self.metric.dist(centers, new_centers)] centers = new_centers if (gs.array(displacements) < self.tol).all(): break self.centers = centers
def group_log_from_identity(self, point, point_type=None): """ Compute the group logarithm of the point at the identity. """ if point_type is None: point_type = self.default_point_type point = self.regularize(point, point_type=point_type) rotations = self.rotations dim_rotations = rotations.dimension if point_type == 'vector': rot_vec = point[:, :dim_rotations] angle = gs.linalg.norm(rot_vec, axis=1) angle = gs.to_ndarray(angle, to_ndim=2, axis=1) translation = point[:, dim_rotations:] skew_rot_vec = rotations.skew_matrix_from_vector(rot_vec) sq_skew_rot_vec = gs.matmul(skew_rot_vec, skew_rot_vec) mask_close_0 = gs.isclose(angle, 0.) mask_close_pi = gs.isclose(angle, gs.pi) mask_else = ~mask_close_0 & ~mask_close_pi mask_close_0_float = gs.cast(mask_close_0, gs.float32) mask_close_pi_float = gs.cast(mask_close_pi, gs.float32) mask_else_float = gs.cast(mask_else, gs.float32) mask_0 = gs.isclose(angle, 0., atol=1e-6) mask_0_float = gs.cast(mask_0, gs.float32) angle += mask_0_float * gs.ones_like(angle) coef_1 = -0.5 * gs.ones_like(angle) coef_2 = gs.zeros_like(angle) coef_2 += mask_close_0_float * (1. / 12. + angle**2 / 720. + angle**4 / 30240. + angle**6 / 1209600.) delta_angle = angle - gs.pi coef_2 += mask_close_pi_float * ( 1. / PI2 + (PI2 - 8.) * delta_angle / (4. * PI3) - ((PI2 - 12.) * delta_angle**2 / (4. * PI4)) + ((-192. + 12. * PI2 + PI4) * delta_angle**3 / (48. * PI5)) - ((-240. + 12. * PI2 + PI4) * delta_angle**4 / (48. * PI6)) + ((-2880. + 120. * PI2 + 10. * PI4 + PI6) * delta_angle**5 / (480. * PI7)) - ((-3360 + 120. * PI2 + 10. * PI4 + PI6) * delta_angle**6 / (480. * PI8))) psi = 0.5 * angle * gs.sin(angle) / (1 - gs.cos(angle)) coef_2 += mask_else_float * (1 - psi) / (angle**2) n_points, _ = point.shape group_log_translation = gs.zeros((n_points, self.n)) for i in range(n_points): translation_i = translation[i] term_1_i = coef_1[i] * gs.dot(translation_i, gs.transpose(skew_rot_vec[i])) term_2_i = coef_2[i] * gs.dot(translation_i, gs.transpose(sq_skew_rot_vec[i])) mask_i_float = gs.get_mask_i_float(i, n_points) group_log_translation += mask_i_float * (translation_i + term_1_i + term_2_i) group_log = gs.concatenate([rot_vec, group_log_translation], axis=1) assert gs.ndim(group_log) == 2 elif point_type == 'matrix': raise NotImplementedError() return group_log
def tangent_extrinsic_to_spherical(self, tangent_vec, base_point=None, base_point_spherical=None): """Convert tangent vector from extrinsic to spherical coordinates. Convert a tangent vector from the extrinsic coordinates in Euclidean space to the spherical coordinates in the hypersphere for. Spherical coordinates are considered from the north pole [0., 0., 1.]. This method is only implemented in dimension 2. Parameters ---------- tangent_vec : array-like, shape=[..., dim] Tangent vector to the sphere, in spherical coordinates. base_point : array-like, shape=[..., dim] Point on the sphere. Unused if `base_point_spherical` is given. Optional, default : None. base_point_spherical : array-like, shape=[..., dim] Point on the sphere, in spherical coordinates. Either `base_point` or `base_point_spherical` must be given. Optional, default : None. Returns ------- tangent_vec_spherical : array-like, shape=[..., dim + 1] Tangent vector to the sphere, at base point, in spherical coordinates relative to the north pole [0., 0., 1.]. """ if self.dim != 2: raise NotImplementedError( "The conversion from to extrinsic coordinates " "spherical coordinates is implemented" " only in dimension 2.") if base_point is None and base_point_spherical is None: raise ValueError('A base point must be given, either in ' 'extrinsic or in spherical coordinates.') if base_point_spherical is None and base_point is not None: base_point_spherical = self.extrinsic_to_spherical(base_point) axes = (2, 0, 1) if base_point_spherical.ndim == 2 else (0, 1) theta = base_point_spherical[..., 0] phi = base_point_spherical[..., 1] theta_safe = gs.where(gs.abs(theta) < gs.atol, gs.atol, theta) zeros = gs.zeros_like(theta) jac_close_0 = gs.array([[gs.ones_like(theta), zeros, zeros], [zeros, gs.ones_like(theta), zeros]]) jac = gs.array([[ gs.cos(theta) * gs.cos(phi), gs.cos(theta) * gs.sin(phi), -gs.sin(theta) ], [ -gs.sin(phi) / gs.sin(theta_safe), gs.cos(phi) / gs.sin(theta_safe), zeros ]]) jac = gs.transpose(jac, axes) jac_close_0 = gs.transpose(jac_close_0, axes) theta_criterion = gs.einsum('...,...ij->...ij', theta, gs.ones_like(jac)) jac = gs.where(gs.abs(theta_criterion) < gs.atol, jac_close_0, jac) tangent_vec_spherical = gs.einsum("...ij,...j->...i", jac, tangent_vec) return tangent_vec_spherical