Surface topology and geometry

Separatrix diagrams

Separatrix diagrams and cylinder diagrams

A separatrix diagram is a couple of permutation (bot,top) that have the same number of cycles in their cycle decompositions. A cylinder diagram is a separatrix diagram together with a bijection between the cycles of bot and top.

A cylinder diagram encodes the combinatorics of cylinder decomposition of a completely periodic direction in a translation surface. If we adjoin coordinates to this combinatorial datum, we have a complete description of the underlying surface. In the case of arithmetic curves, the coordinates can be taken to be rational numbers.

This representation of a surface is used in various constructions:

  • square tiled surfaces
  • Thurston-Veech construction of pseudo-Anosov diffeomorphism
  • description of the cusp of Teichmueller curves

REFERENCES:

[EMZ]A. Eskin, H. Masur, A. Zorich “Principal boundary ... and Siegel-Veech constant”
[MZ]H. Masur, A. Zorich “Multiple saddle connections on flat surfaces and the principal boundary of the moduli spaces of quadratic differentials”
[N]Y. Naveh “Tight upper bounds on the number of invariant components on translation surfaces”, Isr. J. Math. 165, 211-231 (2008)

EXAMPLES:

sage: from surface_dynamics.all import *

Separatrix diagrams:

sage: s = SeparatrixDiagram('(0,1,2)(3,4)(5,6,7)','(0,4,1,2)(3,7)(5,6)')
sage: s
(0,1,2)(3,4)(5,6,7)-(0,4,1,2)(3,7)(5,6)
sage: s.bot_cycle_tuples()
[(0, 1, 2), (3, 4), (5, 6, 7)]
sage: s.top_cycle_tuples()
[(0, 4, 1, 2), (3, 7), (5, 6)]

Cylinder diagrams:

sage: c = CylinderDiagram([((0,),(4,)),((1,2),(0,1,3)),((3,4),(2,))])
sage: print(c)
(0)-(4) (1,2)-(0,1,3) (3,4)-(2)
sage: print(c.separatrix_diagram())
(0)(1,2)(3,4)-(0,1,3)(2)(4)

They can also be built from separatrix diagram:

sage: s = SeparatrixDiagram('(0,1,2)(3,4)(5,6,7)','(0,4,1,2)(3,7)(5,6)')
sage: s
(0,1,2)(3,4)(5,6,7)-(0,4,1,2)(3,7)(5,6)
sage: s.to_cylinder_diagram([(0,1),(1,0),(2,2)])
(0,1,2)-(3,7) (3,4)-(0,4,1,2) (5,6,7)-(5,6)
class surface_dynamics.flat_surfaces.separatrix_diagram.CylinderDiagram(data, check=True)[source]

Bases: surface_dynamics.flat_surfaces.separatrix_diagram.SeparatrixDiagram

Separatrix diagram with pairing.

Each cylinder is stored as a couple (bot,top) for which the orientation is as follows:

+--------------------+
|     <-- top --     |
|                    |
|                    |
|      -- bot -->    |
+--------------------+

INPUT:

  • data - list of 2-tuples - matching of bottom-top pairs

EXAMPLES:

sage: from surface_dynamics.all import *

We first build the simplest cylinder diagram which corresponds to a torus:

sage: CylinderDiagram([((0,),(0,))])
(0)-(0)

The same initialized from a string:

sage: CylinderDiagram('(0)-(0)')
(0)-(0)

The following initialize a cylinder diagram with two cylinder which gives a surface of genus 2 with one singularity of degree 2:

sage: CylinderDiagram([((0,1),(0,2)),((2,),(1,))])
(0,1)-(0,2) (2)-(1)

ALGORITHM:

A cylinder is represented by a couple (i,j) where i is the min in bot and j is the min in top. The data _top_to_cyl and _bot_to_cyl corresponds to the association of a separatrix to the corresponding 2-tuple. The twist coordinate correspond to the shift betwenn those two indices.

an_origami()[source]

Return one origami with this diagram cylinder if any.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: cyl = CylinderDiagram('(0,1)-(0,2) (2,3)-(1,3)')
sage: cyl.an_origami()
(1,2)(3,4)
(1,3,4,2)
automorphism_group(order=False)[source]

Return the automorphism group

INPUT:

  • order - boolean (default: False) - whether or not return the order of the group

EXAMPLES:

sage: from surface_dynamics.all import *

sage: cyl = CylinderDiagram('(0,1)-(0,2) (2,3)-(1,3)')
sage: cyl.automorphism_group()
Permutation Group with generators [(0,3)(1,2)]
bot_to_cyl(j)[source]

Return the cylinder above the separatrix j

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram('(0,2,4)-(1,3,5) (1,5)-(0) (3)-(2,4)')
sage: c
(0,2,4)-(1,3,5) (1,5)-(0) (3)-(2,4)
sage: c.bot_to_cyl(0)
((0, 2, 4), (1, 3, 5))
sage: c.bot_to_cyl(1)
((1, 5), (0,))
sage: c.bot_to_cyl(3)
((3,), (2, 4))
canonical_label(inplace=False, return_map=False)[source]

Return a cylinder diagram with canonical labels.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: import itertools
sage: for p in itertools.permutations([0,1,2,3]):
....:    c = CylinderDiagram([((p[0],),(p[1],)),((p[1],p[2]),(p[0],p[3])),((p[3],),(p[2],))])
....:    cc,m = c.canonical_label(return_map=True)
....:    b  = c.bot() ; t  = c.top()
....:    bb = cc.bot(); tt = cc.top()
....:    print(cc)
....:    print(all(bb[m[i]] == m[b[i]] for i in xrange(c.nseps())))
....:    print(all(tt[m[i]] == m[t[i]] for i in xrange(c.nseps())))
(0,1)-(2,3) (2)-(1) (3)-(0)
True
True
(0,1)-(2,3) (2)-(1) (3)-(0)
True
True
(0,1)-(2,3) (2)-(1) (3)-(0)
True
True
(0,1)-(2,3) (2)-(1) (3)-(0)
True
True
(0,1)-(2,3) (2)-(1) (3)-(0)
True
True
(0,1)-(2,3) (2)-(1) (3)-(0)
True
True
(0,1)-(2,3) (2)-(1) (3)-(0)
True
True
(0,1)-(2,3) (2)-(1) (3)-(0)
True
True
...
(0,1)-(2,3) (2)-(1) (3)-(0)
True
True

sage: import itertools
sage: for p in itertools.permutations([0,1,2,3,4,5]):
....:    c1 = ((p[0],p[4]),(p[0],p[3]))
....:    c2 = ((p[1],p[3]),(p[1],p[5]))
....:    c3 = ((p[2],p[5]),(p[2],p[4]))
....:    c = CylinderDiagram([c1,c2,c3])
....:    cc,m = c.canonical_label(return_map=True)
....:    b  = c.bot() ; t  = c.top()
....:    bb = cc.bot(); tt = cc.top()
....:    print(cc)
....:    print(all(bb[m[i]] == m[b[i]] for i in xrange(c.nseps())))
....:    print(all(tt[m[i]] == m[t[i]] for i in xrange(c.nseps())))
(0,5)-(0,4) (1,4)-(1,3) (2,3)-(2,5)
True
True
(0,5)-(0,4) (1,4)-(1,3) (2,3)-(2,5)
True
True
(0,5)-(0,4) (1,4)-(1,3) (2,3)-(2,5)
True
True
(0,5)-(0,4) (1,4)-(1,3) (2,3)-(2,5)
True
True
(0,5)-(0,4) (1,4)-(1,3) (2,3)-(2,5)
True
True
...
(0,5)-(0,4) (1,4)-(1,3) (2,3)-(2,5)
True
True
(0,5)-(0,4) (1,4)-(1,3) (2,3)-(2,5)
True
True

TESTS:

sage: c = CylinderDiagram('(0,1)-(0,2) (3,5,4)-(1,4,6) (2,6)-(3,5)')
sage: c is c.canonical_label()
False
sage: c.canonical_label() is c.canonical_label()
True
sage: c.canonical_label().canonical_label() is c.canonical_label()
True
cylcoord_to_origami(lengths, heights, twists=None)[source]

Convert coordinates of the cylinders into an origami.

INPUT:

  • lengths - lengths of the separatrices
  • heights - heights of the cylinders
  • twists - twists for cylinders

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram([((0,1),(1,2)),((2,),(0,))])
sage: c.stratum()
H_2(2)
sage: c.cylcoord_to_origami([1,1,1],[1,1]).stratum()
H_2(2)
sage: o1 = c.cylcoord_to_origami([2,1,2],[1,1],[1,0])
sage: o1 = o1.relabel()
sage: o2 = c.cylcoord_to_origami([2,1,2],[1,1],[0,1])
sage: o2 = o2.relabel()
sage: o3 = c.cylcoord_to_origami([2,1,2],[1,1],[1,1])
sage: o3 = o3.relabel()
sage: all(o.stratum() == AbelianStratum(2) for o in [o1,o2,o3])
True
sage: o1 == o2 or o1 == o3 or o3 == o1
False

