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 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 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 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 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 j-th separatrix.

EXAMPLES:

sage: from surface_dynamics 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 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 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 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]

Return the cylinders as a list of pairs (bot, top).

EXAMPLES:

sage: from surface_dynamics 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 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 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 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]

Test the connectedness of this cylinder diagram.

TESTS:

sage: from surface_dynamics 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 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_cone()[source]

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

-> one can obtain ehrhart 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)!

lengths_minimal_solution()[source]

Return an integral solution for the lengths equation with minimal sum

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 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 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 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 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 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 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 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 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
tikz_string(cyl_height=0.8, cyl_sep=1.3, cyl_pos=None, sep_labels=None)[source]

INPUT:

  • cyl_height - (optional) height of cylinders
  • cyl_sep - (optional) vertical space between cylinders
  • cyl_pos - (optional) position of left corners of cylinders. If provided, cyl_sep is ignored.
  • sep_labels - labels of the separatrices
to_directed_graph()[source]

Return a labeled directed graph that encodes the cylinder diagram.

EXAMPLES:

sage: from surface_dynamics 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 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 j-th separatrix.

EXAMPLES:

sage: from surface_dynamics 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 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
volume_contribution()[source]

Return the volume contribution as a generalized multiple zeta values.

EXAMPLES:

sage: from surface_dynamics import *

sage: c0, c1 = AbelianStratum(2).cylinder_diagrams()
sage: v0 = c0.volume_contribution()   # optional - latte_int
sage: v0                              # optional - latte_int
[(1/3, Z((n1)^4))]
sage: v1 = c1.volume_contribution()   # optional - latte_int
sage: v1                              # optional - latte_int
[(1/3, Z((n1)^2, (n1 + n2)^2)), (2/3, Z((n1), (n1 + n2)^3))]
sage: v0 + v1                         # optional - latte_int
[(1/3, Z((n1)^4)), (1/3, Z((n1)^2, (n1 + n2)^2)), (2/3, Z((n1), (n1 + n2)^3))]

sage: for c in AbelianStratum(1,1).cylinder_diagrams():  # optional - latte_int
....:     print(c.volume_contribution())
(1/6 * Sum( 1 / ((n0)^5), n0=1..+oo)
1/3 * Sum( 1 / ((n0)^2*(n0 + n1)^3), n0=1..+oo, n1=1..+oo) + 1/3 * Sum( 1 / ((n0)^3*(n0 + n1)^2), n0=1..+oo, n1=1..+oo)
1/12 * Sum( 1 / ((n1)^2*(n0)^2*(n0 + n1)), n0=1..+oo, n1=1..+oo) + 1/6 * Sum( 1 / ((n1)*(n0)*(n0 + n1)^3), n0=1..+oo, n1=1..+oo) + 1/12 * Sum( 1 / ((n1)^2*(n0)*(n0 + n1)^2), n0=1..+oo, n1=1..+oo) + 1/12 * Sum( 1 / ((n1)*(n0)^2*(n0 + n1)^2), n0=1..+oo, n1=1..+oo)
1/6 * Sum( 1 / ((n0 + n2)^2*(n0 + n1)^3), n0=1..+oo, n1=1..+oo, n2=1..+oo) + 1/6 * Sum( 1 / ((n0 + n2)^3*(n0 + n1)^2), n0=1..+oo, n1=1..+oo, n2=1..+oo)
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 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)
widths_generating_series(var='w')[source]

Generating series of the number of lengths solutions given the widths.

WARNING: when a triangulation is involved, the generating series ignore some lower dimensional polytopes!!

INPUT:

  • var - (optional) an optional name for the variable

EXAMPLES:

sage: from surface_dynamics import CylinderDiagram

sage: c = CylinderDiagram('(0,1)-(0,2) (2)-(1)')
sage: c.widths_generating_series()   # optional - latte_int
(1)/((1 - w0)*(1 - w0*w1))

sage: c = CylinderDiagram('(0)-(2) (1,2,3)-(4,5) (4)-(3) (5)-(0,1)')
sage: c.widths_generating_series()   # optional - latte_int
(1)/((1 - w1*w3)*(1 - w1*w2)*(1 - w0*w1*w3))

