Пример #1
0
    def norm(self, vector, base_point=None):
        """
        Norm of a vector associated to the inner product
        at the tangent space at a base point.

        Note: This only works for positive-definite
        Riemannian metrics and inner products.

        Parameters
        ----------
        vector: array-like, shape=[n_samples, dimension]
                            or shape=[1, dimension]

        base_point: array-like, shape=[n_samples, dimension]
                                or shape=[1, dimension]
        """
        sq_norm = self.squared_norm(vector, base_point)
        norm = gs.sqrt(sq_norm)
        return norm
Пример #2
0
    def regularize(self, point):
        """
        Regularize a point to the canonical representation
        chosen for the Hyperbolic space, to avoid numerical issues.
        """
        assert gs.all(self.belongs(point))
        point = gs.to_ndarray(point, to_ndim=2)

        sq_norm = self.embedding_metric.squared_norm(point)
        real_norm = gs.sqrt(gs.abs(sq_norm))

        mask_0 = gs.isclose(real_norm, 0)
        mask_0 = gs.squeeze(mask_0, axis=1)
        mask_not_0 = ~mask_0
        projected_point = point

        projected_point[mask_not_0] = (point[mask_not_0] /
                                       real_norm[mask_not_0])
        return projected_point
Пример #3
0
    def random_von_mises_fisher(self, kappa=10, n_samples=1):
        """Sample in the 2-sphere with the von Mises distribution.

        Sample in the 2-sphere with the von Mises distribution centered at the
        north pole.

        References
        ----------
        https://en.wikipedia.org/wiki/Von_Mises_distribution

        Parameters
        ----------
        kappa : int, optional
            Kappa parameter of the von Mises distribution.
        n_samples : int, optional
            Number of samples.

        Returns
        -------
        point : array-like, shape=[n_samples, 3]
            Points sampled on the sphere in extrinsic coordinates
            in Euclidean space of dimension 3.
        """
        if self.dim != 2:
            raise NotImplementedError(
                'Sampling from the von Mises Fisher distribution'
                'is only implemented in dimension 2.')
        angle = 2. * gs.pi * gs.random.rand(n_samples)
        angle = gs.to_ndarray(angle, to_ndim=2, axis=1)
        unit_vector = gs.hstack((gs.cos(angle), gs.sin(angle)))
        scalar = gs.random.rand(n_samples)

        coord_z = 1. + 1. / kappa * gs.log(scalar + (1. - scalar) *
                                           gs.exp(gs.array(-2. * kappa)))
        coord_z = gs.to_ndarray(coord_z, to_ndim=2, axis=1)

        coord_xy = gs.sqrt(1. - coord_z**2) * unit_vector

        point = gs.hstack((coord_xy, coord_z))

        if n_samples == 1:
            point = gs.squeeze(point, axis=0)
        return point
Пример #4
0
    def pointwise_norm(self, tangent_vec, base_curve):
        """Compute the point-wise norm of a tangent vector at a base curve.

        Parameters
        ----------
        tangent_vec : array-like, shape=[..., n_sampling_points, ambient_dim]
            Tangent vector to discrete curve.
        base_curve : array-like, shape=[..., n_sampling_points, ambient_dim]
            Point representing a discrete curve.

        Returns
        -------
        norm : array-like, shape=[..., n_sampling_points]
            Point-wise norms.
        """
        sq_norm = self.pointwise_inner_product(
            tangent_vec_a=tangent_vec, tangent_vec_b=tangent_vec,
            base_curve=base_curve)
        return gs.sqrt(sq_norm)
Пример #5
0
    def noise_jacobian(state, sensor_input):
        r"""Compute the matrix associated to the propagation noise.

        The noise being considered multiplicative, it is simply the identity
        scaled by the time step.

        Parameters
        ----------
        state : unused
        sensor_input : array-like, shape=[4]
            Vector representing the information from the sensor.

        Returns
        -------
        jacobian : array-like, shape=[dim_noise, dim]
            Jacobian of the propagation w.r.t. the noise.
        """
        dt, _, _ = Localization.preprocess_input(sensor_input)
        return gs.sqrt(dt) * gs.eye(Localization.dim_noise)
