r"""
Surfaces backed by pyflatsurf.
There should be no need to create such surfaces directly, not even for authors
of sage-flatsurf. Instead just call
:meth:`~flatsurf.geometry.categories.translation_surfaces.TranslationSurfaces.FiniteType.ParentMethods.pyflatsurf`
on a surface which returns such a surface (or rather, a morphism to such a
surface.)
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus()
sage: T = S.pyflatsurf().codomain() # optional: pyflatsurf # random output due to cppyy deprecation warnings
sage: T # optional: pyflatsurf
Surface backed by FlatTriangulationCombinatorial(vertices = (1, -3, 2, -1, 3, -2), faces = (1, 2, 3)(-1, -2, -3)) with vectors {1: (1, 0), 2: (0, 1), 3: (-1, -1)}
Ideally, there should be no need to use the underlying ``FlatTriangulation``
directly, but it can be accessed with
:meth:`Surface_pyflatsurf.flat_triangulation`::
sage: T.flat_triangulation() # optional: pyflatsurf
FlatTriangulationCombinatorial(vertices = (1, -3, 2, -1, 3, -2), faces = (1, 2, 3)(-1, -2, -3)) with vectors {1: (1, 0), 2: (0, 1), 3: (-1, -1)}
"""
# ********************************************************************
# This file is part of sage-flatsurf.
#
# Copyright (C) 2024 Julian Rüth
#
# sage-flatsurf is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# sage-flatsurf is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with sage-flatsurf. If not, see <https://www.gnu.org/licenses/>.
# ********************************************************************
from flatsurf.geometry.surface import OrientedSimilaritySurface
from sage.misc.cachefunc import cached_method
[docs]
class Surface_pyflatsurf(OrientedSimilaritySurface):
r"""
A translation surface backed by pyflatsurf.
Most surfaces in sage-flatsurf, such as the
:class:`~flatsurf.geometry.surface.MutableOrientedSimilaritySurface` are
implemented in Python instead.
EXAMPLES::
sage: from flatsurf import Polygon, MutableOrientedSimilaritySurface
sage: S = MutableOrientedSimilaritySurface(QQ)
sage: S.add_polygon(Polygon(vertices=[(0, 0), (1, 0), (1, 1)]), label=0)
0
sage: S.add_polygon(Polygon(vertices=[(0, 0), (1, 1), (0, 1)]), label=1)
1
sage: S.glue((0, 0), (1, 1))
sage: S.glue((0, 1), (1, 2))
sage: S.glue((0, 2), (1, 0))
sage: S.set_immutable()
sage: T = S.pyflatsurf().codomain() # optional: pyflatsurf
TESTS::
sage: from flatsurf.geometry.pyflatsurf.surface import Surface_pyflatsurf
sage: isinstance(T, Surface_pyflatsurf) # optional: pyflatsurf
True
sage: TestSuite(T).run() # optional: pyflatsurf
Disconnected surfaces can be import from pyflatsurf::
sage: S = MutableOrientedSimilaritySurface(QQ)
sage: S.add_polygon(Polygon(vertices=[(0, 0), (1, 0), (1, 1), (0, 1)]))
0
sage: S.add_polygon(Polygon(vertices=[(0, 0), (1, 0), (1, 1), (0, 1)]))
1
sage: S.glue((0, 0), (0, 2))
sage: S.glue((0, 1), (0, 3))
sage: S.glue((1, 0), (1, 2))
sage: S.glue((1, 1), (1, 3))
sage: S.set_immutable()
sage: T = S.pyflatsurf().codomain() # optional: pyflatsurf
sage: T.is_connected() # optional: pyflatsurf
False
sage: T.flat_triangulation() # optional: pyflatsurf
FlatTriangulationCombinatorial(vertices = (1, -3, 2, -1, 3, -2)(4, -6, 5, -4, 6, -5), faces = (1, 2, 3)(-1, -2, -3)(4, 5, 6)(-4, -5, -6)) with vectors {1: (1, 0), 2: (0, 1), 3: (-1, -1), 4: (1, 0), 5: (0, 1), 6: (-1, -1)}
sage: TestSuite(T).run() # optional: pyflatsurf
"""
[docs]
def __init__(self, flat_triangulation):
self._flat_triangulation = flat_triangulation
from flatsurf.geometry.pyflatsurf.conversion import RingConversion
self._ring_conversion = RingConversion.from_pyflatsurf_from_flat_triangulation(
flat_triangulation
)
from flatsurf.geometry.pyflatsurf.conversion import VectorSpaceConversion
self._vector_space_conversion = VectorSpaceConversion.to_pyflatsurf(
self._ring_conversion.domain() ** 2, ring_conversion=self._ring_conversion
)
from flatsurf.geometry.categories import TranslationSurfaces
category = TranslationSurfaces().FiniteType()
if flat_triangulation.hasBoundary():
category = category.WithBoundary()
else:
category = category.WithoutBoundary()
super().__init__(base=self._ring_conversion.domain(), category=category)
if self.is_connected():
self._refine_category_(category.Connected())
[docs]
def flat_triangulation(self):
r"""
Return the pyflatsurf ``FlatTriangulation`` object underlying this
surface.
.. WARNING::
This surface is supposed to be immutable. However, the
``FlatTriangulation`` is not immutable. If you make any changes to
it, things might break in strange ways. If you want to make
modifications to the ``FlatTriangulation`` be sure to work on a
``clone()`` of it instead.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus()
sage: S.pyflatsurf().codomain().flat_triangulation() # optional: pyflatsurf
FlatTriangulationCombinatorial(vertices = (1, -3, 2, -1, 3, -2), faces = (1, 2, 3)(-1, -2, -3)) with vectors {1: (1, 0), 2: (0, 1), 3: (-1, -1)}
"""
return self._flat_triangulation
[docs]
def is_mutable(self):
r"""
Return whether this surface is mutable.
.. WARNING::
This surface is supposed to be immutable. However, the underlying
``FlatTriangulation`` is not immutable. If you make any changes to
it, things might break in strange ways. If you want to make
modifications to the ``FlatTriangulation`` be sure to work on a
``clone()`` of it instead. See :meth:`flat_triangulation`.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus()
sage: S.pyflatsurf().codomain().is_mutable() # optional: pyflatsurf
False
"""
return False
[docs]
@cached_method
def roots(self):
r"""
Return some root labels for this surface, one for each connected
component of the surface.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus()
sage: T = S.pyflatsurf().codomain() # optional: pyflatsurf
sage: list(T.labels()) # optional: pyflatsurf
[(1, 2, 3), (-3, -1, -2)]
sage: T.roots() # optional: pyflatsurf
((1, 2, 3),)
"""
roots = []
seen = set()
for face in self.flat_triangulation().faces():
root = self._normalize_label(face)
if root not in seen:
roots.append(root)
for root in self.component(root):
seen.add(root)
return tuple(roots)
[docs]
def pyflatsurf(self):
r"""
Return a version of this surface that is backed by pyflatsurf. Since
this surface is already backed by pyflatsurf, this returns a trivial
morphism.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus()
sage: T = S.pyflatsurf().codomain() # optional: pyflatsurf
sage: T.pyflatsurf() # optional: pyflatsurf
Identity endomorphism of Surface backed by FlatTriangulationCombinatorial(vertices = (1, -3, 2, -1, 3, -2), faces = (1, 2, 3)(-1, -2, -3)) with vectors {1: (1, 0), 2: (0, 1), 3: (-1, -1)}
"""
from flatsurf.geometry.morphism import IdentityMorphism
return IdentityMorphism._create_morphism(self)
@classmethod
def _from_flatsurf(cls, surface):
r"""
Return an isomorphism to a :class:`Surface_pyflatsurf` built from
``surface``, i.e., represent ``surface`` in pyflatsurf wrapped as a
:class:`Surface` for sage-flatsurf.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus().triangulate().codomain()
sage: from flatsurf.geometry.pyflatsurf.surface import Surface_pyflatsurf
sage: Surface_pyflatsurf._from_flatsurf(S) # optional: pyflatsurf
pyflatsurf conversion morphism:
From: Triangulation of Translation Surface in H_1(0) built from a square
To: Surface backed by FlatTriangulationCombinatorial(vertices = (1, -3, 2, -1, 3, -2), faces = (1, 2, 3)(-1, -2, -3)) with vectors {1: (1, 0), 2: (0, 1), 3: (-1, -1)}
"""
if isinstance(surface, Surface_pyflatsurf):
return surface
if not surface.is_triangulated():
triangulation = surface.triangulate()
to_pyflatsurf = cls._from_flatsurf(triangulation.codomain())
return to_pyflatsurf * triangulation
from flatsurf.geometry.pyflatsurf.conversion import FlatTriangulationConversion
to_pyflatsurf = FlatTriangulationConversion.to_pyflatsurf(surface)
surface_pyflatsurf = Surface_pyflatsurf(to_pyflatsurf.codomain())
from flatsurf.geometry.pyflatsurf.morphism import Morphism_to_pyflatsurf
return Morphism_to_pyflatsurf._create_morphism(
surface, surface_pyflatsurf, to_pyflatsurf
)
[docs]
def __repr__(self):
r"""
Return a printable representation of this surface, namely, print this
surface essentially as pyflatsurf would.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus().triangulate().codomain()
sage: from flatsurf.geometry.pyflatsurf.surface import Surface_pyflatsurf
sage: S.pyflatsurf().codomain() # optional: pyflatsurf
Surface backed by FlatTriangulationCombinatorial(vertices = (1, -3, 2, -1, 3, -2), faces = (1, 2, 3)(-1, -2, -3)) with vectors {1: (1, 0), 2: (0, 1), 3: (-1, -1)}
"""
return f"Surface backed by {self._flat_triangulation!r}"
[docs]
def apply_matrix(self, m, in_place=None):
r"""
Overrides the generic
:meth:`~flatsurf.geometry.categories.similarity_surfaces.SimilaritySurfaces.ParentMethods.apply_matrix` for this
pyflatsurf backed surface.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus().pyflatsurf().codomain() # optional: pyflatsurf
sage: S.apply_matrix(matrix([[1, 2], [0, 1]])).codomain() # optional: pyflatsurf
Surface backed by FlatTriangulationCombinatorial(vertices = (1, -3, 2, -1, 3, -2), faces = (1, 2, 3)(-1, -2, -3)) with vectors {1: (1, 0), 2: (2, 1), 3: (-3, -1)}
"""
if in_place:
return super().apply_matrix(m, in_place=in_place)
from sage.all import matrix
m = matrix(m, base_ring=self.base_ring())
m = [self._ring_conversion(x) for x in m.list()]
deformation = self._flat_triangulation.applyMatrix(*m)
codomain = Surface_pyflatsurf(deformation.codomain())
from flatsurf.geometry.pyflatsurf.morphism import Morphism_Deformation
return Morphism_Deformation._create_morphism(self, codomain, deformation)
@classmethod
def _normalize_label(cls, label):
r"""
Return a normalized version of the ``label`` of this surface.
Labels are formed by the triple of the attached half edges in order.
They are normalized such that the minimal half edge is first.
EXAMPLES::
sage: from flatsurf.geometry.pyflatsurf.surface import Surface_pyflatsurf
sage: Surface_pyflatsurf._normalize_label((1, 2, 3))
(1, 2, 3)
sage: Surface_pyflatsurf._normalize_label((1, -2, 3))
(-2, 3, 1)
sage: Surface_pyflatsurf._normalize_label((1, -2, -3))
(-3, 1, -2)
"""
label = tuple(edge.id() if hasattr(edge, "id") else int(edge) for edge in label)
shift = label.index(min(*label))
label = label[shift:] + label[:shift]
return label
[docs]
def polygon(self, label):
r"""
Return the polygon with ``label`` in this surface.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus().triangulate().codomain()
sage: S = S.pyflatsurf().codomain() # optional: pyflatsurf
sage: S.polygon((1, 2, 3)) # optional: pyflatsurf
Polygon(vertices=[(0, 0), (1, 0), (1, 1)])
"""
label = Surface_pyflatsurf._normalize_label(label)
from pyflatsurf import flatsurf
half_edges = (flatsurf.HalfEdge(half_edge) for half_edge in label)
vectors = [
self._flat_triangulation.fromHalfEdge(half_edge) for half_edge in half_edges
]
vectors = [self._vector_space_conversion.section(vector) for vector in vectors]
from flatsurf.geometry.polygon import Polygon
return Polygon(edges=vectors)
[docs]
def opposite_edge(self, label, edge):
r"""
Return the polygon and edge that is across from ``edge`` of ``label``.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus().triangulate().codomain()
sage: S = S.pyflatsurf().codomain() # optional: pyflatsurf
sage: S.opposite_edge((1, 2, 3), 0) # optional: pyflatsurf
((-3, -1, -2), 1)
sage: S.opposite_edge((1, 2, 3), 1) # optional: pyflatsurf
((-3, -1, -2), 2)
sage: S.opposite_edge((1, 2, 3), 2) # optional: pyflatsurf
((-3, -1, -2), 0)
"""
label = Surface_pyflatsurf._normalize_label(label)
from pyflatsurf import flatsurf
half_edge = flatsurf.HalfEdge(label[edge])
opposite_half_edge = -half_edge
opposite_label = self._flat_triangulation.face(opposite_half_edge)
opposite_label = Surface_pyflatsurf._normalize_label(opposite_label)
return opposite_label, opposite_label.index(opposite_half_edge.id())
[docs]
def __eq__(self, other):
r"""
Return whether this surface is indistinguishable from ``other``.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus().pyflatsurf().codomain() # optional: pyflatsurf
sage: T = translation_surfaces.square_torus().pyflatsurf().codomain() # optional: pyflatsurf
sage: S == T # optional: pyflatsurf
True
"""
if not isinstance(other, Surface_pyflatsurf):
return False
return self._flat_triangulation == other._flat_triangulation
[docs]
def __hash__(self):
r"""
Return a hash value for this surface that is compatible with
:meth:`__eq__`.
EXAMPLES::
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus().pyflatsurf().codomain() # optional: pyflatsurf
sage: T = translation_surfaces.square_torus().pyflatsurf().codomain() # optional: pyflatsurf
sage: hash(S) == hash(T) # optional: pyflatsurf
True
"""
return hash(self._flat_triangulation)