similarity_surfaces

The category of similarity surfaces.

This module provides shared functionality for all surfaces in sage-flatsurf that are built from Euclidean polygons that are glued by similarities, i.e., identified edges can be transformed into each other by application of rotation and homothety (dilation) and translation.

See flatsurf.geometry.categories for a general description of the category framework in sage-flatsurf.

Normally, you won’t create this (or any other) category directly. The correct category is automatically determined for immutable surfaces.

EXAMPLES:

sage: from flatsurf import MutableOrientedSimilaritySurface
sage: C = MutableOrientedSimilaritySurface(QQ).category()

sage: from flatsurf.geometry.categories import SimilaritySurfaces
sage: C.is_subcategory(SimilaritySurfaces())
True

The easiest way to construct a similarity surface is to use the constructions from flatsurf.geometry.similarity_surface_generators.SimilaritySurfaceGenerators:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0,0), (2,0), (1,4), (0,5)])
sage: similarity_surfaces.self_glued_polygon(P)
Half-Translation Surface in Q_0(0, -1^4) built from a quadrilateral

Another way is to build a surface from scratch (using e.g. flatsurf.geometry.surface.MutableOrientedSimilaritySurface):

sage: P = Polygon(vertices=[(0,0), (1,0), (1,1), (0,1)])
sage: S = MutableOrientedSimilaritySurface(QQ)
sage: S.add_polygon(P)
0
sage: S.add_polygon(2*P)
1
sage: S.add_polygon(3*P)
2
sage: S.glue((0, 1), (1, 3))
sage: S.glue((0, 0), (2, 2))
sage: S.glue((0, 2), (2, 0))
sage: S.glue((0, 3), (1, 1))
sage: S.glue((1, 2), (2, 1))
sage: S.glue((1, 0), (2, 3))
sage: S
Surface built from 3 squares

To perform a sanity check on the obtained surface, you can run its test suite:

sage: TestSuite(S).run()

If there are no errors reported, no consistency problems could be detected in your surface.

Once you mark the surface as immutable, it gets more functionality, e.g., coming from its structure as a translation surface. This also adds more tests to its test suite:

sage: S.category()
Category of finite type oriented similarity surfaces
sage: S.set_immutable()
sage: S.category()
Category of connected without boundary finite type oriented rational similarity surfaces

sage: TestSuite(S).run()

In the following example, we attempt to build a broken surface by gluing more than two edges to each other; however, edges get unglued automatically:

sage: S = MutableOrientedSimilaritySurface.from_surface(S)
sage: S.glue((0, 0), (0, 3))
sage: S.glue((0, 1), (0, 3))
sage: S.glue((0, 2), (0, 3))

sage: S.gluings()
(((0, 2), (0, 3)), ((0, 3), (0, 2)), ((1, 0), (2, 3)), ((1, 2), (2, 1)), ((2, 1), (1, 2)), ((2, 3), (1, 0)))

sage: S.set_immutable()
sage: S.category()
Category of with boundary finite type oriented rational similarity surfaces
sage: TestSuite(S).run()

If we don’t glue all the edges, we get a surface with boundary:

sage: P = Polygon(vertices=[(0,0), (1,0), (1,1), (0,1)])
sage: S = MutableOrientedSimilaritySurface(QQ)
sage: S.add_polygon(P)
0
sage: TestSuite(S).run()
class flatsurf.geometry.categories.similarity_surfaces.SimilaritySurfaces[source]

The category of surfaces built from polygons with edges identified by similarities.

EXAMPLES:

sage: from flatsurf.geometry.categories import SimilaritySurfaces
sage: SimilaritySurfaces()
Category of similarity surfaces
class FiniteType(base_category)[source]

The category of surfaces built by gluing a finite number of Euclidean polygons with similarities.

EXAMPLES:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0,0), (1,0), (1,1), (0,1)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: "FiniteType" in S.category().axioms()
True
class Oriented(base_category)[source]

The category of surfaces built from finitely many Euclidean polygons glued with singularities with an orientation that is compatible with the embedding that the polygons inherit from the real plane.

EXAMPLES:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0,0), (1,0), (1,1), (0,1)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: "Oriented" in S.category().axioms()
True
class ParentMethods[source]

Provides methods available to all surfaces that are built from finitely many Euclidean polygons that are glued by similarities and are oriented with the natural orientation of the polygons in the real plane.

If you want to add functionality for such surfaces you most likely want to put it here.

fundamental_group(base_label=None)[source]

Return the fundamental group of this surface.

reposition_polygons(in_place=False, relabel=None)[source]

We choose a maximal tree in the dual graph of the decomposition into polygons, and ensure that the gluings between two polygons joined by an edge in this tree is by translation.

This guarantees that the group generated by the edge identifications is minimal among representations of the surface. In particular, if for instance you have a translation surface which is anot representable as a translation surface (because polygons are presented with rotations) then after this change it will be representable as a translation surface.

standardize_polygons(in_place=False)[source]

Return a surface with each polygon replaced with a new polygon which differs by translation and reindexing. The new polygon will have the property that vertex zero is the origin, and all vertices lie either in the upper half plane, or on the x-axis with non-negative x-coordinate.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: s=translation_surfaces.veech_double_n_gon(4)
sage: s.polygon(1)
Polygon(vertices=[(0, 0), (-1, 0), (-1, -1), (0, -1)])
sage: [s.opposite_edge(0,i) for i in range(4)]
[(1, 0), (1, 1), (1, 2), (1, 3)]
sage: ss=s.standardize_polygons()
sage: ss.polygon(1)
Polygon(vertices=[(0, 0), (1, 0), (1, 1), (0, 1)])
sage: [ss.opposite_edge(0,i) for i in range(4)]
[(1, 2), (1, 3), (1, 0), (1, 1)]
sage: TestSuite(ss).run()
class ParentMethods[source]

Provides methods available to all surfaces that are built from finitely many polygons in the real plane glued with similarities.

If you want to add functionality for such surfaces you most likely want to put it here.

_test_eq_surface(**options)[source]

Verify that this surface follows our standards for equality of surfaces.

We want two surfaces to compare equal (\(S == T\)) iff they are virtually indistinguishable; so without a lot of non-Pythonic effort, you should not be able to tell them apart. They have (virtually) the same type, are made from equally labeled polygons with indistinguishable coordinates and equal gluings. Any other data that was used when creating them should be indistinguishable. They might of course live at different memory addresses have differences in their internal caches and representation but everything user-facing should be the same.

People often want \(==\) to mean that the two surfaces are isomorphic in some more-or-less strong sense. Such a notion for \(==\) always leads to trouble down the road. The operator \(==\) is used to identify surfaces in caches and identify surfaces in sets. Sometimes “are isomorphic” is a good notion in such cases but most of the time “are indistinguishable” is the much safer default. Also, “are isomorphic” is often costly or, e.g. in the case of infinite surfaces, not even decidable.

Currently, we do treat two surfaces as equal even if they differ by category because categories can presently be changed for immutable surfaces (as more properties of the surface are found.)

EXAMPLES:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0,0), (1,0), (1,1), (0,1)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: S._test_eq_surface()
num_singularities()[source]

EXAMPLES:

sage: from flatsurf import translation_surfaces