sage: c = CylinderDiagram('(0,1,3)-(0,2,5) (2,4)-(1,3) (5)-(4)')
sage: c.widths_generating_series()   # optional - latte_int
(1)/((1 - w0)*(1 - w0*w1)^2*(1 - w0*w1*w2)) + (1)/((1 - w0)*(1 - w0*w1)*(1 - w0*w1*w2)^2)
class surface_dynamics.flat_surfaces.separatrix_diagram.QuadraticCylinderDiagram(arg1, arg2=None)[source]

Bases: sage.structure.sage_object.SageObject

Cylinder diagram for quadratic differentials

Cylinder diagrams are encoded as a Ribbon graph together with a pairing of faces (in particular the number of faces must be even).

EXAMPLES:

sage: from surface_dynamics import *

If you start with strings, the cylinders are preserved but the names of saddle connections are changed:

sage: QuadraticCylinderDiagram('(4,4,5)-(6,6,1) (2,3,2,0)-(1,0,5,3)')
(0,0,1)-(2,2,3) (4,5,4,6)-(3,6,1,5)
cylcoord_to_pillowcase_cover(lengths, heights, twists=None, verbose=False)[source]

Convert coordinates of the cylinders into a pillowcase cover.

The pillow is considered as made of two 1 x 1 squares in order to avoid denominators in the input lengths, heights and twists.

INPUT:

  • lengths - positive integers - lengths of the separatrices
  • heights - positive integers - heights of the cylinders
  • twists - (optional) non-negative integers - twists. The twist is measured as the difference in horizontal coordinates between the smallest element in bottom and the smallest in element in top.

OUTPUT: a pillowcase cover

EXAMPLES:

sage: from surface_dynamics import *

Some pillows in Q(-1^4):

sage: q = QuadraticCylinderDiagram('(0,0)-(1,1)')
sage: q.cylcoord_to_pillowcase_cover([1,1],[1],[0])
g0 = (1)
g1 = (1)
g2 = (1)
g3 = (1)
sage: q.cylcoord_to_pillowcase_cover([1,1],[1],[1])
g0 = (1)
g1 = (1)
g2 = (1)
g3 = (1)
sage: q.cylcoord_to_pillowcase_cover([2,2],[1],[0])
g0 = (1)(2)
g1 = (1,2)
g2 = (1,2)
g3 = (1)(2)
sage: q.cylcoord_to_pillowcase_cover([2,2],[1],[1])
g0 = (1)(2)
g1 = (1,2)
g2 = (1)(2)
g3 = (1,2)
sage: q.cylcoord_to_pillowcase_cover([2,2],[1],[2]) == q.cylcoord_to_pillowcase_cover([2,2],[1],[2])
True
sage: q.cylcoord_to_pillowcase_cover([3,3],[1],[0])
g0 = (1)(2,3)
g1 = (1,3)(2)
g2 = (1,3)(2)
g3 = (1)(2,3)
sage: q.cylcoord_to_pillowcase_cover([3,3],[1],[1])
g0 = (1)(2,3)
g1 = (1,3)(2)
g2 = (1)(2,3)
g3 = (1,2)(3)

sage: q.cylcoord_to_pillowcase_cover([1,1],[2],[0])
g0 = (1)(2)
g1 = (1)(2)
g2 = (1,2)
g3 = (1,2)
sage: q.cylcoord_to_pillowcase_cover([1,1],[2],[1]) == q.cylcoord_to_pillowcase_cover([1,1],[2],[0])
True

sage: q.cylcoord_to_pillowcase_cover([2,2],[2],[1])
g0 = (1)(2)(3,4)
g1 = (1,2)(3)(4)
g2 = (1,3)(2,4)
g3 = (1,4)(2,3)

Two one cylinder examples in Q(2^2):

sage: q1 = QuadraticCylinderDiagram('(0,1,0,1)-(2,3,2,3)')
sage: q2 = QuadraticCylinderDiagram('(0,1,0,2)-(3,1,3,2)')

sage: q1.cylcoord_to_pillowcase_cover([2,2,2,2],[1],[0])
g0 = (1,2,3,4)
g1 = (1,3)(2,4)
g2 = (1,3)(2,4)
g3 = (1,4,3,2)
sage: q1.cylcoord_to_pillowcase_cover([2,2,2,2],[1],[1])
g0 = (1,2,3,4)
g1 = (1,3)(2,4)
g2 = (1,4,3,2)
g3 = (1,3)(2,4)
sage: p = q1.cylcoord_to_pillowcase_cover([2,6,4,4],[3],[1])
sage: p.stratum()
Q_2(2^2)
sage: p.nb_pillows()
24

