Figures

This file contains the code we used to generate many of the figures in the paper Platonic solids and high genus covers of lattice surfaces.

Updated to work with Sage 9.0 (and prior versions) on Apr. 27, 2020.

Run the file code_from_the_article.ipynb:

In [1]:
run code_from_the_article.ipynb
[(0, 45, 13, 17, 9), (1, 49, 14, 16, 8), (2, 48, 10, 15, 7), (3, 47, 11, 19, 6), (4, 46, 12, 18, 5), (20, 21, 22, 23, 24), (25, 30, 38, 56, 53), (26, 34, 39, 57, 52), (27, 33, 35, 58, 51), (28, 32, 36, 59, 50), (29, 31, 37, 55, 54), (40, 44, 43, 42, 41)]
[(0, 4, 3, 2, 1), (5, 33, 36, 48, 40), (6, 32, 37, 49, 44), (7, 31, 38, 45, 43), (8, 30, 39, 46, 42), (9, 34, 35, 47, 41), (10, 19, 27, 23, 55), (11, 18, 26, 24, 59), (12, 17, 25, 20, 58), (13, 16, 29, 21, 57), (14, 15, 28, 22, 56), (50, 51, 52, 53, 54)]
Proof for the Octahedron:
Number of Cusps = 2
Slope 1 yields cylinders given by a permutation that is a product of 2-cycles:
Vertical yields cylinders given by a permutation that is a product of 3-cycles.
Both directions are distinct periodic directions.  No zero appears twice in any square.
Proof for the cube:
Number of Cusps = 3
Vertical yields cylinders given by a permutation that is a product of 4-cycles.
Slope 1 yields cylinders given by a permutation that is a product of 3-cycles.
Slope 2 yields cylinders given by a permutation that is a product of 2-cycles.
All 3 directions are distinct periodic directions.  No zero appears on both the top and bottom of any cylinder.
Proof for the Icosahedron:
Number of Cusps = 3
Vertical yields cylinders given by a permutation that is a product of 5-cycles.
Slope 1/3 yields cylinders given by a permutation that is a product of 3-cycles.
Slope 1/2 yields cylinders given by a permutation that is a product of 2-cycles.
All 3 directions are distinct periodic directions.  No zero appears on both the top and bottom of any cylinder.
Octahedron
4
[Infinity, 1]
[3, 1]
0
1
0


Cube
9
[Infinity, 2, 3]
[4, 2, 3]
1
0
0


Icosahedron
10
[Infinity, 1/2, 1]
[5, 2, 3]
0
1
0


There are 0 trajectories renormalized to short saddle connections.
There are 31 trajectories renormalized to long saddle connections:
['RRRTT', 'RTTTRT', 'RTRTRTT', 'RRRTTRRT', 'RRTTRTRT', 'RRTRTTTT', 'RRTTRRTRT', 'RRTRTRRTT', 'RRTRRTTTT', 'RTRRTTTTT', 'RRTTRTRRRT', 'RTRRTTRRRT', 'RTRRTRTRRT', 'RRTRTTRTRT', 'RRRTTRTTRT', 'RRRTTTTRTT', 'RTRTRTRTRRT', 'RTTRTRTRTRT', 'RTRTRTRTTRT', 'RRTTTRTTRTT', 'RRTTRTTTRRRT', 'RTRTTRTRTRRT', 'RRTTTRTRRTRT', 'RTTRTRRRTTRT', 'RRRTTRTRTTRT', 'RRTRRTTTRRTT', 'RTTTTTTRTRTT', 'RTTRRTRTTTRRT', 'RTRTTRRTRRTRT', 'RRTTTRTTRTRTT', 'RTTRRTTTRRTRRT']
Polygon: (0, 0), (2, 0), (-s^2 + 4, -s^3 + 3*s), (1, -s^3 + 4*s), (s^2 - 2, -s^3 + 3*s)
 Coset Rep Exact Vector Length of Trajectory Vector of Trajectory