Пример #6
0
    def orthonormal_basis(self, basis, base_point=None):
        """Orthonormalize the basis with respect to the metric.

        This corresponds to a renormalization.

        Parameters
        ----------
        basis : array-like, shape=[dim, dim]
            Matrix of a metric.
        base_point

        Returns
        -------
        basis : array-like, shape=[dim, n, n]
            Orthonormal basis.
        """
        norms = self.squared_norm(basis, base_point)

        return gs.einsum('i, ikl->ikl', 1. / gs.sqrt(norms), basis)
Пример #7
0
    def exp(self, tangent_vec, base_point):
        """
        Riemannian exponential of a tangent vector wrt to a base point.
        """
        tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=2)
        base_point = gs.to_ndarray(base_point, to_ndim=2)

        sq_norm_tangent_vec = self.embedding_metric.squared_norm(tangent_vec)
        norm_tangent_vec = gs.sqrt(sq_norm_tangent_vec)

        mask_0 = gs.isclose(sq_norm_tangent_vec, 0.)
        mask_0 = gs.to_ndarray(mask_0, to_ndim=1)
        mask_else = ~mask_0
        mask_else = gs.to_ndarray(mask_else, to_ndim=1)
        mask_0_float = gs.cast(mask_0, gs.float32)
        mask_else_float = gs.cast(mask_else, gs.float32)

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

        coef_1 += mask_0_float * (1. +
                                  COSH_TAYLOR_COEFFS[2] * norm_tangent_vec**2 +
                                  COSH_TAYLOR_COEFFS[4] * norm_tangent_vec**4 +
                                  COSH_TAYLOR_COEFFS[6] * norm_tangent_vec**6 +
                                  COSH_TAYLOR_COEFFS[8] * norm_tangent_vec**8)
        coef_2 += mask_0_float * (1. +
                                  SINH_TAYLOR_COEFFS[3] * norm_tangent_vec**2 +
                                  SINH_TAYLOR_COEFFS[5] * norm_tangent_vec**4 +
                                  SINH_TAYLOR_COEFFS[7] * norm_tangent_vec**6 +
                                  SINH_TAYLOR_COEFFS[9] * norm_tangent_vec**8)
        # This avoids dividing by 0.
        norm_tangent_vec += mask_0_float * 1.0
        coef_1 += mask_else_float * (gs.cosh(norm_tangent_vec))
        coef_2 += mask_else_float * ((gs.sinh(norm_tangent_vec) /
                                      (norm_tangent_vec)))

        exp = (gs.einsum('ni,nj->nj', coef_1, base_point) +
               gs.einsum('ni,nj->nj', coef_2, tangent_vec))

        hyperbolic_space = HyperbolicSpace(dimension=self.dimension)
        exp = hyperbolic_space.regularize(exp)
        return exp
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
Пример #9
0
    def test_intrinsic_and_extrinsic_coords(self):
        """
        Test that the composition of
        intrinsic_to_extrinsic_coords and
        extrinsic_to_intrinsic_coords
        gives the identity.
        """
        point_int = gs.array([.1, 0., 0., .1])
        point_ext = self.space.intrinsic_to_extrinsic_coords(point_int)
        result = self.space.extrinsic_to_intrinsic_coords(point_ext)
        expected = point_int

        self.assertAllClose(result, expected)

        point_ext = (1. / (gs.sqrt(6.)) * gs.array([1., 0., 0., 1., 2.]))
        point_int = self.space.extrinsic_to_intrinsic_coords(point_ext)
        result = self.space.intrinsic_to_extrinsic_coords(point_int)
        expected = point_ext

        self.assertAllClose(result, expected)
Пример #10
0
    def sample(self, n_samples):
        """Generate samples for SPD manifold."""
        if isinstance(self.manifold.metric, SPDMetricLogEuclidean):
            sym_matrix = self.manifold.logm(self.mean)
            mean_euclidean = gs.hstack(
                (
                    gs.diagonal(sym_matrix)[None, :],
                    gs.sqrt(2.0) * gs.triu_to_vec(sym_matrix, k=1)[None, :],
                )
            )[0]
            _samples = self.samples_sym(mean_euclidean, self.cov, n_samples)

        else:
            samples_sym = self.samples_sym(
                gs.zeros(self.manifold.dim), self.cov, n_samples
            )
            mean_half = self.manifold.powerm(self.mean, 0.5)
            _samples = Matrices.mul(mean_half, samples_sym, mean_half)

        return self.manifold.expm(_samples)