sage: translation_surfaces.regular_octagon().num_singularities()
doctest:warning
...
UserWarning: num_singularities() has been deprecated and will be removed in a future version of sage-flatsurf; use len(vertices()) instead
1

sage: S = SymmetricGroup(4)
sage: r = S('(1,2)(3,4)')
sage: u = S('(2,3)')
sage: translation_surfaces.origami(r,u).num_singularities()
2

sage: S = SymmetricGroup(8)
sage: r = S('(1,2,3,4,5,6,7,8)')
sage: u = S('(1,8,5,4)(2,3)(6,7)')
sage: translation_surfaces.origami(r,u).num_singularities()
4
class Oriented(base_category)[source]

The category of oriented surfaces built from Euclidean polygons that are glued by similarities with the orientation compatible with the orientation of the real plane that polygons are defined in.

EXAMPLES:

sage: from flatsurf.geometry.categories import SimilaritySurfaces
sage: SimilaritySurfaces().Oriented()
Category of oriented similarity surfaces
class ElementMethods[source]

Provides methods available to all points of oriented similarity surfaces.

If you want to add functionality for such surfaces, you most likely want to put it here.

angle(numerical=False)[source]

Return the total angle at this point as a multiple of 2π.

INPUT:

  • numerical – a boolean (default: False); whether to return a floating point approximation of the angle or its exact value.

EXAMPLES:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0, 0), (2, 0), (1, 4), (0, 5)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: S(0, 0).angle()
1
sage: S(0, (1, 0)).angle()
1/2
sage: S(0, (1, 0)).angle(numerical=True)
0.500000000000000

See also

turns() for the number of full 2π turns at a point.

edges()[source]

Return the edges of the polygons that contain this point.

Implements polygonal_surfaces.PolygonalSurfaces.ElementMethods.edges() for similarity surfaces.

edges_ccw(start_edge=None, start_holonomy=None)[source]

Return the edges of the polygons that are crossed when walking around this point in counterclockwise direction.

Each edge is reported together with its holonomy vector in the coordinate system of the start_edge.

Each edge is reported “twice”, once when leaving a polygon, and once when entering a polygon.

INPUT:

  • start_edge – an edge or None (default: None); a particular outgoing edge at which to start the walk.

  • start_holonomy – a vector or None (default: None); the holonomy that the first edge reported should use. If None, then use the holonomy that the polygon edge reports normally.

EXAMPLES:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0, 0), (2, 0), (1, 4), (0, 5)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: S(0, 0).edges_ccw()
[((0, 2), (-1, 1)),
 ((0, 1), (1, -4)),
 ((0, 1), (1, -4)),
 ((0, 0), (2, 0)),
 ((0, 0), (2, 0)),
 ((0, 3), (0, 5)),
 ((0, 3), (0, 5)),
 ((0, 2), (-1, 1))]
sage: S(0, 0).edges_ccw(start_edge=(0, 0))
[((0, 0), (2, 0)),
 ((0, 3), (0, 5)),
 ((0, 3), (0, 5)),
 ((0, 2), (-1, 1)),
 ((0, 2), (-1, 1)),
 ((0, 1), (1, -4)),
 ((0, 1), (1, -4)),
 ((0, 0), (2, 0))]
sage: S(0, 0).edges_ccw(start_holonomy=(1, 0))
[((0, 2), (1, 0)),
 ((0, 1), (-5/2, 3/2)),
 ((0, 1), (-5/2, 3/2)),
 ((0, 0), (-1, -1)),
 ((0, 0), (-1, -1)),
 ((0, 3), (5/2, -5/2)),
 ((0, 3), (5/2, -5/2)),
 ((0, 2), (1, 0))]

See also

edges() for the edges containing this point in no particular order.

turns(start_edge=None, start_holonomy=None)[source]

Return the number of total 2π turns at this vertex.

Returns a triple (turns, start, end) where turns is an integer, and start and end are vectors such that start is the vector at which we begin counting the turns, and end is the vector at which we stop counting the turns.

If the total angle at this point is a multiple of 2π, then start and end are parallel.

If the surface has no boundary, then start and end are holonomy vectors of the same edge of the surface.

INPUT:

  • start_edge – an edge or None (default: None); a particular outgoing edge at which to start counting the turns.

  • start_holonomy – a vector or None (default: None); the holonomy that the first edge reported should use. If None, then use the holonomy that the polygon edge reports normally.

ALGORITHM:

We count the half turns of angle π by walking around the vertex and counting how often the outgoing edges go between being clockwise and being counterclockwise from the start_edge.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus()
sage: S(0, 0).turns()
(1, (0, -1), (0, -1))

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0, 0), (2, 0), (1, 4), (0, 5)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: S(0, 0).turns()
(1, (-1, 1), (-1, 1))
sage: S(0, (1, 0)).turns()
Traceback (most recent call last):
...
ValueError: point must be a vertex

See also

angle() for the possibly non-integral number of turns at this point.

class ParentMethods[source]

Provides methods available to all oriented surfaces that are built from Euclidean polygons that are glued by similarities.

If you want to add functionality for such surfaces you most likely want to put it here.

angles(numerical=False, return_adjacent_edges=None)[source]

Return the angles around the vertices of the surface as multiples of 2π.

INPUT:

  • numerical – a boolean (default: False); whether the angles are returned as floating point numbers or as exact algebraic numbers.

EXAMPLES:

sage: from flatsurf import polygons, similarity_surfaces, translation_surfaces
sage: T = polygons.triangle(3, 4, 5)
sage: S = similarity_surfaces.billiard(T)
sage: S.angles()
[1/4, 5/12, 1/3]
sage: S.angles(numerical=True)   # abs tol 1e-14
[0.25, 0.4166666666666667, 0.33333333333333337]

sage: S.angles(return_adjacent_edges=True)
doctest:warning
...
UserWarning: return_adjacent_edges has been deprecated as a keyword argument to angles() and will be removed in a future version of sage-flatsurf; use angle() and edges_ccw()[::2] on each vertex instead.
[(1/4, [(0, 0), (1, 0)]), (5/12, [(0, 2), (1, 1)]), (1/3, [(0, 1), (1, 2)])]
sage: translation_surfaces.regular_octagon().angles()
[3]
sage: S = translation_surfaces.veech_2n_gon(5)
sage: S.angles()
[2, 2]
sage: S.angles(numerical=True)
[2.0, 2.0]
sage: S.angles(return_adjacent_edges=True) # random output
[(2, [(0, 1), (0, 5), (0, 9), (0, 3), (0, 7)]),
 (2, [(0, 0), (0, 4), (0, 8), (0, 2), (0, 6)])]
sage: S.angles(numerical=True, return_adjacent_edges=True) # random output
[(2.0, [(0, 1), (0, 5), (0, 9), (0, 3), (0, 7)]),
 (2.0, [(0, 0), (0, 4), (0, 8), (0, 2), (0, 6)])]

sage: translation_surfaces.veech_2n_gon(6).angles()
[5]
sage: translation_surfaces.veech_double_n_gon(5).angles()
[3]
sage: translation_surfaces.cathedral(1, 1).angles()
[3, 3, 3]

sage: from flatsurf import polygons, similarity_surfaces
sage: B = similarity_surfaces.billiard(polygons.triangle(1, 2, 5))
sage: H = B.minimal_cover(cover_type="half-translation")
sage: S = B.minimal_cover(cover_type="translation")
sage: H.angles()
[1/2, 1/2, 1/2, 5/2]
sage: S.angles()
[1, 1, 5, 1]

