def log_test_data(self):
        theta = gs.pi / 5.0
        rot_vec_base_point = theta / gs.sqrt(3.0) * gs.array([1.0, 1.0, 1.0])
        # Note: the rotation vector for the reference point
        # needs to be regularized.

        # The Logarithm of a point at itself gives 0.
        expected = gs.array([0.0, 0.0, 0.0])

        # General case: this is the inverse test of test 1 for Riemannian exp
        expected = gs.pi / 4 * gs.array([1.0, 0.0, 0.0])
        phi = (gs.pi / 10) / (gs.tan(gs.array(gs.pi / 10)))
        skew = gs.array([[0.0, -1.0, 1.0], [1.0, 0.0, -1.0], [-1.0, 1.0, 0.0]])
        jacobian = (phi * gs.eye(3) + (1 - phi) / 3 * gs.ones([3, 3]) + gs.pi /
                    (10 * gs.sqrt(3.0)) * skew)
        inv_jacobian = gs.linalg.inv(jacobian)
        aux = gs.dot(inv_jacobian, expected)
        rot_vec_2 = SpecialOrthogonal(3, "vector").compose(
            rot_vec_base_point, aux)

        smoke_data = [
            dict(
                point=rot_vec_base_point,
                base_point=rot_vec_base_point,
                expected=gs.array([0.0, 0.0, 0.0]),
            ),
            dict(
                point=rot_vec_2,
                base_point=rot_vec_base_point,
                expected=expected,
            ),
        ]
        return self.generate_tests(smoke_data)
Esempio n. 2
0
    def log(self, point, base_point):
        """
        Riemannian logarithm of a point wrt a base point.
        """
        point = gs.to_ndarray(point, to_ndim=2)
        base_point = gs.to_ndarray(base_point, to_ndim=2)

        norm_base_point = self.embedding_metric.norm(base_point)
        norm_point = self.embedding_metric.norm(point)
        inner_prod = self.embedding_metric.inner_product(base_point, point)
        cos_angle = inner_prod / (norm_base_point * norm_point)
        cos_angle = gs.clip(cos_angle, -1., 1.)

        angle = gs.arccos(cos_angle)
        angle = gs.to_ndarray(angle, to_ndim=1)
        angle = gs.to_ndarray(angle, to_ndim=2, axis=1)

        mask_0 = gs.isclose(angle, 0.)
        mask_else = gs.equal(mask_0, gs.array(False))

        mask_0_float = gs.cast(mask_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. + INV_SIN_TAYLOR_COEFFS[1] * angle**2 +
                                  INV_SIN_TAYLOR_COEFFS[3] * angle**4 +
                                  INV_SIN_TAYLOR_COEFFS[5] * angle**6 +
                                  INV_SIN_TAYLOR_COEFFS[7] * angle**8)
        coef_2 += mask_0_float * (1. + INV_TAN_TAYLOR_COEFFS[1] * angle**2 +
                                  INV_TAN_TAYLOR_COEFFS[3] * angle**4 +
                                  INV_TAN_TAYLOR_COEFFS[5] * angle**6 +
                                  INV_TAN_TAYLOR_COEFFS[7] * angle**8)

        # This avoids division by 0.
        angle += mask_0_float * 1.

        coef_1 += mask_else_float * angle / gs.sin(angle)
        coef_2 += mask_else_float * angle / gs.tan(angle)

        log = (gs.einsum('ni,nj->nj', coef_1, point) -
               gs.einsum('ni,nj->nj', coef_2, base_point))

        mask_same_values = gs.isclose(point, base_point)

        mask_else = gs.equal(mask_same_values, gs.array(False))
        mask_else_float = gs.cast(mask_else, gs.float32)
        mask_else_float = gs.to_ndarray(mask_else_float, to_ndim=1)
        mask_else_float = gs.to_ndarray(mask_else_float, to_ndim=2)
        mask_not_same_points = gs.sum(mask_else_float, axis=1)
        mask_same_points = gs.isclose(mask_not_same_points, 0.)
        mask_same_points = gs.cast(mask_same_points, gs.float32)
        mask_same_points = gs.to_ndarray(mask_same_points, to_ndim=2, axis=1)

        mask_same_points_float = gs.cast(mask_same_points, gs.float32)

        log -= mask_same_points_float * log

        return log
 def exp_test_data(self):
     theta = gs.pi / 5.0
     rot_vec_base_point = theta / gs.sqrt(3.0) * gs.array([1.0, 1.0, 1.0])
     rot_vec_2 = gs.pi / 4 * gs.array([1.0, 0.0, 0.0])
     phi = (gs.pi / 10) / (gs.tan(gs.array(gs.pi / 10)))
     skew = gs.array([[0.0, -1.0, 1.0], [1.0, 0.0, -1.0], [-1.0, 1.0, 0.0]])
     jacobian = (phi * gs.eye(3) + (1 - phi) / 3 * gs.ones([3, 3]) + gs.pi /
                 (10 * gs.sqrt(3.0)) * skew)
     inv_jacobian = gs.linalg.inv(jacobian)
     expected = SpecialOrthogonal(3, "vector").compose(
         (gs.pi / 5.0) / gs.sqrt(3.0) * gs.array([1.0, 1.0, 1.0]),
         gs.dot(inv_jacobian, rot_vec_2),
     )
     smoke_data = [
         dict(
             tangent_vec=gs.array([0.0, 0.0, 0.0]),
             base_point=rot_vec_base_point,
             expected=rot_vec_base_point,
         ),
         dict(
             tangent_vec=rot_vec_2,
             base_point=rot_vec_base_point,
             expected=expected,
         ),
     ]
     return self.generate_tests(smoke_data)