If the lengths are not compatible with the cylinder diagram a ValueError is raised:

sage: c.cylcoord_to_origami([1,2,3],[1,1])
Traceback (most recent call last):
...
ValueError: lengths are not compatible with cylinder equations

TESTS:

sage: c = CylinderDiagram([((0,),(1,)), ((1,2,3),(0,2,3))])
sage: c
(0)-(1) (1,2,3)-(0,2,3)
sage: lengths = [1,1,1,1]
sage: heights = [1,1]
sage: c.cylcoord_to_origami(lengths,heights,[0,0])
(1)(2,3,4)
(1,2)(3,4)
sage: c.cylcoord_to_origami(lengths,heights,[0,1])
(1)(2,3,4)
(1,2,3)(4)
sage: c.cylcoord_to_origami(lengths,heights,[0,2])
(1)(2,3,4)
(1,2,4)(3)
cylcoord_to_origami_iterator(lengths, heights)[source]

Convert coordinates of the cylinders into an origami.

INPUT:

  • lengths - lengths of the separatrices
  • heights - heights of the cylinders

OUTPUT:

  • iterator over all possible origamis with those lengths and heights...

EXAMPLES:

sage: from surface_dynamics.all import *

sage: cyl = CylinderDiagram('(0,1,2)-(3,1,2) (3)-(0)')
sage: for o in cyl.cylcoord_to_origami_iterator((1,1,1,1),(1,1)):
....:     print(o)
(1,2,3)(4)
(1,4)(2,3)
(1,2,3)(4)
(1,2,4)(3)
(1,2,3)(4)
(1,3,4)(2)

The number of origamis generated is just the product of the widths:

sage: sum(1 for _ in cyl.cylcoord_to_origami_iterator((2,1,1,2),(3,2)))
8
cylinders()[source]

Cylinders of self

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram('(0,2,4)-(1,3,5) (1,5)-(0) (3)-(2,4)')
sage: c
(0,2,4)-(1,3,5) (1,5)-(0) (3)-(2,4)
sage: c.cylinders()
[((0, 2, 4), (1, 3, 5)), ((1, 5), (0,)), ((3,), (2, 4))]
dual_graph()[source]

The dual graph of the stable curve at infinity in the horizontal direction.

This graph is defines as follows. Cut each horizontal cylinder along a circumference, then the vertices are the equivalence class of half cylinder modulo the relation “linked by a saddle connection” and the edges are the circumferences.

EXAMPLES:

sage: from surface_dynamics.all import *

We consider the three diagrams of the stratum H(1,1):

sage: c1 = CylinderDiagram('(0,1,2,3)-(0,1,2,3)')
sage: c1.stratum()
H_2(1^2)
sage: c1.dual_graph()
Looped multi-graph on 1 vertex
sage: c2 = CylinderDiagram('(0,1)-(1,2) (2,3)-(0,3)')
sage: c2.stratum()
H_2(1^2)
sage: c2.dual_graph()
Looped multi-graph on 1 vertex
sage: c3 = CylinderDiagram('(0,1)-(2,3) (2)-(0) (3)-(1)')
sage: c3.stratum()
H_2(1^2)
sage: c3.dual_graph()
Looped multi-graph on 2 vertices
horizontal_symmetry()[source]

Return the cylinder diagram obtained by reflecting the cylinder configuration along the horizontal axis.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram('(0,3,4)-(0,3,5) (1,2,5)-(1,2,4)')
sage: c.horizontal_symmetry()
(0,5,3)-(0,4,3) (1,4,2)-(1,5,2)

sage: c.separatrix_diagram().horizontal_symmetry() == c.horizontal_symmetry().separatrix_diagram()
True

sage: A = AbelianStratum(2,2)
sage: all(c.horizontal_symmetry().stratum() == A for c in A.cylinder_diagrams())
True
inverse()[source]

Return the inverse cylinder diagram

The inverse of a cylinder diagram is the cylinder diagram in which all cylinders have been reversed. It corresponds to the multiplication by -1 on the underlying Abelian differential.

Combinatorially the operation is b0-t0 ... bk-tk becomes t0-b0 ... tk-bk

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram('(0,1)-(0,2) (3,5,4)-(1,4,6) (2,6)-(3,5)')
sage: c
(0,1)-(0,2) (2,6)-(3,5) (3,5,4)-(1,4,6)
sage: c.inverse()
(0,2)-(0,1) (1,4,6)-(3,5,4) (3,5)-(2,6)

The operation can also be defined at the level of the separatrix diagrams and the two operation commutes:

sage: c.separatrix_diagram().inverse() == c.inverse().separatrix_diagram()
True

The inversion can also be seen as the composition of the horizontal and vertical symmetries:

sage: c.horizontal_symmetry().vertical_symmetry() == c.inverse()
True
sage: c.vertical_symmetry().horizontal_symmetry() == c.inverse()
True

The inversion is an involution on cylinder diagrams:

sage: all(cc.inverse().inverse() == cc for cc in AbelianStratum(4).cylinder_diagrams()) # long time
True
is_connected()[source]

Check the connectedness of this cylinder diagram.

TESTS:

sage: from surface_dynamics.all import *

sage: CylinderDiagram('(0)-(1) (1)-(0)').is_connected()
True
sage: CylinderDiagram('(0,1)-(0) (2)-(1,2)').is_connected()
True

sage: CylinderDiagram('(0)-(0) (1)-(1)').is_connected()
False
sage: CylinderDiagram('(0,1)-(3) (2)-(2) (3)-(0,1)').is_connected()
False
is_hyperelliptic(verbose=False)[source]

Test of hyperellipticity

Each stratum of Abelian differentials as up to three connected components. For the strata H(2g-2) and H(g-1,g-1) there is a special component called hyperelliptic in which all translation surfaces (X,omega) in that component are such that X is hyperelliptic.

This function returns True if and only if the cylinder diagrams correspond to a decomposition of a surface associated to the hyperelliptic components in H(2g-2) or H(g-1,g-1).

EXAMPLES:

sage: from surface_dynamics.all import *

In genus 2, strata H(2) and H(1,1), all surfaces are hyperelliptic:

sage: for c in AbelianStratum(2).cylinder_diagrams():
....:     print(c)
....:     print(c.is_hyperelliptic())
(0,2,1)-(0,2,1)
True
(0,1)-(0,2) (2)-(1)
True

sage: for c in AbelianStratum(1,1).cylinder_diagrams():
....:     print(c)
....:     print(c.is_hyperelliptic())
(0,3,1,2)-(0,3,1,2)
True
(0,1,2)-(0,1,3) (3)-(2)
True
(0,3)-(0,2) (1,2)-(1,3)
True
(0,1)-(2,3) (2)-(1) (3)-(0)
True

In higher genera, some of them are, some of them are not:

sage: C = AbelianStratum(4).cylinder_diagrams()
sage: len(C)
15
sage: len(filter(lambda c: c.is_hyperelliptic(), C))
5

sage: C = AbelianStratum(2,2).cylinder_diagrams()
sage: len(C)
41
sage: len(filter(lambda c: c.is_hyperelliptic(), C))
11
lengths_polytope(heights)[source]

Return the rational polyhedron corresponding to the set of length with the given fixed heights.

-> one can obtain ehrhard series for each of them! It tells us that we have a nice asymptotics... and the asymptotics is simply given by the volume of this polytope (up to the ignored twists parameters)!

matrix_relation()[source]

Return the matrix of relation on the lengths of the separatrices.

The output matrix has size ncyls times nseps.

EXAMPLES:

sage: from surface_dynamics.all import *

For a one cylinder diagram, there is no relations:

sage: cyl = CylinderDiagram('(0,1,2,3)-(0,1,2,3)')
sage: cyl.matrix_relation()
[0 0 0 0]

Here is an example in the stratum H(2):

sage: cyl = CylinderDiagram('(0,1)-(0,2) (2)-(1)')
sage: cyl.stratum()
H_2(2)
sage: cyl.matrix_relation()
[ 0  1 -1]
[ 0 -1  1]
origami_iterator(n)[source]

Iteration over all origamis with n squares.

INPUT:

  • n - positive integer - the number of squares

EXAMPLES:

sage: from surface_dynamics.all import *

sage: cyl = CylinderDiagram('(0,1,2)-(3,1,2) (3)-(0)')
sage: for o in cyl.origami_iterator(4):
....:     print(o)
....:     print(o.stratum())
....:     print(o.nb_squares()))
(1,2,3)(4)
(1,4)(2,3)
H_2(1^2)
4
(1,2,3)(4)
(1,2,4)(3)
H_2(1^2)
4
(1,2,3)(4)
(1,3,4)(2)
H_2(1^2)
4
origamis(n=None)[source]

Return the set of origamis having n squares.