sage: H.angles(return_adjacent_edges=True)
[(1/2, [...]), (1/2, [...]), (1/2, [...]), (5/2, [...])]
sage: S.angles(return_adjacent_edges=True)
[(1, [...]), (1, [...]), (5, [...]), (1, [...])]

For self-glued edges, no angle is reported for the “vertex” at the midpoint of the edge:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0,0), (2,0), (1,4), (0,5)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: S.angles()
[1]

Non-convex examples:

sage: from flatsurf import Polygon, MutableOrientedSimilaritySurface
sage: S = MutableOrientedSimilaritySurface(QQ)
sage: L = Polygon(vertices=[(0,0),(1,0),(2,0),(2,1),(1,1),(1,2),(0,2),(0,1)])
sage: S.add_polygon(L)
0
sage: S.glue((0, 0), (0, 5))
sage: S.glue((0, 1), (0, 3))
sage: S.glue((0, 2), (0, 7))
sage: S.glue((0, 4), (0, 6))
sage: S.set_immutable()
sage: S.angles()
[3]

sage: S = MutableOrientedSimilaritySurface(QQ)
sage: P = Polygon(vertices=[(0,0),(1,0),(2,0),(2,1),(3,1),(3,2),(2,2),(1,2),(1,1),(0,1)])
sage: S.add_polygon(P)
0
sage: S.glue((0, 0), (0, 8))
sage: S.glue((0, 1), (0, 6))
sage: S.glue((0, 2), (0, 9))
sage: S.glue((0, 3), (0, 5))
sage: S.glue((0, 4), (0, 7))
sage: S.set_immutable()
sage: S.angles()
[2, 2]

Angles can also be determined for surfaces with boundary:

sage: from flatsurf import MutableOrientedSimilaritySurface
sage: P = Polygon(vertices=[(0, 0), (1, 0), (1, 1), (0, 2)])
sage: S = MutableOrientedSimilaritySurface(QQ)
sage: S.add_polygon(P)
0
sage: S.set_immutable()
sage: S.angles()
[1/4, 1/4, 3/8, 1/8]
change_ring(ring)[source]

Return a copy of this surface whose polygons are defined over ring.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.veech_2n_gon(4)
sage: T = S.change_ring(AA)
sage: T.base_ring()
Algebraic Real Field

This method creates a copy even if the base ring is unchanged:

sage: S.change_ring(S.base_ring()) is S
False

Note that the resulting surface might not be functional if the polygon cannot be represented in the ring:

sage: S.change_ring(QQ).polygon(0)
Traceback (most recent call last):
...
TypeError: Unable to coerce 1/2*a + 1 to a rational
copy(relabel=False, mutable=False, lazy=None, new_field=None, optimal_number_field=False)[source]

Returns a copy of this surface. The method takes several flags to modify how the copy is taken.

If relabel is True, then instead of returning an exact copy, it returns a copy indexed by the non-negative integers. This uses the Surface_list implementation. If relabel is False (default), then we return an exact copy. The returned surface uses the Surface_dict implementation.

The mutability flag returns if the resulting surface should be mutable or not. By default, the resulting surface will not be mutable.

If lazy is True, then the surface is copied by reference. This is the only type of copy possible for infinite surfaces. The parameter defaults to False for finite surfaces, and defaults to True for infinite surfaces.

The new_field parameter can be used to place the vertices in a larger field than the basefield for the original surface.

The optimal_number_field option can be used to find a best NumberField containing the (necessarily finite) surface.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: ss=translation_surfaces.ward(3)
sage: ss.is_mutable()
False
sage: s=ss.copy(mutable=True)
doctest:warning
...
UserWarning: copy() has been deprecated and will be removed from a future version of sage-flatsurf; for surfaces of finite type use MutableOrientedSimilaritySurface.from_surface() instead.
sage: s.is_mutable()
True
sage: TestSuite(s).run()
sage: s == ss
False

Changing the base field:

sage: s=translation_surfaces.veech_double_n_gon(5)
sage: ss=s.copy(mutable=False,new_field=AA)
doctest:warning
...
UserWarning: copy() has been deprecated and will be removed from a future version of sage-flatsurf; for surfaces of finite type use MutableOrientedSimilaritySurface.from_surface() instead.
Use set_immutable() to make the resulting surface immutable. Use change_ring() to change the field over which the surface is defined.
sage: TestSuite(ss).run()
sage: ss.base_ring()
Algebraic Real Field

Optimization of number field:

sage: s = translation_surfaces.arnoux_yoccoz(3)
sage: ss = s.copy(new_field=AA).copy(optimal_number_field=True)
doctest:warning
...
UserWarning: copy() has been deprecated and will be removed from a future version of sage-flatsurf; for surfaces of finite type use MutableOrientedSimilaritySurface.from_surface() instead.
Use set_immutable() to make the resulting surface immutable. Use change_ring() to change the field over which the surface is defined.
doctest:warning
...
UserWarning: copy() has been deprecated and will be removed from a future version of sage-flatsurf; for surfaces of finite type use MutableOrientedSimilaritySurface.from_surface() instead.
Use set_immutable() to make the resulting surface immutable. There is currently no replacement for optimal number field.
If you are relying on this features, let the authors of sage-flatsurf know and we will try to make it available again.
sage: TestSuite(ss).run()
sage: ss.base_ring().discriminant()
-44
delaunay_decompose(codomain=None)[source]

Return a Delaunay decomposition of this surface, i.e., a representation of this surface such that the circumscribed disk of each polygon contains exactly the vertices of that polygon.

ALGORITHM:

The Delaunay decomposition is obtained by removing ambiguous edges from a Delaunay triangulation. An edge is ambiguous if after its removal the circumscribed disk of each polygon (still) contains no vertices in its interior.

INPUT:

  • codomain – a Delaunay decomposed surface or None (default: None); if present, the morphism returned goes from this surface to codomain.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.octagon_and_squares()
sage: a = S.base_ring().gens()[0]
sage: M = Matrix([[1, 2 + a], [0, 1]])
sage: S = M * S
sage: S = S.triangulate().codomain()

sage: S = S.delaunay_decompose().codomain()
sage: S.is_delaunay_decomposed()
True
sage: S
Delaunay cell decomposition of Triangulation of Translation Surface in H_3(4) built from 2 quadrilaterals and an octagon
sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(edges=[(4, 0), (-2, 1), (-2, -1)])
sage: T = similarity_surfaces.self_glued_polygon(P)

sage: T = T.delaunay_decompose().codomain()
sage: T.is_delaunay_decomposed()
True
sage: T
Delaunay cell decomposition of Half-Translation Surface in Q_0(-1^4) built from an isosceles triangle
sage: M = matrix([[2, 1], [1, 1]])
sage: U = M * translation_surfaces.infinite_staircase()

sage: U = U.delaunay_decompose().codomain()
sage: U.is_delaunay_decomposed()
True

sage: U.root()
(0, 0)
sage: U.polygon((0, 0))
Polygon(vertices=[(0, 0), (-1, 0), (-1, -1), (0, -1)])
delaunay_decomposition(triangulated=None, delaunay_triangulated=None, in_place=False, direction=None, relabel=None)[source]