One two cylinders example in Q(2^2):

sage: q = QuadraticCylinderDiagram('(0,1)-(2,3) (0,3)-(1,2)')
sage: p = q.cylcoord_to_pillowcase_cover([1,1,1,1], [2,2], [0,1])
sage: p
g0 = (1,4,2,3)
g1 = (1,3,2,4)
g2 = (1,2)(3,4)
g3 = (1,2)(3,4)
sage: p.stratum()
Q_2(2^2)
sage: q.cylcoord_to_pillowcase_cover([1,3,1,3], [2,2], [0,1]).stratum()
Q_2(2^2)

TESTS:

sage: from surface_dynamics import *
sage: q = QuadraticCylinderDiagram('(0,0)-(1,1)')
sage: q.cylcoord_to_pillowcase_cover([1,2],[1])
Traceback (most recent call last):
...
ValueError: sum of lengths on top and bottom differ
cylinders(dart=False)[source]

Cylinders of self

Return a list of pairs (bot, top) where, by convention the bottom corresponds to the face with smaller index in the list of faces.

EXAMPLES:

sage: from surface_dynamics import *
sage: from surface_dynamics.flat_surfaces.separatrix_diagram import QuadraticCylinderDiagram
sage: rg = RibbonGraph(edges='(0,1)(2,3)(4,5)(6,7)', faces='(0,1)(2,4,5)(3)(6,7)', connected=False)
sage: q = QuadraticCylinderDiagram(rg, '(0,1)(2,3)')
sage: q.cylinders()
[((0, 0), (1, 2, 2)), ((1,), (3, 3))]
sage: q.cylinders(True)
[((0, 1), (2, 4, 5)), ((3,), (6, 7))]
edges()[source]

The set of edges.

lengths_cone()[source]

Return the polytope of admissible lengths.

EXAMPLES:

sage: from surface_dynamics import *

sage: rg = RibbonGraph(edges='(0,1)(2,3)(4,5)(6,7)', faces='(0,1)(2,4,5)(3)(6,7)', connected=False)
sage: q = QuadraticCylinderDiagram(rg, '(0,1)(2,3)')
sage: q.stratum()
Q_0(1, -1^5)
sage: q.lengths_cone().rays_list()
[[1, 0, 1, 0], [1, 2, 0, 1]]

sage: rg = RibbonGraph(edges='(0,1)(2,3)(4,5)(6,7)', faces='(0,2,3)(1)(4,6,7)(5)', connected=False)
sage: q = QuadraticCylinderDiagram(rg, '(0,2)(1,3)')

sage: rg = RibbonGraph(edges='(1,2)(3,4)(5,6)(7,8)(9,10)(11,12)', faces='(1,2)(3,4,6)(5)(8)(7,9,10)(11,12)', connected=False)
sage: q = QuadraticCylinderDiagram(rg, '(0,1)(2,4)(3,5)')
sage: q.stratum()
Q_0(1^2, -1^6)
sage: L = q.lengths_cone()
sage: L
A 3-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex and 3 rays
ncyls()

Number of cylinders.

nseps()

Number of edges

num_cylinders()[source]

Number of cylinders.

num_darts()[source]

Number of darts

num_edges()[source]

Number of edges

stratum()[source]

Return the stratum of quadratic differentials associated to this cylinder diagram

EXAMPLES:

sage: from surface_dynamics import *

sage: QuadraticCylinderDiagram('(0,0)-(1,1)').stratum()
Q_0(-1^4)

sage: QuadraticCylinderDiagram('(0,0)-(1,1,2,2,3,3)').stratum()
Q_0(1, -1^5)

sage: QuadraticCylinderDiagram('(0,2,3,2)-(1,0,1,3)').stratum()
Q_2(2^2)

sage: QuadraticCylinderDiagram('(0,1)-(2,3) (0)-(4,4) (1)-(5,5) (2)-(6,6) (3)-(7,7)').stratum()
Q_0(2^2, -1^8)
widths_generating_series(var='w')[source]

Generating series of the number of saddle connection lengths.

Warning

