Exemplo n.º 1
0
    def __init__(self, metrics):
        self.n_metrics = gs.len(metrics)
        dimensions = [metric.dimension for metric in metrics]
        signatures = [metric.signature for metric in metrics]

        self.metrics = metrics
        self.dimensions = dimensions
        self.signatures = signatures
        super(ProductRiemannianMetric,
              self).__init__(dimension=gs.sum(dimensions),
                             signature=map(sum, signatures))
Exemplo n.º 2
0
    def test_estimate_default_gradient_descent_so3(self):
        points = self.so3.random_uniform(2)

        mean_vec = FrechetMean(metric=self.so3.bi_invariant_metric,
                               method='default')
        mean_vec.fit(points)

        logs = self.so3.bi_invariant_metric.log(points, mean_vec.estimate_)
        result = gs.sum(logs, axis=0)
        expected = gs.zeros_like(points[0])
        self.assertAllClose(result, expected)
Exemplo n.º 3
0
    def test_sample_belong(self):
        """Test that sample samples in the simplex.

        Check that samples belong to the simplex,
        i.e. that their components sum up to one.
        """
        points = self.dirichlet.random_point(self.n_points)
        samples = self.dirichlet.sample(points, self.n_samples)
        result = gs.sum(samples, -1)
        expected = gs.ones((self.n_points, self.n_samples))
        self.assertAllClose(expected, result)
 def test_weighted_frechet_mean(self):
     """Test for weighted mean."""
     data = gs.array([[0.1, 0.2], [0.25, 0.35]])
     weights = gs.array([3.0, 1.0])
     mean_o = FrechetMean(metric=self.metric, point_type="vector", lr=1.0)
     mean_o.fit(data, weights=weights)
     result = mean_o.estimate_
     expected = self.metric.exp(
         weights[1] / gs.sum(weights) * self.metric.log(data[1], data[0]),
         data[0])
     self.assertAllClose(result, expected)
Exemplo n.º 5
0
    def test_to_tangent(self):
        """Test to_tangent.

        Test that the result belongs to the tangent space to
        the simplex.
        """
        vectors = -5 + 2 * gs.random.rand(self.n_points, self.dim + 1)
        projected_vectors = self.categorical.to_tangent(vectors)
        result = gs.sum(projected_vectors, -1)
        expected = gs.zeros(self.n_points)
        self.assertAllClose(expected, result, atol=1e-05)
Exemplo n.º 6
0
    def mean(self, points, weights=None):
        """
        The Frechet mean of (weighted) points computed with the
        Euclidean metric is the weighted average of the points
        in the Euclidean space.
        """
        if isinstance(points, list):
            points = gs.vstack(points)
        points = gs.to_ndarray(points, to_ndim=2)
        n_points = gs.shape(points)[0]

        if isinstance(weights, list):
            weights = gs.vstack(weights)
        elif weights is None:
            weights = gs.ones((n_points, ))

        weighted_points = gs.einsum('n,nj->nj', weights, points)
        mean = (gs.sum(weighted_points, axis=0) / gs.sum(weights))
        mean = gs.to_ndarray(mean, to_ndim=2)
        return mean