Пример #11
0
    def pointwise_norm(self, tangent_vec, base_curve):
        """Compute the norm of tangent vector components at base curve.

        TODO: (revise this to refer to action on single elements)
        Compute the norms of the components of a (series of) tangent
        vector(s) at (a) base curve(s).

        Parameters
        ----------
        tangent_vec :
        base_curve :

        Returns
        -------
        norm :
        """
        sq_norm = self.pointwise_inner_product(tangent_vec_a=tangent_vec,
                                               tangent_vec_b=tangent_vec,
                                               base_curve=base_curve)
        return gs.sqrt(sq_norm)
Пример #12
0
    def log(self, curve, base_curve):
        """
        Riemannian logarithm of a curve wrt a base curve.
        """
        if not isinstance(self.embedding_metric, EuclideanMetric):
            raise AssertionError('The logarithm map is only implemented '
                                 'for dicretized curves embedded in a '
                                 'Euclidean space.')
        curve = gs.to_ndarray(curve, to_ndim=3)
        base_curve = gs.to_ndarray(base_curve, to_ndim=3)
        n_curves, n_sampling_points, n_coords = curve.shape

        curve_srv = self.square_root_velocity(curve)
        base_curve_srv = self.square_root_velocity(base_curve)

        base_curve_velocity = (n_sampling_points - 1) * (base_curve[:, 1:, :] -
                                                         base_curve[:, :-1, :])
        base_curve_velocity_norm = self.pointwise_norm(base_curve_velocity,
                                                       base_curve[:, :-1, :])

        inner_prod = self.pointwise_inner_product(curve_srv - base_curve_srv,
                                                  base_curve_velocity,
                                                  base_curve[:, :-1, :])
        coef_1 = gs.sqrt(base_curve_velocity_norm)
        coef_2 = 1 / base_curve_velocity_norm**(3 / 2) * inner_prod

        term_1 = gs.einsum('ij,ijk->ijk', coef_1, curve_srv - base_curve_srv)
        term_2 = gs.einsum('ij,ijk->ijk', coef_2, base_curve_velocity)
        log_derivative = term_1 + term_2

        log_starting_points = self.embedding_metric.log(
            point=curve[:, 0, :], base_point=base_curve[:, 0, :])
        log_starting_points = gs.transpose(
            np.tile(log_starting_points, (1, 1, 1)), (1, 0, 2))

        log_cumsum = gs.hstack(
            [gs.zeros((n_curves, 1, n_coords)),
             np.cumsum(log_derivative, -2)])
        log = log_starting_points + 1 / (n_sampling_points - 1) * log_cumsum

        return log
Пример #13
0
    def test_intrinsic_and_extrinsic_coords(self):
        """
        Test that the composition of
        intrinsic_to_extrinsic_coords and
        extrinsic_to_intrinsic_coords
        gives the identity.
        """
        space = Hypersphere(dim=2)
        point_int = gs.array([0.1, 0.0])
        point_ext = space.intrinsic_to_extrinsic_coords(point_int)
        result = space.extrinsic_to_intrinsic_coords(point_ext)
        expected = point_int

        self.assertAllClose(result, expected)

        point_ext = 1. / (gs.sqrt(2.)) * gs.array([1.0, 0.0, 1.0])
        point_int = space.extrinsic_to_intrinsic_coords(point_ext)
        result = space.intrinsic_to_extrinsic_coords(point_int)
        expected = point_ext

        self.assertAllClose(result, expected)
Пример #14
0
    def intrinsic_to_extrinsic_coords(self, point_intrinsic):
        """
        Convert from the intrinsic coordinates in the Hypersphere,
        to the extrinsic coordinates in Euclidean space.

        Parameters
        ----------
        point_intrinsic : array-like, shape=[n_samples, dimension]

        Returns
        -------
        point_extrinsic : array-like, shape=[n_samples, dimension + 1]
        """
        point_intrinsic = gs.to_ndarray(point_intrinsic, to_ndim=2)

        coord_0 = gs.sqrt(1. - gs.linalg.norm(point_intrinsic, axis=-1)**2)
        coord_0 = gs.to_ndarray(coord_0, to_ndim=2, axis=-1)

        point_extrinsic = gs.concatenate([coord_0, point_intrinsic], axis=-1)

        return point_extrinsic