1 & $RRRTT$ & $ \left(-3 s^{2} + 12,\,-5 s^{3} + 19 s\right) $ & 16.2386 & (7.85410, 14.2128) \\
2 & $RRRTTRRT$ & $ \left(-21 s^{2} + 76,\,-5 s^{3} + 19 s\right) $ & 49.0816 & (46.9787, 14.2128) \\
3 & $RTTTRT$ & $ \left(-19 s^{2} + 72,\,-7 s^{3} + 25 s\right) $ & 49.1630 & (45.7426, 18.0171) \\
4 & $RRTRTTTT$ & $ \left(-16 s^{2} + 59,\,-33 s^{3} + 120 s\right) $ & 94.9181 & (36.8885, 87.4567) \\
5 & $RTRRTTRRRT$ & $ \left(-29 s^{2} + 106,\,-27 s^{3} + 99 s\right) $ & 98.0031 & (65.9230, 72.5173) \\
6 & $RRTTRTRRRT$ & $ \left(-30 s^{2} + 109,\,-27 s^{3} + 98 s\right) $ & 98.2417 & (67.5410, 71.3418) \\
7 & $RRTRRTTTT$ & $ \left(12 s^{2} - 41,\,-41 s^{3} + 148 s\right) $ & 110.117 & (-24.4164, 107.376) \\
8 & $RTRRTTTTT$ & $ \left(-18 s^{2} + 67,\,-39 s^{3} + 142 s\right) $ & 111.810 & (42.1246, 103.572) \\
9 & $RTRTRTT$ & $ \left(-23 s^{2} + 84,\,-39 s^{3} + 141 s\right) $ & 114.941 & (52.2148, 102.396) \\
10 & $RRTTRTRT$ & $ \left(3 s^{2} - 10,\,-55 s^{3} + 199 s\right) $ & 144.704 & (-5.85410, 144.586) \\
11 & $RRRTTTTRTT$ & $ \left(13 s^{2} - 42,\,-55 s^{3} + 199 s\right) $ & 146.570 & (-24.0344, 144.586) \\
12 & $RRTTRRTRT$ & $ \left(-68 s^{2} + 247,\,-21 s^{3} + 76 s\right) $ & 162.687 & (153.026, 55.2268) \\
13 & $RRTRTRRTT$ & $ \left(-66 s^{2} + 239,\,-27 s^{3} + 98 s\right) $ & 164.109 & (147.790, 71.3418) \\
14 & $RTRRTRTRRT$ & $ \left(-57 s^{2} + 208,\,-63 s^{3} + 229 s\right) $ & 211.047 & (129.228, 166.856) \\
15 & $RRRTTRTTRT$ & $ \left(-87 s^{2} + 318,\,-29 s^{3} + 105 s\right) $ & 211.985 & (197.769, 76.3215) \\
16 & $RRTTRTTTRRRT$ & $ \left(-82 s^{2} + 297,\,-79 s^{3} + 286 s\right) $ & 277.395 & (183.679, 207.870) \\
17 & $RRTRTTRTRT$ & $ \left(-168 s^{2} + 609,\,-53 s^{3} + 192 s\right) $ & 401.859 & (376.830, 139.606) \\
18 & $RTTRTRRRTTRT$ & $ \left(-103 s^{2} + 374,\,-135 s^{3} + 487 s\right) $ & 422.378 & (231.658, 353.182) \\
19 & $RTTTTTTRTRTT$ & $ \left(-88 s^{2} + 319,\,-149 s^{3} + 536 s\right) $ & 435.359 & (197.387, 388.041) \\
20 & $RRTRRTTTRRTT$ & $ \left(-98 s^{2} + 356,\,-158 s^{3} + 572 s\right) $ & 470.627 & (220.567, 415.740) \\
21 & $RRTTTRTTRTT$ & $ \left(40 s^{2} - 145,\,-209 s^{3} + 756 s\right) $ & 556.471 & (-89.7214, 549.190) \\
22 & $RTRTRTRTRRT$ & $ \left(-155 s^{2} + 562,\,-171 s^{3} + 619 s\right) $ & 568.635 & (347.795, 449.872) \\
23 & $RRRTTRTRTTRT$ & $ \left(-150 s^{2} + 544,\,-198 s^{3} + 716 s\right) $ & 619.524 & (336.705, 520.038) \\
24 & $RRTTTRTRRTRT$ & $ \left(-158 s^{2} + 572,\,-198 s^{3} + 716 s\right) $ & 628.894 & (353.649, 520.038) \\
25 & $RTTRTRTRTRT$ & $ \left(-362 s^{2} + 1312,\,-114 s^{3} + 412 s\right) $ & 865.091 & (811.728, 299.131) \\
26 & $RTTRRTRTTTRRT$ & $ \left(-241 s^{2} + 876,\,-277 s^{3} + 1003 s\right) $ & 909.040 & (542.946, 729.083) \\
27 & $RTRTRTRTTRT$ & $ \left(-380 s^{2} + 1377,\,-125 s^{3} + 452 s\right) $ & 912.920 & (851.853, 328.283) \\
28 & $RTRTTRTRTRRT$ & $ \left(-253 s^{2} + 916,\,-279 s^{3} + 1009 s\right) $ & 926.224 & (566.363, 732.888) \\
29 & $RTTRRTTTRRTRRT$ & $ \left(-15 s^{2} + 56,\,-375 s^{3} + 1357 s\right) $ & 986.655 & (35.2705, 986.025) \\
30 & $RTRTTRRTRRTRT$ & $ \left(15 s^{2} - 52,\,-395 s^{3} + 1429 s\right) $ & 1038.64 & (-31.2705, 1038.17) \\
31 & $RRTTTRTTRTRTT$ & $ \left(-650 s^{2} + 2352,\,-282 s^{3} + 1020 s\right) $ & 1631.66 & (1453.72, 740.945) \\
Found 0 mistakes in the formula.
 Inverse of Coset Rep Exact Vector Length of Trajectory Vector of Trajectory