If n is None then return the origamis with less number of squares.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: cyl = CylinderDiagram('(0,1,2)-(0,1,3) (3)-(2)')
sage: o5 = cyl.origamis(5)
sage: o5[0]
(1,2,3,4)(5)
(1,5,4,2,3)
sage: o5[1].nb_squares()
5
sage: o5[2].stratum_component()
H_2(1^2)^hyp
separatrix_diagram()[source]

Return the underlying separatrix diagram

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1)(2,3,4)','(0,3)(1,4,2)'); s
(0,1)(2,3,4)-(0,3)(1,4,2)
sage: c = s.to_cylinder_diagram([(0,1),(1,0)]); c
(0,1)-(1,4,2) (2,3,4)-(0,3)
sage: c.separatrix_diagram() == s
True
smallest_integer_lengths()[source]

Check if there is a integer solution that satisfy the cylinder conditions.

If there is a solution, the function returns a list a pair (total_length, list_of_lengths) that consists of the sum of the length of the separatrices together with the list of lengths. Otherwise, returns False.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram('(0,1)-(0,2) (2,3)-(1,3)')
sage: c.smallest_integer_lengths()
(4, [1, 1, 1, 1])
sage: c = CylinderDiagram('(0,1,2)-(3) (3)-(0) (4)-(1,2,4)')
sage: c.smallest_integer_lengths()
False

sage: c = CylinderDiagram('(0,1)-(0,5) (2)-(3) (3,6)-(8) (4,8)-(6,7) (5)-(2,4) (7)-(1)')
sage: c.smallest_integer_lengths()
(13, [1, 2, 1, 1, 1, 2, 1, 2, 2])
spin_parity()[source]

Return the spin parity of any surface that is built from this cylinder diagram.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram('(0,1,2,3,4)-(0,1,2,3,4)')
sage: c.spin_parity()
0
sage: c = CylinderDiagram('(0,1,2,3,4)-(0,1,4,2,3)')
sage: c.spin_parity()
1

sage: c = CylinderDiagram('(0,2,6,1)-(0,8,1,9,2,5,7,4) (3,7,4,8,9,5)-(3,6)')
sage: c.spin_parity()
0
sage: c = CylinderDiagram('(3,7,4,8,9,5)-(0,8,1,9,2,5,7,4) (0,2,6,1)-(3,6)')
sage: c.spin_parity()
1
stratum_component()[source]

Return the connected component of stratum of self.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: CylinderDiagram('(0,1)-(0,2) (2)-(1)').stratum_component()
H_2(2)^hyp

sage: c = CylinderDiagram('(0,3,2,1)-(1,4,3,2) (4,7,6,5)-(0,7,6,5)')
sage: c.stratum_component()
H_4(3^2)^hyp
sage: c = CylinderDiagram('(0,1,4)-(1,6,7) (2,5,3)-(0,2,4) (6)-(5) (7)-(3)')
sage: c.stratum_component()
H_4(3^2)^nonhyp

sage: c = CylinderDiagram('(0,6)-(1,7) (1,5,4,3,2)-(2,6,5,4,3) (7,9,8)-(0,9,8)')
sage: c.stratum_component()
H_5(4^2)^hyp
sage: c = CylinderDiagram('(0,2,6,1)-(0,8,1,9,2,5,7,4) (3,7,4,8,9,5)-(3,6)')
sage: c.stratum_component()
H_5(4^2)^even
sage: c = CylinderDiagram('(3,7,4,8,9,5)-(0,8,1,9,2,5,7,4) (0,2,6,1)-(3,6)')
sage: c.stratum_component()
H_5(4^2)^odd
symmetries()[source]

Return a triple (horiz_sym, vert_sym, inv_sym)

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram('(0,1)-(2,3,5) (2,3,4)-(1) (5)-(0,4)')
sage: c.symmetries()
(False, True, False)

sage: c.horizontal_symmetry().is_isomorphic(c)
False
sage: c.vertical_symmetry().is_isomorphic(c)
True
sage: c.inverse().is_isomorphic(c)
False
to_directed_graph()[source]

Return a labeled directed graph that encodes the cylinder diagram.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram('(0,1,5)-(2,5) (2)-(0,1,3) (3,4)-(4)'); c
(0,1,5)-(2,5) (2)-(0,1,3) (3,4)-(4)
sage: G = c.to_directed_graph(); G
Looped multi-digraph on 6 vertices
sage: G.edges()
[(0, 1, 'b'), (0, 1, 't'), (0, 2, 'c'), (0, 5, 'c'), (1, 2, 'c'), (1, 3, 't'), (1, 5, 'b'), (1, 5, 'c'), (2, 0, 'c'), (2, 1, 'c'), (2, 2, 'b'), (2, 3, 'c'), (2, 5, 't'), (3, 0, 't'), (3, 4, 'b'), (3, 4, 'c'), (4, 3, 'b'), (4, 4, 'c'), (4, 4, 't'), (5, 0, 'b'), (5, 2, 'c'), (5, 2, 't'), (5, 5, 'c')]
to_ribbon_graph()[source]

Return a ribbon graph

A ribbon graph is a graph embedded in an oriented surface such that its complement is a union of topological discs. To a cylinder diagram we associate the graph which consists of separatrices together with a choice of one vertical edge in each cylinder.

The edges of the ribbon graph are labeled by (i,nseps+i) for separatrices and by (2(nseps+j),2(nseps+j)+1) for vertical in cylinders.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: C = CylinderDiagram([((0,1),(0,2)),((2,),(1,))])
sage: C.stratum()
H_2(2)
sage: R = C.to_ribbon_graph(); R
Ribbon graph with 1 vertex, 5 edges and 2 faces
sage: l,m = R.cycle_basis(intersection=True)
sage: m.rank() == 2 * C.genus()
True

TESTS:

sage: f = lambda c: c.to_ribbon_graph().cycle_basis(intersection=True)[1]

sage: a = AbelianStratum(2)
sage: all(f(c).rank() == 4 for c in a.cylinder_diagrams())
True
sage: a = AbelianStratum(1,1)
sage: all(f(c).rank() == 4 for c in a.cylinder_diagrams())
True
to_ribbon_graph_with_holonomies(lengths, heights, twists)[source]
top_to_cyl(j)[source]

Return the cylinder below the separatrix j

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram('(0,2,4)-(1,3,5) (1,5)-(0) (3)-(2,4)')
sage: c.top_to_cyl(0)
((1, 5), (0,))
sage: c.top_to_cyl(2)
((3,), (2, 4))
vertical_symmetry()[source]

Return the cylinder diagram obtained by reflecting the cylinder configuration along the vertical axis.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: c = CylinderDiagram('(0,3,4)-(0,3,5) (1,2,5)-(1,2,4)')
sage: c.vertical_symmetry()
(0,4,3)-(0,5,3) (1,5,2)-(1,4,2)

sage: c.separatrix_diagram().vertical_symmetry() == c.vertical_symmetry().separatrix_diagram()
True

sage: A = AbelianStratum(2,2)
sage: all(c.vertical_symmetry().stratum() == A for c in A.cylinder_diagrams())
True
widths_and_heights_iterator(n)[source]

Iterate over the possible integer widths and heights of the cylinders for which the corresponding translation surface has area n.

At each iteration, the output is a pair of (lengths,heights). You can then use cylcoord_to_origami() to build the corresponding origami.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: cyl = CylinderDiagram([((0,1),(0,2)),((2,),(1,))])
sage: cyl
(0,1)-(0,2) (2)-(1)

sage: it = cyl.widths_and_heights_iterator(10)
sage: l,h = it.next()
sage: print(l)
(2, 1, 1)
sage: print(h)
[3, 1]
sage: cyl.cylcoord_to_origami(l,h)
(1,2,3)(4,5,6)(7,8,9)(10)
(1,4,7)(2,5,8)(3,6,9,10)
class surface_dynamics.flat_surfaces.separatrix_diagram.SeparatrixDiagram(data, top=None, check=True, copy=True)[source]

Bases: sage.structure.sage_object.SageObject

Separatrix diagram of oriented foliation.

A separatrix diagram is a 2-tuple of permutations (bot,top) such that bot and top share the same number of cycles.

bot (resp. top) has to be thought a bottom (resp. top) of a potential face as in the following:

    -- bot -->
-------------------
   <-- top --

The order for bot and top is choosen in such a way that it cooresponds to the orientation of a face.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,2)(1,3,4)','(0,4)(2,1,3)')
sage: print(s)
(0,2)(1,3,4)-(0,4)(1,3,2)
sage: print(s.stratum())
H_3(4)
automorphism_group(implementation='graph')[source]

Return the automorphism group of self.

That is the centralizer of the permutations top and bottom.

INPUT:

  • implementation - either graph or gap

EXAMPLES:

sage: from surface_dynamics.all import *

sage: S = SeparatrixDiagram('(0,3,1,4,2)','(0,1,2,3,4)')
sage: G1 = S.automorphism_group(implementation='graph'); G1
Permutation Group with generators [(0,1,2,3,4)]
sage: G2 = S.automorphism_group(implementation='gap'); G2
Subgroup of (Symmetric group of order 5! as a permutation group) generated by [(1,2,3,4,5), (1,4,2,5,3)]
sage: G1.is_isomorphic(G2)
True
bot()[source]