Esempio n. 4
0
def asymptotic_modulation(dim, theta):
    """Compute the asymptotic modulation factor.

    Parameters
    ----------
    dim: dimension of the sphere (embedded in R^{dim+1})
    theta: radius of the bubble distribution

    Returns
    -------
    tuple (modulation factor, std-dev on the modulation factor)
    """
    gamma = 1.0 / dim + (1.0 - 1.0 / dim) * theta / gs.tan(theta)
    return (1.0 / gamma)**2
Esempio n. 5
0
    def log(self, point, base_point):
        """
        Compute the Riemannian logarithm at point base_point,
        of point wrt the metric obtained by
        embedding of the n-dimensional sphere
        in the (n+1)-dimensional euclidean space.

        This gives a tangent vector at point base_point.

        :param base_point: point on the n-dimensional sphere
        :param point: point on the n-dimensional sphere
        :return log: tangent vector at base_point
        """
        point = gs.to_ndarray(point, to_ndim=2)
        base_point = gs.to_ndarray(base_point, to_ndim=2)

        norm_base_point = self.embedding_metric.norm(base_point)
        norm_point = self.embedding_metric.norm(point)
        inner_prod = self.embedding_metric.inner_product(base_point, point)
        cos_angle = inner_prod / (norm_base_point * norm_point)
        cos_angle = gs.clip(cos_angle, -1.0, 1.0)

        angle = gs.arccos(cos_angle)

        mask_0 = gs.isclose(angle, 0.0)
        mask_else = gs.equal(mask_0, False)

        coef_1 = gs.zeros_like(angle)
        coef_2 = gs.zeros_like(angle)

        coef_1[mask_0] = (
                      1. + INV_SIN_TAYLOR_COEFFS[1] * angle[mask_0] ** 2
                      + INV_SIN_TAYLOR_COEFFS[3] * angle[mask_0] ** 4
                      + INV_SIN_TAYLOR_COEFFS[5] * angle[mask_0] ** 6
                      + INV_SIN_TAYLOR_COEFFS[7] * angle[mask_0] ** 8)
        coef_2[mask_0] = (
                      1. + INV_TAN_TAYLOR_COEFFS[1] * angle[mask_0] ** 2
                      + INV_TAN_TAYLOR_COEFFS[3] * angle[mask_0] ** 4
                      + INV_TAN_TAYLOR_COEFFS[5] * angle[mask_0] ** 6
                      + INV_TAN_TAYLOR_COEFFS[7] * angle[mask_0] ** 8)

        coef_1[mask_else] = angle[mask_else] / gs.sin(angle[mask_else])
        coef_2[mask_else] = angle[mask_else] / gs.tan(angle[mask_else])

        log = (gs.einsum('ni,nj->nj', coef_1, point)
               - gs.einsum('ni,nj->nj', coef_2, base_point))

        return log