10 & $\big(T^2(T^2R)^2\big)^{-1}$ & $ \left(-52 s^{2} + 183,\,-3 s^{3} + 12 s\right) $ & 111.521 & (111.138, 9.23305) \\
16 & $\big(TR^3T^3(TR^3)^2TR\big)^{-1}$ & $ \left(-62 s^{2} + 227,\,-89 s^{3} + 326 s\right) $ & 277.350 & (141.318, 238.647) \\
17 & $\big(T^2(T^2R)^2R^2\big)^{-1}$ & $ \left(-48 s^{2} + 174,\,-34 s^{3} + 126 s\right) $ & 142.196 & (107.666, 92.8855) \\
18 & $\big(T^2RT(RTR^2)^2\big)^{-1}$ & $ \left(-88 s^{2} + 319,\,-75 s^{3} + 272 s\right) $ & 279.518 & (197.387, 197.910) \\
19 & $\big(T^2RT(TR^3)^3\big)^{-1}$ & $ \left(-64 s^{2} + 234,\,-54 s^{3} + 198 s\right) $ & 205.478 & (145.554, 145.035) \\
20 & $\big(T(TR)^2R(RT)^2R^2\big)^{-1}$ & $ \left(-94 s^{2} + 341,\,-81 s^{3} + 294 s\right) $ & 300.613 & (211.095, 214.025) \\
21 & $\big(T^2RT^3R^3TR\big)^{-1}$ & $ \left(-109 s^{2} + 390,\,-13 s^{3} + 49 s\right) $ & 242.130 & (239.366, 36.4832) \\
22 & $\big(TRT^3(TR)^2R^2\big)^{-1}$ & $ \left(-93 s^{2} + 334,\,-19 s^{3} + 71 s\right) $ & 212.102 & (205.477, 52.5981) \\
23 & $\big(T(R^2TR)^4R^2T^2R^2\big)^{-1}$ & $ \left(-66 s^{2} + 239,\,-89 s^{3} + 322 s\right) $ & 276.716 & (147.790, 233.944) \\
24 & $\big((TR)^2RT^2R^3TR\big)^{-1}$ & $ \left(-83 s^{2} + 302,\,-91 s^{3} + 331 s\right) $ & 305.441 & (187.297, 241.275) \\
25 & $\big(T^2(T^2R^3)^2TR^2\big)^{-1}$ & $ \left(-159 s^{2} + 576,\,-13 s^{3} + 47 s\right) $ & 357.899 & (356.267, 34.1320) \\
26 & $\big(T^6R^3T^2R^2\big)^{-1}$ & $ \left(-116 s^{2} + 418,\,-6 s^{3} + 22 s\right) $ & 258.195 & (257.692, 16.1150) \\
27 & $\big(T^2(T^2R)^2TR^3\big)^{-1}$ & $ \left(-165 s^{2} + 592,\,-11 s^{3} + 41 s\right) $ & 365.237 & (363.976, 30.3278) \\
28 & $\big(TRT^2(T^2R)^2R^2\big)^{-1}$ & $ \left(-164 s^{2} + 590,\,-34 s^{3} + 126 s\right) $ & 375.042 & (363.358, 92.8855) \\
29 & $\big(T^3(TR)^2R^2T^2R^3\big)^{-1}$ & $ \left(-117 s^{2} + 424,\,-85 s^{3} + 311 s\right) $ & 347.229 & (262.310, 227.512) \\
30 & $\big(T^2R^2(TR^3T)^2R^3\big)^{-1}$ & $ \left(-143 s^{2} + 522,\,-23 s^{3} + 83 s\right) $ & 329.919 & (324.379, 60.2066) \\
31 & $\big(T(TR^3TR)^2RTR^2\big)^{-1}$ & $ \left(-272 s^{2} + 984,\,-48 s^{3} + 174 s\right) $ & 621.137 & (608.105, 126.569) \\
Found 0 mistakes in the formula.
In [2]:
from flatsurf import *

Polygons

Define ${\mathbb Q}(\sqrt{3})$ and the equilateral triangle.

In [3]:
x = var("x")
Q_adjoin_sqrt3.<sqrt3> = NumberField(x^2-3, embedding=AA(sqrt(3)))
triangle_top = 2*polygons.regular_ngon(3, field=Q_adjoin_sqrt3)
triangle_bottom = -1*triangle_top
show(triangle_top.plot())
show(triangle_bottom.plot())

Define the square:

In [4]:
square = polygons.square()
show(square.plot())

Define the field $F={\mathbb Q}(s)$ where $s=2 \sin(\frac{\pi}{5})$.

In [5]:
s_AA = AA(2*sin(pi/5))
F.<s> = NumberField(s_AA.minpoly(), embedding=s_AA)
In [6]:
pentagon_top = 2 * polygons.regular_ngon(5, field=F)
pentagon_bottom = (-1) * pentagon_top
show(pentagon_top.plot())
show(pentagon_bottom.plot())

Octahedron

In [7]:
octahedron_surface = Surface_dict(base_ring=Q_adjoin_sqrt3)
In [8]:
for label in range(8):
    if label%2 == 0:
        octahedron_surface.add_polygon(triangle_top, label=label)
    else:
        octahedron_surface.add_polygon(triangle_bottom, label=label)