Exemplo n.º 7
0
    def test_sample_von_mises_fisher(self):
        """
        Check that the maximum likelihood estimates of the mean and
        concentration parameter are close to the real values. A first
        estimation of the concentration parameter is obtained by a
        closed-form expression and improved through the Newton method.
        """
        dim = 2
        n_points = 1000000
        sphere = Hypersphere(dim)

        # check mean value for concentrated distribution
        kappa = 10000000
        points = sphere.random_von_mises_fisher(kappa, n_points)
        sum_points = gs.sum(points, axis=0)
        mean = gs.array([0., 0., 1.])
        mean_estimate = sum_points / gs.linalg.norm(sum_points)
        expected = mean
        result = mean_estimate
        self.assertTrue(gs.allclose(result, expected,
                                    atol=MEAN_ESTIMATION_TOL))
        # check concentration parameter for dispersed distribution
        kappa = 1.
        points = sphere.random_von_mises_fisher(kappa, n_points)
        sum_points = gs.sum(points, axis=0)
        mean_norm = gs.linalg.norm(sum_points) / n_points
        kappa_estimate = (mean_norm * (dim + 1. - mean_norm**2) /
                          (1. - mean_norm**2))
        kappa_estimate = gs.cast(kappa_estimate, gs.float64)
        p = dim + 1
        n_steps = 100
        for _ in range(n_steps):
            bessel_func_1 = scipy.special.iv(p / 2., kappa_estimate)
            bessel_func_2 = scipy.special.iv(p / 2. - 1., kappa_estimate)
            ratio = bessel_func_1 / bessel_func_2
            denominator = 1. - ratio**2 - (p - 1.) * ratio / kappa_estimate
            mean_norm = gs.cast(mean_norm, gs.float64)
            kappa_estimate = kappa_estimate - (ratio - mean_norm) / denominator
        result = kappa_estimate
        expected = kappa
        self.assertAllClose(result, expected, atol=KAPPA_ESTIMATION_TOL)
Exemplo n.º 8
0
def linear_mean(points, weights=None, point_type='vector'):
    """Compute the weighted linear mean.

    The linear mean is the Frechet mean when points:
    - lie in a Euclidean space with Euclidean metric,
    - lie in a Minkowski space with Minkowski metric.

    Parameters
    ----------
    points : array-like, shape=[n_samples, dim]
        Points to be averaged.
    weights : array-like, shape=[n_samples, 1], optional
        Weights associated to the points.

    Returns
    -------
    mean : array-like, shape=[1, dim]
        Weighted linear mean of the points.
    """
    # TODO(ninamiolane): Factorize this code to handle lists
    # in the whole codebase
    if isinstance(points, list):
        points = gs.stack(points, axis=0)
    if isinstance(weights, list):
        weights = gs.stack(weights, axis=0)

    n_points = geomstats.vectorization.get_n_points(
        points, point_type)

    if weights is None:
        weights = gs.ones((n_points,))
    sum_weights = gs.sum(weights)

    einsum_str = '...,...j->...j'
    if point_type == 'matrix':
        einsum_str = '...,...jk->...jk'

    weighted_points = gs.einsum(einsum_str, weights, points)

    mean = gs.sum(weighted_points, axis=0) / sum_weights
    return mean
Exemplo n.º 9
0
    def test_closest_neighbor_index(self):
        """Check that the closest neighbor is one of neighbors."""
        n_samples = 10
        points = self.space.random_uniform(n_samples=n_samples)
        point = points[0, :]
        neighbors = points[1:, :]
        index = self.metric.closest_neighbor_index(point, neighbors)
        closest_neighbor = points[index, :]

        test = gs.sum(gs.all(points == closest_neighbor, axis=1))
        result = test > 0
        self.assertTrue(result)
Exemplo n.º 10
0
def linear_mean(points, weights=None, point_type='vector'):
    """Compute the weighted linear mean.

    The linear mean is the Frechet mean when points:
    - lie in a Euclidean space with Euclidean metric,
    - lie in a Minkowski space with Minkowski metric.

    Parameters
    ----------
    points : array-like, shape=[..., dim]
        Points to be averaged.
    weights : array-like, shape=[...,]
        Weights associated to the points.
        Optional, default: None.

    Returns
    -------
    mean : array-like, shape=[dim,]
        Weighted linear mean of the points.
    """
    if isinstance(points, list):
        points = gs.stack(points, axis=0)
    if isinstance(weights, list):
        weights = gs.stack(weights, axis=0)

    n_points = geomstats.vectorization.get_n_points(
        points, point_type)

    if weights is None:
        weights = gs.ones((n_points,))
    sum_weights = gs.sum(weights)

    einsum_str = '...,...j->...j'
    if point_type == 'matrix':
        einsum_str = '...,...jk->...jk'

    weighted_points = gs.einsum(einsum_str, weights, points)

    mean = gs.sum(weighted_points, axis=0) / sum_weights
    return mean