Return the Delaunay Decomposition of this surface.

INPUT:

  • triangulated - None; ignored

  • delaunay_triangulated - None; ignored

  • in_place - boolean (default: False); whether this surface is modified and returned or just a copy of the surface

  • direction - None; ignored

  • relabel - None; ignored

EXAMPLES:

sage: from flatsurf import translation_surfaces, Polygon, similarity_surfaces
sage: s0 = translation_surfaces.octagon_and_squares()
sage: a = s0.base_ring().gens()[0]
sage: m = Matrix([[1,2+a],[0,1]])
sage: s = m*s0
sage: s = s.triangulate().codomain()
sage: ss = s.delaunay_decomposition()
doctest:warning
...
UserWarning: delaunay_decomposition() has been deprecated and will be removed in a future version of sage-flatsurf; use delaunay_decompose().codomain() instead

sage: len(ss.polygons())
3

sage: p = Polygon(edges=[(4,0),(-2,1),(-2,-1)])
sage: s0 = similarity_surfaces.self_glued_polygon(p)
sage: s = s0.delaunay_decomposition()
sage: TestSuite(s).run()

sage: m = matrix([[2,1],[1,1]])
sage: s = m*translation_surfaces.infinite_staircase()
sage: ss = s.delaunay_decomposition()
doctest:warning
...
UserWarning: delaunay_decomposition() has been deprecated and will be removed in a future version of sage-flatsurf; use delaunay_decompose().codomain() instead
sage: ss.root()
(0, 0)
sage: ss.polygon(ss.root())
Polygon(vertices=[(0, 0), (-1, 0), (-1, -1), (0, -1)])
sage: TestSuite(ss).run()
sage: ss.is_delaunay_decomposed()
True
delaunay_triangulate()[source]

Return a Delaunay triangulation of this surface.

EXAMPLES:

sage: from flatsurf import translation_surfaces

sage: M = matrix([[2, 1], [1, 1]])
sage: S = M * translation_surfaces.infinite_staircase()
sage: S = S.delaunay_triangulate().codomain()

sage: S.root()
(0, 0)
sage: S.polygon((0, 0))
Polygon(vertices=[(0, 0), (-1, 0), (-1, -1)])

sage: S.is_delaunay_triangulated()
True
delaunay_triangulation(triangulated=None, in_place=False, direction=None, relabel=None)[source]

Return a Delaunay triangulation of this surface.

INPUT:

  • triangulated - None; ignored

  • in_place - boolean (default: False); whether this ssurface is modified and returned or just a copy of the surface

  • direction - None; ignored

  • relabel - None; ignored

EXAMPLES:

sage: from flatsurf import translation_surfaces

sage: m = matrix([[2, 1], [1, 1]])
sage: s = m * translation_surfaces.infinite_staircase()
sage: ss = s.delaunay_triangulation()
doctest:warning
...
UserWarning: delaunay_triangulation() is deprecated and will be removed in a future version of sage-flatsurf; use delaunay_triangulate().codomain() instead
sage: ss.root()
(0, 0)
sage: ss.polygon((0, 0))
Polygon(vertices=[(0, 0), (-1, 0), (-1, -1)])
sage: TestSuite(ss).run()
sage: ss.is_delaunay_triangulated()
True
edge_matrix(p, e=None)[source]

Returns the 2x2 matrix representing a similarity which when applied to the polygon with label \(p\) makes it so the edge \(e\) can be glued to its opposite edge by translation.

If \(e\) is not provided, then \(p\) should be a pair consisting of a polygon label and an edge.

EXAMPLES:

sage: from flatsurf.geometry.similarity_surface_generators import SimilaritySurfaceGenerators
sage: s = SimilaritySurfaceGenerators.example()
sage: s.polygon(0)
Polygon(vertices=[(0, 0), (2, -2), (2, 0)])
sage: s.polygon(1)
Polygon(vertices=[(0, 0), (2, 0), (1, 3)])
sage: s.opposite_edge(0,0)
(1, 1)
sage: m = s.edge_matrix(0, 0)
sage: m
[   1  1/2]
[-1/2    1]
sage: m * vector((2,-2)) == -vector((-1, 3))
True
edge_transformation(p, e)[source]

Return the similarity bringing the provided edge to the opposite edge.

EXAMPLES:

sage: from flatsurf.geometry.similarity_surface_generators import SimilaritySurfaceGenerators
sage: s = SimilaritySurfaceGenerators.example()
sage: s.polygon(0)
Polygon(vertices=[(0, 0), (2, -2), (2, 0)])
sage: s.polygon(1)
Polygon(vertices=[(0, 0), (2, 0), (1, 3)])
sage: s.opposite_edge(0,0)
(1, 1)
sage: g = s.edge_transformation(0,0)
sage: g((0,0))
(1, 3)
sage: g((2,-2))
(2, 0)
is_convex(label, edge, strict=False)[source]

Return whether the polygon with label is convex after it has been glued with the polygon across its edge.

INPUT:

  • label – a label of a polygon in this surface

  • edge – the index of an edge of the polygon with label

  • strict – a boolean (default: False); whether to determine that the polygon is strictly convex, i.e., none of its inner angles greater or equal than π.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.mcmullen_L(1, 1, 1, 1)
sage: S.is_convex(0, 0)
True
sage: S.is_convex(0, 0, strict=True)
False

sage: S.is_convex(2, 0)
True
is_delaunay_decomposed(limit=None)[source]

Return if the decomposition of the surface into polygons is Delaunay.

INPUT:

  • limit – an integer or None (default: None); check only limit many polygons if set

is_delaunay_triangulated(limit=None)[source]

Return whether the surface is triangulated and the triangulation is Delaunay.

INPUT:

  • limit – an integer or None (default: None); check only limit many edges if set

join_polygons(p1, e1, test=False, in_place=False)[source]

Join polygons across the provided edge (p1,e1). By default, it returns the surface obtained by joining the two polygons together. It raises a ValueError if gluing the two polygons together results in a non-convex polygon. This is done to the current surface if in_place is True, and otherwise a mutable copy is made and then modified.

If test is True then instead of changing the surface, it just checks to see if the change would be successful and returns True if successful or False if not.

EXAMPLES:

sage: from flatsurf import translation_surfaces, MutableOrientedSimilaritySurface
sage: ss = translation_surfaces.ward(3)
sage: s = MutableOrientedSimilaritySurface.from_surface(ss)
sage: s.join_polygons(0,0, in_place=True)
Translation Surface built from an equilateral triangle and a pentagon with 2 marked vertices
sage: s.polygon(0)
Polygon(vertices=[(0, 0), (1, -a), (2, 0), (3, a), (2, 2*a), (0, 2*a), (-1, a)])
sage: s.join_polygons(0,4, in_place=True)
Translation Surface built from a rhombus
sage: s.polygon(0)
Polygon(vertices=[(0, 0), (1, -a), (2, 0), (3, a), (2, 2*a), (1, 3*a), (0, 2*a), (-1, a)])
minimal_cover(cover_type='translation')[source]

Return the minimal translation or half-translation cover of the surface.

Cover type may be either “translation”, “half-translation” or “planar”.

The minimal planar cover of a surface S is the smallest cover C so that the developing map from the universal cover U to the plane induces a well defined map from C to the plane. This is an infinite translation surface that is naturally a branched cover of the plane.

EXAMPLES:

sage: from flatsurf import polygons, MutableOrientedSimilaritySurface
sage: s = MutableOrientedSimilaritySurface(QQ)
sage: square = polygons.square(base_ring=QQ)
sage: s.add_polygon(square)
0
sage: s.glue((0,0), (0,1))
sage: s.glue((0,2) ,(0,3))
sage: cs = s
sage: ts = cs.minimal_cover(cover_type="translation")
sage: ts
Minimal Translation Cover of Rational Cone Surface built from a square
sage: from flatsurf.geometry.categories import TranslationSurfaces
sage: ts in TranslationSurfaces()
True
sage: hts = cs.minimal_cover(cover_type="half-translation")
sage: hts
Minimal Half-Translation Cover of Genus 0 Rational Cone Surface built from a square
sage: from flatsurf.geometry.categories import HalfTranslationSurfaces
sage: hts in HalfTranslationSurfaces()
True
sage: TestSuite(hts).run()
sage: ps = cs.minimal_cover(cover_type="planar")
sage: ps
Minimal Planar Cover of Genus 0 Rational Cone Surface built from a square
sage: ps in TranslationSurfaces()
True
sage: TestSuite(ps).run()

sage: from flatsurf import similarity_surfaces
sage: S = similarity_surfaces.example()
sage: T = S.minimal_cover(cover_type="translation")
sage: T
Minimal Translation Cover of Genus 1 Surface built from 2 isosceles triangles
sage: T in TranslationSurfaces()
True
sage: T.polygon(T.root())
Polygon(vertices=[(0, 0), (2, -2), (2, 0)])
point(label, point, ring=None, limit=None)[source]

Return a point in this surface.

INPUT:

  • label - label of the polygon

  • point - coordinates of the point inside the polygon or the index of the vertex of the polygon.

  • ring (optional) - a ring for the coordinates

  • limit (optional) - undocumented (only relevant if the point corresponds to a singularity in an infinite surface)

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: s = translation_surfaces.square_torus()
sage: pc = s.minimal_cover(cover_type="planar")
sage: pc.point(pc.root(), (0, 0))
Vertex 0 of polygon (0, (x, y) |-> (x, y))
sage: pc.point(pc.root(), 0)
Vertex 0 of polygon (0, (x, y) |-> (x, y))
sage: pc.point(pc.root(), 1)
Vertex 0 of polygon (0, (x, y) |-> (x + 1, y))
sage: pc.point(pc.root(), (1, 1))
Vertex 0 of polygon (0, (x, y) |-> (x + 1, y + 1))
sage: z = pc.point(pc.root(),(sqrt(2)-1,sqrt(3)-1),ring=AA)
doctest:warning
...
UserWarning: the ring parameter is deprecated and will be removed in a future version of sage-flatsurf; define the surface over a larger ring instead so that this points' coordinates live in the base ring
sage: next(iter(z.coordinates(next(iter(z.labels()))))).parent()
Vector space of dimension 2 over Algebraic Real Field
sage: s = translation_surfaces.cathedral(2, 3)
sage: s.point(0, 0)
Vertex 0 of polygon 0
sage: s.point(0, (0, 0))
Vertex 0 of polygon 0
sage: s.point(0, (1, 1))
Point (1, 0) of polygon 0
sage: s.point(0, 1)
Vertex 0 of polygon 1
ramified_cover(d, data)[source]

Return a ramified cover of this surface.

INPUT:

  • d - integer (the degree of the cover)

  • data - a dictionary which to a pair (label, edge_num) associates a permutation of {1,…,d}

EXAMPLES:

The L-shape origami:

sage: import flatsurf
sage: T = flatsurf.translation_surfaces.square_torus()
sage: T.ramified_cover(3, {(0,0): '(1,2)', (0,1): '(1,3)'})
Translation Surface in H_2(2) built from 3 squares
sage: O = T.ramified_cover(3, {(0,0): '(1,2)', (0,1): '(1,3)'})
sage: O.stratum()
H_2(2)
random_flip(repeat=1, in_place=False)[source]

Perform random edge flip on a triangulated surface.

INPUT:

  • repeat – integer (default 1). The number of edge flip to perform.

  • in_place – whether the transformation is done in place.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: ss = translation_surfaces.ward(3).triangulate()
sage: ss.random_flip(15)  # random
Translation Surface in H_1(0^3) built from 6 triangles
relabel(relabeling=None, in_place=False)[source]

Return a surface whose polygons have been relabeled according to relabeling.

INPUT:

  • relabeling – a dict, a callable, or None (default: None); the mapping from labels of this surface to labels of the relabeled surface. If None, then relabel to the non-negative integers.

  • in_place – a boolean (default: False); whether to modify this surface or return a relabeled copy instead.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.veech_double_n_gon(5)
sage: SS = S.relabel({0: 1, 1: 2})
sage: SS
Translation Surface in H_2(2) built from 2 regular pentagons

sage: SS.root()
1

sage: SS.opposite_edge(1, 0)
(2, 0)

sage: TestSuite(SS).run()

The relabeling can also be a callable:

sage: SSS = SS.relabel(lambda label: label -1)
sage: SSS == S
True

However, this is only supported on finite-type surfaces:

sage: S = translation_surfaces.infinite_staircase()
sage: S.labels()
(0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, …)

sage: S.relabel(lambda label: -label)
Traceback (most recent call last):
...
NotImplementedError: cannot relabel a surface with an infinite number of polygons with a callable relabeling yet
sage: S.relabel({1: 'X'})
Traceback (most recent call last):
...
NotImplementedError: cannot relabel a surface with an infinite number of polygons with a dict relabeling yet

For infinite surfaces, we only support relabeling to the non-negative integers:

sage: S.relabel().labels() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, …)

saddle_connections(squared_length_bound, initial_label=None, initial_vertex=None, sc_list=None, check=False)[source]

Returns a list of saddle connections on the surface whose length squared is less than or equal to squared_length_bound. The length of a saddle connection is measured using holonomy from polygon in which the trajectory starts.

If initial_label and initial_vertex are not provided, we return all saddle connections satisfying the bound condition.

If initial_label and initial_vertex are provided, it only provides saddle connections emanating from the corresponding vertex of a polygon. If only initial_label is provided, the added saddle connections will only emanate from the corresponding polygon.

If sc_list is provided the found saddle connections are appended to this list and the resulting list is returned.

If check==True it uses the checks in the SaddleConnection class to sanity check our results.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: s = translation_surfaces.square_torus()
sage: sc_list = s.saddle_connections(13, check=True)
sage: len(sc_list)
32
set_vertex_zero(label, v, in_place=False)[source]

Applies a combinatorial rotation to the polygon with the provided label.

This makes what is currently vertex v of this polygon vertex 0. In other words, what is currently vertex (or edge) e will now become vertex (e-v)%n where n is the number of sides of the polygon.

For the updated polygons, the polygons will be translated so that vertex 0 is the origin.

EXAMPLES:

Example with polygon glued to another polygon:

sage: from flatsurf import translation_surfaces
sage: s = translation_surfaces.veech_double_n_gon(4)
sage: s.polygon(0)
Polygon(vertices=[(0, 0), (1, 0), (1, 1), (0, 1)])
sage: [s.opposite_edge(0,i) for i in range(4)]
[(1, 0), (1, 1), (1, 2), (1, 3)]
sage: ss = s.set_vertex_zero(0,1)
sage: ss.polygon(0)
Polygon(vertices=[(0, 0), (0, 1), (-1, 1), (-1, 0)])
sage: [ss.opposite_edge(0,i) for i in range(4)]
[(1, 1), (1, 2), (1, 3), (1, 0)]
sage: TestSuite(ss).run()

Example with polygon glued to self:

sage: s = translation_surfaces.veech_2n_gon(2)
sage: s.polygon(0)
Polygon(vertices=[(0, 0), (1, 0), (1, 1), (0, 1)])
sage: [s.opposite_edge(0,i) for i in range(4)]
[(0, 2), (0, 3), (0, 0), (0, 1)]
sage: ss = s.set_vertex_zero(0,3)
sage: ss.polygon(0)
Polygon(vertices=[(0, 0), (0, -1), (1, -1), (1, 0)])
sage: [ss.opposite_edge(0,i) for i in range(4)]
[(0, 2), (0, 3), (0, 0), (0, 1)]
sage: TestSuite(ss).run()
singularity(label, v, limit=None)[source]

Represents the Singularity associated to the v-th vertex of the polygon with label label.

If the surface is infinite, the limit can be set. In this case the construction of the singularity is successful if the sequence of vertices hit by passing through edges closes up in limit or less steps.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: s = translation_surfaces.square_torus()
sage: pc = s.minimal_cover(cover_type="planar")
sage: pc.singularity(pc.root(), 0)
doctest:warning
...
UserWarning: Singularity() is deprecated and will be removed in a future version of sage-flatsurf. Use surface.point() instead.
Vertex 0 of polygon (0, (x, y) |-> (x, y))
sage: pc.singularity(pc.root(), 0, limit=1)
Traceback (most recent call last):
...
ValueError: number of edges at singularity exceeds limit
some_elements()[source]

Return some typical points of this surface (for testing).

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus()
sage: list(S.some_elements())
[Point (1/2, 1/2) of polygon 0, Vertex 0 of polygon 0]
subdivide()[source]

Return a copy of this surface whose polygons have been partitioned into smaller triangles with subdivide().

EXAMPLES:

A surface consisting of a single triangle:

sage: from flatsurf import MutableOrientedSimilaritySurface, Polygon

sage: S = MutableOrientedSimilaritySurface(QQ)
sage: S.add_polygon(Polygon(edges=[(1, 0), (0, 1), (-1, -1)]), label="Δ")
'Δ'

Subdivision of this surface yields a surface with three triangles:

sage: T = S.subdivide()
sage: T.labels()
(('Δ', 0), ('Δ', 1), ('Δ', 2))

Note that the new labels are old labels plus an index. We verify that the triangles are glued correctly:

sage: list(T.gluings())
[((('Δ', 0), 1), (('Δ', 1), 2)),
 ((('Δ', 0), 2), (('Δ', 2), 1)),
 ((('Δ', 1), 1), (('Δ', 2), 2)),
 ((('Δ', 1), 2), (('Δ', 0), 1)),
 ((('Δ', 2), 1), (('Δ', 0), 2)),
 ((('Δ', 2), 2), (('Δ', 1), 1))]

If we add another polygon to the original surface and glue things, we can see how existing gluings are preserved when subdividing:

sage: S.add_polygon(Polygon(edges=[(1, 0), (0, 1), (-1, 0), (0, -1)]), label='□')
'□'

sage: S.glue(("Δ", 0), ("□", 2))
sage: S.glue(("□", 1), ("□", 3))

sage: T = S.subdivide()

sage: T.labels()
(('Δ', 0), ('□', 2), ('Δ', 1), ('Δ', 2), ('□', 3), ('□', 1), ('□', 0))
sage: list(sorted(T.gluings()))
[((('Δ', 0), 0), (('□', 2), 0)),
 ((('Δ', 0), 1), (('Δ', 1), 2)),
 ((('Δ', 0), 2), (('Δ', 2), 1)),
 ((('Δ', 1), 1), (('Δ', 2), 2)),
 ((('Δ', 1), 2), (('Δ', 0), 1)),
 ((('Δ', 2), 1), (('Δ', 0), 2)),
 ((('Δ', 2), 2), (('Δ', 1), 1)),
 ((('□', 0), 1), (('□', 1), 2)),
 ((('□', 0), 2), (('□', 3), 1)),
 ((('□', 1), 0), (('□', 3), 0)),
 ((('□', 1), 1), (('□', 2), 2)),
 ((('□', 1), 2), (('□', 0), 1)),
 ((('□', 2), 0), (('Δ', 0), 0)),
 ((('□', 2), 1), (('□', 3), 2)),
 ((('□', 2), 2), (('□', 1), 1)),
 ((('□', 3), 0), (('□', 1), 0)),
 ((('□', 3), 1), (('□', 0), 2)),
 ((('□', 3), 2), (('□', 2), 1))]
subdivide_edges(parts=2)[source]

Return a copy of this surface whose edges have been split into parts equal pieces each.

INPUT:

  • parts – a positive integer (default: 2)

EXAMPLES:

A surface consisting of a single triangle:

sage: from flatsurf import MutableOrientedSimilaritySurface
sage: from flatsurf.geometry.polygon import Polygon

sage: S = MutableOrientedSimilaritySurface(QQ)
sage: S.add_polygon(Polygon(edges=[(1, 0), (0, 1), (-1, -1)]), label="Δ")
'Δ'

Subdividing this triangle yields a triangle with marked points along the edges:

sage: T = S.subdivide_edges()

If we add another polygon to the original surface and glue them, we can see how existing gluings are preserved when subdividing:

sage: S.add_polygon(Polygon(edges=[(1, 0), (0, 1), (-1, 0), (0, -1)]), label='□')
'□'

sage: S.glue(("Δ", 0), ("□", 2))
sage: S.glue(("□", 1), ("□", 3))

sage: T = S.subdivide_edges().codomain()
sage: list(sorted(T.gluings()))
[(('Δ', 0), ('□', 5)),
 (('Δ', 1), ('□', 4)),
 (('□', 2), ('□', 7)),
 (('□', 3), ('□', 6)),
 (('□', 4), ('Δ', 1)),
 (('□', 5), ('Δ', 0)),
 (('□', 6), ('□', 3)),
 (('□', 7), ('□', 2))]
subdivide_polygon(p, v1, v2, test=False, new_label=None)[source]

Cut the polygon with label p along the diagonal joining vertex v1 to vertex v2. This cuts p into two polygons, one will keep the same label. The other will get a new label, which can be provided via new_label. Otherwise a default new label will be provided. If test=False, then the surface will be changed (in place). If test=True, then it just checks to see if the change would be successful

The convention is that the resulting subdivided polygon which has an oriented edge going from the original vertex v1 to vertex v2 will keep the label p. The other polygon will get a new label.

The change will be done in place.

surface_point(*args, **kwargs)[source]

Return a point in this surface.

This is an alias for point().

tangent_bundle(ring=None)[source]

Return the tangent bundle

INPUT:

  • ring – an optional field (defaults to the coordinate field of the surface)

tangent_vector(lab, p, v, ring=None)[source]

Return a tangent vector.

INPUT:

  • lab – label of a polygon

  • p – coordinates of a point in the polygon

  • v – coordinates of a vector in R^2

EXAMPLES:

sage: from flatsurf.geometry.chamanara import chamanara_surface
sage: S = chamanara_surface(1/2)
sage: S.tangent_vector(S.root(), (1/2,1/2), (1,1))
SimilaritySurfaceTangentVector in polygon 1 based at (1/2, -3/2) with vector (1, 1)
sage: K.<sqrt2> = QuadraticField(2)
sage: S.tangent_vector(S.root(), (1/2,1/2), (1,sqrt2), ring=K)
SimilaritySurfaceTangentVector in polygon 1 based at (1/2, -3/2) with vector (1, sqrt2)
triangle_flip(label, edge, in_place=False, test=None, direction=None)[source]

Flips the diagonal of the quadrilateral formed by two triangles glued together along the provided edge of the polygon with label.

The diagonal is flipped by turning it counterclockwise (from the point of view of the polygon with label.) The label is given to the triangle which has the edge that was previously the edge preceding edge in the polygon. The diagonal does not change its index in the triangles.

In other words,

+<----------+       +<----------+
|\          ^       |          /^
| \      L' |       | L'      / |
|  \e'      |       |        /  |
|   \|      |       |       /e  |
|e+1 \      |       |      /-   |
|     \     |       |     /     |
|      \    |       |   -/      |
|       \   |       | e'/   L   |
|  L    |\  |       |  /        |
|        e\ |       | /      e-1|
v e-1      \|       v/  e+1     |
+---------->+       +---------->+

where L is label, L' is the label of the opposite polygon, e is edge and e' is the edge index of the diagonal in L'.

INPUT:

  • label – a polygon label

  • edge – an int; the index of an edge of the triangle with label

  • in_place – a bool (default: False) - whether this surface is modified or a modified copy is produced instead.

  • test – a bool (default: False) - whether not to perform the flip but return whether the flip is possible.

  • direction – ignored

EXAMPLES:

sage: from flatsurf import similarity_surfaces, MutableOrientedSimilaritySurface

sage: S = similarity_surfaces.right_angle_triangle(1, 1)
sage: S.triangle_flip(0, 0)
Traceback (most recent call last):
...
ValueError: cannot flip this edge because the surrounding quadrilateral is not strictly convex

sage: S.triangle_flip(0, 1)
Genus 0 Rational Cone Surface built from 2 isosceles triangles

sage: S.triangle_flip(0, 2)
Traceback (most recent call last):
...
ValueError: cannot flip this edge because the surrounding quadrilateral is not strictly convex

We can flip edges of self-glued polygons:

sage: from flatsurf import Polygon, MutableOrientedSimilaritySurface, similarity_surfaces

sage: P = Polygon(edges=[(2, 0), (-1, 3), (-1, -3)])
sage: T = MutableOrientedSimilaritySurface.from_surface(similarity_surfaces.self_glued_polygon(P))

sage: T.triangle_flip(0, 1, in_place=True)
Half-Translation Surface built from a triangle

sage: T.set_immutable()

sage: from flatsurf.geometry.categories import DilationSurfaces
sage: T in DilationSurfaces()
True
sage: T.labels()
(0,)
sage: T.polygons()
(Polygon(vertices=[(0, 0), (-1, -3), (2, 0)]),)
sage: T.gluings()
(((0, 0), (0, 0)), ((0, 1), (0, 1)), ((0, 2), (0, 2)))
triangulate(in_place=False, label=None, relabel=None)[source]

Return a triangulated version of this surface. (This may be mutable or not depending on the input.)

INPUT:

  • in_place – a boolean (default: False); whether to modify this surface or return a triangulated copy instead.

  • label – a label or None (default: None); if set, then only the polygon with that label is triangulated and all other polygons are unchanged. Otherwise, all polygons are triangulated.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.mcmullen_L(1, 1, 1, 1)
sage: S.triangulate()
Triangulation morphism:
  From: Translation Surface in H_2(2) built from 3 squares
  To:   Triangulation of Translation Surface in H_2(2) built from 3 squares

A non-strictly convex example that caused trouble at some point:

sage: from flatsurf import similarity_surfaces, Polygon sage: P = Polygon(edges=[(1, 1), (-3, -1), (1, 0), (1, 0)]) sage: S = similarity_surfaces.self_glued_polygon(P) sage: S Half-Translation Surface in Q_0(0, -1^4) built from a triangle

sage: T = S.triangulate().codomain() sage: len(T.polygon((0, 0)).vertices()) 3

The surface returned is explicitly a triangulation of the original surface. Use flatsurf.geometry.surface.MutableOrientedSimilaritySurface.from_surface() or relabel() to get a primitive surface:

sage: T
Triangulation of Half-Translation Surface in Q_0(0, -1^4) built from a triangle
sage: T.relabel()
Half-Translation Surface in Q_0(0, -1^4) built from 2 triangles
sage: from flatsurf import MutableOrientedSimilaritySurface
sage: T = MutableOrientedSimilaritySurface.from_surface(T)
sage: T.set_immutable()
sage: T
Half-Translation Surface in Q_0(0, -1^4) built from 2 triangles

We can also only triangulate part of a surface, namely a single polygon:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.cathedral(1, 1)
sage: S
Translation Surface in H_4(2^3) built from 2 squares, a hexagon with 4 marked vertices and an octagon
sage: S.triangulate(label=1).codomain().relabel()
Translation Surface in H_4(2^3) built from 2 isosceles triangles, 5 triangles, a right triangle, 2 squares and an octagon
triangulation_mapping()[source]

Return a SurfaceMapping triangulating the surface or None if the surface is already triangulated.

underlying_surface()[source]

Return this surface.

EXAMPLES:

sage: from flatsurf import MutableOrientedSimilaritySurface
sage: S = MutableOrientedSimilaritySurface(QQ)
sage: S.underlying_surface() is S
doctest:warning
...
UserWarning: underlying_surface() has been deprecated and will be removed in a future version of sage-flatsurf; this function has no effect anymore since there is no distinction between a surface and its underlying surface anymore
True
vector_space()[source]

Return the vector space in which self naturally embeds.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus()
sage: S.vector_space()
doctest:warning
...
UserWarning: vector_space() has been deprecated and will be removed in a future version of sage-flatsurf; use base_ring()**2 or base_ring().fraction_field()**2 instead
Vector space of dimension 2 over Rational Field

sage: S.base_ring()**2
Vector space of dimension 2 over Rational Field
class ParentMethods[source]

Provides methods available to all surfaces that are built from Euclidean polygons that are glued by similarities.

If you want to add functionality for such surfaces you most likely want to put it here.

apply_matrix(m, in_place=None)[source]

Apply the 2×2 matrix m to the polygons of this surface and return a morphism from this surface to the deformed surface.

INPUT:

  • m – a 2×2 matrix

  • in_place – a boolean (default: True); whether to modify this surface itself or return a modified copy of this surface instead.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus()
sage: morphism = S.apply_matrix(matrix([[2, 0], [0, 1]]), in_place=False)

sage: morphism.domain()
Translation Surface in H_1(0) built from a square
sage: morphism.codomain()
Translation Surface in H_1(0) built from a rectangle

sage: morphism.codomain().polygon(0)
Polygon(vertices=[(0, 0), (2, 0), (2, 1), (0, 1)])
cohomology(k=1, coefficients=None, relative=None, implementation='dual', category=None)[source]

Return the k-th simplicial cohomology group of this surface.

INPUT:

  • k – an integer (default: 1)

  • coefficients – a ring (default: the reals); consider cohomology with coefficients in this ring

  • relative – a set (default: the empty set); if non-empty, then relative cohomology with respect to this set is constructed.

  • implementation – a string (default: "dual"); the algorithm used to compute the cohomology groups. Currently only "dual" is supported, i.e., the groups are computed as duals of the generic homology groups from SageMath.

  • category – a category; if not specified, a category for the cohomology group is chosen automatically depending on coefficients.

EXAMPLES:

sage: from flatsurf import dilation_surfaces
sage: S = dilation_surfaces.genus_two_square(1/2, 1/3, 1/4, 1/5)
sage: S.cohomology()
H¹(Genus 2 Positive Dilation Surface built from 2 right triangles and a hexagon)
sage: S.cohomology(0)
H⁰(Genus 2 Positive Dilation Surface built from 2 right triangles and a hexagon)
homology(k=1, coefficients=None, relative=None, implementation='generic', category=None)[source]

Return the k-th simplicial homology group of this surface.

INPUT:

  • k – an integer (default: 1)

  • coefficients – a ring (default: the integer ring); consider the homology with coefficients in this ring

  • relative – a set (default: the empty set); if non-empty, then relative homology with respect to this set is constructed.

  • implementation – a string (default: "generic"); the algorithm used to compute the homology groups. Currently only "generic" is supported, i.e., the groups are computed with the generic homology machinery from SageMath.

  • category – a category; if not specified, a category for the homology group is chosen automatically depending on coefficients.

EXAMPLES:

sage: from flatsurf import dilation_surfaces
sage: S = dilation_surfaces.genus_two_square(1/2, 1/3, 1/4, 1/5)
sage: S.homology()
H₁(Genus 2 Positive Dilation Surface built from 2 right triangles and a hexagon)

sage: S.homology(0)
H₀(Genus 2 Positive Dilation Surface built from 2 right triangles and a hexagon)
is_cone_surface()[source]

Return whether this surface is a cone surface, i.e., glued edges can be transformed into each other with isometries.

Note

This is a stronger requirement than the usual definition of a cone surface, see cone_surfaces for details.

Note

This method is used to determine whether this surface is in the category of ConeSurfaces. Surfaces can override this method to perform specialized logic, see the note in flatsurf.geometry.categories for performance considerations.

EXAMPLES:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0,0), (1,0), (1,1), (0,1)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: S.is_cone_surface()
True
is_dilation_surface(positive=False)[source]

Return whether this surface is a dilation surface, i.e., whether glued edges can be transformed into each other by translation followed by a dilation (multiplication by a diagonal matrix).

Note

This method is used to determine whether this surface is in the category of DilationSurfaces or Positive. Surfaces can override this method to perform specialized logic, see the note in categories for performance considerations.

INPUT:

  • positive – a boolean (default: False); whether the entries of the diagonal matrix must be positive or are allowed to be negative.

EXAMPLES:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0,0), (2,0), (1,4), (0,5)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: S.is_dilation_surface()
True
sage: S.is_dilation_surface(positive=True)
False
is_rational_surface()[source]

Return whether this surface is a rational surface, i.e., the rotational part of all gluings is a rational multiple of π.

Note

This method is used to determine whether this surface satisfies the Rational axiom. Surfaces can override this method to perform specialized logic, see the note in flatsurf.geometry.categories for performance considerations.

EXAMPLES:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0,0), (1,0), (1,1), (0,1)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: S.is_rational_surface()
True
is_translation_surface(positive=True)[source]

Return whether this surface is a translation surface, i.e., glued edges can be transformed into each other by translations.

This method must be implemented if this surface is a dilation surface.

Note

This method is used to determine whether this surface is in the category of HalfTranslationSurfaces or TranslationSurfaces. Surfaces can override this method to perform specialized logic, see the note in categories for performance considerations.

INPUT:

  • positive – a boolean (default: True); whether the transformation must be a translation or is allowed to be a half-translation, i.e., a reflection about a point (equivalently, a rotation by π).

EXAMPLES:

sage: from flatsurf import Polygon, similarity_surfaces
sage: P = Polygon(vertices=[(0,0), (1,0), (1,1), (0,1)])
sage: S = similarity_surfaces.self_glued_polygon(P)
sage: S.is_translation_surface()
False
sage: S.is_translation_surface(False)
True
sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.square_torus()
sage: S.is_translation_surface()
True
refined_category()[source]

Return the smallest subcategory that this surface is in by consulting how its edges are glued.

The result of this method can be fed to _refine_category_ to change the category of the surface (and enable functionality specific to the smaller classes of surfaces).

Note

If a surface cannot implement the various is_ methods used in the implementation of this method (i.e., if any of them throws a NotImplementedError,) then this method refined_category must be overridden to skip that check. We don’t want to actively catch a NotImplementedError and instead encourage authors to explicitly select the category their surfaces lives in.

EXAMPLES:

sage: from flatsurf import MutableOrientedSimilaritySurface
sage: S = MutableOrientedSimilaritySurface(QQ)

sage: from flatsurf import polygons
sage: S.add_polygon(polygons.square(), label=0)
0
sage: S.refined_category()
Category of connected with boundary finite type translation surfaces

sage: S.glue((0, 0), (0, 2))
sage: S.glue((0, 1), (0, 3))
sage: S.refined_category()
Category of connected without boundary finite type translation surfaces
class Rational(base_category)[source]

The axiom satisfied by similarity surfaces where all similarities that describe how edges are glued only use rational rotations, i.e., rotations by a rational multiple of π.

Note that this differs slightly from the usual definition of “rational”. Normally, a surface would be rational if it can be described using only such similarities. Here we require that the similarities used are actually of that kind.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.infinite_staircase()
sage: "Rational" in S.category().axioms()
True
class ParentMethods[source]

Provides methods available to all surfaces built from Euclidean polygons glued by similarities that have rational monodromy, i.e., monodromy gives similarities whose rotational part has finite order.

If you want to add functionality for such surfaces you most likely want to put it here.

is_rational_surface()[source]

Return whether all edges of this surface are glued with similarities whose rotational part is by a rational multiple of π, i.e., return True since this is a rational surface.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: S = translation_surfaces.infinite_staircase()
sage: S.is_rational_surface()
True
class SubcategoryMethods[source]
Rational()[source]

Return the subcategory of surfaces with rational monodromy, see Rational.

EXAMPLES:

sage: from flatsurf.geometry.categories import SimilaritySurfaces
sage: C = SimilaritySurfaces()
sage: C.Rational()
Category of rational similarity surfaces
super_categories()[source]

Return the categories that a similarity surface is also a member of, namely the surfaces formed by Euclidean polygons.

EXAMPLES:

sage: from flatsurf.geometry.categories import SimilaritySurfaces
sage: SimilaritySurfaces().super_categories()
[Category of euclidean polygonal surfaces]