Пример #15
0
    def dist(self, point_a, point_b):
        """Geodesic distance between two points.

        Note: It only works for positive definite
        Riemannian metrics.

        Parameters
        ----------
        point_a : array-like, shape=[..., dim]
            Point.
        point_b : array-like, shape=[..., dim]
            Point.

        Returns
        -------
        dist : array-like, shape=[...,]
            Distance.
        """
        sq_dist = self.squared_dist(point_a, point_b)
        dist = gs.sqrt(sq_dist)
        return dist
Пример #16
0
    def _intrinsic_to_extrinsic_coordinates(point):
        """Convert intrinsic to extrinsic coordinates.

        Convert the parameterization of a point in hyperbolic space
        from its intrinsic coordinates, to its extrinsic coordinates
        in Minkowski space.

        Parameters
        ----------
        point : array-like, shape=[..., dim]
            Point in hyperbolic space in intrinsic coordinates.

        Returns
        -------
        point_extrinsic : array-like, shape=[..., dim + 1]
            Point in hyperbolic space in extrinsic coordinates.
        """
        coord_0 = gs.sqrt(1.0 + gs.sum(point ** 2, axis=-1))
        point_extrinsic = gs.concatenate([coord_0[..., None], point], axis=-1)

        return point_extrinsic
Пример #17
0
    def intrinsic_to_extrinsic_coords(self, point_intrinsic):
        """
        Convert the parameterization of a point on the Hyperbolic space
        from its intrinsic coordinates, to its extrinsic coordinates
        in Minkowski space.

        Parameters
        ----------
        point_intrinsic : array-like, shape=[n_samples, dimension]

        Returns
        -------
        point_extrinsic : array-like, shape=[n_samples, dimension + 1]
        """
        point_intrinsic = gs.to_ndarray(point_intrinsic, to_ndim=2)

        coord_0 = gs.sqrt(1. + gs.linalg.norm(point_intrinsic, axis=-1) ** 2)
        coord_0 = gs.to_ndarray(coord_0, to_ndim=2, axis=1)

        point_extrinsic = gs.concatenate([coord_0, point_intrinsic], axis=-1)

        return point_extrinsic
Пример #18
0
    def exp(self, tangent_vec, base_point):
        """
        Riemannian exponential of a tangent vector wrt to a base point.
        """
        tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=2)
        base_point = gs.to_ndarray(base_point, to_ndim=2)

        sq_norm_tangent_vec = self.embedding_metric.squared_norm(tangent_vec)
        norm_tangent_vec = gs.sqrt(sq_norm_tangent_vec)

        mask_0 = gs.isclose(sq_norm_tangent_vec, 0)
        mask_0 = gs.to_ndarray(mask_0, to_ndim=1)
        mask_else = ~mask_0
        mask_else = gs.to_ndarray(mask_else, to_ndim=1)

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

        coef_1[mask_0] = (1. +
                          COSH_TAYLOR_COEFFS[2] * norm_tangent_vec[mask_0]**2 +
                          COSH_TAYLOR_COEFFS[4] * norm_tangent_vec[mask_0]**4 +
                          COSH_TAYLOR_COEFFS[6] * norm_tangent_vec[mask_0]**6 +
                          COSH_TAYLOR_COEFFS[8] * norm_tangent_vec[mask_0]**8)
        coef_2[mask_0] = (1. +
                          SINH_TAYLOR_COEFFS[3] * norm_tangent_vec[mask_0]**2 +
                          SINH_TAYLOR_COEFFS[5] * norm_tangent_vec[mask_0]**4 +
                          SINH_TAYLOR_COEFFS[7] * norm_tangent_vec[mask_0]**6 +
                          SINH_TAYLOR_COEFFS[9] * norm_tangent_vec[mask_0]**8)

        coef_1[mask_else] = gs.cosh(norm_tangent_vec[mask_else])
        coef_2[mask_else] = (gs.sinh(norm_tangent_vec[mask_else]) /
                             norm_tangent_vec[mask_else])

        exp = (gs.einsum('ni,nj->nj', coef_1, base_point) +
               gs.einsum('ni,nj->nj', coef_2, tangent_vec))

        hyperbolic_space = HyperbolicSpace(dimension=self.dimension)
        exp = hyperbolic_space.regularize(exp)
        return exp