Exemplo n.º 11
0
    def inner_product(
            self, tangent_vec_a, tangent_vec_b, base_point=None,
            point_type=None):
        """Compute the inner-product of two tangent vectors at a base point.

        Inner product defined by the Riemannian metric at point `base_point`
        between tangent vectors `tangent_vec_a` and `tangent_vec_b`.

        Parameters
        ----------
        tangent_vec_a : array-like, shape=[n_samples, dimension + 1]
            First tangent vector at base point.
        tangent_vec_b : array-like, shape=[n_samples, dimension + 1]
            Second tangent vector at base point.
        base_point : array-like, shape=[n_samples, dimension + 1], optional
            Point on the manifold.
        point_type : str, {'vector', 'matrix'}
            Type of representation used for points.

        Returns
        -------
        inner_prod : array-like, shape=[n_samples, 1]
            Inner-product of the two tangent vectors.
        """
        if base_point is None:
            base_point = [None, ] * self.n_metrics

        if point_type is None:
            point_type = self.default_point_type
        if point_type == 'vector':
            tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=2)
            tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=2)
            base_point = gs.to_ndarray(base_point, to_ndim=2)
            intrinsic = self._is_intrinsic(tangent_vec_b)
            args = {'tangent_vec_a': tangent_vec_a,
                    'tangent_vec_b': tangent_vec_b,
                    'base_point': base_point}
            inner_prod = self._iterate_over_metrics(
                'inner_product', args, intrinsic)
            return gs.sum(gs.hstack(inner_prod), axis=1)
        elif point_type == 'matrix':
            tangent_vec_a = gs.to_ndarray(tangent_vec_a, to_ndim=3)
            tangent_vec_b = gs.to_ndarray(tangent_vec_b, to_ndim=3)
            base_point = gs.to_ndarray(base_point, to_ndim=3)
            inner_products = [metric.inner_product(tangent_vec_a[:, i],
                                                   tangent_vec_b[:, i],
                                                   base_point[:, i])
                              for i, metric in enumerate(self.metrics)]
            return sum(inner_products)
        else:
            raise ValueError('invalid point_type argument: {}, expected '
                             'either matrix of vector'.format(point_type))
Exemplo n.º 12
0
    def log(self, point, base_point, n_steps=N_STEPS, step='euler'):
        """Compute logarithm map associated to the affine connection.

        Solve the boundary value problem associated to the geodesic equation
        using the Christoffel symbols and conjugate gradient descent.

        Parameters
        ----------
        point : array-like, shape=[n_samples, dim]
            Point on the manifold.
        base_point : array-like, shape=[n_samples, dim]
            Point on the manifold.
        n_steps : int
            The number of discrete time steps to take in the integration.
        step : str, {'euler', 'rk4'}
            The numerical scheme to use for integration.

        Returns
        -------
        tangent_vec : array-like, shape=[n_samples, dim]
            Tangent vector at the base point.
        """
        point = gs.to_ndarray(point, to_ndim=2)
        base_point = gs.to_ndarray(base_point, to_ndim=2)
        n_samples = len(base_point)

        def objective(velocity):
            """Define the objective function."""
            velocity = velocity.reshape(base_point.shape)
            delta = self.exp(velocity, base_point, n_steps, step) - point
            loss = 1. / self.dim * gs.sum(delta**2, axis=1)
            return 1. / n_samples * gs.sum(loss)

        objective_grad = autograd.elementwise_grad(objective)

        def objective_with_grad(velocity):
            """Create helpful objective func wrapper for autograd comp."""
            return objective(velocity), objective_grad(velocity)

        tangent_vec = gs.random.rand(gs.sum(gs.shape(base_point)))
        res = minimize(objective_with_grad,
                       tangent_vec,
                       method='L-BFGS-B',
                       jac=True,
                       options={
                           'disp': False,
                           'maxiter': 25
                       })

        tangent_vec = res.x
        tangent_vec = gs.reshape(tangent_vec, base_point.shape)
        return tangent_vec