In [9]:
octahedron_surface.change_base_label(0)
In [10]:
for label in range(8):
    gluings = build_adj_oct(0,label)
    for edge in range(3):
        octahedron_surface.change_edge_gluing(label, edge, gluings[edge][1], (edge - gluings[edge][0]) % 3)
In [11]:
octahedron = ConeSurface(octahedron_surface)
In [12]:
gs = octahedron.graphical_surface()
In [13]:
gs.make_adjacent(0,1)
gs.make_adjacent(3,2)
gs.make_adjacent(2,0)
gs.make_adjacent(2,1)
gs.make_adjacent(5,0)
gs.make_adjacent(5,2)
gs.make_adjacent(4,1)
In [14]:
def apply_display_options(graphical_surface):
    '''
    Modify the graphical surface to make it display a prettier graphic.
    '''
    if 'color' in graphical_surface.polygon_options:
        del graphical_surface.polygon_options['color']
    graphical_surface.polygon_options['rgbcolor']="#E0EEFF"
    graphical_surface.will_plot_edge_labels = False
    graphical_surface.non_adjacent_edge_options['thickness']=0.25
    graphical_surface.polygon_label_options['fontsize']=20
In [15]:
apply_display_options(gs)
In [16]:
octahedron.plot()
Out[16]:
In [17]:
octahedron.plot().save_image("octahedron_net.pdf")

Icosahedron

In [18]:
icosahedron_surface = Surface_dict(base_ring=Q_adjoin_sqrt3)
In [19]:
for label in range(20):
    if label%2 == 0:
        icosahedron_surface.add_polygon(triangle_top, label=label)
    else:
        icosahedron_surface.add_polygon(triangle_bottom, label=label)
In [20]:
icosahedron_surface.change_base_label(0)
In [21]:
for label in range(20):
    gluings = build_adj_icosa(0,label)
    for edge in range(3):
        icosahedron_surface.change_edge_gluing(label, edge, gluings[edge][1], (edge - gluings[edge][0]) % 3)
In [22]:
icosahedron = ConeSurface(icosahedron_surface)
In [23]:
gs = icosahedron.graphical_surface()
In [24]:
for label in range(0,8,2):
    gs.make_adjacent(label,0)
    gs.make_adjacent(label,1)
    gs.make_adjacent(label,2)
    gs.make_adjacent(label+3,0)
    gs.make_adjacent(label+3,1)
    gs.make_adjacent(label+3,2)
gs.make_adjacent(1,0)
gs.make_adjacent(8,0)
In [25]:
apply_display_options(gs)
In [26]:
icosahedron.plot()
Out[26]:
In [27]:
icosahedron.plot().save_image("icosahedron_net.pdf")

Cube

In [28]:
cube_surface = Surface_dict(base_ring=QQ)
In [29]:
for label in range(6):
    cube_surface.add_polygon(square, label=label)
In [30]:
cube_surface.change_base_label(0)
In [31]:
for label in range(6):
    gluings = build_adj_cube(0,label)
    for edge in range(4):
        cube_surface.change_edge_gluing(label, edge, gluings[edge][1], (edge + 2 - gluings[edge][0]) % 4)
In [32]:
cube = ConeSurface(cube_surface)
In [33]:
gs = cube.graphical_surface()
In [34]:
for edge in range(4):
    gs.make_adjacent(1, edge)
gs.make_adjacent(2, 1)
In [35]:
apply_display_options(gs)
In [36]:
cube.plot()
Out[36]:
In [37]:
cube.plot().save_image("cube_net.pdf")

Cube unfolding

Define the unfolded surface:

In [38]:
cube_unfolding_surface = Surface_dict(QQ)

Add polygons indexed by pairs $(sheet,sq)$.

In [39]:
for sheet in range(4):
    for sq in range(6):
        cube_unfolding_surface.add_polygon(square, label=(sheet,sq))

Set the base label:

In [40]:
cube_unfolding_surface.change_base_label((0,0))

Glue edges together:

In [41]:
for sheet in range(4):
    for sq in range(6):
        adj_list = build_adj_cube(sheet,sq)
        for edge in range(4):
            cube_unfolding_surface.change_edge_gluing((sheet,sq), edge, tuple(adj_list[edge]), (edge+2)%4)

Make it so that we can no longer change the surface.

In [42]:
cube_unfolding_surface.set_immutable()

We define cube_unfolding to be the translation surface built as above.

In [43]:
cube_unfolding = TranslationSurface(cube_unfolding_surface)
In [44]:
gs = cube_unfolding.graphical_surface()
In [45]:
apply_display_options(gs)
In [46]:
gs.will_plot_polygon_labels = False
In [47]:
gs.non_adjacent_edge_options['thickness']=0.125
gs.adjacent_edge_options['thickness']=0.125
In [48]:
for label in cube_unfolding.label_iterator():
    if gs.is_visible(label):
        gs.hide(label)