Пример #19
0
    def exp(self, tangent_vec, base_curve):
        """
        Riemannian exponential of a tangent vector wrt to a base curve.
        """
        if not isinstance(self.embedding_metric, EuclideanMetric):
            raise AssertionError('The exponential map is only implemented '
                                 'for dicretized curves embedded in a '
                                 'Euclidean space.')
        base_curve = gs.to_ndarray(base_curve, to_ndim=3)
        tangent_vec = gs.to_ndarray(tangent_vec, to_ndim=3)
        n_sampling_points = base_curve.shape[1]

        base_curve_srv = self.square_root_velocity(base_curve)

        tangent_vec_derivative = (n_sampling_points - 1) * (
            tangent_vec[:, 1:, :] - tangent_vec[:, :-1, :])
        base_curve_velocity = (n_sampling_points - 1) * (base_curve[:, 1:, :] -
                                                         base_curve[:, :-1, :])
        base_curve_velocity_norm = self.pointwise_norm(base_curve_velocity,
                                                       base_curve[:, :-1, :])

        inner_prod = self.pointwise_inner_product(tangent_vec_derivative,
                                                  base_curve_velocity,
                                                  base_curve[:, :-1, :])
        coef_1 = 1 / gs.sqrt(base_curve_velocity_norm)
        coef_2 = -1 / (2 * base_curve_velocity_norm**(5 / 2)) * inner_prod

        term_1 = gs.einsum('ij,ijk->ijk', coef_1, tangent_vec_derivative)
        term_2 = gs.einsum('ij,ijk->ijk', coef_2, base_curve_velocity)
        srv_initial_derivative = term_1 + term_2

        end_curve_srv = self.l2_metric.exp(tangent_vec=srv_initial_derivative,
                                           base_curve=base_curve_srv)
        end_curve_starting_point = self.embedding_metric.exp(
            tangent_vec=tangent_vec[:, 0, :], base_point=base_curve[:, 0, :])
        end_curve = self.square_root_velocity_inverse(
            end_curve_srv, end_curve_starting_point)

        return end_curve
Пример #20
0
    def dist(self, curve_a, curve_b):
        """
        Geodesic distance between two discretized curves.
        """
        assert curve_a.shape == curve_b.shape
        curve_a = gs.to_ndarray(curve_a, to_ndim=3)
        curve_b = gs.to_ndarray(curve_b, to_ndim=3)

        n_curves, n_sampling_points, n_coords = curve_a.shape

        curve_a = gs.reshape(curve_a, (n_curves * n_sampling_points, n_coords))
        curve_b = gs.reshape(curve_b, (n_curves * n_sampling_points, n_coords))

        dist = self.embedding_metric.dist(curve_a, curve_b)
        dist = gs.reshape(dist, (n_curves, n_sampling_points))
        n_sampling_points_float = gs.array(n_sampling_points)
        n_sampling_points_float = gs.cast(n_sampling_points_float, gs.float32)
        dist = gs.sqrt(gs.sum(dist**2, -1) / n_sampling_points_float)
        dist = gs.to_ndarray(dist, to_ndim=1)
        dist = gs.to_ndarray(dist, to_ndim=2, axis=1)

        return dist
Пример #21
0
    def test_intrinsic_and_extrinsic_coords(self):
        """
        Test that the composition of
        intrinsic_to_extrinsic_coords and
        extrinsic_to_intrinsic_coords
        gives the identity.
        """
        point_int = gs.array([.1, 0., 0., .1])
        point_ext = self.space.intrinsic_to_extrinsic_coords(point_int)
        result = self.space.extrinsic_to_intrinsic_coords(point_ext)
        expected = point_int
        expected = helper.to_vector(expected)

        gs.testing.assert_allclose(result, expected)

        point_ext = 1. / (gs.sqrt(6.)) * gs.array([1., 0., 0., 1., 2.])
        point_int = self.space.extrinsic_to_intrinsic_coords(point_ext)
        result = self.space.intrinsic_to_extrinsic_coords(point_int)
        expected = point_ext
        expected = helper.to_vector(expected)

        gs.testing.assert_allclose(result, expected)
