Source code for ddg.geometry.moebius_models

"""Möbius geometry module.

Contains model classes and functions for conversion between the models.
"""
import numpy as np

from ddg.geometry import euclidean_models
from ddg.geometry.geometry_model_templates import CayleyKleinGeometry, MetricGeometry
from ddg.geometry.intersection import meet
from ddg.geometry.projections import (
    inverse_stereographic_project,
    stereographic_project,
)
from ddg.geometry.quadrics import Quadric
from ddg.math.symmetric_matrices import symmetric_matrix_from_diagonal

__all__ = [
    "ProjectiveModel",
    "ParaboloidModel",
    "EuclideanModel",
    "euclidean_to_projective",
    "projective_to_euclidean",
    "paraboloid_to_projective_and_back",
]


[docs]class EuclideanModel: """Euclidean model of Möbius geometry. .. rubric:: Model space The model space is Euclidean space together with a single point at infinity, which we represent by ``float('inf')``. .. rubric:: Representation of objects Möbius Spheres in this model are Euclidean spheres or subspaces (spheres with center at infinity). """
class _Template(CayleyKleinGeometry): """ Common superclass for sphere model and paraboloid model, just to avoid duplicate code. Subclasses just need to have an attribute/property `absolute` and it will be used in the computations defined in the template methods of this auxiliary class. """ def angle(self, s1, s2): """Angle between two Möbius spheres. Parameters ---------- s1, s2 : Quadric Möbius spheres. Returns ------- float """ s1 = self.pole_of_sphere(s1) s2 = self.pole_of_sphere(s2) return np.arccos(np.sqrt(self.cayley_klein_distance(s1, s2))) def sphere_from_pole(self, subspace) -> Quadric: """Get a Möbius sphere from its pole. Returns intersection of `Q` and `Q.polarize(subspace)`, where `Q` is the Möbius quadric. Parameters ---------- subspace : Subspace Returns ------- Quadric """ Q = self.absolute return meet(Q, Q.polarize(subspace)) def pole_of_sphere(self, sphere): """Return pole corresponding to sphere. Parameters ---------- sphere : Quadric Returns ------- Subspace """ return self.absolute.polarize(sphere.subspace) def __contains__(self, point): return point in self.absolute
[docs]class ProjectiveModel(_Template): """Projective model of Möbius geometry. .. rubric:: Model space The Möbius quadric is the quadric with matrix ``diag([1,...,1, -1])``, which can be thought of as the unit sphere. .. rubric:: Representation of objects Spheres in this model are represented by quadrics which are intersections of the Möbius quadric with a subspace. Parameters ---------- dimension : int Attributes ---------- dimension : int """ @property def absolute(self): """The absolute quadric with matrix ``diag([1,...,1, -1])``. Returns ------- Quadric """ matrix = np.eye(self.dimension + 2) matrix[-1, -1] = -1 return Quadric(matrix)
[docs]class ParaboloidModel(_Template): """Paraboloid model of Möbius geometry. .. rubric:: Model space The Möbius quadric is the quadric with matrix:: I | --+----- | 0 1 | 1 0 Which can be thought of as a paraboloid. .. rubric:: Representation of objects Spheres in this model are represented by quadrics which are intersections of the Möbius quadric with a subspace. Parameters ---------- dimension : int Attributes ---------- dimension : int """ @property def absolute(self): """The absolute quadric. Returns the quadric with matrix :: I | --+----- | 0 1 | 1 0 Returns ------- Quadric """ diagonal = np.ones(self.dimension + 2) diagonal[[-2, -1]] = 0 return Quadric(symmetric_matrix_from_diagonal(diagonal, parabolic=True))
class _ProjectiveSubgeometry(CayleyKleinGeometry, MetricGeometry): """Common superclass for submodels of projective Möbius geometry: hyperbolic_models.HemisphereModel, euclidean_models.MoebiusModel and spherical_models.ProjectiveModel. avoids duplicate code. """ def __init__(self, dimension): super().__init__(dimension) self._moebius = ProjectiveModel(self.dimension) # Regular non-special methods can be forwarded like this self.absolute = self._moebius.absolute self.angle = self._moebius.angle # Special methods should be defined on the class and not in the instance dictionary. # See # https://docs.python.org/3/reference/datamodel.html#special-method-lookup def __contains__(self, point): return self._moebius.__contains__(point)
[docs]def euclidean_to_projective(object_, embedded=False): r"""Convert from Euclidean model to projective model. This function works by embedding into the equatorial plane, then projecting stereographically. Parameters ---------- object_ : Subspace or SphereLike Object in n-dimensional ambient space. embedded : bool (default=False) If False, `object_` is embedded into the equatorial plane before projecting. Returns ------- Quadric An intersection of the Möbius quadric with a subspace in (n+1)-dimensonal ambient space. """ if not embedded: object_ = object_.embed() return inverse_stereographic_project(object_)
[docs]def projective_to_euclidean(object_, embedded=False): r"""Convert a Möbius sphere to a Euclidean sphere or subspace. This function works by projecting stereographically to the equatorial plane, then computing coordinates. Parameters ---------- object_ : Quadric Intersection of Möbius quadric with a subspace, all in (n+1)-dim. ambient space. embedded : bool (default=False) Whether to return the object in the equatorial plane or in n-dim. ambient space. Returns ------- SphereLike or Subspace """ img = stereographic_project(object_) if not embedded: img = img.unembed() if isinstance(img, Quadric): geo = euclidean_models.ProjectiveModel(img.ambient_dimension) img = geo.quadric_to_sphere(img) return img
[docs]def paraboloid_to_projective_and_back(object_): r"""Convert between projective and paraboloid models. Transform an object with the transformation that takes the projective model quadric to the paraboloid model quadric. This transformation is an involution, i.e. applying it again is the same as undoing the transformation. The transformation is :: I | ---+--------------------- | 1/sqrt(2) 1/sqrt(2) | 1/sqrt(2) -1/sqrt(2) Where I is the identity matrix of the appropriate size. Warnings -------- This WILL mutate `object_`. Parameters ---------- object_ : Transformable Returns ------- object_ : type(object\_) Transformed `object_`. """ trafo = np.eye(object_.ambient_dimension + 1) trafo[-2:, -2:] = np.sqrt(0.5) * np.array([[1, 1], [1, -1]]) object_ = object_.transform(trafo) return object_