In [49]:
rot_matrix = matrix(QQ,[[0,-1],[1,0]])
show(rot_matrix)

Layout the positions of squares with $sq=0$:

In [50]:
from flatsurf.geometry.similarity import SimilarityGroup
SG = SimilarityGroup(QQ)
for sheet in range(4):
    gs.make_visible((sheet,0))
    gp = gs.graphical_polygon((sheet,0))
    gp.set_transformation(SG(rot_matrix^sheet*vector([-9/2,0])-vector([1/2,1/2])))

Layout the other squares:

In [51]:
for sheet in range(4):
    gs.make_adjacent((sheet,0),(sheet+1)%4)
    for edge in range(4):
        gs.make_adjacent((sheet,1),edge)
    gs.make_adjacent((sheet,2),(sheet+1)%4)
In [52]:
gs.edge_label_options['fontsize']=5
gs.edge_label_options['position']='inside'
gs.edge_label_options['t']=1/2
In [53]:
gs.plot()
Out[53]:
In [54]:
plt = gs.plot()
In [55]:
gs.polygon_label_options['fontsize']=7
In [56]:
for sheet in range(4):
    for sq in range(6):
        label = (sheet, sq)
        gp = gs.graphical_polygon(label)
        plt += gp.plot_label(sq, **gs.polygon_label_options)
plt
Out[56]:
In [57]:
code = ord('a')
for sq in range(6):
    for e in range(4):
        (sheet2,sq2),e2 = cube_unfolding.opposite_edge((0,sq),e)
        if not gs.is_adjacent((0,sq),e) and sq2>sq:
            for sheet in range(4):
                gp = gs.graphical_polygon((sheet,sq))
                plt += gp.plot_edge_label((e+sheet)%4, chr(code) + str(sheet), **gs.edge_label_options)
                gp = gs.graphical_polygon(((sheet+sheet2)%4, sq2))
                plt += gp.plot_edge_label((e2+sheet)%4, chr(code) + str(sheet), **gs.edge_label_options)
            code = code + 1
In [58]:
plt
Out[58]:
In [59]:
gs.polygon_label_options['fontsize']=10
for sheet in range(4):
    gp = gs.graphical_polygon((sheet, 0))
    v = (rot_matrix^sheet)*vector([-3/4,0])
    plt += text(str(sheet), v, **gs.polygon_label_options)
In [60]:
gs.graphical_polygon((0,0))
Out[60]:
GraphicalPolygon with vertices [(-5.0, -0.5), (-4.0, -0.5), (-4.0, 0.5), (-5.0, 0.5)]
In [61]:
plt
Out[61]:
In [62]:
plt.save_image("cube_unfolding.pdf")
plt.save_image("cube_unfolding.svg")

The double pentagon

In [63]:
# The double pentagon surface Pi5 was defined as part of Appendix C.
In [64]:
double_pentagon_surface = Surface_dict(base_ring=F)
In [65]:
double_pentagon_surface.add_polygon(pentagon_top, label=0)
double_pentagon_surface.add_polygon(pentagon_bottom, label=1)
for i in range(5):
    double_pentagon_surface.change_edge_gluing(0,i,1,i)
In [66]:
double_pentagon_surface.change_base_label(0)
In [67]:
Pi5 = TranslationSurface(double_pentagon_surface)
In [68]:
gs = Pi5.graphical_surface(edge_labels="letter")
In [69]:
gs.make_adjacent(0,4)
In [70]:
apply_display_options(gs)
In [71]:
gs.will_plot_polygon_labels = False
gs.will_plot_edge_labels = True
In [72]:
gs.edge_label_options['t']=0.5
gs.edge_label_options['position']='outside'
gs.edge_label_options['fontsize']=20
In [73]:
gs.plot()
Out[73]:
In [74]:
sigma0 = Pi5.tangent_vector(1,Pi5.polygon(1).vertex(2),(1,0)).straight_line_trajectory()
sigma1 = Pi5.tangent_vector(0,(0,0),(1,0)).straight_line_trajectory()
In [75]:
gt0 = sigma0.graphical_trajectory()
In [76]:
gp0 = gs.graphical_polygon(0)
gp1 = gs.graphical_polygon(1)
In [77]:
v0=gp0.transformed_vertex(0)
v0
Out[77]:
(0, 0)
In [78]:
v0=gp0.transformed_vertex
In [79]:
plt = gs.plot() + \
    sigma0.plot(color="green", thickness="3") + \
    sigma1.plot(color="red", thickness="3") + \
    text("$\\sigma_0$", (-2*cos(pi/5), 0.05), color = 'black', fontsize=30, vertical_alignment="bottom") + \
    text("$\\sigma_1$", (1, 0.05), color = 'black', fontsize=30, vertical_alignment="bottom") + \
    point2d(gp0.transformed_vertex(0),size=60, color="black",zorder=10) + \
    text("$v_0$", gp0.transformed_vertex(0), color = 'black', fontsize=30, vertical_alignment="top" , horizontal_alignment="left") + \
    point2d(gp0.transformed_vertex(1),size=60, color="black",zorder=10) + \
    text("$v_3$", gp0.transformed_vertex(1), color = 'black', fontsize=30, vertical_alignment="top" , horizontal_alignment="left") + \
    point2d(gp0.transformed_vertex(4),size=60, color="black",zorder=10) + \
    text("$v_4$", gp0.transformed_vertex(4)+vector((0,0.1)), color = 'black', fontsize=30, vertical_alignment="bottom" , horizontal_alignment="right") + \
    point2d(gp1.transformed_vertex(2),size=60, color="black",zorder=10) + \
    text("$v_2$", gp1.transformed_vertex(2), color = 'black', fontsize=30, vertical_alignment="top" , horizontal_alignment="right")