Exemplo n.º 13
0
        def cost_fun(param):
            """Compute the energy of the polynomial curve defined by param.

            Parameters
            ----------
            param : array-like, shape=(degree - 1, dim)
                Parameters of the curve coordinates' polynomial functions of time.

            Returns
            -------
            energy : float
                Energy of the polynomial approximation of the geodesic.
            length : float
                Length of the polynomial approximation of the geodesic.
            curve : array-like, shape=(n_times, dim)
                Polynomial approximation of the geodesic.
            velocity : array-like, shape=(n_times, dim)
                Velocity of the polynomial approximation of the geodesic.
            """
            last_coef = end_point - initial_point - gs.sum(param, axis=0)
            coef = gs.vstack((initial_point, param, last_coef))

            t = gs.linspace(0.0, 1.0, n_times)
            t_curve = [t**i for i in range(degree + 1)]
            t_curve = gs.stack(t_curve)
            curve = gs.einsum("ij,ik->kj", coef, t_curve)

            t_velocity = [i * t**(i - 1) for i in range(1, degree + 1)]
            t_velocity = gs.stack(t_velocity)
            velocity = gs.einsum("ij,ik->kj", coef[1:], t_velocity)

            if curve.min() < 0:
                return np.inf, np.inf, curve, np.nan

            velocity_sqnorm = self.squared_norm(vector=velocity,
                                                base_point=curve)
            length = gs.sum(velocity_sqnorm**(1 / 2)) / n_times
            energy = gs.sum(velocity_sqnorm) / n_times
            return energy, length, curve, velocity
Exemplo n.º 14
0
 def test_sample_von_mises_fisher_mean(self, dim, mean, kappa, n_points):
     """
     Check that the maximum likelihood estimates of the mean and
     concentration parameter are close to the real values. A first
     estimation of the concentration parameter is obtained by a
     closed-form expression and improved through the Newton method.
     """
     space = self.space(dim)
     points = space.random_von_mises_fisher(mu=mean, kappa=kappa, n_samples=n_points)
     sum_points = gs.sum(points, axis=0)
     result = sum_points / gs.linalg.norm(sum_points)
     expected = mean
     self.assertAllClose(result, expected, atol=MEAN_ESTIMATION_TOL)
Exemplo n.º 15
0
    def test_sample(self):
        """
        Test that the sample method samples variates from beta distributions
        with the specified parameters, using the law of large numbers
        """
        n_samples = self.n_samples
        tol = (n_samples * 10)**(-0.5)
        point = self.beta.random_uniform(n_samples)
        samples = self.beta.sample(point, n_samples * 10)
        result = gs.mean(samples, axis=1)
        expected = point[:, 0] / gs.sum(point, axis=1)

        self.assertAllClose(result, expected, rtol=tol, atol=tol)
Exemplo n.º 16
0
    def ball_to_half_space_tangent(tangent_vec, base_point):
        """Convert ball to half-space tangent coordinates.

        Convert the parameterization of a tangent vector to the
        hyperbolic space from its Poincare ball coordinates, to
        the Poinare half-space coordinates.

        Parameters
        ----------
        tangent_vec : array-like, shape=[..., dim]
            Tangent vector at the base point in the Poincare ball.
        base_point : array-like, shape=[..., dim]
            Point in the Poincare ball.

        Returns
        -------
        tangent_vec_half_spacel : array-like, shape=[..., dim]
            Tangent vector in the Poincare half-space.

        """
        sq_norm = gs.sum(base_point ** 2, axis=-1)
        den = 1 + sq_norm - 2 * base_point[..., -1]
        scalar_prod = gs.sum(base_point * tangent_vec, -1)
        component_1 = gs.einsum(
            "...i,...->...i", tangent_vec[..., :-1], 2.0 / den
        ) - 4.0 * gs.einsum(
            "...i,...->...i",
            base_point[..., :-1],
            (scalar_prod - tangent_vec[..., -1]) / den ** 2,
        )
        component_2 = (
            -2.0 * scalar_prod / den
            - 2 * (1.0 - sq_norm) * (scalar_prod - tangent_vec[..., -1]) / den ** 2
        )
        tangent_vec_half_space = gs.concatenate(
            [component_1, component_2[..., None]], axis=-1
        )

        return tangent_vec_half_space