The bot permutation as a list from 0 to nseps-1

Warning: the output list should not be modified

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0)(1,2)','(0,1)(2)')
sage: s.bot()
[0, 2, 1]
bot_cycle_string()[source]

Return the cycles of the top permutation as a string.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: S = SeparatrixDiagram('(0,2)(3,4)','(0)(1,2,3)')
sage: S.bot_cycle_string()
'(0,2)(1)(3,4)'
bot_cycle_tuples()[source]

Return the cycles of the bottom permutation as a list of tuples.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: S = SeparatrixDiagram('(0,2)(3,4)','(0)(1,2,3)')
sage: S.bot_cycle_tuples()
[(0, 2), (1,), (3, 4)]
bot_orbit(i)[source]

Return the orbit of i under the bot permutation

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1)(2,5)(3,4,6)','(0,1,5)(2,3,6)(4)')
sage: s.bot_orbit(0)
(0, 1)
sage: s.bot_orbit(4)
(3, 4, 6)
bot_perm()[source]

Return the bot as a permutation (element of a group)

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0)(1,2)','(0,1)(2)')
sage: s.bot_perm()
(2,3)
canonical_label(inplace=False)[source]

Relabel self according to some canonical labels.

The result is cached.

INPUT:

  • inplace - boolean (default: True) - if True modify self if not return a new separatrix diagram.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: bot = '(0,1,3,6,7,5)(2,4)(8)(9)'
sage: top = '(0)(1,2)(3,4,5)(6,7,8,9)'
sage: s = SeparatrixDiagram(bot,top)
sage: s.canonical_label()
(0)(1)(2,3,4,5,6,7)(8,9)-(0,1,2,3)(4,7,9)(5)(6,8)

TESTS:

sage: from surface_dynamics.all import *

sage: bot = [3,2,4,0,1]
sage: top = [1,0,3,4,2]
sage: b = [None]*5; t = [None]*5
sage: for p in Permutations([0,1,2,3,4]):
....:     for i in xrange(5):
....:         b[p[i]] = p[bot[i]]
....:         t[p[i]] = p[top[i]]
....:     s = SeparatrixDiagram(b,t)
....:     print(s.canonical_label())
(0,1)(2,3,4)-(0,2,4)(1,3)
(0,1)(2,3,4)-(0,2,4)(1,3)
(0,1)(2,3,4)-(0,2,4)(1,3)
(0,1)(2,3,4)-(0,2,4)(1,3)
(0,1)(2,3,4)-(0,2,4)(1,3)
(0,1)(2,3,4)-(0,2,4)(1,3)
...
(0,1)(2,3,4)-(0,2,4)(1,3)
(0,1)(2,3,4)-(0,2,4)(1,3)
(0,1)(2,3,4)-(0,2,4)(1,3)
(0,1)(2,3,4)-(0,2,4)(1,3)
cylinder_diagram_iterator(connected=True, up_to_isomorphism=True)[source]

Construct all cylinder diagrams from given separatrix diagram (i.e. a pair of permutations).

INPUT:

  • connected - boolean (default: True) - if true, returns only connected cylinder diagrams.
  • up_to_isomorphism - boolean (default: True) - take care of isomorphism problem. It is memory efficient and probably faster to set this option to False.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1)(2,3)(4,5)','(1,2)(3,4)(5,0)')
sage: for c in s.cylinder_diagram_iterator(): print(c)
(0,5)-(0,4) (1,4)-(1,3) (2,3)-(2,5)
(0,3)-(0,5) (1,2)-(1,4) (4,5)-(2,3)
(0,5)-(3,4) (1,4)-(0,2) (2,3)-(1,5)
sage: G = s.automorphism_group(); G
Permutation Group with generators [(0,1)(2,5)(3,4), (0,2,4)(1,3,5)]
sage: G.order()
6
sage: sum(1 for _ in s.cylinder_diagram_iterator(up_to_isomorphism=False))
6

Here is an example with some symmetry:

sage: s = SeparatrixDiagram('(0)(1)(2,3)(4,5,6)-(0,1)(2,4)(3,5)(6)')
sage: s.vertical_symmetry().canonical_label() == s
True
sage: s.cylinder_diagrams()
[(0,1)-(0,4) (2,3,4)-(5,6) (5)-(2) (6)-(1,3),
 (0,1)-(4) (2,4,3)-(5,6) (5)-(0,2) (6)-(1,3),
 (0,3,1)-(0,6) (2,6)-(4,5) (4)-(1) (5)-(2,3)]
cylinder_diagrams(connected=True, up_to_isomorphism=True)[source]

Return the list of cylinder diagrams associated to this separatrix diagram.

We warn that the cylinder diagram may be renumeroted in the output list (in order to prevent repetitions). If you care about numerotation the option up_to_isomorphism should be set to False.

INPUT:

  • connected - boolean (default: True)
  • up_to_isomorphism - boolean (default: True)

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0)(1)(2)','(0)(1)(2)')
sage: for c in s.cylinder_diagrams(connected=True): print(c)
(0)-(2) (1)-(0) (2)-(1)
sage: for c in s.cylinder_diagrams(connected=False): print(c)
(0)-(0) (1)-(1) (2)-(2)
(0)-(1) (1)-(0) (2)-(2)
(0)-(2) (1)-(0) (2)-(1)

sage: s = SeparatrixDiagram('(0,1)(2)','(0)(1,2)')
sage: for c in s.cylinder_diagrams(): print(c)
(0,1)-(0,2) (2)-(1)

In the example below, there is no isomorphism problem for the cylinder diagram generation as the separatrix diagram admit no automorphism:

sage: s = SeparatrixDiagram('(0,3)(1,4,5)(2)','(0)(1,2)(3,4,5)')
sage: for c in s.cylinder_diagrams(): print(c)
(0,1,2)-(0,1,5) (3,5)-(2,4) (4)-(3)
(0,2,3)-(2,5) (1,4)-(0,1,3) (5)-(4)
(0,3,1)-(5) (2,5)-(3,4) (4)-(0,2,1)
sage: for c in s.cylinder_diagrams(up_to_isomorphism=False): print(c)
(0,3)-(1,2) (1,4,5)-(0) (2)-(3,4,5)
(0,3)-(1,2) (1,4,5)-(3,4,5) (2)-(0)
(0,3)-(3,4,5) (1,4,5)-(1,2) (2)-(0)
sage: s.automorphism_group()
Permutation Group with generators [()]
degree()[source]

Return the degree (number of separatrices) of this separatrix diagram.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: S = SeparatrixDiagram('(0,1)(2,3)','(1,3,2)(0)')
sage: S.degree()
4
euler_characteristic()[source]

Return the Euler characteristic

EXAMPLES:

sage: from surface_dynamics.all import *

sage: SeparatrixDiagram('(0)','(0)').euler_characteristic()
0

sage: CylinderDiagram([((0,),(0,))]).euler_characteristic()
0
sage: CylinderDiagram([((0,1),(0,2)), ((2,),(1,))]).euler_characteristic()
-2
genus()[source]

Return the genus

EXAMPLES:

sage: from surface_dynamics.all import *

sage: CylinderDiagram([((0,),(0,))]).genus()
1
sage: CylinderDiagram([((0,1),(0,1))]).genus()
1
sage: CylinderDiagram([((0,1,2),(0,1,2))]).genus()
2
sage: CylinderDiagram([((0,1,2,3),(0,1,2,3))]).genus()
2
sage: CylinderDiagram([((0,1,2,3,4),(0,1,2,3,4))]).genus()
3
homological_dimension_of_cylinders()[source]

Returns the dimension in the first homology group of the span of waist curves of horizontal cylinders.

EXAMPLES:

sage: from surface_dynamics.all import *

Homological dimension in the stratum H(2):

sage: c = CylinderDiagram('(0,1,2)-(0,1,2)')
sage: c.stratum()
H_2(2)
sage: c.homological_dimension_of_cylinders()
1
sage: c = CylinderDiagram('(0,1)-(1,2) (2)-(0)')
sage: c.stratum()
H_2(2)
sage: c.homological_dimension_of_cylinders()
2

Homological dimensions for cylinder diagrams in H(1,1):

sage: c = CylinderDiagram('(0,1,2,3)-(0,1,2,3)')
sage: c.stratum()
H_2(1^2)
sage: c.homological_dimension_of_cylinders()
1
sage: c = CylinderDiagram('(0,1)-(0,2) (2,3)-(1,3)')
sage: c.stratum()
H_2(1^2)
sage: c.homological_dimension_of_cylinders()
2
sage: c = CylinderDiagram('(0,1,2)-(1,2,3) (3)-(0)')
sage: c.stratum()
H_2(1^2)
sage: c.homological_dimension_of_cylinders()
2
sage: c = CylinderDiagram('(0,1)-(2,3) (2)-(0) (3)-(1)')
sage: c.stratum()
H_2(1^2)
sage: c.homological_dimension_of_cylinders()
2
horizontal_symmetry()[source]