plt
Out[79]:
In [80]:
plt.save_image("pi5sc.pdf")
In [81]:
gs.will_plot_edge_labels = False
In [82]:
x=[]
for i in range(4):
    x.append(Pi5.tangent_vector(0,Pi5.polygon(0).vertex(4)/2,R^(i-1)*vector([1,0])).straight_line_trajectory())
    x[i].flow(1)
In [83]:
plt = Pi5.plot()
for i in range(4):
    plt = plt + x[i].plot(color="black", thickness="2")
plt
Out[83]:
In [84]:
gp = gs.graphical_polygon(0)
In [85]:
label_options = {'t':0.5, 'position':'outside', 'fontsize':30, 'color':'black'}
for i in range(4):
    plt += gp.plot_edge_label(i, "$x_"+str(i)+"$", **label_options)
plt
Out[85]:
In [86]:
plt.save_image("pi5_fund_group.svg")

Dodecahedron

In [87]:
dodecahedron_surface = Surface_dict(base_ring=F)
In [88]:
for label in range(12):
    dodecahedron_surface.add_polygon(pentagon_top, label=label)
In [89]:
dodecahedron_surface.change_base_label(0)
In [90]:
for label in range(12):
    gluings = build_adj_dodec(0,label)
    for edge in range(5):
        dodecahedron_surface.change_edge_gluing(label, edge, gluings[edge][1], (edge + 2*gluings[edge][0]) % 5)
In [91]:
dodecahedron = ConeSurface(dodecahedron_surface)
In [92]:
gs = dodecahedron.graphical_surface()
In [93]:
for edge in range(5):
    gs.make_adjacent(0,edge)
gs.make_adjacent(2,4)
gs.make_adjacent(8,1)
for edge in range(5):
    gs.make_adjacent(6,edge)
In [94]:
apply_display_options(gs)
In [95]:
dodecahedron.plot()
Out[95]:
In [96]:
dodecahedron.plot().save_image("dodecahedron_net.pdf")

We construct something useful for writing symbols on the edges of the unfolded dodecahedron below.

For each edge of the dodecahedron where two polygons meet that are not joined in the net, we will choose a letter. Also for each letter we will choose a favorite polygon label indident to this edge.

In [97]:
edge_to_letter = {}
favorite_label = {}
letter = 'a'
for label in range(12):
    for edge in range(5):
        if not gs.is_adjacent(label,edge):
            if (label, edge) not in edge_to_letter:
                edge_to_letter[(label, edge)] = letter
                edge_to_letter[dodecahedron.opposite_edge(label, edge)] = letter
                favorite_label[letter] = label
                letter = chr(ord(letter)+1) # increment letter

Dodecahedron unfolding

In [98]:
gs = Dtilde.graphical_surface()
In [99]:
apply_display_options(gs)
In [100]:
gs.will_plot_polygon_labels = False
In [101]:
gs.non_adjacent_edge_options['thickness']=0.125
gs.adjacent_edge_options['thickness']=0.125
In [102]:
for label in Dtilde.label_iterator():
    if gs.is_visible(label):
        gs.hide(label)
In [103]:
from flatsurf.geometry.similarity import SimilarityGroup
SG = SimilarityGroup(Dtilde.base_ring())
for sheet in range(10):
    gs.make_visible((sheet,0))
    gp = gs.graphical_polygon((sheet,0))
    if sheet%2 == 0:
        gp.set_transformation(SG(R**sheet*vector((20,-5))-pentagon_bottom.circumscribing_circle().center()))
    else:
        gp.set_transformation(SG(R**sheet*vector((20,-5))-pentagon_top.circumscribing_circle().center()))
In [104]:
for sheet in range(10):
    for edge in range(5):
        gs.make_adjacent((sheet,0),edge)
    gs.make_adjacent((sheet,2),(4+3*sheet)%5)
    gs.make_adjacent((sheet,8),(1+3*sheet)%5)
    for edge in range(5):
        gs.make_adjacent((sheet,6),edge)
In [105]:
gs.edge_label_options['fontsize']=2.9
gs.edge_label_options['t']=1/2
In [106]:
gs.plot()
Out[106]:
In [107]:
plt = gs.plot()
In [108]:
edge_vector_to_digit = {}
for i in range(10):
    v = R^i*vector(F,[2,0])
    v.set_immutable()
    edge_vector_to_digit[v]=i