When a triangulation is involved, the generating series ignore some lower dimensional polytopes that are counted twice!

EXAMPLES:

sage: from surface_dynamics import *

sage: q = QuadraticCylinderDiagram('(0,1,2,3,3)-(0,4,4,2,1)')
sage: q.widths_generating_series()                             # optional - latte_int
(1)/((1 - w)^3*(1 - w^2))

sage: q = QuadraticCylinderDiagram('(0,0,1,1,2,2)-(3,3,4,4)')
sage: q.widths_generating_series()                             # optional - latte_int
(3)/((1 - w^2)^4)

sage: q = QuadraticCylinderDiagram('(0,0,1)-(2,2,3) (1,4)-(3,4)')
sage: q.widths_generating_series()                             # optional - latte_int
(1)/((1 - w1)*(1 - w0*w1)*(1 - w0^2))

sage: q = QuadraticCylinderDiagram('(0,0,1,2,3)-(1,4,4,5,6) (2,5,7,7,8)-(3,6,8,9,9)')
sage: q.widths_generating_series()                             # optional - latte_int

sage: q = QuadraticCylinderDiagram('(0,0,1,2,3)-(1,4,4,5,6) (2,5,7,8,8)-(3,6,7,9,9)')
sage: q.widths_generating_series()                             # optional - latte_int
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 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 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 generated by [(1,2,3,4,5), (1,4,2,5,3)] of (Symmetric group of order 5! as a permutation group)
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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()
[7, 0, 8, 2, 5, 4, 3, 1, 6]

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

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

EXAMPLES:

sage: from surface_dynamics 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 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 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 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 import *

sage: S = SeparatrixDiagram('(0,1)(2,3)','(1,3,2)(0)')
sage: S.degree()
4
num_edges()

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

EXAMPLES:

sage: from surface_dynamics 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 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()
[6, 2, 1, 5, 0, 8, 7, 4, 3]

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

Return the angles around each vertex

EXAMPLES:

sage: from surface_dynamics 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 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 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 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 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 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 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 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 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 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 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 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)]
vertices()[source]

Return a list of pairs (vout, vin) where each pair represent a vertex and vout, vin are respectively the labels of outgoing/incoming separatrices at this vertex.

EXAMPLES:

sage: from surface_dynamics import CylinderDiagram
sage: from surface_dynamics.misc.permutation import perm_cycles

sage: c = CylinderDiagram('(0,1)-(0,2,6,7,4,5,3,8) (2,7,3,4,8,6,5)-(1)')
sage: c.vertices()
[([0, 1, 8, 7], [0, 4, 2, 1]), ([2, 4, 5], [3, 6, 5]), ([3, 6], [7, 8])]
sage: perm_cycles(c.outgoing_edges_perm())
[[0, 1, 8, 7], [2, 4, 5], [3, 6]]
sage: perm_cycles(c.incoming_edges_perm())
[[0, 4, 2, 1], [3, 6, 5], [7, 8]]

sage: c = CylinderDiagram('(0,1,3,4,2)-(0,1,5,7,6) (5,6)-(4) (7)-(2,3)')
sage: perm_cycles(c.outgoing_edges_perm())
[[0, 3], [1, 6], [2, 4], [5, 7]]
sage: perm_cycles(c.incoming_edges_perm())
[[0, 5], [1, 2], [3, 4], [6, 7]]
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 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.move_backward(i, v, g01, g23)[source]

Helper function to build pillowcase covers

EXAMPLES:

sage: from surface_dynamics.flat_surfaces.separatrix_diagram import move_backward
sage: g23 = [1,2,0]
sage: g01 = [2,0,1]
sage: i,v = 0,0
sage: for _ in range(6):
....:     i,v = move_backward(i, v, g01, g23)
....:     print("%d %d" % (i,v))
2 1
2 0
1 1
1 0
0 1
0 0

sage: i,v = 0,2
sage: for _ in range(6):
....:     i,v = move_backward(i, v, g01, g23)
....:     print("%d %d" % (i,v))
1 3
1 2
2 3
2 2
0 3
0 2
surface_dynamics.flat_surfaces.separatrix_diagram.move_forward(i, v, g01, g23)[source]

Helper function to build pillowcase covers

EXAMPLES:

sage: from surface_dynamics.flat_surfaces.separatrix_diagram import move_forward
sage: g23 = [1,2,0]
sage: g01 = [2,0,1]
sage: i,v = 0,0
sage: for _ in range(6):
....:     i,v = move_forward(i, v, g01, g23)
....:     print("%d %d" % (i,v))
0 1
1 0
1 1
2 0
2 1
0 0

sage: i,v = 0,2
sage: for _ in range(6):
....:     i,v = move_forward(i, v, g01, g23)
....:     print("%d %d" % (i,v))
0 3
2 2
2 3
1 2
1 3
0 2
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 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 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 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.simplex_count(rays)[source]
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 so that e(i) is the other half of the edge starting at i. The fixed points of e corresponds to edge glued to themselves. 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 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

TODO:

  • Oriented ribbon graphs are exactly separatrix diagrams. An orientation on a Ribbon graphs is a choice of orientation for each vertex so that each face has all of its edges oriented in the same direction as we go along the boundaries.
class surface_dynamics.flat_surfaces.homology.RibbonGraph(vertices=None, edges=None, faces=None, connected=True, 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. The edges are always considered to be (i, ~i) where i is the bit complement of the integer i (~0 = -1, ~-3 = 2). So that an edge always has a canonical representative number given by the non-negative version.

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 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 support a total of 2 n darts.

automorphism_group(fix_vertices=False, fix_edges=False, fix_faces=False)[source]

Return the automorphism group of this ribbon graph.

The automorphism is simply the intersection of the centralizers of the defining permutations.

EXAMPLES:

sage: from surface_dynamics import *
sage: r = RibbonGraph('(1,6,5)(2,3,4)', '(1,2)(3,4)(5,6)', '(1,4,2,5)(3)(6)')
sage: r.automorphism_group().cardinality()
2
sage: r.automorphism_group(fix_faces=True).cardinality()
1

sage: r = RibbonGraph('(1,4,5)(2,6,3)', '(1,2)(3,4)(5,6)', '(1,3)(2,5)(4,6)')
sage: r.automorphism_group().cardinality()
6

Examples in genus 1:

sage: r = RibbonGraph('(1,5,4)(6,3,2)', '(1,2)(3,4)(5,6)', '(1,3,5,2,4,6)')
sage: A = r.automorphism_group()
sage: A
Subgroup generated by [(1,2)(3,4)(5,6), (1,3,5,2,4,6), (1,5,4)(2,6,3)] of
(Symmetric group of order 6! as a permutation group)
sage: A.cardinality()
6
sage: r.automorphism_group(fix_faces=True) == A
True

sage: r = RibbonGraph('(1,3,2,4)', '(1,2)(3,4)', '(1,3,2,4)')
sage: r.automorphism_group().cardinality()
4
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 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 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 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 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 import *

sage: r = RibbonGraph(edges='(0,1)(2,3)(4,5)',faces='(0,2,4)(1)(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 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_connected(force_computation=False)[source]
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 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 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 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
length_rational_fraction(var='b')[source]

Return the generating series for the number of lengths with the given boundaries

monodromy_group()[source]

Return the group generated by the three defining permutations.

EXAMPLES:

sage: from surface_dynamics import RibbonGraph

sage: r1 = RibbonGraph(vertices='(0)(2)(1,3,4,5)', edges='(0,1)(2,3)(4,5)')
sage: G1 = r1.monodromy_group()
sage: G1
Subgroup generated by [(1,3,4,5), (0,1)(2,3)(4,5), (0,5,3,2,1)] of
(Symmetric group of order 6! as a permutation group)
sage: G1.is_isomorphic(SymmetricGroup(5))
True

sage: r2 = RibbonGraph(vertices='(0)(2)(1,3,4)(5,6,7)', edges='(0,1)(2,3)(4,5)(6,7)')
sage: G2 = r2.monodromy_group()
sage: G2
Subgroup generated by [(1,3,4)(5,6,7), (0,1)(2,3)(4,5)(6,7), (0,4,7,5,3,2,1)] of
(Symmetric group of order 8! as a permutation group)
sage: G2.is_isomorphic(PSL(2,7))
True

sage: r3 = RibbonGraph(vertices='(1)(5)', edges='(1,5)', faces='(1,5)')
sage: r3.monodromy_group()
Subgroup generated by [(), (1,5)] of
(Symmetric group of order 2! as a permutation group)
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 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 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 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 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.