Return the horizontal symmetric of this separatrix diagram.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1,2,3)(4,5)','(1,2,3)(4,5,0)')
sage: sh = s.horizontal_symmetry()
sage: print(sh)
(0,5,4)(1,3,2)-(0,3,2,1)(4,5)

sage: sh.cylinder_diagrams()
[(0,2,4)-(1,5) (1,3,5)-(0,2,3,4)]
sage: [c.horizontal_symmetry().canonical_label() for c in s.cylinder_diagrams()]
[(0,2,4)-(1,5) (1,3,5)-(0,2,3,4)]
incoming_edges_perm()[source]

Permutation associated to turning around vertices in trigonometric order.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1)','(2,3)')
sage: s.incoming_edges_perm()
[1, 0, 3, 2]

sage: s = SeparatrixDiagram('(0,5,2)(1,3,4)(6,7,8)','(0,3,7,8)(1,5)(2,4,6)')
sage: s.incoming_edges_perm()
[4, 2, 1, 8, 7, 3, 0, 6, 5]
inverse()[source]

Return the inverse of this separatrix diagram, that is the one we obtain after application of -Id.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1,2)(3,4,5,6,7,8)-(0,1,3,5,7)(2,4,6,8)')
sage: s.inverse()
(0,1,3,5,7)(2,4,6,8)-(0,1,2)(3,4,5,6,7,8)
sage: s.horizontal_symmetry().vertical_symmetry() == s.inverse()
True
sage: s.vertical_symmetry().horizontal_symmetry() == s.inverse()
True
is_in_normal_form()[source]

Test normal form

Return True if self is in normal form and False otherwise.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1,2)(3,4,5)(6,7,8)','(0,3,7,8)(1,5)(2,4,6)')
sage: s.is_in_normal_form()
False
sage: s.canonical_label().is_in_normal_form()
True
is_isomorphic(other, return_map=False)[source]

Test whether self is isomorphic to other.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: bot = [1,2,0,3]
sage: top = [1,0,3,2]
sage: s = SeparatrixDiagram(bot,top); s
(0,1,2)(3)-(0,1)(2,3)
sage: m = [3,0,1,2]
sage: bot2 = [0]*4
sage: top2 = [0]*4
sage: for i in xrange(4):
....:     bot2[m[i]] = m[bot[i]]
....:     top2[m[i]] = m[top[i]]
sage: ss = SeparatrixDiagram(bot2,top2)
sage: s.is_isomorphic(ss)
True
sage: m = [1,2,0,3]
sage: for i in xrange(4):
....:   bot2[m[i]] = m[bot[i]]
....:   top2[m[i]] = m[top[i]]
sage: ss = SeparatrixDiagram(bot2,top2)
sage: s.is_isomorphic(ss)
True
ncyls()[source]

Return the number of cylinders of this separatrix diagram.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: S = SeparatrixDiagram('(0,1)(2,3)','(1,3,2)(0)')
sage: S.ncyls()
2
nseps()

Return the degree (number of separatrices) of this separatrix diagram.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: S = SeparatrixDiagram('(0,1)(2,3)','(1,3,2)(0)')
sage: S.degree()
4
outgoing_edges_perm()[source]

Permutation associated to turning around vertices in trigonometric order.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1)','(2,3)')
sage: s.outgoing_edges_perm()
[1, 0, 3, 2]

sage: s = SeparatrixDiagram('(0,5,2)(1,3,4)(6,7,8)','(0,3,7,8)(1,5)(2,4,6)')
sage: s.outgoing_edges_perm()
[7, 0, 8, 2, 5, 4, 3, 1, 6]
profile()[source]

Return the angles around each vertex

EXAMPLES:

sage: from surface_dynamics.all import *

sage: a = AbelianStratum(1,1,0)
sage: s = a.separatrix_diagrams()[0]
sage: s.profile()
[2, 2, 1]
relabel(perm, inplace=False)[source]

Relabel self according to the permutation perm.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0)(2,3,4)','(0,3,2)(1)')
sage: s
(0)(1)(2,3,4)-(0,3,2)(1)(4)
sage: s.relabel(perm=[1,0,2,3,4])
(0)(1)(2,3,4)-(0)(1,3,2)(4)
sage: s.relabel(perm=[1,2,0,3,4])
(0,3,4)(1)(2)-(0,1,3)(2)(4)
stratum()[source]

Return the Abelian stratum this separatrix diagram belongs to.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: SeparatrixDiagram('(0)(1)(2)','(0)(1)(2)').stratum()
H_1(0^3)
sage: SeparatrixDiagram('(0,1)(2)','(0,2)(1)').stratum()
H_2(2)
symmetries()[source]

Return a triple of boolean (horiz_sym, vert_sym, inverse_sym) which correspond to the symmetry of self.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1,2)(3,4,5)-(0,1)(2,3,4,5)')
sage: s.symmetries()
(False, True, False)
sage: s.horizontal_symmetry().is_isomorphic(s)
False
sage: s.vertical_symmetry().is_isomorphic(s)
True
sage: s.inverse().is_isomorphic(s)
False

sage: s = SeparatrixDiagram('(0,1,3,5)(2,4)-(0,4,1,5)(2,3)')
sage: s.symmetries()
(True, False, False)
sage: s.horizontal_symmetry().is_isomorphic(s)
True
sage: s.vertical_symmetry().is_isomorphic(s)
False
sage: s.inverse().is_isomorphic(s)
False

sage: s = SeparatrixDiagram('(0,1,3,5)(2,4)-(0,3,2,1)(5,4)')
sage: s.symmetries()
(False, False, True)
sage: s.horizontal_symmetry().is_isomorphic(s)
False
sage: s.vertical_symmetry().is_isomorphic(s)
False
sage: s.inverse().is_isomorphic(s)
True

sage: s = SeparatrixDiagram('(0)(1,2,3,4,5)-(0,1,2,5,3)(4)')
sage: s.symmetries()
(False, False, False)
sage: s.horizontal_symmetry().is_isomorphic(s)
False
sage: s.vertical_symmetry().is_isomorphic(s)
False
sage: s.inverse().is_isomorphic(s)
False

TESTS:

sage: sym = lambda s: (s.horizontal_symmetry().is_isomorphic(s),
....:                  s.vertical_symmetry().is_isomorphic(s),
....:                  s.inverse().is_isomorphic(s))
sage: from surface_dynamics.flat_surfaces.separatrix_diagram import separatrix_diagram_iterator
sage: for s in separatrix_diagram_iterator((2,2,2,2)):
....:     assert s.symmetries() == sym(s)
sage: for s in separatrix_diagram_iterator((4,2)):
....:     assert s.symmetries() == sym(s)
to_cylinder_diagram(pairing)[source]

Return a cylinder diagram with the given pairing

The pairing should be a list of 2-tuples of integer.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1,3)(2,4)','(0,2)(1,4,3)'); s
(0,1,3)(2,4)-(0,2)(1,4,3)

sage: s.to_cylinder_diagram([(0,0),(1,1)])
(0,1,3)-(0,2) (2,4)-(1,4,3)
sage: s.to_cylinder_diagram([(1,1),(0,0)])
(0,1,3)-(0,2) (2,4)-(1,4,3)

sage: s.to_cylinder_diagram([(0,1),(1,0)])
(0,1,3)-(1,4,3) (2,4)-(0,2)
sage: s.to_cylinder_diagram([(1,0),(0,1)])
(0,1,3)-(1,4,3) (2,4)-(0,2)
to_directed_graph()[source]

Return a graph that encodes this separatrix diagram.

The vertices correspond to separatrix and the edges are of two types

  • ‘b’ neighboor corresponds to the right neighbors on the bottom permutation
  • ‘t’ edges correspond to the neighbor of the top permutation

EXAMPLES:

sage: from surface_dynamics.all import *

sage: S = SeparatrixDiagram('(0,1)(2,3,4)','(0,3,2)(1,4)')
sage: G = S.to_directed_graph(); G
Looped multi-digraph on 5 vertices
sage: G.vertices()
[0, 1, 2, 3, 4]
sage: G.edges()
[(0, 1, 'b'), (0, 3, 't'), (1, 0, 'b'), (1, 4, 't'), (2, 0, 't'), (2, 3, 'b'), (3, 2, 't'), (3, 4, 'b'), (4, 1, 't'), (4, 2, 'b')]
top()[source]

Return the top permutation of self as a list.

Warning: the output should not be modified

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1,3)(2,4)','(0,4)(1,2,3)')
sage: s.top()
[4, 2, 3, 1, 0]
top_cycle_string()[source]

Return the cycle of the top permutation as a string.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: S = SeparatrixDiagram('(0,2)(3,4)','(0)(1,2,3)')
sage: S.top_cycle_string()
'(0)(1,2,3)(4)'
top_cycle_tuples()[source]

Return the cycle of the top permutation as a list of tuples.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: S = SeparatrixDiagram('(0,2)(3,4)','(0)(1,2,3)')
sage: S.top_cycle_tuples()
[(0,), (1, 2, 3), (4,)]
top_orbit(i)[source]