In [109]:
for sheet in range(10):
    for label in range(12):
        gp = gs.graphical_polygon((sheet,label))
        for edge in range(5):
            edge_in_dodecahedron = (edge + 2*sheet) % 5
            if (label,edge_in_dodecahedron) in edge_to_letter:
                letter = edge_to_letter[(label,edge_in_dodecahedron)]
                label_glued = dodecahedron.opposite_edge(label,edge_in_dodecahedron)[0]
                if label < label_glued:
                    v = Dtilde.polygon((sheet,label)).edge(edge)
                else:
                    v = -Dtilde.polygon((sheet,label)).edge(edge)
                v.set_immutable()
                number = edge_vector_to_digit[v]
                plt += gp.plot_edge_label(edge, letter + str(number), **gs.edge_label_options)
In [110]:
plt
Out[110]:
In [111]:
gs.polygon_label_options['fontsize']=4
In [112]:
for label in Dtilde.label_iterator():
    gp = gs.graphical_polygon(label)
    plt += gp.plot_label(label[1], **gs.polygon_label_options)
plt
Out[112]:
In [113]:
gs.polygon_label_options['fontsize']=6
for sheet in range(10):
    gp = gs.graphical_polygon((sheet, 6))
    if sheet%2 == 1:
        v = gp.transform(-pentagon_top.circumscribing_circle().center())
    else:
        v = gp.transform(-pentagon_bottom.circumscribing_circle().center())
    plt += text(str(sheet), v/1.6, **gs.polygon_label_options)
In [114]:
plt
Out[114]:
In [115]:
plt.save_image("dodecahedron_unfolding.pdf")

Closed trajectories on the dodecahedron

In [116]:
from flatsurf.geometry.surface_objects import SaddleConnection
In [117]:
import itertools
In [118]:
gs = dodecahedron.graphical_surface()
In [119]:
gs.will_plot_polygon_labels = False

Below we quickly generate 31 pictures of saddle connections corresponding to the 31 lines in Table 3 of the paper.

In [120]:
for i in range(31):
    print("Generating picture for saddle connection %s:" % (i+1))
    holonomy_vector = sadd_conn_total_sort[i][1]
    sc = SaddleConnection(dodecahedron, (0, 0), holonomy_vector)
    plt = gs.plot() + sc.plot(color="black", thickness=0.5)
    show(plt)
Generating picture for saddle connection 1:
Generating picture for saddle connection 2:
Generating picture for saddle connection 3:
Generating picture for saddle connection 4:
Generating picture for saddle connection 5:
Generating picture for saddle connection 6:
Generating picture for saddle connection 7:
Generating picture for saddle connection 8:
Generating picture for saddle connection 9:
Generating picture for saddle connection 10:
Generating picture for saddle connection 11:
Generating picture for saddle connection 12:
Generating picture for saddle connection 13:
Generating picture for saddle connection 14:
Generating picture for saddle connection 15:
Generating picture for saddle connection 16:
Generating picture for saddle connection 17:
Generating picture for saddle connection 18:
Generating picture for saddle connection 19:
Generating picture for saddle connection 20:
Generating picture for saddle connection 21:
Generating picture for saddle connection 22:
Generating picture for saddle connection 23:
Generating picture for saddle connection 24:
Generating picture for saddle connection 25:
Generating picture for saddle connection 26:
Generating picture for saddle connection 27:
Generating picture for saddle connection 28:
Generating picture for saddle connection 29:
Generating picture for saddle connection 30:
Generating picture for saddle connection 31:

We found that we get nicer pictures by minimizing the number of breaks in the rendered saddle connection. Here a break is a segment that cuts across two non-adjacent polygons in the image. The following counts the number of breaks in a saddle connection.

In [121]:
def count_breaks(sc):
    r""" Count the number of breaks in a saddle connection when drawn. """
    count = 0
    traj = sc.trajectory()
    for k in range(traj.combinatorial_length()-1):
        seg = traj.segment(k)
        if not gs.is_adjacent( seg.polygon_label(), seg.end().position().get_edge() ):
            count += 1
    return count

Now we present optimized pictures where we minimize the number of breaks under all images of a saddle connection by an isometry of the dodecahedron. Unfortunately, we don't know a way to do this quickly, so it is a long computation.

In [122]:
for i in range(31):
    print("Generating optimized picture for saddle connection %s:" % (i+1))
    holonomy_vector1 = sadd_conn_total_sort[i][1]
    holonomy_vector2 = R^3 * vector( [holonomy_vector1[0], -holonomy_vector1[1]] )
    min_sc = None
    for hol_vec in [holonomy_vector1, holonomy_vector2]:
        for label in range(12):
            for vertex in range(5):
                w = R^(2*vertex) * hol_vec
                sc = SaddleConnection(dodecahedron, (label, vertex), w)    
                start = sc.start_tangent_vector()
                end = sc.end_tangent_vector()
                assert dodecahedron.surface_point(start.polygon_label(), start.point()) == \
                    dodecahedron.surface_point(end.polygon_label(), end.point())
                breaks = count_breaks(sc)
                if min_sc is None or breaks < min_breaks:
                    min_sc = sc
                    min_breaks = breaks
    plt = gs.plot() + min_sc.plot(color="black", thickness=0.5)
    show(plt)
    plt.save_image("closed_sc_"+str(i+1)+".pdf")