def msd_hbar_s2(location, data):
    """compute the mean-square deviation from the location to the points of
    the dataset and the mean Hessian of square distance at the location"""
    sphere = Hypersphere(2)
    num_sample = len(data)
    assert num_sample > 0, "Dataset needs to have at least one data"
    var = 0.0
    hbar = 0.0
    for item in data:
        sq_dist = sphere.metric.squared_dist(location, item)[0, 0]
        var = var + sq_dist
        # hbar = E(h(dist ^ 2)) with h(t) = sqrt(t) cot( sqrt(t) )  for kappa=1
        if sq_dist > 1e-4:
            d = gs.sqrt(sq_dist)
            h = d / gs.tan(d)
        else:
            h = 1.0 - sq_dist / 3.0 - sq_dist ** 2 / 45.0 - 2 / 945 * \
                sq_dist ** 3 - sq_dist ** 4 / 4725
        hbar = hbar + h
    return var / num_sample, hbar / num_sample
Esempio n. 7
0
    def log(self, point, base_point):
        """
        Riemannian logarithm of a point wrt a base point.
        """
        point = gs.to_ndarray(point, to_ndim=2)
        base_point = gs.to_ndarray(base_point, to_ndim=2)

        norm_base_point = self.embedding_metric.norm(base_point)
        norm_point = self.embedding_metric.norm(point)
        inner_prod = self.embedding_metric.inner_product(base_point, point)
        cos_angle = inner_prod / (norm_base_point * norm_point)
        cos_angle = gs.clip(cos_angle, -1.0, 1.0)

        angle = gs.arccos(cos_angle)

        mask_0 = gs.isclose(angle, 0.0)
        mask_else = gs.equal(mask_0, gs.cast(gs.array(False), gs.int8))

        coef_1 = gs.zeros_like(angle)
        coef_2 = gs.zeros_like(angle)

        coef_1[mask_0] = (
                      1. + INV_SIN_TAYLOR_COEFFS[1] * angle[mask_0] ** 2
                      + INV_SIN_TAYLOR_COEFFS[3] * angle[mask_0] ** 4
                      + INV_SIN_TAYLOR_COEFFS[5] * angle[mask_0] ** 6
                      + INV_SIN_TAYLOR_COEFFS[7] * angle[mask_0] ** 8)
        coef_2[mask_0] = (
                      1. + INV_TAN_TAYLOR_COEFFS[1] * angle[mask_0] ** 2
                      + INV_TAN_TAYLOR_COEFFS[3] * angle[mask_0] ** 4
                      + INV_TAN_TAYLOR_COEFFS[5] * angle[mask_0] ** 6
                      + INV_TAN_TAYLOR_COEFFS[7] * angle[mask_0] ** 8)

        coef_1[mask_else] = angle[mask_else] / gs.sin(angle[mask_else])
        coef_2[mask_else] = angle[mask_else] / gs.tan(angle[mask_else])

        log = (gs.einsum('ni,nj->nj', coef_1, point)
               - gs.einsum('ni,nj->nj', coef_2, base_point))

        return log
Esempio n. 8
0
    7.0 / 360.0,
    -31.0 / 15120.0,
    127.0 / 604800.0,
]
INV_TANH_TAYLOR_COEFFS = [1.0, 1.0 / 3.0, -1.0 / 45.0, 2.0 / 945.0, -1.0 / 4725.0]
ARCTANH_CARD_TAYLOR_COEFFS = [1.0, 1.0 / 3.0, 1.0 / 5.0, 1 / 7.0, 1.0 / 9]


cos_close_0 = {"function": gs.cos, "coefficients": COS_TAYLOR_COEFFS}
sinc_close_0 = {"function": lambda x: gs.sin(x) / x, "coefficients": SINC_TAYLOR_COEFFS}
inv_sinc_close_0 = {
    "function": lambda x: x / gs.sin(x),
    "coefficients": INV_SINC_TAYLOR_COEFFS,
}
inv_tanc_close_0 = {
    "function": lambda x: x / gs.tan(x),
    "coefficients": INV_TANC_TAYLOR_COEFFS,
}
cosc_close_0 = {
    "function": lambda x: (1 - gs.cos(x)) / x ** 2,
    "coefficients": COSC_TAYLOR_COEFFS,
}
var_sinc_close_0 = {
    "function": lambda x: (x - gs.sin(x)) / x ** 3,
    "coefficients": [-k for k in SINC_TAYLOR_COEFFS[1:]],
}
var_inv_tanc_close_0 = {
    "function": lambda x: (1 - (x / gs.tan(x))) / x ** 2,
    "coefficients": VAR_INV_TAN_TAYLOR_COEFFS,
}
sinch_close_0 = {
Esempio n. 9
0
    1., -1. / 6., 7. / 360., -31. / 15120., 127. / 604800.
]
INV_TANH_TAYLOR_COEFFS = [1., 1. / 3., -1. / 45., 2. / 945., -1. / 4725.]
ARCTANH_CARD_TAYLOR_COEFFS = [1., 1. / 3., 1. / 5., 1 / 7., 1. / 9]

