Source code for geomstats.geometry.manifold

"""Manifold module.

In other words, a topological space that locally resembles
Euclidean space near each point.

Lead author: Nina Miolane.
"""

import abc

import geomstats.backend as gs
import geomstats.errors


[docs] class Manifold(abc.ABC): r"""Class for manifolds. Parameters ---------- dim : int Dimension of the manifold. shape : tuple of int Shape of one element of the manifold. Optional, default : None. default_coords_type : str, {'intrinsic', 'extrinsic', etc} Coordinate type. Optional, default: 'intrinsic'. equip : bool If True, equip space with default metric. Attributes ---------- point_ndim : int Dimension of point array. default_point_type : str Point type: "vector" or "matrix". """ def __init__( self, dim, shape, default_coords_type="intrinsic", equip=True, ): geomstats.errors.check_integer(dim, "dim") if not isinstance(shape, tuple): raise ValueError("Expected a tuple for the shape argument.") self.dim = dim self.shape = shape self.default_coords_type = default_coords_type self.point_ndim = len(self.shape) if self.point_ndim == 1: self.default_point_type = "vector" elif self.point_ndim == 2: self.default_point_type = "matrix" else: self.default_point_type = "other" if equip: self.equip_with_metric()
[docs] def equip_with_metric(self, Metric=None, **metric_kwargs): """Equip manifold with a Riemannian metric. Parameters ---------- Metric : RiemannianMetric object If None, default metric will be used. """ if Metric is None: out = self.default_metric() if isinstance(out, tuple): Metric, kwargs = out kwargs.update(metric_kwargs) metric_kwargs = kwargs else: Metric = out self.metric = Metric(self, **metric_kwargs) return self
[docs] def equip_with_group_action(self, group_action): """Equip manifold with group action. Parameters ---------- group_action : str Group action. """ self.group_action = group_action
def _check_equip_with_quotient_structure(self): if not hasattr(self, "_quotient_map"): raise ValueError("No quotient structure defined for this manifold.") for structure_name in ("metric", "group_action"): if not hasattr(self, structure_name): raise ValueError(f"Need to equip with `{structure_name}` first")
[docs] def equip_with_quotient_structure(self): """Equip manifold with quotient structure. Creates attributes `quotient` and `fiber_bundle`. """ self._check_equip_with_quotient_structure() key = type(self.metric), self.group_action out = self._quotient_map.get(key, None) if out is None: raise ValueError(f"No mapping for key: {key}") FiberBundle_, QuotientMetric_ = out self.fiber_bundle = FiberBundle_(total_space=self) self.quotient = self.new(equip=False) self.quotient.equip_with_metric(QuotientMetric_, fiber_bundle=self.fiber_bundle)
[docs] @abc.abstractmethod def belongs(self, point, atol=gs.atol): """Evaluate if a point belongs to the manifold. Parameters ---------- point : array-like, shape=[..., *point_shape] Point to evaluate. atol : float Absolute tolerance. Optional, default: backend atol. Returns ------- belongs : array-like, shape=[...,] Boolean evaluating if point belongs to the manifold. """
[docs] @abc.abstractmethod def is_tangent(self, vector, base_point, atol=gs.atol): """Check whether the vector is tangent at base_point. Parameters ---------- vector : array-like, shape=[..., *point_shape] Vector. base_point : array-like, shape=[..., *point_shape] Point on the manifold. atol : float Absolute tolerance. Optional, default: backend atol. Returns ------- is_tangent : bool Boolean denoting if vector is a tangent vector at the base point. """
[docs] @abc.abstractmethod def to_tangent(self, vector, base_point): """Project a vector to a tangent space of the manifold. Parameters ---------- vector : array-like, shape=[..., *point_shape] Vector. base_point : array-like, shape=[..., *point_shape] Point on the manifold. Returns ------- tangent_vec : array-like, shape=[..., *point_shape] Tangent vector at base point. """
[docs] @abc.abstractmethod def random_point(self, n_samples=1, bound=1.0): """Sample random points on the manifold according to some distribution. If the manifold is compact, preferably a uniform distribution will be used. Parameters ---------- n_samples : int Number of samples. Optional, default: 1. bound : float Bound of the interval in which to sample for non compact manifolds. Optional, default: 1. Returns ------- samples : array-like, shape=[..., *point_shape] Points sampled on the manifold. """
[docs] def regularize(self, point): """Regularize a point to the canonical representation for the manifold. Parameters ---------- point : array-like, shape=[..., dim] Point. Returns ------- regularized_point : array-like, shape=[..., *point_shape] Regularized point. """ return gs.copy(point)
[docs] def random_tangent_vec(self, base_point, n_samples=1): """Generate random tangent vec. Parameters ---------- n_samples : int Number of samples. Optional, default: 1. base_point : array-like, shape={[n_samples, *point_shape], [*point_shape,]} Point. Returns ------- tangent_vec : array-like, shape=[..., *point_shape] Tangent vec at base point. """ if ( n_samples > 1 and base_point.ndim > len(self.shape) and n_samples != len(base_point) ): raise ValueError( "The number of base points must be the same as the " "number of samples, when the number of base points is different from 1." ) batch_size = () if n_samples == 1 else (n_samples,) return self.to_tangent( gs.random.normal(size=batch_size + self.shape), base_point )