Exemplo n.º 17
0
    def test_random_von_mises_general_dim_mean(self):
        for dim in [2, 9]:
            sphere = Hypersphere(dim)
            n_points = 100000

            # check mean value for concentrated distribution
            kappa = 10
            points = sphere.random_von_mises_fisher(kappa=kappa,
                                                    n_samples=n_points)
            sum_points = gs.sum(points, axis=0)
            expected = gs.array([1.0] + [0.0] * dim)
            result = sum_points / gs.linalg.norm(sum_points)
            self.assertAllClose(result, expected, atol=KAPPA_ESTIMATION_TOL)
Exemplo n.º 18
0
    def _loss(self, X, y, param, shape, weights=None):
        """Compute the loss associated to the geodesic regression.

        Parameters
        ----------
        X : {array-like, sparse matrix}, shape=[...,}]
            Training input samples.
        y : array-like, shape=[..., {dim, [n,n]}]
            Training target values.
        param : array-like, shape=[2, {dim, [n,n]}]
            Parameters intercept and coef of the geodesic regression,
            vertically stacked.
        weights : array-like, shape=[...,]
            Weights associated to the points.
            Optional, default: None.

        Returns
        -------
        _ : float
            Loss.
        """
        intercept, coef = gs.split(param, 2)
        intercept = gs.reshape(intercept, shape)
        coef = gs.reshape(coef, shape)
        intercept = gs.cast(intercept, dtype=y.dtype)
        coef = gs.cast(coef, dtype=y.dtype)
        if self.method == "extrinsic":
            base_point = self.space.projection(intercept)
            penalty = self.regularization * gs.sum((base_point - intercept)**2)
        else:
            base_point = intercept
            penalty = 0
        tangent_vec = self.space.to_tangent(coef, base_point)
        distances = self.metric.squared_dist(
            self._model(X, tangent_vec, base_point), y)
        if weights is None:
            weights = 1.0
        return 1.0 / 2.0 * gs.sum(weights * distances) + penalty
Exemplo n.º 19
0
    def mobius_add(self, point_a, point_b):
        """Compute the mobius addition of two points.

        Mobius addition is necessary for computation of the log and exp
        using the 'poincare' representation set as point_type.

        Parameters
        ----------
        point_a : array-like, shape=[n_samples, dimension + 1]
                              or shape=[1, dimension + 1]
        point_b : array-like, shape=[n_samples, dimension + 1]
                              or shape=[1, dimension + 1]

        Returns
        -------
        mobius_add : array-like, shape=[n_samples, 1]
                           or shape=[1, 1]
        """
        norm_point_a = gs.sum(point_a**2, axis=-1, keepdims=True)

        # to redefine to use autograd
        norm_point_a = gs.repeat(norm_point_a, point_a.shape[-1], -1)

        norm_point_b = gs.sum(point_b**2, axis=-1, keepdims=True)
        norm_point_b = gs.repeat(norm_point_b, point_a.shape[-1], -1)

        sum_prod_a_b = (point_a * point_b).sum(-1, keepdims=True)

        sum_prod_a_b = gs.repeat(sum_prod_a_b, point_a.shape[-1], -1)

        add_nominator = ((1 + 2 * sum_prod_a_b + norm_point_b) * point_a +
                         (1 - norm_point_a) * point_b)

        add_denominator = (1 + 2 * sum_prod_a_b + norm_point_a * norm_point_b)

        mobius_add = add_nominator / add_denominator

        return mobius_add
Exemplo n.º 20
0
    def log(self, point, base_point, **kwargs):
        """Compute Riemannian logarithm of a point wrt a base point.

        Parameters
        ----------
        point : array-like, shape=[..., dim]
            Point in the Poincare ball.
        base_point : array-like, shape=[..., dim]
            Point in the Poincare ball.

        Returns
        -------
        log : array-like, shape=[..., dim]
            Tangent vector at the base point equal to the Riemannian logarithm
            of point at the base point.
        """
        mobius_addition = self.mobius_add(-base_point, point)
        squared_norm_add = gs.sum(mobius_addition**2, axis=-1)
        squared_norm_bp = gs.sum(base_point**2, axis=-1)
        coef = (1 - squared_norm_bp) * utils.taylor_exp_even_func(
            squared_norm_add, utils.arctanh_card_close_0)
        log = gs.einsum("...,...j->...j", coef, mobius_addition)
        return log
Exemplo n.º 21
0
        def coefficients(ind_k):
            param_k = base_point[..., ind_k]
            param_sum = gs.sum(base_point, -1)
            c1 = 1 / gs.polygamma(1, param_k) / (
                1 / gs.polygamma(1, param_sum)
                - gs.sum(1 / gs.polygamma(1, base_point), -1))
            c2 = - c1 * gs.polygamma(2, param_sum) / gs.polygamma(1, param_sum)

            mat_ones = gs.ones((n_points, self.dim, self.dim))
            mat_diag = from_vector_to_diagonal_matrix(
                - gs.polygamma(2, base_point) / gs.polygamma(1, base_point))
            arrays = [gs.zeros((1, ind_k)),
                      gs.ones((1, 1)),
                      gs.zeros((1, self.dim - ind_k - 1))]
            vec_k = gs.tile(gs.hstack(arrays), (n_points, 1))
            val_k = gs.polygamma(2, param_k) / gs.polygamma(1, param_k)
            vec_k = gs.einsum('i,ij->ij', val_k, vec_k)
            mat_k = from_vector_to_diagonal_matrix(vec_k)

            mat = gs.einsum('i,ijk->ijk', c2, mat_ones)\
                - gs.einsum('i,ijk->ijk', c1, mat_diag) + mat_k

            return 1 / 2 * mat
Exemplo n.º 22
0
    def test_estimate_default_gradient_descent_so_matrix(self):
        points = self.so_matrix.random_uniform(2)
        mean_vec = FrechetMean(
            metric=self.so_matrix.bi_invariant_metric,
            method="default",
            init_step_size=1.0,
        )
        mean_vec.fit(points)
        logs = self.so_matrix.bi_invariant_metric.log(points,
                                                      mean_vec.estimate_)
        result = gs.sum(logs, axis=0)
        expected = gs.zeros_like(points[0])

        self.assertAllClose(result, expected, atol=1e-5)
Exemplo n.º 23
0
    def __init__(self, group,
                 inner_product_mat_at_identity=None,
                 left_or_right='left', **kwargs):
        super(InvariantMetric, self).__init__(dim=group.dim, **kwargs)

        self.group = group
        if inner_product_mat_at_identity is None:
            inner_product_mat_at_identity = gs.eye(self.group.dim)

        geomstats.errors.check_parameter_accepted_values(
            left_or_right, 'left_or_right', ['left', 'right'])

        eigenvalues = gs.linalg.eigvalsh(inner_product_mat_at_identity)
        mask_pos_eigval = gs.greater(eigenvalues, 0.)
        n_pos_eigval = gs.sum(gs.cast(mask_pos_eigval, gs.int32))
        mask_neg_eigval = gs.less(eigenvalues, 0.)
        n_neg_eigval = gs.sum(gs.cast(mask_neg_eigval, gs.int32))
        mask_null_eigval = gs.isclose(eigenvalues, 0.)
        n_null_eigval = gs.sum(gs.cast(mask_null_eigval, gs.int32))

        self.inner_product_mat_at_identity = inner_product_mat_at_identity
        self.left_or_right = left_or_right
        self.signature = (n_pos_eigval, n_null_eigval, n_neg_eigval)
