passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,865 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # sage.doctest: needs planarity
3
+ """
4
+ Schnyder's algorithm for straight-line planar embeddings
5
+
6
+ A module for computing the (x,y) coordinates for a straight-line planar
7
+ embedding of any connected planar graph with at least three vertices. Uses
8
+ Walter Schnyder's Algorithm from [Sch1990]_.
9
+
10
+ AUTHORS:
11
+
12
+ - Jonathan Bober, Emily Kirkman (2008-02-09) -- initial version
13
+ """
14
+ # ****************************************************************************
15
+ # Copyright (C) 2008 Jonathan Bober and Emily Kirkman
16
+ #
17
+ # Distributed under the terms of the GNU General Public License (GPL)
18
+ # https://www.gnu.org/licenses/
19
+ # ****************************************************************************
20
+
21
+ from sage.sets.set import Set
22
+ from .all import DiGraph
23
+
24
+
25
+ def _triangulate(g, comb_emb):
26
+ """
27
+ Helper function to schnyder method for computing coordinates in the plane to
28
+ plot a planar graph with no edge crossings.
29
+
30
+ Given a connected graph g with at least 3 vertices and a planar combinatorial
31
+ embedding comb_emb of g, modify g in place to form a graph whose faces are
32
+ all triangles, and return the set of newly created edges. Also ``comb_emb``
33
+ is updated in place.
34
+
35
+ The simple way to triangulate a face is to just pick a vertex and draw
36
+ an edge from that vertex to every other vertex in the face. Think that this
37
+ might ultimately result in graphs that don't look very nice when we draw them
38
+ so we have decided on a different strategy. After handling special cases,
39
+ we add an edge to connect every third vertex on each face. If this edge is
40
+ a repeat, we keep the first edge of the face and retry the process at the next
41
+ edge in the face list. (By removing the special cases we guarantee that this
42
+ method will work on one of these attempts.)
43
+
44
+ INPUT:
45
+
46
+ - ``g`` -- the graph to triangulate
47
+ - ``comb_emb`` -- a planar combinatorial embedding of g
48
+
49
+ OUTPUT: a list of edges that are added to the graph (in place)
50
+
51
+ EXAMPLES::
52
+
53
+ sage: from sage.graphs.schnyder import _triangulate
54
+ sage: g = Graph(graphs.CycleGraph(4))
55
+ sage: g.is_planar(set_embedding=True)
56
+ True
57
+ sage: _triangulate(g, g._embedding)
58
+ [(2, 0), (1, 3)]
59
+
60
+ sage: g = graphs.PathGraph(3)
61
+ sage: g.is_planar(set_embedding=True)
62
+ True
63
+ sage: new_edges = _triangulate(g, g._embedding)
64
+ sage: [sorted(e) for e in new_edges]
65
+ [[0, 2]]
66
+
67
+ TESTS:
68
+
69
+ :issue:`29522` is fixed::
70
+
71
+ sage: g = Graph(2)
72
+ sage: _triangulate(g, {})
73
+ Traceback (most recent call last):
74
+ ...
75
+ NotImplementedError: _triangulate() only knows how to handle connected graphs
76
+ sage: g = Graph([(0, 1)])
77
+ sage: _triangulate(g, {})
78
+ Traceback (most recent call last):
79
+ ...
80
+ ValueError: a Graph with less than 3 vertices doesn't have any triangulation
81
+ sage: g = Graph(3)
82
+ sage: _triangulate(g, {})
83
+ Traceback (most recent call last):
84
+ ...
85
+ NotImplementedError: _triangulate() only knows how to handle connected graphs
86
+ """
87
+ # first make sure that the graph has at least 3 vertices, and that it is connected
88
+ if not g.is_connected():
89
+ raise NotImplementedError("_triangulate() only knows how to handle connected graphs")
90
+ if g.order() < 3:
91
+ raise ValueError("a Graph with less than 3 vertices doesn't have any triangulation")
92
+
93
+ # At this point we know that the graph is connected, has at least 3
94
+ # vertices. This is where the real work starts.
95
+
96
+ faces = g.faces(comb_emb)
97
+ # We start by finding all of the faces of this embedding.
98
+
99
+ edges_added = [] # The list of edges that we add to the graph.
100
+ # This will be returned at the end.
101
+
102
+ for face in faces:
103
+ new_face = []
104
+ if len(face) < 3:
105
+ raise RuntimeError('Triangulate method created face %s with < 3 edges.' % face)
106
+ if len(face) == 3:
107
+ continue # This face is already triangulated
108
+ elif len(face) == 4: # In this special case just add diagonal edge to square
109
+ u, v, w, x = (e[0] for e in face)
110
+ if w == u or g.has_edge(w, u):
111
+ u, v, w, x = v, w, x, u
112
+ new_face = (w, u)
113
+ comb_emb[w].insert(comb_emb[w].index(x), u)
114
+ comb_emb[u].insert(comb_emb[u].index(v), w)
115
+ g.add_edge(new_face)
116
+ edges_added.append(new_face)
117
+ else:
118
+ N = len(face)
119
+ i = 0
120
+ while i < N - 1:
121
+ new_edge = (face[i + 1][1], face[i][0]) # new_edge is from third vertex in face to first
122
+ if g.has_edge(new_edge) or new_edge[0] == new_edge[1]: # check for repeats
123
+ new_face.append(face[i]) # if repeated, keep first edge in face instead
124
+ if i == N - 2: # if we are two from the end, found a triangle already
125
+ break
126
+ i += 1
127
+ continue
128
+
129
+ g.add_edge(new_edge)
130
+ edges_added.append(new_edge)
131
+ comb_emb[new_edge[0]].insert(comb_emb[new_edge[0]].index((face + new_face)[i + 2][1]), new_edge[1])
132
+ comb_emb[new_edge[1]].insert(comb_emb[new_edge[1]].index(face[i][1]), new_edge[0])
133
+ new_face.append((new_edge[1], new_edge[0]))
134
+ i += 2
135
+ if i != N:
136
+ new_face.append(face[-1])
137
+ faces.append(new_face)
138
+
139
+ return edges_added
140
+
141
+
142
+ def _normal_label(g, comb_emb, external_face):
143
+ r"""
144
+ Helper function to schnyder method for computing coordinates in
145
+ the plane to plot a planar graph with no edge crossings.
146
+
147
+ Constructs a normal labelling of a triangular graph g, given the
148
+ planar combinatorial embedding of g and a designated external
149
+ face. Returns labels dictionary. The normal label is constructed
150
+ by first contracting the graph down to its external face, then
151
+ expanding the graph back to the original while simultaneously
152
+ adding angle labels.
153
+
154
+ INPUT:
155
+
156
+ - ``g`` -- the graph to find the normal labeling of (g must be triangulated)
157
+ - ``comb_emb`` -- a planar combinatorial embedding of g
158
+ - ``external_face`` -- the list of three edges in the external face of g
159
+
160
+ OUTPUT: x; tuple with entries
161
+
162
+ x[0] = dict of dicts of normal labeling for each vertex of g and each
163
+ adjacent neighbors u,v (u < v) of vertex:
164
+
165
+ { vertex : { (u,v): angel_label } }
166
+
167
+ x[1] = (v1,v2,v3) tuple of the three vertices of the external face.
168
+
169
+ EXAMPLES::
170
+
171
+ sage: from sage.graphs.schnyder import _triangulate, _normal_label, _realizer
172
+ sage: g = Graph(graphs.CycleGraph(7))
173
+ sage: g.is_planar(set_embedding=True)
174
+ True
175
+ sage: faces = g.faces(g._embedding)
176
+ sage: _triangulate(g, g._embedding)
177
+ [(2, 0), (4, 2), (6, 4), (5, 0), (3, 5), (1, 3), (4, 0), (3, 0)]
178
+ sage: tn = _normal_label(g, g._embedding, faces[0])
179
+ sage: _realizer(g, tn)
180
+ ({0: [<sage.graphs.schnyder.TreeNode object at ...>]},
181
+ (1, 0, 2))
182
+ """
183
+ contracted = []
184
+ contractible = []
185
+
186
+ labels = {}
187
+
188
+ # For now we will not take the order of the outer face into account.
189
+ # We will correct this in the end of this function.
190
+ external_vertices = sorted([external_face[0][0],
191
+ external_face[1][0],
192
+ external_face[2][0]])
193
+ v1, v2, v3 = external_vertices
194
+ v1_neighbors = Set(g.neighbors(v1))
195
+
196
+ neighbor_count = {v: 0 for v in g}
197
+ for v in g.neighbors(v1):
198
+ neighbor_count[v] = len(v1_neighbors.intersection(Set(g.neighbors(v))))
199
+
200
+ for v in v1_neighbors:
201
+ if v in [v1, v2, v3]:
202
+ continue
203
+ if neighbor_count[v] == 2:
204
+ contractible.append(v)
205
+
206
+ # contraction phase:
207
+
208
+ while g.order() > 3:
209
+ try:
210
+ v = contractible.pop()
211
+ except Exception:
212
+ raise RuntimeError('Contractible list is empty but graph still has %d vertices. (Expected 3.)' % g.order())
213
+
214
+ # going to contract v
215
+ v_neighbors = Set(g.neighbors(v))
216
+ contracted.append((v, v_neighbors,
217
+ v_neighbors - v1_neighbors - Set([v1])))
218
+ g.delete_vertex(v)
219
+ v1_neighbors -= Set([v])
220
+ for w in v_neighbors - v1_neighbors - Set([v1]):
221
+ # adding edge (v1, w)
222
+ g.add_edge((v1, w))
223
+ if g.order() == 3:
224
+ break
225
+ v1_neighbors += v_neighbors - Set([v1])
226
+ contractible = []
227
+ for w in g.neighbors(v1):
228
+ if (len(v1_neighbors.intersection(Set(g.neighbors(w)))) == 2
229
+ and w not in [v1, v2, v3]):
230
+ contractible.append(w)
231
+
232
+ # expansion phase:
233
+
234
+ v1, v2, v3 = g.vertices(sort=True) # always in sorted order
235
+
236
+ labels[v1] = {(v2, v3): 1}
237
+ labels[v2] = {(v1, v3): 2}
238
+ labels[v3] = {(v1, v2): 3}
239
+
240
+ while contracted:
241
+ v, new_neighbors, neighbors_to_delete = contracted.pop()
242
+ # going to add back vertex v
243
+ labels[v] = {}
244
+
245
+ for w in neighbors_to_delete:
246
+ g.delete_edge((v1, w))
247
+
248
+ if len(neighbors_to_delete) == 0:
249
+ # we are adding v into the face new_neighbors
250
+ w1, w2, w3 = sorted(new_neighbors)
251
+
252
+ labels[v] = {(w1, w2): labels[w3].pop((w1, w2)),
253
+ (w2, w3): labels[w1].pop((w2, w3)),
254
+ (w1, w3): labels[w2].pop((w1, w3))}
255
+ labels[w1][tuple(sorted((w2, v)))] = labels[v][(w2, w3)]
256
+ labels[w1][tuple(sorted((w3, v)))] = labels[v][(w2, w3)]
257
+
258
+ labels[w2][tuple(sorted((w1, v)))] = labels[v][(w1, w3)]
259
+ labels[w2][tuple(sorted((w3, v)))] = labels[v][(w1, w3)]
260
+
261
+ labels[w3][tuple(sorted((w1, v)))] = labels[v][(w1, w2)]
262
+ labels[w3][tuple(sorted((w2, v)))] = labels[v][(w1, w2)]
263
+ else:
264
+ new_neighbors_set = Set(new_neighbors)
265
+ angles_out_of_v1 = set()
266
+ vertices_in_order = []
267
+ l = []
268
+ for angle in labels[v1].keys():
269
+ if len(Set(angle).intersection(new_neighbors_set)) == 2:
270
+ angles_out_of_v1.add(angle)
271
+ l = l + list(angle)
272
+ # find a unique element in l
273
+ l.sort()
274
+ i = 0
275
+ while i < len(l):
276
+ if l[i] == l[i + 1]:
277
+ i += 2
278
+ else:
279
+ break
280
+
281
+ angle_set = Set(angles_out_of_v1)
282
+
283
+ vertices_in_order.append(l[i])
284
+ while angles_out_of_v1:
285
+ for angle in angles_out_of_v1:
286
+ if vertices_in_order[-1] in angle:
287
+ break
288
+ if angle[0] == vertices_in_order[-1]:
289
+ vertices_in_order.append(angle[1])
290
+ else:
291
+ vertices_in_order.append(angle[0])
292
+ angles_out_of_v1.remove(angle)
293
+
294
+ w = vertices_in_order
295
+
296
+ # is w[0] a 2 or a 3?
297
+ top_label = labels[w[0]][tuple(sorted((v1, w[1])))]
298
+ if top_label == 3:
299
+ bottom_label = 2
300
+ else:
301
+ bottom_label = 3
302
+ i = 0
303
+ while i < len(w) - 1:
304
+ labels[v][tuple(sorted((w[i], w[i + 1])))] = 1
305
+ labels[w[i]][tuple(sorted((w[i + 1], v)))] = top_label
306
+ labels[w[i + 1]][tuple(sorted((w[i], v)))] = bottom_label
307
+ i += 1
308
+
309
+ labels[v][tuple(sorted((v1, w[0])))] = bottom_label
310
+ labels[v][tuple(sorted((v1, w[-1])))] = top_label
311
+
312
+ labels[w[0]][tuple(sorted((v1, v)))] = top_label
313
+ labels[w[-1]][tuple(sorted((v1, v)))] = bottom_label
314
+ labels[v1][tuple(sorted((w[0], v)))] = 1
315
+ labels[v1][tuple(sorted((w[-1], v)))] = 1
316
+
317
+ # delete all the extra labels
318
+
319
+ for angle in angle_set:
320
+ labels[v1].pop(angle)
321
+
322
+ labels[w[0]].pop(tuple(sorted((v1, w[1]))))
323
+ labels[w[-1]].pop(tuple(sorted((v1, w[-2]))))
324
+
325
+ i = 1
326
+ while i < len(w) - 1:
327
+ labels[w[i]].pop(tuple(sorted((v1, w[i + 1]))))
328
+ labels[w[i]].pop(tuple(sorted((v1, w[i - 1]))))
329
+ i += 1
330
+
331
+ for w in new_neighbors:
332
+ g.add_edge((v, w))
333
+
334
+ # Up to this point we did not take the order of the external face into
335
+ # account. Since the combinatorial embedding of a triangulation is unique up
336
+ # to the choice of the outer face and reflection, this might lead to a
337
+ # reflection of the Schnyder drawing resulting from this labeling which is
338
+ # not conformal with comb_emb any longer. Therefore, we might have to swap
339
+ # the labels 1 and 2.
340
+ if (v1, v2) in external_face:
341
+ for u in labels:
342
+ for v, w in labels[u]:
343
+ if labels[u][v, w] == 1:
344
+ labels[u][v, w] = 2
345
+ elif labels[u][v, w] == 2:
346
+ labels[u][v, w] = 1
347
+ v1, v2 = v2, v1
348
+
349
+ return labels, (v1, v2, v3)
350
+
351
+
352
+ def _realizer(g, x, example=False):
353
+ """
354
+ Given a triangulated graph g and a normal labeling constructs the
355
+ realizer and returns a dictionary of three trees determined by the
356
+ realizer, each spanning all interior vertices and rooted at one of
357
+ the three external vertices.
358
+
359
+ A realizer is a directed graph with edge labels that span all interior
360
+ vertices from each external vertex. It is determined by giving direction
361
+ to the edges that have the same angle label on both sides at a vertex.
362
+ (Thus the direction actually points to the parent in the tree.) The
363
+ edge label is set as whatever the matching angle label is. Then from
364
+ any interior vertex, following the directed edges by label will
365
+ give a path to each of the three external vertices.
366
+
367
+ INPUT:
368
+
369
+ - ``g`` -- the graph to compute the realizer of
370
+ - ``x`` -- tuple with entries
371
+
372
+ x[0] = dict of dicts representing a normal labeling of g. For
373
+ each vertex of g and each adjacent neighbors u,v (u < v) of
374
+ vertex: { vertex : { (u,v): angle_label } }
375
+
376
+ x[1] = (v1, v2, v3) tuple of the three external vertices (also
377
+ the roots of each tree)
378
+
379
+ OUTPUT: x; tuple with entries
380
+
381
+ x[0] = dict of lists of TreeNodes:
382
+
383
+ { root_vertex : [ list of all TreeNodes under root_vertex ] }
384
+
385
+ x[1] = (v1,v2,v3) tuple of the three external vertices (also the
386
+ roots of each tree)
387
+
388
+ EXAMPLES::
389
+
390
+ sage: from sage.graphs.schnyder import _triangulate, _normal_label, _realizer
391
+ sage: g = Graph(graphs.CycleGraph(7))
392
+ sage: g.is_planar(set_embedding=True)
393
+ True
394
+ sage: faces = g.faces(g._embedding)
395
+ sage: _triangulate(g, g._embedding)
396
+ [(2, 0), (4, 2), (6, 4), (5, 0), (3, 5), (1, 3), (4, 0), (3, 0)]
397
+ sage: tn = _normal_label(g, g._embedding, faces[0])
398
+ sage: _realizer(g, tn)
399
+ ({0: [<sage.graphs.schnyder.TreeNode object at ...>]},
400
+ (1, 0, 2))
401
+ """
402
+ normal_labeling, (v1, v2, v3) = x
403
+ realizer = DiGraph()
404
+
405
+ tree_nodes = {}
406
+ for v in g:
407
+ tree_nodes[v] = [TreeNode(label=v, children=[]),
408
+ TreeNode(label=v, children=[]),
409
+ TreeNode(label=v, children=[])]
410
+
411
+ for v in g:
412
+ ones = []
413
+ twos = []
414
+ threes = []
415
+ l = [ones, twos, threes]
416
+ for angle, value in normal_labeling[v].items():
417
+ l[value - 1] += list(angle)
418
+
419
+ ones.sort()
420
+ twos.sort()
421
+ threes.sort()
422
+
423
+ i = 0
424
+ while i < len(ones) - 1:
425
+ if ones[i] == ones[i + 1]:
426
+ realizer.add_edge((ones[i], v), label=1)
427
+ tree_nodes[v][0].append_child(tree_nodes[ones[i]][0])
428
+ i += 1
429
+ i += 1
430
+ i = 0
431
+ while i < len(twos) - 1:
432
+ if twos[i] == twos[i + 1]:
433
+ realizer.add_edge((twos[i], v), label=2)
434
+ tree_nodes[v][1].append_child(tree_nodes[twos[i]][1])
435
+ i += 1
436
+ i += 1
437
+ i = 0
438
+ while i < len(threes) - 1:
439
+ if threes[i] == threes[i + 1]:
440
+ realizer.add_edge((threes[i], v), label=3)
441
+ tree_nodes[v][2].append_child(tree_nodes[threes[i]][2])
442
+ i += 1
443
+ i += 1
444
+
445
+ _compute_coordinates(realizer, (tree_nodes, (v1, v2, v3)))
446
+
447
+ if example:
448
+ realizer.show(talk=True, edge_labels=True)
449
+
450
+ return tree_nodes, (v1, v2, v3)
451
+
452
+
453
+ def _compute_coordinates(g, x):
454
+ r"""
455
+ Given a triangulated graph g with a dict of trees given by the
456
+ realizer and tuple of the external vertices, we compute the
457
+ coordinates of a planar geometric embedding in the grid.
458
+
459
+ The coordinates will be set to the ``_pos`` attribute of g.
460
+
461
+ INPUT:
462
+
463
+ - ``g`` -- the graph to compute the coordinates of
464
+ - ``x`` -- tuple with entries
465
+
466
+ x[0] = dict of tree nodes for the three trees with each external
467
+ vertex as root:
468
+
469
+ { root_vertex : [ list of all TreeNodes under root_vertex ] }
470
+
471
+ x[1] = (v1, v2, v3) tuple of the three external vertices (also
472
+ the roots of each tree)
473
+
474
+ EXAMPLES::
475
+
476
+ sage: from sage.graphs.schnyder import _triangulate, _normal_label, _realizer, _compute_coordinates
477
+ sage: g = Graph(graphs.CycleGraph(7))
478
+ sage: g.is_planar(set_embedding=True)
479
+ True
480
+ sage: faces = g.faces(g._embedding)
481
+ sage: _triangulate(g, g._embedding)
482
+ [(2, 0), (4, 2), (6, 4), (5, 0), (3, 5), (1, 3), (4, 0), (3, 0)]
483
+ sage: tn = _normal_label(g, g._embedding, faces[0])
484
+ sage: r = _realizer(g, tn)
485
+ sage: _compute_coordinates(g,r)
486
+ sage: g.get_pos()
487
+ {0: [0, 5], 1: [5, 1], 2: [1, 0], 3: [4, 1], 4: [1, 1], 5: [2, 2], 6: [1, 2]}
488
+ """
489
+
490
+ tree_nodes, (v1, v2, v3) = x
491
+ # find the roots of each tree:
492
+ t1, t2, t3 = tree_nodes[v1][0], tree_nodes[v2][1], tree_nodes[v3][2]
493
+
494
+ # Compute the number of descendants and depth of each node in
495
+ # each tree.
496
+ t1.compute_number_of_descendants()
497
+ t2.compute_number_of_descendants()
498
+ t3.compute_number_of_descendants()
499
+
500
+ t1.compute_depth_of_self_and_children()
501
+ t2.compute_depth_of_self_and_children()
502
+ t3.compute_depth_of_self_and_children()
503
+
504
+ coordinates = {} # the dict to pass to g.set_pos()
505
+
506
+ # Setting coordinates for external vertices
507
+ coordinates[t1.label] = [g.order() - 2, 1]
508
+ coordinates[t2.label] = [0, g.order() - 2]
509
+ coordinates[t3.label] = [1, 0]
510
+
511
+ for v in g.vertices(sort=False):
512
+ if v not in [t1.label, t2.label, t3.label]:
513
+ # Computing coordinates for v
514
+ r = [0, 0, 0]
515
+
516
+ for i in [0, 1, 2]:
517
+ # Computing size of region i:
518
+
519
+ # Tracing up tree (i + 1) % 3
520
+ p = tree_nodes[v][(i + 1) % 3]
521
+ while p is not None:
522
+ q = tree_nodes[p.label][i].number_of_descendants
523
+ # Adding number of descendants from Tree i nodes with
524
+ # labels on path up tree (i + 1) % 3
525
+ r[i] += q
526
+ p = p.parent
527
+
528
+ # Tracing up tree (i - 1) % 3
529
+ p = tree_nodes[v][(i - 1) % 3]
530
+ while p is not None:
531
+ q = tree_nodes[p.label][i].number_of_descendants
532
+ # Adding number of descendants from Tree i nodes with
533
+ # labels on path up tree (i - 1) % 3
534
+ r[i] += q
535
+ p = p.parent
536
+
537
+ q = tree_nodes[v][i].number_of_descendants
538
+ # Subtracting
539
+ r[i] -= q
540
+
541
+ # Subtracting
542
+ q = tree_nodes[v][(i - 1) % 3].depth
543
+ r[i] -= q
544
+
545
+ if sum(r) != g.order() - 1:
546
+ raise RuntimeError("Computing coordinates failed: vertex %s's coordinates sum to %s. Expected %s" % (v, sum(r), g.order() - 1))
547
+
548
+ coordinates[v] = r[:-1]
549
+
550
+ g.set_pos(coordinates) # Setting _pos attribute to store coordinates
551
+
552
+
553
+ class TreeNode:
554
+ """
555
+ A class to represent each node in the trees used by ``_realizer`` and
556
+ ``_compute_coordinates`` when finding a planar geometric embedding in
557
+ the grid.
558
+
559
+ Each tree node is doubly linked to its parent and children.
560
+
561
+ INPUT:
562
+
563
+ - ``parent`` -- the parent TreeNode of ``self``
564
+ - ``children`` -- list of TreeNode children of ``self``
565
+ - ``label`` -- the associated realizer vertex label
566
+
567
+ EXAMPLES::
568
+
569
+ sage: from sage.graphs.schnyder import TreeNode
570
+ sage: tn = TreeNode(label=5)
571
+ sage: tn2 = TreeNode(label=2,parent=tn)
572
+ sage: tn3 = TreeNode(label=3)
573
+ sage: tn.append_child(tn3)
574
+ sage: tn.compute_number_of_descendants()
575
+ 2
576
+ sage: tn.number_of_descendants
577
+ 2
578
+ sage: tn3.number_of_descendants
579
+ 1
580
+ sage: tn.compute_depth_of_self_and_children()
581
+ sage: tn3.depth
582
+ 2
583
+ """
584
+ def __init__(self, parent=None, children=None, label=None):
585
+ """
586
+ INPUT:
587
+
588
+ - ``parent`` -- the parent TreeNode of ``self``
589
+ - ``children`` -- list of TreeNode children of ``self``
590
+ - ``label`` -- the associated realizer vertex label
591
+
592
+ EXAMPLES::
593
+
594
+ sage: from sage.graphs.schnyder import TreeNode
595
+ sage: tn = TreeNode(label=5)
596
+ sage: tn2 = TreeNode(label=2,parent=tn)
597
+ sage: tn3 = TreeNode(label=3)
598
+ sage: tn.append_child(tn3)
599
+ sage: tn.compute_number_of_descendants()
600
+ 2
601
+ sage: tn.number_of_descendants
602
+ 2
603
+ sage: tn3.number_of_descendants
604
+ 1
605
+ sage: tn.compute_depth_of_self_and_children()
606
+ sage: tn3.depth
607
+ 2
608
+ """
609
+ if children is None:
610
+ children = []
611
+ self.parent = parent
612
+ self.children = children
613
+ self.label = label
614
+ self.number_of_descendants = 1
615
+
616
+ def compute_number_of_descendants(self):
617
+ """
618
+ Compute the number of descendants of ``self`` and all descendants.
619
+
620
+ For each TreeNode, sets result as attribute ``self.number_of_descendants``.
621
+
622
+ EXAMPLES::
623
+
624
+ sage: from sage.graphs.schnyder import TreeNode
625
+ sage: tn = TreeNode(label=5)
626
+ sage: tn2 = TreeNode(label=2,parent=tn)
627
+ sage: tn3 = TreeNode(label=3)
628
+ sage: tn.append_child(tn3)
629
+ sage: tn.compute_number_of_descendants()
630
+ 2
631
+ sage: tn.number_of_descendants
632
+ 2
633
+ sage: tn3.number_of_descendants
634
+ 1
635
+ sage: tn.compute_depth_of_self_and_children()
636
+ sage: tn3.depth
637
+ 2
638
+ """
639
+ n = 1
640
+ for child in self.children:
641
+ n += child.compute_number_of_descendants()
642
+ self.number_of_descendants = n
643
+ return n
644
+
645
+ def compute_depth_of_self_and_children(self):
646
+ """
647
+ Compute the depth of ``self`` and all descendants.
648
+
649
+ For each TreeNode, sets result as ``attribute self.depth``.
650
+
651
+ EXAMPLES::
652
+
653
+ sage: from sage.graphs.schnyder import TreeNode
654
+ sage: tn = TreeNode(label=5)
655
+ sage: tn2 = TreeNode(label=2,parent=tn)
656
+ sage: tn3 = TreeNode(label=3)
657
+ sage: tn.append_child(tn3)
658
+ sage: tn.compute_number_of_descendants()
659
+ 2
660
+ sage: tn.number_of_descendants
661
+ 2
662
+ sage: tn3.number_of_descendants
663
+ 1
664
+ sage: tn.compute_depth_of_self_and_children()
665
+ sage: tn3.depth
666
+ 2
667
+ """
668
+ if self.parent is None:
669
+ self.depth = 1
670
+ else:
671
+ self.depth = self.parent.depth + 1
672
+ for child in self.children:
673
+ child.compute_depth_of_self_and_children()
674
+
675
+ def append_child(self, child):
676
+ """
677
+ Add a child to list of children.
678
+
679
+ EXAMPLES::
680
+
681
+ sage: from sage.graphs.schnyder import TreeNode
682
+ sage: tn = TreeNode(label=5)
683
+ sage: tn2 = TreeNode(label=2,parent=tn)
684
+ sage: tn3 = TreeNode(label=3)
685
+ sage: tn.append_child(tn3)
686
+ sage: tn.compute_number_of_descendants()
687
+ 2
688
+ sage: tn.number_of_descendants
689
+ 2
690
+ sage: tn3.number_of_descendants
691
+ 1
692
+ sage: tn.compute_depth_of_self_and_children()
693
+ sage: tn3.depth
694
+ 2
695
+ """
696
+ if child in self.children:
697
+ return
698
+ self.children.append(child)
699
+ child.parent = self
700
+
701
+
702
+ def minimal_schnyder_wood(graph, root_edge=None, minimal=True, check=True):
703
+ """
704
+ Return the minimal Schnyder wood of a planar rooted triangulation.
705
+
706
+ INPUT:
707
+
708
+ - ``graph`` -- a planar triangulation, given by a graph with an embedding
709
+
710
+ - ``root_edge`` -- a pair of vertices (default: from ``-1`` to ``-2``);
711
+ the third boundary vertex is then determined using the orientation and
712
+ will be labelled ``-3``
713
+
714
+ - ``minimal`` -- boolean (default: ``True``); whether to return a
715
+ minimal or a maximal Schnyder wood
716
+
717
+ - ``check`` -- boolean (default: ``True``); whether to check if the input
718
+ is a planar triangulation
719
+
720
+ OUTPUT:
721
+
722
+ A planar graph, with edges oriented and colored. The three outer
723
+ edges of the initial graph are removed. For the three outer vertices the
724
+ list of the neighbors stored in the combinatorial embedding is in the order
725
+ of the incident edges between the two incident (and removed) outer edges,
726
+ and not a cyclic shift of it.
727
+
728
+ The algorithm is taken from [Bre2000]_ (section 4.2).
729
+
730
+ EXAMPLES::
731
+
732
+ sage: from sage.graphs.schnyder import minimal_schnyder_wood
733
+ sage: g = Graph([(0,-1),(0,-2),(0,-3),(-1,-2),(-2,-3),
734
+ ....: (-3,-1)], format='list_of_edges')
735
+ sage: g.set_embedding({-1:[-2,0,-3],-2:[-3,0,-1],
736
+ ....: -3:[-1,0,-2],0:[-1,-2,-3]})
737
+ sage: newg = minimal_schnyder_wood(g)
738
+ sage: newg.edges(sort=True)
739
+ [(0, -3, 'red'), (0, -2, 'blue'), (0, -1, 'green')]
740
+ sage: newg.plot(color_by_label={'red':'red','blue':'blue', # needs sage.plot
741
+ ....: 'green':'green',None:'black'})
742
+ Graphics object consisting of 8 graphics primitives
743
+
744
+ A larger example::
745
+
746
+ sage: g = Graph([(0,-1),(0,2),(0,1),(0,-3),(-1,-3),(-1,2),
747
+ ....: (-1,-2),(1,2),(1,-3),(2,-2),(1,-2),(-2,-3)], format='list_of_edges')
748
+ sage: g.set_embedding({-1:[-2,2,0,-3],-2:[-3,1,2,-1],
749
+ ....: -3:[-1,0,1,-2],0:[-1,2,1,-3],1:[-2,-3,0,2],2:[-1,-2,1,0]})
750
+ sage: newg = minimal_schnyder_wood(g)
751
+ sage: newg.edges(sort=True, key=lambda e:(str(e[0]),str(e[1])))
752
+ [(0, -1, 'green'),
753
+ (0, -3, 'red'),
754
+ (0, 2, 'blue'),
755
+ (1, -2, 'blue'),
756
+ (1, -3, 'red'),
757
+ (1, 0, 'green'),
758
+ (2, -1, 'green'),
759
+ (2, -2, 'blue'),
760
+ (2, 1, 'red')]
761
+ sage: newg2 = minimal_schnyder_wood(g, minimal=False)
762
+ sage: newg2.edges(sort=True, key=lambda e:(str(e[0]),str(e[1])))
763
+ [(0, -1, 'green'),
764
+ (0, -3, 'red'),
765
+ (0, 1, 'blue'),
766
+ (1, -2, 'blue'),
767
+ (1, -3, 'red'),
768
+ (1, 2, 'green'),
769
+ (2, -1, 'green'),
770
+ (2, -2, 'blue'),
771
+ (2, 0, 'red')]
772
+
773
+ TESTS::
774
+
775
+ sage: minimal_schnyder_wood(graphs.RandomTriangulation(5))
776
+ Digraph on 5 vertices
777
+ sage: minimal_schnyder_wood(graphs.CompleteGraph(5))
778
+ Traceback (most recent call last):
779
+ ...
780
+ ValueError: not a planar graph
781
+ sage: minimal_schnyder_wood(graphs.WheelGraph(5))
782
+ Traceback (most recent call last):
783
+ ...
784
+ ValueError: not a triangulation
785
+ sage: minimal_schnyder_wood(graphs.OctahedralGraph(),root_edge=(0,5))
786
+ Traceback (most recent call last):
787
+ ...
788
+ ValueError: not a valid root edge
789
+ """
790
+ if root_edge is None:
791
+ a = -1
792
+ b = -2
793
+ else:
794
+ a, b = root_edge
795
+
796
+ if check:
797
+ if not graph.is_planar():
798
+ raise ValueError('not a planar graph')
799
+ if not all(len(u) == 3 for u in graph.faces()):
800
+ raise ValueError('not a triangulation')
801
+ if a not in graph.neighbors(b):
802
+ raise ValueError('not a valid root edge')
803
+
804
+ new_g = DiGraph()
805
+ emb = graph.get_embedding()
806
+
807
+ # finding the third outer vertex c
808
+ emb_b = emb[b]
809
+ idx_a = emb_b.index(a)
810
+ c = emb_b[(idx_a + 1) % len(emb_b)]
811
+
812
+ # initialisation
813
+ for i in emb[c]:
814
+ if i != a and i != b:
815
+ new_g.add_edge((i, -3, 'red'))
816
+
817
+ path = list(emb[c])
818
+ idxa = path.index(a)
819
+ path = path[idxa:] + path[:idxa]
820
+ neighbors_in_path = {i: len([u for u in graph.neighbors(i) if u in path])
821
+ for i in graph}
822
+ removable_nodes = [u for u in path if neighbors_in_path[u] == 2 and
823
+ u != a and u != b]
824
+
825
+ # iterated path shortening
826
+ while len(path) > 2:
827
+ if minimal:
828
+ v = removable_nodes[-1] # node to be removed from path
829
+ else:
830
+ v = removable_nodes[0] # node to be removed from path
831
+ idx_v = path.index(v)
832
+ left = path[idx_v - 1]
833
+ new_g.add_edge((v, left, 'green'))
834
+ right = path[idx_v + 1]
835
+ new_g.add_edge((v, right, 'blue'))
836
+ neighbors_v = emb[v]
837
+ idx_left = neighbors_v.index(left)
838
+ neighbors_v = neighbors_v[idx_left:] + neighbors_v[:idx_left]
839
+ idx_right = neighbors_v.index(right)
840
+ inside = neighbors_v[1:idx_right]
841
+ new_g.add_edges([(w, v, 'red') for w in inside])
842
+ path = path[:idx_v] + inside + path[idx_v + 1:]
843
+ # updating the table of neighbors_in_path
844
+ for w in inside:
845
+ for x in graph.neighbors(w):
846
+ neighbors_in_path[x] += 1
847
+ for x in graph.neighbors(v):
848
+ neighbors_in_path[x] -= 1
849
+ # updating removable nodes
850
+ removable_nodes = [u for u in path if neighbors_in_path[u] == 2 and
851
+ u != a and u != b]
852
+
853
+ def relabel(w):
854
+ return -3 if w == c else w
855
+
856
+ emb = {relabel(v): [relabel(u) for u in emb[v]] for v in graph}
857
+ for u, v, w in (a, b, -3), (b, -3, a), (-3, a, b):
858
+ idx = emb[u].index(v)
859
+ if idx == 0:
860
+ emb[u] = emb[u][1:-1]
861
+ else:
862
+ emb[u] = emb[u][idx+1:] + emb[u][:idx-1]
863
+
864
+ new_g.set_embedding(emb)
865
+ return new_g