Пример #22
0
def estimation(kalman, initial_state, inputs, observations, obs_freq):
    """Carry out the state estimation for a specific system.

    Parameters
    ----------
    kalman : KalmanFilter
        Filter used to estimate the state.
    initial_state : array-like, shape=[dim]
        Guess of the true initial state.
    inputs : list(array-like, shape=[dim_input])
        Inputs received by the propagation sensor.
    observations : array-like, shape=[len(inputs) + 1/obs_freq, dim_obs]
        Measurements of the system.
    obs_freq : int
        Number of time steps between observations.

    Returns
    -------
    traj : array-like, shape=[len(inputs) + 1, dim]
        Estimated trajectory.
    three_sigmas : array-like, shape=[len(inputs) + 1, dim]
        3-sigma envelope of the estimated state covariance.
    """
    kalman.state = 1 * initial_state

    traj = [1 * kalman.state]
    uncertainty = [1 * gs.diagonal(kalman.covariance)]
    for i, _ in enumerate(inputs):
        kalman.propagate(inputs[i])
        if i > 0 and i % obs_freq == obs_freq - 1:
            kalman.update(observations[(i // obs_freq)])
        traj.append(1 * kalman.state)
        uncertainty.append(1 * gs.diagonal(kalman.covariance))
    traj = gs.array(traj)
    uncertainty = gs.array(uncertainty)
    three_sigmas = 3 * gs.sqrt(uncertainty)

    return traj, three_sigmas
Пример #23
0
    def test_exp_vectorization(self):
        point = gs.array([[1.0, 1.0], [1.0, 1.0]])
        tangent_vec = gs.array([[2.0, 1.0], [2.0, 1.0]])
        result = self.metric.exp(tangent_vec, point)

        point = point[0]
        tangent_vec = tangent_vec[0]
        circle_center = point[0] + point[1] * tangent_vec[1] / tangent_vec[0]
        circle_radius = gs.sqrt((circle_center - point[0]) ** 2 + point[1] ** 2)

        moebius_d = 1
        moebius_c = 1 / (2 * circle_radius)
        moebius_b = circle_center - circle_radius
        moebius_a = (circle_center + circle_radius) * moebius_c

        point_complex = point[0] + 1j * point[1]
        tangent_vec_complex = tangent_vec[0] + 1j * tangent_vec[1]

        point_moebius = (
            1j
            * (moebius_d * point_complex - moebius_b)
            / (moebius_c * point_complex - moebius_a)
        )
        tangent_vec_moebius = (
            -1j
            * tangent_vec_complex
            * (1j * moebius_c * point_moebius + moebius_d) ** 2
        )

        end_point_moebius = point_moebius * gs.exp(tangent_vec_moebius / point_moebius)
        end_point_complex = (moebius_a * 1j * end_point_moebius + moebius_b) / (
            moebius_c * 1j * end_point_moebius + moebius_d
        )
        end_point_expected = gs.hstack(
            [np.real(end_point_complex), np.imag(end_point_complex)]
        )
        expected = gs.stack([end_point_expected, end_point_expected])
        self.assertAllClose(result, expected)
Пример #24
0
    def random_von_mises_fisher(self, kappa=10, n_samples=1):
        """
        Sample in the 2-sphere with the von Mises distribution
        centered in the north pole.
        """
        if self.dimension != 2:
            raise NotImplementedError(
                'Sampling from the von Mises Fisher distribution'
                'is only implemented in dimension 2.')
        angle = 2. * gs.pi * gs.random.rand(n_samples)
        angle = gs.to_ndarray(angle, to_ndim=2, axis=1)
        unit_vector = gs.hstack((gs.cos(angle), gs.sin(angle)))
        scalar = gs.random.rand(n_samples)

        coord_z = 1. + 1. / kappa * gs.log(scalar +
                                           (1. - scalar) * gs.exp(-2. * kappa))
        coord_z = gs.to_ndarray(coord_z, to_ndim=2, axis=1)

        coord_xy = gs.sqrt(1. - coord_z**2) * unit_vector

        point = gs.hstack((coord_xy, coord_z))

        return point
Пример #25
0
            def _exp(tangent_vec, base_point):
                circle_center = (
                    base_point[0] + base_point[1] * tangent_vec[1] / tangent_vec[0]
                )
                circle_radius = gs.sqrt(
                    (circle_center - base_point[0]) ** 2 + base_point[1] ** 2
                )

                moebius_d = 1
                moebius_c = 1 / (2 * circle_radius)
                moebius_b = circle_center - circle_radius
                moebius_a = (circle_center + circle_radius) * moebius_c

                point_complex = base_point[0] + 1j * base_point[1]
                tangent_vec_complex = tangent_vec[0] + 1j * tangent_vec[1]

                point_moebius = (
                    1j
                    * (moebius_d * point_complex - moebius_b)
                    / (moebius_c * point_complex - moebius_a)
                )
                tangent_vec_moebius = (
                    -1j
                    * tangent_vec_complex
                    * (1j * moebius_c * point_moebius + moebius_d) ** 2
                )

                end_point_moebius = point_moebius * gs.exp(
                    tangent_vec_moebius / point_moebius
                )
                end_point_complex = (moebius_a * 1j * end_point_moebius + moebius_b) / (
                    moebius_c * 1j * end_point_moebius + moebius_d
                )
                end_point_expected = gs.hstack(
                    [np.real(end_point_complex), np.imag(end_point_complex)]
                )
                return end_point_expected
Пример #26
0
    def noise_jacobian(state, sensor_input):
        r"""Compute the matrix associated to the propagation noise.

        The noise is supposed additive and only applies to the speed part.
        The Jacobian is given by :math:`\begin{bmatrix} 0 & \sqrt{dt}
        \end{bmatrix}`.

        Parameters
        ----------
        state : unused
        sensor_input : array-like, shape=[2]
            Vector representing the information from the accelerometer.

        Returns
        -------
        jacobian : array-like, shape=[dim_noise, dim]
            Jacobian of the propagation w.r.t. the noise.
        """
        dt, _ = sensor_input
        dim = LocalizationLinear.dim
        position_wrt_noise = gs.zeros((dim // 2, dim // 2))
        speed_wrt_noise = gs.sqrt(dt) * gs.eye(dim // 2)
        jac = gs.vstack((position_wrt_noise, speed_wrt_noise))
        return jac
Пример #27
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
Пример #28
0
    def intrinsic_to_extrinsic_coords(self, point_intrinsic):
        """Convert point from intrinsic to extrensic coordinates.

        Convert from the intrinsic coordinates in the Hypersphere,
        to the extrinsic coordinates in Euclidean space.

        Parameters
        ----------
        point_intrinsic : array-like, shape=[n_samples, dimension]

        Returns
        -------
        point_extrinsic : array-like, shape=[n_samples, dimension + 1]
        """
        point_intrinsic = gs.to_ndarray(point_intrinsic, to_ndim=2)

        # FIXME: The next line needs to be guarded against taking the sqrt of
        #        negative numbers.
        coord_0 = gs.sqrt(1. - gs.linalg.norm(point_intrinsic, axis=-1)**2)
        coord_0 = gs.to_ndarray(coord_0, to_ndim=2, axis=-1)

        point_extrinsic = gs.concatenate([coord_0, point_intrinsic], axis=-1)

        return point_extrinsic
Пример #29
0
    def _intrinsic_to_extrinsic_coordinates(point_intrinsic):
        """Convert intrinsic to extrinsic coordinates.

        Convert the parameterization of a point in hyperbolic space
        from its intrinsic coordinates, to its extrinsic coordinates
        in Minkowski space.

        Parameters
        ----------
        point_intrinsic : array-like, shape=[..., dim]
            Point in hyperbolic space in intrinsic coordinates.

        Returns
        -------
        point_extrinsic : array-like, shape=[..., dim + 1]
            Point in hyperbolic space in extrinsic coordinates.
        """
        coord_0 = gs.sqrt(1. + gs.linalg.norm(point_intrinsic, axis=-1)**2)
        coord_0 = gs.to_ndarray(coord_0, to_ndim=1)
        coord_0 = gs.to_ndarray(coord_0, to_ndim=2, axis=1)

        point_extrinsic = gs.hstack([coord_0, point_intrinsic])

        return point_extrinsic
Пример #30
0
    def random_von_mises_fisher(
            self, mu=None, kappa=10, n_samples=1, max_iter=100):
        """Sample with the von Mises-Fisher distribution.

        This distribution corresponds to the maximum entropy distribution
        given a mean. In dimension 2, a closed form expression is available.
        In larger dimension, rejection sampling is used according to [Wood94]_

        References
        ----------
        https://en.wikipedia.org/wiki/Von_Mises-Fisher_distribution

        .. [Wood94]   Wood, Andrew T. A. “Simulation of the von Mises Fisher
                      Distribution.” Communications in Statistics - Simulation
                      and Computation, June 27, 2007.
                      https://doi.org/10.1080/03610919408813161.

        Parameters
        ----------
        mu : array-like, shape=[dim]
            Mean parameter of the distribution.
        kappa : float
            Kappa parameter of the von Mises distribution.
            Optional, default: 10.
        n_samples : int
            Number of samples.
            Optional, default: 1.
        max_iter : int
            Maximum number of trials in the rejection algorithm. In case it
            is reached, the current number of samples < n_samples is returned.
            Optional, default: 100.

        Returns
        -------
        point : array-like, shape=[n_samples, dim + 1]
            Points sampled on the sphere in extrinsic coordinates
            in Euclidean space of dimension dim + 1.
        """
        dim = self.dim

        if dim == 2:
            angle = 2. * gs.pi * gs.random.rand(n_samples)
            angle = gs.to_ndarray(angle, to_ndim=2, axis=1)
            unit_vector = gs.hstack((gs.cos(angle), gs.sin(angle)))
            scalar = gs.random.rand(n_samples)

            coord_x = 1. + 1. / kappa * gs.log(
                scalar + (1. - scalar) * gs.exp(gs.array(-2. * kappa)))
            coord_x = gs.to_ndarray(coord_x, to_ndim=2, axis=1)
            coord_yz = gs.sqrt(1. - coord_x ** 2) * unit_vector
            sample = gs.hstack((coord_x, coord_yz))

        else:
            # rejection sampling in the general case
            sqrt = gs.sqrt(4 * kappa ** 2. + dim ** 2)
            envelop_param = (-2 * kappa + sqrt) / dim
            node = (1. - envelop_param) / (1. + envelop_param)
            correction = kappa * node + dim * gs.log(1. - node ** 2)

            n_accepted, n_iter = 0, 0
            result = []
            while (n_accepted < n_samples) and (n_iter < max_iter):
                sym_beta = beta.rvs(
                    dim / 2, dim / 2, size=n_samples - n_accepted)
                sym_beta = gs.cast(sym_beta, node.dtype)
                coord_x = (1 - (1 + envelop_param) * sym_beta) / (
                    1 - (1 - envelop_param) * sym_beta)
                accept_tol = gs.random.rand(n_samples - n_accepted)
                criterion = (
                    kappa * coord_x
                    + dim * gs.log(1 - node * coord_x)
                    - correction) > gs.log(accept_tol)
                result.append(coord_x[criterion])
                n_accepted += gs.sum(criterion)
                n_iter += 1
            if n_accepted < n_samples:
                logging.warning(
                    'Maximum number of iteration reached in rejection '
                    'sampling before n_samples were accepted.')
            coord_x = gs.concatenate(result)
            coord_rest = _Hypersphere(dim - 1).random_uniform(n_accepted)
            coord_rest = gs.einsum(
                '...,...i->...i', gs.sqrt(1 - coord_x ** 2), coord_rest)
            sample = gs.concatenate([coord_x[..., None], coord_rest], axis=1)

        if mu is not None:
            sample = utils.rotate_points(sample, mu)

        return sample if (n_samples > 1) else sample[0]