Exemplo n.º 24
0
    def dist(self, point_a, point_b):
        """Compute the geodesic distance between two points.

        Parameters
        ----------
        point_a : array-like, shape=[..., dim]
            First point in the Poincare ball.
        point_b : array-like, shape=[..., dim]
            Second point in the Poincare ball.

        Returns
        -------
        dist : array-like, shape=[...,]
            Geodesic distance between the two points.
        """
        point_a_norm = gs.clip(gs.sum(point_a**2, -1), 0.0, 1 - EPSILON)
        point_b_norm = gs.clip(gs.sum(point_b**2, -1), 0.0, 1 - EPSILON)

        diff_norm = gs.sum((point_a - point_b) ** 2, -1)
        norm_function = 1 + 2 * diff_norm / ((1 - point_a_norm) * (1 - point_b_norm))

        dist = gs.log(norm_function + gs.sqrt(norm_function**2 - 1))
        dist *= self.scale
        return dist
Exemplo n.º 25
0
    def baker_campbell_hausdorff(self, matrix_a, matrix_b, order=2):
        """Calculate the Baker-Campbell-Hausdorff approximation of given order.

        The implementation is based on [CM2009a]_ with the pre-computed
        constants taken from [CM2009b]_. Our coefficients are truncated to
        enable us to calculate BCH up to order 15.

        This represents Z = log(exp(X)exp(Y)) as an infinite linear combination
        of the form Z = sum z_i e_i where z_i are rational numbers and e_i are
        iterated Lie brackets starting with e_1 = X, e_2 = Y, each e_i is given
        by some i',i'': e_i = [e_i', e_i''].

        Parameters
        ----------
        matrix_a, matrix_b : array-like, shape=[..., n, n]
            Matrices.
        order : int
            The order to which the approximation is calculated. Note that this
            is NOT the same as using only e_i with i < order.
            Optional, default 2.

        References
        ----------
        .. [CM2009a] F. Casas and A. Murua. An efficient algorithm for
            computing the Baker–Campbell–Hausdorff series and some of its
            applications. Journal of Mathematical Physics 50, 2009
        .. [CM2009b] http://www.ehu.eus/ccwmuura/research/bchHall20.dat
        """
        if order > 15:
            raise NotImplementedError("BCH is not implemented for order > 15.")

        number_of_hom_degree = gs.array(
            [2, 1, 2, 3, 6, 9, 18, 30, 56, 99, 186, 335, 630, 1161, 2182]
        )
        n_terms = gs.sum(number_of_hom_degree[:order])

        el = [matrix_a, matrix_b]
        result = matrix_a + matrix_b

        for i in gs.arange(2, n_terms):
            i_p = BCH_COEFFICIENTS[i, 1] - 1
            i_pp = BCH_COEFFICIENTS[i, 2] - 1

            el.append(self.bracket(el[i_p], el[i_pp]))
            result += (
                float(BCH_COEFFICIENTS[i, 3]) / float(BCH_COEFFICIENTS[i, 4]) * el[i]
            )
        return result
Exemplo n.º 26
0
    def test_srv_metric_dist_and_geod(self):
        """Test that the length of the geodesic gives the distance.

        N.B: Here curve_a and curve_b are seen as curves in R3 and not S2.
        """
        geod = self.srv_metric_r3.geodesic(
            initial_curve=self.curve_a, end_curve=self.curve_b
        )
        geod = geod(self.times)
        srv = self.srv_metric_r3.srv_transform(geod)
        srv_derivative = self.n_discretized_curves * (srv[1:, :] - srv[:-1, :])
        norms = self.srv_metric_r3.l2_metric.norm(srv_derivative)
        result = gs.sum(norms, 0) / self.n_discretized_curves

        expected = self.srv_metric_r3.dist(self.curve_a, self.curve_b)
        self.assertAllClose(result, expected)