Return the orbit of i under the top permutation.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1)(2,5)(3,4,6)','(0,1,5)(2,3,6)(4)')
sage: s.top_orbit(0)
(0, 1, 5)
sage: s.top_orbit(6)
(2, 3, 6)
top_perm()[source]

Return the top as a permutation

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0)(1,2)','(1)(0,2)')
sage: s.top_perm()
(1,3)
vertical_symmetry()[source]

Return the vertical symmetric of this separatrix diagram.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: s = SeparatrixDiagram('(0,1,2,3)(4,5)','(1,2,3)(4,5,0)')
sage: sv = s.vertical_symmetry()
sage: print(sv)
(0,3,2,1)(4,5)-(0,5,4)(1,3,2)

sage: sv.cylinder_diagrams()
[(0,1,3,4)-(2,3,5) (2,5)-(0,1,4)]
sage: [c.vertical_symmetry().canonical_label() for c in sv.cylinder_diagrams()]
[(0,1,3,4)-(2,3,5) (2,5)-(0,1,4)]
surface_dynamics.flat_surfaces.separatrix_diagram.cyclic_direction(x, y, z)[source]

Returns 1 or -1 depending on the cyclic ordering of (x,y,z)

TESTS:

sage: from surface_dynamics.flat_surfaces.separatrix_diagram import cyclic_direction
sage: cyclic_direction(0,1,2)
1
sage: cyclic_direction(1,2,0)
1
sage: cyclic_direction(2,0,1)
1
sage: cyclic_direction(2,1,0)
-1
sage: cyclic_direction(1,0,2)
-1
sage: cyclic_direction(0,2,1)
-1
surface_dynamics.flat_surfaces.separatrix_diagram.hyperelliptic_cylinder_diagram_iterator(a, verbose=False)[source]

Return an iterator over cylinder diagrams of Abelian differentials that double covers Q((a-2), -1^(a+2)).

The generator is up to isomorphism.

TODO:

  • An optimization could be obtained by considering the generation of k-subsets of {1,...,n} up to the cyclic symmetry of the tree.

INPUT:

  • a - integer - angle of the conical singularity of the quadratic differential.
  • verbose - integer (default: 0) - output various information during the iteration (mainly for debug).

EXAMPLES:

sage: from surface_dynamics.all import *

sage: from surface_dynamics.flat_surfaces.separatrix_diagram import hyperelliptic_cylinder_diagram_iterator
sage: it = hyperelliptic_cylinder_diagram_iterator(3)
sage: c = it.next(); c
(0,1)-(0,2) (2)-(1)
sage: c.stratum_component()
H_2(2)^hyp

sage: hyp = AbelianStratum(2,2).hyperelliptic_component()
sage: all(c.stratum_component() == hyp for c in hyperelliptic_cylinder_diagram_iterator(6))
True
surface_dynamics.flat_surfaces.separatrix_diagram.orientation_cover(alpha, phi, a, verbose=0)[source]

Build the cylinder diagram of Abelian differentials that double covers it.

A quadratic differrential separatrix diagram is given by three permutations

  • sigma: the permutation of 1/2-separatrices around vertices
  • alpha: the permutation of 1/2-separatrices that describe the separatrices
    (it is a fixed point free involution)
  • phi: the permutation of 1/2-separatrices that describe the cycles.

INPUT:

  • alpha – permutation
  • phi – permutation
  • a – number of half separatrices

EXAMPLES:

sage: from surface_dynamics.all import *

sage: from surface_dynamics.flat_surfaces.separatrix_diagram import orientation_cover
sage: alpha = [3, 2, 1, 0, 5, 4, 7, 6]
sage: phi = [3, 1, 0, 2, 5, 4, 7, 6]
sage: orientation_cover(alpha,phi,3)
(0,2)-(0,1) (1)-(2)
surface_dynamics.flat_surfaces.separatrix_diagram.separatrix_diagram_fast_iterator(profile, ncyls=None)[source]

Iterator over separatrix diagram with given profile

Return a list of 3-tuples [bot, top, s] where bot and top are list on 0, ..., nseps-1 that corresponds to a separatrix diagram with profile profile while s is the element conjugacy class corresponding to the profile which equals bot * top.

If ncyls is not None, it should be a list of integers from which the number of cylinders is considered.

Warning: each isomorphism class of separatrix diagram is output more than once in general. If you want a unique representative in each isomorphism class you may consider the method separatrix_diagram_iterator instead.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: from surface_dynamics.flat_surfaces.separatrix_diagram import separatrix_diagram_fast_iterator
sage: for s in separatrix_diagram_fast_iterator([3]): print(s)
([0, 2, 1], [1, 0, 2], [(0, 1, 2)])
([1, 2, 0], [1, 2, 0], [(0, 2, 1)])
([2, 1, 0], [1, 0, 2], [(0, 2, 1)])
sage: for s in separatrix_diagram_fast_iterator([2,2]): print(s)
([0, 2, 3, 1], [1, 2, 0, 3], [(0, 1), (2, 3)])
([0, 1, 3, 2], [1, 0, 2, 3], [(0, 1), (2, 3)])
([1, 2, 3, 0], [1, 2, 3, 0], [(0, 2), (1, 3)])
([1, 3, 2, 0], [1, 2, 0, 3], [(0, 2), (1, 3)])
([3, 2, 1, 0], [1, 0, 3, 2], [(0, 2), (1, 3)])
([3, 1, 0, 2], [1, 2, 0, 3], [(0, 3), (1, 2)])
([2, 3, 0, 1], [1, 0, 3, 2], [(0, 3), (1, 2)])
surface_dynamics.flat_surfaces.separatrix_diagram.separatrix_diagram_iterator(profile, ncyls=None)[source]

Iterator over separatrix diagram with given profile and number of cylinders.

Warning: to prevent isomorphism class to be output twice the function implement a cache mechanism. If you intend to iterate through a huge class of separatrix_diagram and do not care about isomorphism problem use separatrix_diagram_fast_iterator instead.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: from surface_dynamics.flat_surfaces.separatrix_diagram import separatrix_diagram_iterator

sage: for s in separatrix_diagram_iterator([1,1]): print(s)
(0,1)-(0,1)
(0)(1)-(0)(1)

sage: for s in separatrix_diagram_iterator([3]): print(s)
(0)(1,2)-(0,1)(2)
(0,1,2)-(0,1,2)

sage: for s in separatrix_diagram_iterator([2,2]): print(s)
(0)(1,2,3)-(0,1,2)(3)
(0)(1)(2,3)-(0,1)(2)(3)
(0,1,2,3)-(0,1,2,3)
(0,1)(2,3)-(0,2)(1,3)

sage: sum(1 for s in separatrix_diagram_iterator([3,2,2]))
64
surface_dynamics.flat_surfaces.separatrix_diagram.string_to_cycle(s)[source]

TESTS:

sage: from surface_dynamics.flat_surfaces.separatrix_diagram import string_to_cycle
sage: string_to_cycle('(3,1,2)')
(3, 1, 2)
surface_dynamics.flat_surfaces.separatrix_diagram.two_non_connected_perms_canonical_labels(bot, top)[source]

EXAMPLES:

sage: from surface_dynamics.flat_surfaces.separatrix_diagram import two_non_connected_perms_canonical_labels
sage: two_non_connected_perms_canonical_labels([3,2,1,0],[0,1,2,3])
([1, 0, 3, 2], [0, 1, 2, 3])

Homology

Simplicial complex, homology of surfaces and translation surfaces

In this module are implemented simple homology computation for translation surfaces. There are three main classes:

  • RibbonGraph: decomposition of a surface into polygons. The combinatorics is stored as a triple of permutations v (vertices), e (edges), f (faces) so that the product vef is the identity. The domain of the permutations correspond to the half edges or darts. The permutation e is an involution without fixed point so that e(i) is the other half of the edge starting at i. The permutation v is obtained by turning around a vertex, while f turning around a face.
  • RibbonGraphWithAngles: a ribbon graph with an additional angle structure.
  • RibbonGraphWithHolonomies: a ribbon graph with an additional holonomy structure on its edges.

EXAMPLES:

sage: from surface_dynamics.all import *

To create a ribbon graph you just need to fix two of the permutations v, e, f:

sage: R = RibbonGraph(vertices='(0,1,4,3)(5,2)',edges='(0,3)(1,2)(4,5)')
sage: R
Ribbon graph with 2 vertices, 3 edges and 3 faces

The vertices, edges and faces are by definition the cycles of the permutation. Calling the method vertices(), edges() or faces() gives you access to these cycles:

sage: R.vertices()
([0, 1, 4, 3], [2, 5])
sage: R.edges()
([0, 3], [1, 2], [4, 5])
sage: R.faces()
([0, 4, 2], [1, 5], [3])

Given a half edge (i.e. a dart), you can get the index of the vertex, edge or face it belongs with the methods dart_to_vertex(), dart_to_edge() and dart_to_edge():