cos_close_0 = {'function': gs.cos, 'coefficients': COS_TAYLOR_COEFFS}
sinc_close_0 = {
    'function': lambda x: gs.sin(x) / x,
    'coefficients': SINC_TAYLOR_COEFFS
}
inv_sinc_close_0 = {
    'function': lambda x: x / gs.sin(x),
    'coefficients': INV_SINC_TAYLOR_COEFFS
}
inv_tanc_close_0 = {
    'function': lambda x: x / gs.tan(x),
    'coefficients': INV_TANC_TAYLOR_COEFFS
}
cosc_close_0 = {
    'function': lambda x: (1 - gs.cos(x)) / x**2,
    'coefficients': COSC_TAYLOR_COEFFS
}
var_sinc_close_0 = {
    'function': lambda x: (x - gs.sin(x)) / x**3,
    'coefficients': [-k for k in SINC_TAYLOR_COEFFS[1:]]
}
var_inv_tanc_close_0 = {
    'function': lambda x: (1 - (x / gs.tan(x))) / x**2,
    'coefficients': VAR_INV_TAN_TAYLOR_COEFFS
}
sinch_close_0 = {
    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
Esempio n. 11
0
    def log(self, point, base_point):
        """Compute the Riemannian logarithm of a point.

        Parameters
        ----------
        point : array-like, shape=[..., dim + 1]
            Point on the hypersphere.
        base_point : array-like, shape=[..., dim + 1]
            Point on the hypersphere.

        Returns
        -------
        log : array-like, shape=[..., dim + 1]
            Tangent vector at the base point equal to the Riemannian logarithm
            of point at the base point.
        """
        norm_base_point = self.embedding_metric.norm(base_point)
        norm_point = self.embedding_metric.norm(point)
        inner_prod = self.embedding_metric.inner_product(base_point, point)
        cos_angle = inner_prod / (norm_base_point * norm_point)
        cos_angle = gs.clip(cos_angle, -1., 1.)

        angle = gs.arccos(cos_angle)
        angle = gs.to_ndarray(angle, to_ndim=1)
        angle = gs.to_ndarray(angle, to_ndim=2, axis=1)

        mask_0 = gs.isclose(angle, 0.)
        mask_else = gs.equal(mask_0, gs.array(False))

        mask_0_float = gs.cast(mask_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. + INV_SIN_TAYLOR_COEFFS[1] * angle**2 +
                                  INV_SIN_TAYLOR_COEFFS[3] * angle**4 +
                                  INV_SIN_TAYLOR_COEFFS[5] * angle**6 +
                                  INV_SIN_TAYLOR_COEFFS[7] * angle**8)
        coef_2 += mask_0_float * (1. + INV_TAN_TAYLOR_COEFFS[1] * angle**2 +
                                  INV_TAN_TAYLOR_COEFFS[3] * angle**4 +
                                  INV_TAN_TAYLOR_COEFFS[5] * angle**6 +
                                  INV_TAN_TAYLOR_COEFFS[7] * angle**8)

        # This avoids division by 0.
        angle += mask_0_float * 1.

        coef_1 += mask_else_float * angle / gs.sin(angle)
        coef_2 += mask_else_float * angle / gs.tan(angle)

        log = (gs.einsum('...i,...j->...j', coef_1, point) -
               gs.einsum('...i,...j->...j', coef_2, base_point))

        mask_same_values = gs.isclose(point, base_point)

        mask_else = gs.equal(mask_same_values, gs.array(False))
        mask_else_float = gs.cast(mask_else, gs.float32)
        mask_else_float = gs.to_ndarray(mask_else_float, to_ndim=1)
        mask_else_float = gs.to_ndarray(mask_else_float, to_ndim=2)
        mask_not_same_points = gs.sum(mask_else_float, axis=1)
        mask_same_points = gs.isclose(mask_not_same_points, 0.)
        mask_same_points = gs.cast(mask_same_points, gs.float32)
        mask_same_points = gs.to_ndarray(mask_same_points, to_ndim=2, axis=1)

        mask_same_points_float = gs.cast(mask_same_points, gs.float32)

        log -= mask_same_points_float * log

        return log
Esempio n. 12
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
Esempio n. 13
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