Exemplo n.º 27
0
    def test_srv_norm(self, curve_a, curve_b, times):
        l2_metric_s2 = L2CurvesMetric(ambient_manifold=s2)
        srv_metric_r3 = SRVMetric(ambient_manifold=r3)
        curves_ab = l2_metric_s2.geodesic(curve_a, curve_b)
        curves_ab = curves_ab(times)
        srvs_ab = srv_metric_r3.srv_transform(curves_ab)

        result = srv_metric_r3.l2_curves_metric.norm(srvs_ab)
        products = srvs_ab * srvs_ab
        sums = [gs.sum(product) for product in products]
        squared_norm = gs.array(sums) / (srvs_ab.shape[-2] + 1)
        expected = gs.sqrt(squared_norm)
        self.assertAllClose(result, expected)

        result = result.shape
        expected = [srvs_ab.shape[0]]
        self.assertAllClose(result, expected)
Exemplo n.º 28
0
    def inner_product(self, tangent_vec_a, tangent_vec_b, base_point=None):
        """
        Inner product defined by the Riemannian metric at point base_point
        between tangent vectors tangent_vec_a and tangent_vec_b.
        """
        if base_point is None:
            base_point = [
                None,
            ] * self.n_metrics

        inner_products = [
            self.metrics[i].inner_product(tangent_vec_a[i], tangent_vec_b[i],
                                          base_point[i])
            for i in range(self.n_metrics)
        ]
        inner_product = gs.sum(inner_products)
        return inner_product
Exemplo n.º 29
0
    def baker_campbell_hausdorff(self, matrix_a, matrix_b, order=2):
        """Calculate the Baker Campbell Hausdorff approximation of given order.

        We use the algorithm published by Casas / Murua in their paper
        "An efficient algorithm for computing the Baker–Campbell–Hausdorff
        series and some of its applications" in J.o.Math.Physics 50 (2009) as
        well as their computed constants from
        http://www.ehu.eus/ccwmuura/research/bchHall20.dat.
        Our file is truncated to enable us to calculate BCH up to order 15

        This represents Z =log(exp(X)exp(Y)) as an infinite linear combination
        of the form
        Z = sum z_i e_i
        where z_i are rational numbers and e_i are iterated Lie brackets
        starting with e_1 = X, e_2 = Y, each e_i is given by some i',i'':
        e_i = [e_i', e_i''].

        Parameters
        ----------
        matrix_a: array-like, shape=[n_sample, n, n]
        matrix_b: array-like, shape=[n_sample, n, n]
        order: int
            the order to which the approximation is calculated. Note that this
            is NOT the same as using only e_i with i < order
        """
        if order > 15:
            raise NotImplementedError("BCH is not implemented for order > 15.")

        number_of_hom_degree = gs.array(
            [2, 1, 2, 3, 6, 9, 18, 30, 56, 99, 186, 335, 630, 1161, 2182])
        n_terms = gs.sum(number_of_hom_degree[:order])

        ei = gs.zeros((n_terms, self.n, self.n))
        ei[0] = matrix_a
        ei[1] = matrix_b
        result = matrix_a + matrix_b

        for i in gs.arange(2, n_terms):
            i_p = BCH_INFO[i, 1] - 1
            i_pp = BCH_INFO[i, 2] - 1

            ei[i] = self.lie_bracket(ei[i_p], ei[i_pp])
            result = result + BCH_INFO[i, 3] / float(BCH_INFO[i, 4]) * ei[i]

        return result
Exemplo n.º 30
0
    def _belongs_ball(point, tolerance=TOLERANCE):
        """Evaluate if a point belongs to the Hyperbolic space (poin. ball).

        Evaluate if a point belongs to the Hyperbolic space based on
        the poincare ball representation, i.e. evaluate if its
        squared norm is lower than one.

        Parameters
        ----------
        point : array-like, shape=[n_samples, dimension]
                Input points.
        tolerance : float, optional

        Returns
        -------
        belongs : array-like, shape=[n_samples, 1]
        """
        return gs.sum(point**2, -1) < (1 + tolerance)