sage: R.dart_to_vertex(1)
0
sage: 1 in R.vertices()[0]
True
sage: R.dart_to_vertex(2)
1
sage: 2 in R.vertices()[1]
True

sage: R.dart_to_edge(3)
0
sage: R.dart_to_edge(4)
2

sage: R.dart_to_face(4)
0
sage: R.dart_to_face(3)
2

To initialize a ribbon graph with angles, you have to input the standard data to initialize a ribbon graph plus a list of positive rational numbers which corresponds to the angles between darts (more precisely, the number at position i is the angle between i and v(i)):

sage: e = '(0,1)(2,3)'
sage: f = '(0,2,1,3)'
sage: a = [1/2,1/2,1/2,1/2]
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.spin_parity()
1
class surface_dynamics.flat_surfaces.homology.RibbonGraph(vertices=None, edges=None, faces=None, check=True)[source]

Bases: sage.structure.sage_object.SageObject

Generic class for Ribbon graph.

A Ribbon graph (or fat graph or combinatorial map) is a graph embedded in a surface. This class uses representation as a triple (v,e,f) of permutations such that vef = 1 and the action of the group generated by v,e,f acts transitvely in the the domain. The cycles of v are considered as vertices, the ones of e are considered as edges and the ones of f as the faces. Each element of the domain is a half-edge which is called a dart. A dart is also associated to an oriented edge.

The domain of the permutations must be a subset of [0, ..., N-1] for some N.

A dense ribbon graph has the following attributes

  • total_darts - non negative integer - the total number darts
  • num_darts - non negative integer - the number of active darts
  • active_darts - bitset - list of lengths _total_darts with True or False. The position i is True if i is an active dart.
  • vertices, vertices_inv - list - partial permutations of [0,N] which are inverse of each other
  • vertex_cycles - the cycles of the partial permutation vertices
  • dart_to_vertex_index
  • edges, edges_inv - list - partial permutations of [0,N] which are inverse of each other
  • edge_cycles - the cycles of the partial permutation edge
  • dart_to_edge_index
  • faces, faces_inv - list - partial permutations of [0,N] which are inverse of each other
  • face_cycles - the cycles of the partial permutation faces
  • dart_to_face_index

EXAMPLES:

sage: from surface_dynamics.all import *


sage: RibbonGraph([],[],[])
Ribbon graph with 1 vertex, 0 edge and 1 face
sage: RibbonGraph('()','(0,1)','(0,1)')
Ribbon graph with 2 vertices, 1 edge and 1 face

sage: G = RibbonGraph('(0,3)(1,2)','(0,1)(2,3)','(0,2)(1,3)')
sage: G
Ribbon graph with 2 vertices, 2 edges and 2 faces
sage: G.darts()
[0, 1, 2, 3]
sage: G.genus()
0

sage: G = RibbonGraph(edges='(0,2)(1,3)(4,6)(5,7)',faces='(0,1,2,3,4,5,6,7)')
sage: G
Ribbon graph with 1 vertex, 4 edges and 1 face
sage: G.darts()
[0, 1, 2, 3, 4, 5, 6, 7]
sage: G.genus()
2

sage: G = RibbonGraph(vertices='(0,2,3,6)(1,4,5,7)')
sage: G
Ribbon graph with 2 vertices, 4 edges and 4 faces
sage: G.edges()
([0, 1], [2, 3], [4, 5], [6, 7])
sage: G.genus()
0
add_extra_darts(n)[source]

Add extra darts to the current vertex in order to support a total of n darts.

boundaries()[source]

Return the list of cycles which are boundaries.

A cycle is a boundary if it bounds a face.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: r = RibbonGraph('(1,2,3)(4,5,6)','(1,2)(3,4)(5,6)')
sage: r.boundaries()
[[(1, 2)],  [(2, 1), (3, 4), (6, 5), (4, 3)], [(5, 6)]]

sage: r = RibbonGraph('(1,2,3)(4,5)(6,7,8)',edges='(1,2)(3,4)(5,6)(7,8)')
sage: r.boundaries()
[[(1, 2)],  [(2, 1), (3, 4), (5, 6), (8, 7), (6, 5), (4, 3)], [(7, 8)]]
collapse(spanning_tree=None)[source]

Return a ribbon graph callapsed along a spanning tree.

The resulting graph is on the same surface as the preceding but has only one vertex. It could be used twice to provide a polygonal representation with one vertex and one face.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: R = RibbonGraph(vertices='(0,1,2,5)(3,7)(4,10,9)(6,11,12)(8,13)')
sage: R.genus()
1
sage: R.num_vertices()
5
sage: R.num_edges()
7
sage: R.num_faces()
2
sage: R2 = R.collapse()
sage: R2
Ribbon graph with 1 vertex, 3 edges and 2 faces
sage: R
Ribbon graph with 5 vertices, 7 edges and 2 faces
sage: R3 = R2.dual().collapse().dual()
sage: R3
Ribbon graph with 1 vertex, 2 edges and 1 face
cycle_basis(intersection=False, verbose=False)[source]

Returns a base of oriented cycles of the Ribbon graph modulo boundaries.

If intersection is set to True then the method also returns the intersection matrix of the cycles.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: r = RibbonGraph('(1,2,3)(4,5,6)','(1,2)(3,4)(5,6)')
sage: r.cycle_basis()
[]

sage: r = RibbonGraph('(1,2,3)(4,5)(6,7,8)',edges='(1,2)(3,4)(5,6)(7,8)')
sage: r.cycle_basis()
[]

sage: r = RibbonGraph('(1,4,5)(2,3)(6,7,8)',edges='(1,2)(3,4)(5,6)(7,8)')
sage: r.cycle_basis()
[]

sage: e = '(1,3)(2,4)(5,7)(6,8)'
sage: f = '(1,2,3,4,5,6,7,8)'
sage: r = RibbonGraph(edges=e,faces=f)
sage: r.cycle_basis()
[[[1, 3]], [[2, 4]], [[5, 7]], [[6, 8]]]

sage: f = '(0,10,13)(6,17,11)(2,14,7)(15,12,3)(16,20,19)(18,1,9)(4,22,21)(23,8,5)'
sage: e = tuple((i,i+1) for i in xrange(0,24,2))
sage: r = RibbonGraph(edges=e,faces=f); r
Ribbon graph with 2 vertices, 12 edges and 8 faces
sage: c,m = r.cycle_basis(intersection=True)
sage: c
[[(0, 1), [4, 5]], [[8, 9]], [[12, 13]], [[14, 15], (1, 0)]]
sage: m
[ 0  1  0  0]
[-1  0  0  0]
[ 0  0  0  1]
[ 0  0 -1  0]
dart_to_edge(i, orientation=False)[source]

Returns the edge the darts i belongs to.

If orientation is set to True then the output is a 2-tuple (e,o) where e is the index of the edge and o is its orientation as +1 or -1.

dart_to_face(i)[source]
dart_to_vertex(i)[source]

Return the vertex on which the dart i is attached.

darts()[source]

Return the list of darts

dual()[source]

Returns the dual Ribbon graph.

The dual ribbon graph of (v,e,f) is (f^{-1}, e, v^{-1}).

EXAMPLES:

sage: from surface_dynamics.all import *

sage: r = RibbonGraph(edges='(0,1)',faces='(0)(1)'); r
Ribbon graph with 1 vertex, 1 edge and 2 faces
sage: r.dual()
Ribbon graph with 2 vertices, 1 edge and 1 face
edge_orbit(i)[source]

Return the orbit of the dart i under the permutation that defines the edges.

edge_perm()[source]

Return the permutation that define the edges.

edges()[source]

Return the set of edges.

euler_characteristic()[source]

Returns the Euler characteristic of the embedded surface.

The Euler characteristic of a surface complex is V - E + F, where V is the number of vertices, E the number of edges and F the number of faces.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: r = RibbonGraph(edges='(0,1)(2,3)(4,5)',faces='(0,2,4)(3,5)')
sage: r.euler_characteristic()
2

sage: r = RibbonGraph(edges='(0,1)(2,3)',faces='(0,2,1,3)')
sage: r.euler_characteristic()
0
face_orbit(i)[source]

Return the orbit of i under the permutation associated to faces.

face_perm()[source]

Return the permutation that defines the face.

faces()[source]

Return the list of faces.

genus()[source]

Return the genus of the surface associated to this Ribbon graph.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: R = RibbonGraph(vertices='(1)(2)',edges='(1,2)')
sage: R.genus()
0

sage: e='(1,3)(2,4)'
sage: f='(1,2,3,4)'
sage: RibbonGraph(edges=e,faces=f).genus()
1

sage: e='(1,3)(2,4)(5,7)(6,8)'
sage: f='(1,2,3,4,5,6,7,8)'
sage: RibbonGraph(edges=e,faces=f).genus()
2

sage: e='(1,3)(2,4)(5,7)(6,8)(9,11)(10,12)'
sage: f='(1,2,3,4,5,6,7,8,9,10,11,12)'
sage: RibbonGraph(edges=e,faces=f).genus()
3
is_cycle(c)[source]