Generating optimized picture for saddle connection 1:
Generating optimized picture for saddle connection 2:
Generating optimized picture for saddle connection 3:
Generating optimized picture for saddle connection 4:
Generating optimized picture for saddle connection 5:
Generating optimized picture for saddle connection 6:
Generating optimized picture for saddle connection 7:
Generating optimized picture for saddle connection 8:
Generating optimized picture for saddle connection 9:
Generating optimized picture for saddle connection 10:
Generating optimized picture for saddle connection 11:
Generating optimized picture for saddle connection 12:
Generating optimized picture for saddle connection 13:
Generating optimized picture for saddle connection 14:
Generating optimized picture for saddle connection 15:
Generating optimized picture for saddle connection 16:
Generating optimized picture for saddle connection 17:
Generating optimized picture for saddle connection 18:
Generating optimized picture for saddle connection 19:
Generating optimized picture for saddle connection 20:
Generating optimized picture for saddle connection 21:
Generating optimized picture for saddle connection 22:
Generating optimized picture for saddle connection 23:
Generating optimized picture for saddle connection 24:
Generating optimized picture for saddle connection 25:
Generating optimized picture for saddle connection 26:
Generating optimized picture for saddle connection 27:
Generating optimized picture for saddle connection 28:
Generating optimized picture for saddle connection 29:
Generating optimized picture for saddle connection 30:
Generating optimized picture for saddle connection 31:

Below we generate pictures of saddle connections corresponding to the lines in Table 4 of the paper.

In [123]:
for i,data in shortest_table.items():
    print("Generating picture for shortest saddle connection %s:" % (i+1))
    holonomy_vector = data[1]
    sc = SaddleConnection(dodecahedron, (0, 0), holonomy_vector)
    plt = gs.plot() + sc.plot(color="black", thickness=0.5)
    show(plt)
Generating picture for shortest saddle connection 10:
Generating picture for shortest saddle connection 16:
Generating picture for shortest saddle connection 17:
Generating picture for shortest saddle connection 18:
Generating picture for shortest saddle connection 19:
Generating picture for shortest saddle connection 20:
Generating picture for shortest saddle connection 21:
Generating picture for shortest saddle connection 22:
Generating picture for shortest saddle connection 23:
Generating picture for shortest saddle connection 24:
Generating picture for shortest saddle connection 25:
Generating picture for shortest saddle connection 26:
Generating picture for shortest saddle connection 27:
Generating picture for shortest saddle connection 28:
Generating picture for shortest saddle connection 29:
Generating picture for shortest saddle connection 30:
Generating picture for shortest saddle connection 31:

Now we generate optimized pictures.

In [124]:
for i,data in shortest_table.items():
    print("Generating optimized picture for shortest saddle connection %s:" % (i+1))
    holonomy_vector1 = data[1]
    holonomy_vector2 = R^3 * vector( [holonomy_vector1[0], -holonomy_vector1[1]] )
    min_sc = None
    for hol_vec in [holonomy_vector1, holonomy_vector2]:
        for label in range(12):
            for vertex in range(5):
                w = R^(2*vertex) * hol_vec
                sc = SaddleConnection(dodecahedron, (label, vertex), w)    
                start = sc.start_tangent_vector()
                end = sc.end_tangent_vector()
                assert dodecahedron.surface_point(start.polygon_label(), start.point()) == \
                    dodecahedron.surface_point(end.polygon_label(), end.point())
                breaks = count_breaks(sc)
                if min_sc is None or breaks < min_breaks:
                    min_sc = sc
                    min_breaks = breaks
    plt = gs.plot() + min_sc.plot(color="black", thickness=0.5)
    show(plt)
    plt.save_image("shortest_sc_"+str(i+1)+".pdf")
Generating optimized picture for shortest saddle connection 10:
Generating optimized picture for shortest saddle connection 16:
Generating optimized picture for shortest saddle connection 17:
Generating optimized picture for shortest saddle connection 18:
Generating optimized picture for shortest saddle connection 19:
Generating optimized picture for shortest saddle connection 20:
Generating optimized picture for shortest saddle connection 21:
Generating optimized picture for shortest saddle connection 22:
Generating optimized picture for shortest saddle connection 23:
Generating optimized picture for shortest saddle connection 24:
Generating optimized picture for shortest saddle connection 25:
Generating optimized picture for shortest saddle connection 26:
Generating optimized picture for shortest saddle connection 27:
Generating optimized picture for shortest saddle connection 28:
Generating optimized picture for shortest saddle connection 29:
Generating optimized picture for shortest saddle connection 30:
Generating optimized picture for shortest saddle connection 31: