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 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.

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
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_decomposition(triangulated=False, delaunay_triangulated=False, in_place=False, direction=None, relabel=None)[source]#

Return the Delaunay Decomposition of this surface.

INPUT:

  • triangulated (boolean) - If true, the algorithm assumes the surface is already triangulated. It does this without verification.

  • delaunay_triangulated (boolean) - If true, the algorithm assumes the surface is already delaunay_triangulated. It does this without verification.

  • in_place (boolean) - If true, the triangulating and the triangle flips are done in place. Otherwise, a mutable copy of the surface is made.

  • direction - (None or Vector with two entries in the base field) - Used to determine labels when a pair of triangles is flipped. Each triangle has a unique separatrix which points in the provided direction or its negation. As such a vector determines a sign for each triangle. A pair of adjacent triangles have opposite signs. Labels are chosen so that this sign is preserved (as a function of labels).

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()
sage: ss = s.delaunay_decomposition(triangulated=True)
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()
sage: ss.root()
(0, (0, 1, 2))
sage: ss.polygon(ss.root())
Polygon(vertices=[(0, 0), (1, 0), (1, 1), (0, 1)])
sage: TestSuite(ss).run()
sage: ss.is_delaunay_decomposed(limit=10)
True
delaunay_triangulation(triangulated=False, in_place=False, direction=None, relabel=None)[source]#

Returns a Delaunay triangulation of a surface, or make some triangle flips to get closer to the Delaunay decomposition.

INPUT:

  • triangulated (boolean) - If true, the algorithm assumes the surface is already triangulated. It does this without verification.

  • in_place (boolean) - If true, the triangulating and the triangle flips are done in place. Otherwise, a mutable copy of the surface is made.

  • direction (None or Vector) - with two entries in the base field

    Used to determine labels when a pair of triangles is flipped. Each triangle has a unique separatrix which points in the provided direction or its negation. As such a vector determines a sign for each triangle. A pair of adjacent triangles have opposite signs. Labels are chosen so that this sign is preserved (as a function of labels).

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()
sage: ss.root()
(0, (0, 1, 2))
sage: ss.polygon((0, (0, 1, 2)))
Polygon(vertices=[(0, 0), (1, 0), (1, 1)])
sage: TestSuite(ss).run()
sage: ss.is_delaunay_triangulated(limit=10)
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_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_map, in_place=False)[source]#

Attempt to relabel the polygons according to a relabeling_map, which takes as input a current label and outputs a new label for the same polygon. The method returns a pair (surface,success) where surface is the relabeled surface, and success is a boolean value indicating the success of the operation. The operation will fail if the implementation of the underlying surface does not support labels used in the image of the relabeling map. In this case, other (arbitrary) labels will be used to replace the labels of the surface, and the resulting surface should still be okay.

Currently, the relabeling_map must be a dictionary.

If in_place is True then the relabeling is done to the current surface, otherwise a mutable copy is made before relabeling.

ToDo:
  • Allow relabeling_map to be a function rather than just a dictionary. This will allow it to work for infinite surfaces.

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: s=translation_surfaces.veech_double_n_gon(5)
sage: ss,valid=s.relabel({0:1, 1:2})
sage: valid
True
sage: ss.root()
1
sage: ss.opposite_edge(1,0)
(2, 0)
sage: len(ss.polygons())
2
sage: TestSuite(ss).run()
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
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()
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, -1, 0) 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, -1, 0) based at (1/2, -3/2) with vector (1, sqrt2)
triangle_flip(l1, e1, in_place=False, test=False, direction=None)[source]#

Flips the diagonal of the quadrilateral formed by two triangles glued together along the provided edge (l1,e1). This can be broken into two steps: join along the edge to form a convex quadilateral, then cut along the other diagonal. Raises a ValueError if this quadrilateral would be non-convex. In this case no changes to the surface are made.

The direction parameter defaults to (0,1). This is used to decide how the triangles being glued in are labeled. Let p1 be the triangle associated to label l1, and p2 be the triangle associated to l2 but moved by a similarity to share the edge (l1,e1). Each triangle has a exactly one separatrix leaving a vertex which travels in the provided direction or its opposite. (For edges we only count as sepatrices traveling counter-clockwise around the triangle.) This holds for p1 and p2 and the separatrices must point in opposite directions.

The above description gives two new triangles t1 and t2 which must be glued in (obtained by flipping the diagonal of the quadrilateral). Up to swapping t1 and t2 we can assume the separatrix in t1 in the provided direction (or its opposite) points in the same direction as that of p1. Further up to cyclic permutation of vertex labels we can assume that the separatrices in p1 and t1 start at the vertex with the same index (an element of {0,1,2}). The same can be done for p2 and t2. We apply the label l1 to t1 and the label l2 to t2. This precisely determines how t1 and t2 should be used to replace p1 and p2.

INPUT:

  • l1 - label of polygon

  • e1 - (integer) edge of the polygon

  • in_place (boolean) - If True do the flip to the current surface which must be mutable. In this case the updated surface will be returned. Otherwise a mutable copy is made and then an edge is flipped, which is then returned.

  • test (boolean) - If True we don’t actually flip, and we return True or False depending on whether or not the flip would be successful.

  • direction (2-dimensional vector) - Defaults to (0,1). The choice of this vector determines how the newly added triangles are labeled.

EXAMPLES:

sage: from flatsurf import similarity_surfaces, MutableOrientedSimilaritySurface, Polygon

sage: s = similarity_surfaces.right_angle_triangle(ZZ(1),ZZ(1))
sage: s.polygon(0)
Polygon(vertices=[(0, 0), (1, 0), (0, 1)])
sage: s.triangle_flip(0, 0, test=True)
False
sage: s.triangle_flip(0, 1, test=True)
True
sage: s.triangle_flip(0, 2, test=True)
False

sage: s = similarity_surfaces.right_angle_triangle(ZZ(1),ZZ(1))
sage: s = MutableOrientedSimilaritySurface.from_surface(s)
sage: s.triangle_flip(0, 0, in_place=True)
Traceback (most recent call last):
...
ValueError: Gluing triangles along this edge yields a non-convex quadrilateral.
sage: s.triangle_flip(0,1,in_place=True)
Rational Cone Surface built from 2 isosceles triangles
sage: s.polygon(0)
Polygon(vertices=[(0, 0), (1, 1), (0, 1)])
sage: s.polygon(1)
Polygon(vertices=[(0, 0), (-1, -1), (0, -1)])
sage: s.gluings()
(((0, 0), (1, 0)), ((0, 1), (0, 2)), ((0, 2), (0, 1)), ((1, 0), (0, 0)), ((1, 1), (1, 2)), ((1, 2), (1, 1)))
sage: s.triangle_flip(0,2,in_place=True)
Traceback (most recent call last):
...
ValueError: Gluing triangles along this edge yields a non-convex quadrilateral.

sage: p = Polygon(edges=[(2,0),(-1,3),(-1,-3)])
sage: s = similarity_surfaces.self_glued_polygon(p)
sage: s = MutableOrientedSimilaritySurface.from_surface(s)
sage: s.triangle_flip(0,1,in_place=True)
Half-Translation Surface built from a triangle

sage: s.set_immutable()

sage: from flatsurf.geometry.categories import DilationSurfaces
sage: s in DilationSurfaces()
True
sage: s.labels()
(0,)
sage: s.polygons()
(Polygon(vertices=[(0, 0), (-3, -3), (-1, -3)]),)
sage: s.gluings()
(((0, 0), (0, 0)), ((0, 1), (0, 1)), ((0, 2), (0, 2)))
sage: TestSuite(s).run()
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.)

If label=None (as default) all polygons are triangulated. Otherwise, label should be a polygon label. In this case, just this polygon is split into triangles.

This is done in place if in_place is True (defaults to False).

EXAMPLES:

sage: from flatsurf import translation_surfaces
sage: s=translation_surfaces.mcmullen_L(1,1,1,1)
sage: ss=s.triangulate()
sage: gs=ss.graphical_surface()
sage: gs.make_all_visible()
sage: gs
Graphical representation of Translation Surface in H_2(2) built from 6 isosceles triangles

A non-strictly convex example that caused trouble:

sage: from flatsurf import similarity_surfaces, Polygon sage: s=similarity_surfaces.self_glued_polygon(Polygon(edges=[(1,1),(-3,-1),(1,0),(1,0)])) sage: s=s.triangulate() sage: len(s.polygon(0).vertices()) 3

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.

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 translation followed by a reflection in 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]