Test whether c is a cycle.

A path is a sequence of oriented edges such that each edge starts where the preceding one ends. A cycle is a path which starts where it ends.

is_plane()[source]

Returns true if and only if the ribbon graph belongs in a sphere. In other words if it has genus 0.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: r = RibbonGraph(vertices='(0)(1)',edges='(0,1)')
sage: r.is_plane()
True

sage: r = RibbonGraph(vertices='(0,1)',edges='(0,1)')
sage: r.is_plane()
True

sage: r = RibbonGraph(edges='(0,1)(2,3)',faces='(0,2)(1,3)')
sage: r.is_plane()
True

sage: r = RibbonGraph(edges='(0,1)(2,3)',faces='(0,2,1,3)')
sage: r.is_plane()
False
is_plane_tree()[source]

Returns True if and only if the ribbon graph is a planar tree. In other words, it has genus 0 and only one face.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: r = RibbonGraph(vertices='(0)(1)',edges='(0,1)')
sage: r.is_plane_tree()
True

sage: r = RibbonGraph(vertices='(0)(1,2,4)(3)(5)',edges='(0,1)(2,3)(4,5)')
sage: r.is_plane_tree()
True

sage: r = RibbonGraph(vertices='(0,1)',edges='(0,1)')
sage: r.is_plane_tree()
False
sage: r.is_plane()
True
is_triangulated()[source]

Returns True if the surface is triangulated. In other words, faces consist only of the product of 3-cycles.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: r = RibbonGraph(edges='(0,1)(2,3)(4,5)',faces='(0,2,4)(1,5,3)')
sage: r.is_triangulated()
True

sage: r = RibbonGraph(edges='(0,1)(2,3)',faces='(0,2,1,3)')
sage: r.is_triangulated()
False
num_darts()[source]

Returns the number of darts.

num_edges()[source]

Returns the number of edges.

num_faces()[source]

Return the number of faces.

num_vertices()[source]

Returns the number of vertices.

relabel(perm=None)[source]

perm is a of range(0,N)

If perm is None, relabel the darts on 0,2M keeping the relative order of the darts.

spanning_tree()[source]

Return a spanning tree

OUTPUT:

  • spanning tree as a DiGraph
  • remaining edges as 2-tuples (i,e[i])

EXAMPLES:

sage: from surface_dynamics.all import *

sage: R = RibbonGraph('(1,2,3)','(1,2)(3,4)')
sage: R
Ribbon graph with 2 vertices, 2 edges and 2 faces
sage: T,o = R.spanning_tree()
sage: T
Digraph on 2 vertices
sage: T.edges()
[(0, 1, (3, 4))]
sage: o
[(1, 2)]

sage: R = RibbonGraph('(1,2,3)(4,5,6)','(1,2)(3,4)(5,6)')
sage: R
Ribbon graph with 2 vertices, 3 edges and 3 faces
sage: T,o = R.spanning_tree()
sage: T
Digraph on 2 vertices
sage: T.edges()
[(0, 1, (3, 4))]
sage: o
[(1, 2), (5, 6)]

sage: e = '(1,3)(5,7)(2,4)(6,8)'
sage: f = '(1,2,3,4,5,6,7,8)'
sage: R = RibbonGraph(edges=e, faces=f)
sage: T,o = R.spanning_tree()
sage: T
Digraph on 1 vertex
sage: o
([1, 3], [2, 4], [5, 7], [6, 8])
vertex_orbit(i)[source]

Return the orbit of i under the permutation that define the vertices.

vertex_perm()[source]

Returns the permutation that define the vertices.

vertices()[source]

Return the list of vertices as cycles decomposition of the vertex permutation.

class surface_dynamics.flat_surfaces.homology.RibbonGraphWithAngles(vertices=None, edges=None, faces=None, angles=None)[source]

Bases: surface_dynamics.flat_surfaces.homology.RibbonGraph

A Ribbon graph with angles between edges

Currently angles can only be rational multiples of pi.

TODO:

  • allows any kind of angles by providing a sum for the total and considering each angle as a (projective) portion of the total angle.
angle_at_vertex(v)[source]

Angle at a vertex (coefficient of pi)

angle_at_vertices()[source]

Return the list of angles at a vertex.

angle_between_darts(d1, d2)[source]

Return the angle between the darts d1 and d2

has_trivial_holonomy()[source]

Test whether self has trivial holonomy representation

EXAMPLES:

sage: from surface_dynamics.all import *

sage: e = '(0,1)(2,3)'
sage: f = '(0,2,1,3)'
sage: a = [1/2,1/2,1/2,1/2]
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.has_trivial_holonomy()
True

sage: e = '(0,1)(2,3)(4,5)'
sage: f = '(0,2,4)(1,5,3)'
sage: a = [1/3,7/15,1/5,1/5,7/15,1/3]
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.has_trivial_holonomy()
False
holonomy_representation()[source]

Return the holonomy representation in SO(2) as two lists.

The first list correspond to cycles around vertices, while the second correspond to a cycle basis that generate homology.

EXAMPLES:

sage: from surface_dynamics.all import *

sage: e = '(0,1)(2,3)'
sage: f = '(0,2,1,3)'
sage: a = [1/2,1/2,1/2,1/2]
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.holonomy_representation()
([0], [0, 0])

The standard cube:

sage: e = tuple((i,i+1) for i in xrange(0,24,2))
sage: f = '(0,20,7,10)(16,22,19,21)(2,9,5,23)(14,3,17,1)(12,8,15,11)(18,4,13,6)'
sage: a = [1/2]*24
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.holonomy_representation()
([3/2, 3/2, 3/2, 3/2, 3/2, 3/2, 3/2, 3/2], [])

Two copies of a triangle:

sage: e = '(0,1)(2,3)(4,5)'
sage: f = '(0,2,4)(1,5,3)'
sage: a = [1/2,1/6,1/3,1/3,1/6,1/2]
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.holonomy_representation()
([1, 1/2, 1/2], [])

sage: a = [1/3,7/15,1/5,1/5,7/15,1/3]
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.holonomy_representation()
([2/3, 2/3, 2/3], [])
spin_parity(check=True, verbose=False)[source]

Return the spin parity of the Ribbon graph with angles.

The surface should be holonomy free and with odd multiple of 2 pi angles.

EXAMPLES:

sage: from surface_dynamics.all import *

We first consider the case of the torus:

sage: e = '(0,1)(2,3)'
sage: f = '(0,2,1,3)'
sage: a = [1/2,1/2,1/2,1/2]
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.spin_parity()
1

Then the case of genus 2 surface (with an angle of 6pi):

sage: e = '(0,1)(2,3)(4,5)(6,7)'
sage: f = '(0,2,4,3,6,1,7,5)'
sage: a = [1/2,1/2,1,1/2,1/2,1,3/2,1/2]
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.spin_parity()
1

sage: e = '(0,1)(2,3)(4,5)(6,7)'
sage: f = '(0,2,4,6,1,3,5,7)'
sage: a = [1/2,1/2,1,1,1,1,1/2,1/2]
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.spin_parity()
1

sage: e = '(0,1)(2,3)(4,5)(6,7)'
sage: f = '(0,2,4,6,1,3,5,7)'
sage: a = [3/4]*8
sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
sage: r.spin_parity()
1

In genus 3 two spin parities occur for one conical angle 10pi:

sage: e = '(0,1)(2,3)(4,5)(6,7)(8,9)(10,11)'
sage: f1 = '(0,4,6,8,10,2,1,9,11,5,7,3)'
sage: f2 = '(0,4,6,8,10,2,1,5,7,9,11,3)'
sage: a = [1/2,1/2,1/2,1/2] + [1]*8
sage: r1 = RibbonGraphWithAngles(edges=e,faces=f1,angles=a)
sage: r1.spin_parity()
1
sage: r2 = RibbonGraphWithAngles(edges=e,faces=f2,angles=a)
sage: r2.spin_parity()
0
winding(c)[source]

Return winding number along the cycle c.

This is NOT well defined because it depends on the way we choose to pass on the left or on the right at singularity.

class surface_dynamics.flat_surfaces.homology.RibbonGraphWithHolonomies(vertices=None, edges=None, faces=None, holonomies=None)[source]

Bases: surface_dynamics.flat_surfaces.homology.RibbonGraph

A Ribbon graph with holonomies.

For now

surface_dynamics.flat_surfaces.homology.angle(v)[source]

Return the argument of the vector v.

surface_dynamics.flat_surfaces.homology.clean_perm_data(vertices, edges, faces, check)[source]

TESTS:

sage: from surface_dynamics.flat_surfaces.homology import clean_perm_data
sage: clean_perm_data([2,1,0],[1,2,0],None, check=True)
([2, 1, 0], [1, 2, 0], [0, 2, 1])
sage: clean_perm_data([1,0,2],[2,1,0],None, check=True)
([1, None, 2], [2, None, 0], [2, None, 1])