Beispiel #1
0
    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)
Beispiel #2
0
    def draw(self, n_r=7, n_theta=25, scale=0.05):
        """Draw the disk regularly sampled with corresponding triangles."""
        self.set_ax()
        self.ax.set_axis_off()
        plt.tight_layout()

        coords_r = gs.linspace(0.0, gs.pi / 4.0, n_r)
        coords_theta = gs.linspace(0.0, 2.0 * gs.pi, n_theta)

        coords_x = gs.to_numpy(gs.outer(coords_r, gs.cos(coords_theta)))
        coords_y = gs.to_numpy(gs.outer(coords_r, gs.sin(coords_theta)))

        self.ax.fill(
            list(coords_x[-1, :]),
            list(coords_y[-1, :]),
            color="grey",
            alpha=0.1,
            zorder=-1,
        )
        for i_r in range(n_r):
            self.ax.plot(
                coords_x[i_r, :],
                coords_y[i_r, :],
                linewidth=0.6,
                color="grey",
                alpha=0.6,
                zorder=-1,
            )
        for i_t in range(n_theta):
            self.ax.plot(
                coords_x[:, i_t],
                coords_y[:, i_t],
                linewidth=0.6,
                color="grey",
                alpha=0.6,
                zorder=-1,
            )

        for r in gs.linspace(0.0, gs.pi / 4, n_r):
            for theta in gs.linspace(0.0, 2.0 * gs.pi, n_theta // 2 + 1):
                if theta == 0.0:
                    self.draw_triangle(0.0, 0.0, scale)
                else:
                    self.draw_triangle(r, theta, scale)
Beispiel #3
0
def grad(y_pred,
         y_true,
         metric=SE3.left_canonical_metric,
         representation='vector'):
    """
    Closed-form for the gradient of pose_loss.

    :return: tangent vector at point y_pred.
    """
    if y_pred.ndim == 1:
        y_pred = gs.expand_dims(y_pred, axis=0)
    if y_true.ndim == 1:
        y_true = gs.expand_dims(y_true, axis=0)

    if representation == 'vector':
        grad = lie_group.grad(y_pred, y_true, SE3, metric)

    if representation == 'quaternion':

        y_pred_rot_vec = SO3.rotation_vector_from_quaternion(y_pred[:, :4])
        y_pred_pose = gs.hstack([y_pred_rot_vec, y_pred[:, 4:]])
        y_true_rot_vec = SO3.rotation_vector_from_quaternion(y_true[:, :4])
        y_true_pose = gs.hstack([y_true_rot_vec, y_true[:, 4:]])
        grad = lie_group.grad(y_pred_pose, y_true_pose, SE3, metric)

        differential = gs.zeros((1, 6, 7))

        upper_left_block = gs.zeros((1, 3, 4))
        lower_right_block = gs.zeros((1, 3, 3))
        quat_scalar = y_pred[:, :1]
        quat_vec = y_pred[:, 1:4]

        quat_vec_norm = gs.linalg.norm(quat_vec, axis=1)
        quat_sq_norm = quat_vec_norm**2 + quat_scalar**2
        # TODO(nina): check that this sq norm is 1?

        quat_arctan2 = gs.arctan2(quat_vec_norm, quat_scalar)
        differential_scalar = -2 * quat_vec / (quat_sq_norm)
        differential_vec = (
            2 *
            (quat_scalar / quat_sq_norm - 2 * quat_arctan2 / quat_vec_norm) *
            gs.outer(quat_vec, quat_vec) / quat_vec_norm**2 +
            2 * quat_arctan2 / quat_vec_norm * gs.eye(3))

        upper_left_block[0, :, :1] = differential_scalar.transpose()
        upper_left_block[0, :, 1:] = differential_vec
        lower_right_block[0, :, :] = gs.eye(3)

        differential[0, :3, :4] = upper_left_block
        differential[0, 3:, 4:] = lower_right_block

        grad = gs.matmul(grad, differential)

    grad = gs.squeeze(grad, axis=0)
    return grad
    def riemannian_submersion(point):
        """Compute the correlation matrix associated to an SPD matrix.

        Parameters
        ----------
        point : array-like, shape=[..., n, n]
            SPD matrix.

        Returns
        -------
        cor : array_like, shape=[..., n, n]
            Full rank correlation matrix.
        """
        diagonal = Matrices.diagonal(point)**(-0.5)
        return point * gs.outer(diagonal, diagonal)
    def diag_action(diagonal_vec, point):
        r"""Apply a diagonal matrix on an SPD matrices by congruence.

        The action of :math:`D` on :math:`\Sigma` is given by :math:`D
        \Sigma D. The diagonal matrix must be passed as a vector representing
        its diagonal.

        Parameters
        ----------
        diagonal_vec : array-like, shape=[..., n]
            Vector coefficient of the diagonal matrix.
        point : array-like, shape=[..., n, n]
            Symmetric Positive definite matrix.

        Returns
        -------
        mat : array-like, shape=[..., n, n]
            Symmetric matrix obtained by the action of `diagonal_vec` on
            `point`.
        """
        return point * gs.outer(diagonal_vec, diagonal_vec)
Beispiel #6
0
def grad(y_pred, y_true,
         metric=SO3.bi_invariant_metric,
         representation='vector'):

    y_pred = gs.expand_dims(y_pred, axis=0)
    y_true = gs.expand_dims(y_true, axis=0)

    if representation == 'vector':
        grad = lie_group.grad(y_pred, y_true, SO3, metric)

    if representation == 'quaternion':
        differential = gs.zeros((1, 6, 7))

        differential = gs.zeros((1, 3, 4))
        quat_scalar = y_pred[:, :1]
        quat_vec = y_pred[:, 1:]

        quat_vec_norm = gs.linalg.norm(quat_vec, axis=1)
        quat_sq_norm = quat_vec_norm ** 2 + quat_scalar ** 2

        quat_arctan2 = gs.arctan2(quat_vec_norm, quat_scalar)
        differential_scalar = - 2 * quat_vec / (quat_sq_norm)
        differential_vec = (2 * (quat_scalar / quat_sq_norm
                                 - 2 * quat_arctan2 / quat_vec_norm)
                            * gs.outer(quat_vec, quat_vec) / quat_vec_norm ** 2
                            + 2 * quat_arctan2 / quat_vec_norm * gs.eye(3))

        differential[0, :, :1] = differential_scalar.transpose()
        differential[0, :, 1:] = differential_vec

        y_pred = SO3.rotation_vector_from_quaternion(y_pred)
        y_true = SO3.rotation_vector_from_quaternion(y_true)

        grad = lie_group.grad(y_pred, y_true, SO3, metric)

        grad = gs.matmul(grad, differential)

    grad = gs.squeeze(grad, axis=0)
    return grad
    def jacobian_translation(self, point, left_or_right='left'):
        """
        Compute the jacobian matrix of the differential
        of the left/right translations from the identity to point in SO(n).
        """
        assert self.belongs(point)
        assert left_or_right in ('left', 'right')

        if self.n == 3:
            point = self.regularize(point)
            n_points, _ = point.shape

            angle = gs.linalg.norm(point, axis=1)
            angle = gs.expand_dims(angle, axis=1)

            coef_1 = gs.zeros([n_points, 1])
            coef_2 = gs.zeros([n_points, 1])

            mask_0 = gs.isclose(angle, 0)
            mask_0 = gs.squeeze(mask_0, axis=1)
            coef_1[mask_0] = (1 - angle[mask_0]**2 / 12 -
                              angle[mask_0]**4 / 720 -
                              angle[mask_0]**6 / 30240)
            coef_2[mask_0] = (1 / 12 + angle[mask_0]**2 / 720 +
                              angle[mask_0]**4 / 30240 +
                              angle[mask_0]**6 / 1209600)

            mask_pi = gs.isclose(angle, gs.pi)
            mask_pi = gs.squeeze(mask_pi, axis=1)
            delta_angle = angle[mask_pi] - gs.pi
            coef_1[mask_pi] = (-gs.pi * delta_angle / 4 - delta_angle**2 / 4 -
                               gs.pi * delta_angle**3 / 48 -
                               delta_angle**4 / 48 -
                               gs.pi * delta_angle**5 / 480 -
                               delta_angle**6 / 480)
            coef_2[mask_pi] = (1 - coef_1[mask_pi]) / angle[mask_pi]**2

            mask_else = ~mask_0 & ~mask_pi
            coef_1[mask_else] = ((angle[mask_else] / 2) /
                                 gs.tan(angle[mask_else] / 2))
            coef_2[mask_else] = (1 - coef_1[mask_else]) / angle[mask_else]**2

            jacobian = gs.zeros((n_points, self.dimension, self.dimension))

            for i in range(n_points):
                if left_or_right == 'left':
                    jacobian[i] = (coef_1[i] * gs.identity(self.dimension) +
                                   coef_2[i] * gs.outer(point[i], point[i]) +
                                   skew_matrix_from_vector(point[i]) / 2)

                else:
                    jacobian[i] = (coef_1[i] * gs.identity(self.dimension) +
                                   coef_2[i] * gs.outer(point[i], point[i]) -
                                   skew_matrix_from_vector(point[i]) / 2)

        else:
            if left_or_right == 'right':
                raise NotImplementedError(
                    'The jacobian of the right translation'
                    ' is not implemented.')
            jacobian = self.matrix_from_rotation_vector(point)

        assert jacobian.ndim == 3

        return jacobian
Beispiel #8
0
    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)
Beispiel #9
0
    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
Beispiel #10
0
    def jacobian_translation(self, point, left_or_right='left'):
        """Compute the jacobian matrix corresponding to translation.

        Compute the jacobian matrix of the differential
        of the left/right translations from the identity to point in SO(3).

        Parameters
        ----------
        point : array-like, shape=[..., 3]
        left_or_right : str, {'left', 'right'}, optional
            default: 'left'
        point_type : str, {'vector', 'matrix'}, optional
            default: self.default_point_type

        Returns
        -------
        jacobian : array-like, shape=[..., 3, 3]
        """
        geomstats.error.check_parameter_accepted_values(
            left_or_right, 'left_or_right', ['left', 'right'])

        point = self.regularize(point)

        n_points, _ = point.shape

        angle = gs.linalg.norm(point, axis=-1)
        angle = gs.expand_dims(angle, axis=-1)

        coef_1 = gs.zeros([n_points, 1])
        coef_2 = gs.zeros([n_points, 1])

        # This avoids dividing by 0.
        mask_0 = gs.isclose(angle, 0.)
        mask_0_float = gs.cast(mask_0, gs.float32) + self.epsilon

        coef_1 += mask_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_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)

        # This avoids dividing by 0.
        mask_pi = gs.isclose(angle, gs.pi)
        mask_pi_float = gs.cast(mask_pi, gs.float32) + self.epsilon

        delta_angle = angle - gs.pi
        coef_1 += mask_pi_float * (TAYLOR_COEFFS_1_AT_PI[1] * delta_angle +
                                   TAYLOR_COEFFS_1_AT_PI[2] * delta_angle**2 +
                                   TAYLOR_COEFFS_1_AT_PI[3] * delta_angle**3 +
                                   TAYLOR_COEFFS_1_AT_PI[4] * delta_angle**4 +
                                   TAYLOR_COEFFS_1_AT_PI[5] * delta_angle**5 +
                                   TAYLOR_COEFFS_1_AT_PI[6] * delta_angle**6)

        angle += mask_0_float
        coef_2 += mask_pi_float * ((1 - coef_1) / angle**2)

        # This avoids dividing by 0.
        mask_else = ~mask_0 & ~mask_pi
        mask_else_float = gs.cast(mask_else, gs.float32) + self.epsilon

        # This avoids division by 0.
        angle += mask_pi_float
        coef_1 += mask_else_float * ((angle / 2) / gs.tan(angle / 2))
        coef_2 += mask_else_float * ((1 - coef_1) / angle**2)
        jacobian = gs.zeros((n_points, self.dim, self.dim))
        n_points_tensor = gs.array(n_points)
        for i in range(n_points):
            # This avoids dividing by 0.
            mask_i_float = (gs.get_mask_i_float(i, n_points_tensor) +
                            self.epsilon)

            sign = -1.
            if left_or_right == 'left':
                sign = +1.

            jacobian_i = (coef_1[i] * gs.eye(self.dim) +
                          coef_2[i] * gs.outer(point[i], point[i]) +
                          sign * self.skew_matrix_from_vector(point[i]) / 2.)

            jacobian += gs.einsum('n,ij->nij', mask_i_float, jacobian_i)

        return jacobian
Beispiel #11
0
    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\'.')
Beispiel #12
0
    def jacobian_translation(self,
                             point,
                             left_or_right='left',
                             point_type=None):
        """
        Compute the jacobian matrix of the differential
        of the left/right translations from the identity to point in SO(n).
        """
        assert left_or_right in ('left', 'right')

        if point_type is None:
            point_type = self.default_point_type
        assert self.belongs(point, point_type)

        if point_type == 'vector':
            if self.n == 3:
                point = self.regularize(point, point_type=point_type)

                n_points, _ = point.shape

                angle = gs.linalg.norm(point, axis=1)
                angle = gs.expand_dims(angle, axis=1)

                coef_1 = gs.zeros([n_points, 1])
                coef_2 = gs.zeros([n_points, 1])

                mask_0 = gs.isclose(angle, 0)
                mask_0 = gs.squeeze(mask_0, axis=1)
                coef_1[mask_0] = (TAYLOR_COEFFS_1_AT_0[0] +
                                  TAYLOR_COEFFS_1_AT_0[2] * angle[mask_0]**2 +
                                  TAYLOR_COEFFS_1_AT_0[4] * angle[mask_0]**4 +
                                  TAYLOR_COEFFS_1_AT_0[6] * angle[mask_0]**6)
                coef_2[mask_0] = (TAYLOR_COEFFS_2_AT_0[0] +
                                  TAYLOR_COEFFS_2_AT_0[2] * angle[mask_0]**2 +
                                  TAYLOR_COEFFS_2_AT_0[4] * angle[mask_0]**4 +
                                  TAYLOR_COEFFS_2_AT_0[6] * angle[mask_0]**6)

                mask_pi = gs.isclose(angle, gs.pi)
                mask_pi = gs.squeeze(mask_pi, axis=1)
                delta_angle = angle[mask_pi] - gs.pi
                coef_1[mask_pi] = (TAYLOR_COEFFS_1_AT_PI[1] * delta_angle +
                                   TAYLOR_COEFFS_1_AT_PI[2] * delta_angle**2 +
                                   TAYLOR_COEFFS_1_AT_PI[3] * delta_angle**3 +
                                   TAYLOR_COEFFS_1_AT_PI[4] * delta_angle**4 +
                                   TAYLOR_COEFFS_1_AT_PI[5] * delta_angle**5 +
                                   TAYLOR_COEFFS_1_AT_PI[6] * delta_angle**6)

                coef_2[mask_pi] = (1 - coef_1[mask_pi]) / angle[mask_pi]**2

                mask_else = ~mask_0 & ~mask_pi
                coef_1[mask_else] = ((angle[mask_else] / 2) /
                                     gs.tan(angle[mask_else] / 2))
                coef_2[mask_else] = ((1 - coef_1[mask_else]) /
                                     angle[mask_else]**2)

                jacobian = gs.zeros((n_points, self.dimension, self.dimension))
                for i in range(n_points):
                    sign = -1
                    if left_or_right == 'left':
                        sign = +1

                    jacobian[i] = (
                        coef_1[i] * gs.identity(self.dimension) +
                        coef_2[i] * gs.outer(point[i], point[i]) +
                        sign * self.skew_matrix_from_vector(point[i]) / 2)

            else:
                if left_or_right == 'right':
                    raise NotImplementedError(
                        'The jacobian of the right translation'
                        ' is not implemented.')
                jacobian = self.matrix_from_rotation_vector(point)

            assert gs.ndim(jacobian) == 3

        elif point_type == 'matrix':
            raise NotImplementedError()

        return jacobian