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
sage/graphs/genus.pyx ADDED
@@ -0,0 +1,622 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ """
3
+ Genus
4
+
5
+ This file contains a moderately-optimized implementation to compute the
6
+ genus of simple connected graph. It runs about a thousand times faster
7
+ than the previous version in Sage, not including asymptotic improvements.
8
+
9
+ The algorithm works by enumerating combinatorial embeddings of a graph,
10
+ and computing the genus of these via the Euler characteristic. We view
11
+ a combinatorial embedding of a graph as a pair of permutations `v,e`
12
+ which act on a set `B` of `2|E(G)|` "darts". The permutation `e` is an
13
+ involution, and its orbits correspond to edges in the graph. Similarly,
14
+ The orbits of `v` correspond to the vertices of the graph, and those of
15
+ `f = ve` correspond to faces of the embedded graph.
16
+
17
+ The requirement that the group `<v,e>` acts transitively on `B` is
18
+ equivalent to the graph being connected. We can compute the genus of a
19
+ graph by
20
+
21
+ `2 - 2g = V - E + F`
22
+
23
+ where `E`, `V`, and `F` denote the number of orbits of `e`, `v`, and
24
+ `f` respectively.
25
+
26
+ We make several optimizations to the naive algorithm, which are
27
+ described throughout the file.
28
+ """
29
+
30
+ # ****************************************************************************
31
+ # Copyright (C) 2010 Tom Boothby <tomas.boothby@gmail.com>
32
+ #
33
+ # This program is free software: you can redistribute it and/or modify
34
+ # it under the terms of the GNU General Public License as published by
35
+ # the Free Software Foundation, either version 2 of the License, or
36
+ # (at your option) any later version.
37
+ # https://www.gnu.org/licenses/
38
+ # ****************************************************************************
39
+
40
+ from libc.string cimport memcpy
41
+ from memory_allocator cimport MemoryAllocator
42
+ from cysignals.signals cimport sig_on, sig_off
43
+
44
+ # cimport sage.combinat.permutation_cython
45
+
46
+ from sage.combinat.permutation_cython cimport next_swap, reset_swap
47
+
48
+ from sage.graphs.base.dense_graph cimport DenseGraph
49
+ from sage.graphs.graph import Graph
50
+
51
+
52
+ cdef inline int edge_map(int i) noexcept:
53
+ """
54
+ We might as well make the edge map nice, since the vertex map is so
55
+ slippery. This is the fastest way I could find to establish the
56
+ correspondence `i <-> i + 1` if `i` is even.
57
+ """
58
+ return i - 2 * (i & 1) + 1
59
+
60
+
61
+ cdef class simple_connected_genus_backtracker:
62
+ r"""
63
+ A class which computes the genus of a DenseGraph through an extremely slow
64
+ but relatively optimized algorithm. This is "only" exponential for graphs
65
+ of bounded degree, and feels pretty snappy for 3-regular graphs. The
66
+ generic runtime is
67
+
68
+ `|V(G)| \prod_{v \in V(G)} (deg(v)-1)!`
69
+
70
+ which is `2^{|V(G)|}` for 3-regular graphs, and can achieve `n(n-1)!^{n}`
71
+ for the complete graph on `n` vertices. We can handily compute the genus of
72
+ `K_6` in milliseconds on modern hardware, but `K_7` may take a few days.
73
+ Don't bother with `K_8`, or any graph with more than one vertex of degree 10
74
+ or worse, unless you can find an a priori lower bound on the genus and
75
+ expect the graph to have that genus.
76
+
77
+ .. WARNING::
78
+
79
+ THIS MAY SEGFAULT OR HANG ON:
80
+ * DISCONNECTED GRAPHS
81
+ * DIRECTED GRAPHS
82
+ * LOOPED GRAPHS
83
+ * MULTIGRAPHS
84
+
85
+ EXAMPLES::
86
+
87
+ sage: import sage.graphs.genus
88
+ sage: G = graphs.CompleteGraph(6)
89
+ sage: G = Graph(G, sparse=False)
90
+ sage: bt = sage.graphs.genus.simple_connected_genus_backtracker(G._backend.c_graph()[0])
91
+ sage: bt.genus() #long time
92
+ 1
93
+ sage: bt.genus(cutoff=1)
94
+ 1
95
+ sage: G = graphs.PetersenGraph()
96
+ sage: G = Graph(G, sparse=False)
97
+ sage: bt = sage.graphs.genus.simple_connected_genus_backtracker(G._backend.c_graph()[0])
98
+ sage: bt.genus()
99
+ 1
100
+ sage: G = graphs.FlowerSnark()
101
+ sage: G = Graph(G, sparse=False)
102
+ sage: bt = sage.graphs.genus.simple_connected_genus_backtracker(G._backend.c_graph()[0])
103
+ sage: bt.genus()
104
+ 2
105
+ """
106
+ cdef MemoryAllocator mem
107
+ cdef int **vertex_darts
108
+ cdef int *face_map
109
+ cdef int *degree
110
+ cdef int *visited
111
+ cdef int *face_freeze
112
+ cdef int **swappers
113
+ cdef int num_darts, num_verts, num_cycles, record_genus
114
+
115
+ def __init__(self, DenseGraph G):
116
+ """
117
+ Initialize the genus_backtracker object.
118
+
119
+ TESTS::
120
+
121
+ sage: import sage.graphs.genus
122
+ sage: G = Graph(sparse=False) # indirect doctest
123
+ sage: gb = sage.graphs.genus.simple_connected_genus_backtracker(G._backend.c_graph()[0])
124
+ sage: G = Graph(graphs.CompleteGraph(4), sparse=False)
125
+ sage: gb = sage.graphs.genus.simple_connected_genus_backtracker(G._backend.c_graph()[0])
126
+ sage: gb.genus()
127
+ 0
128
+ """
129
+ self.num_darts = G.num_arcs
130
+ self.num_verts = G.num_verts
131
+
132
+ if self.num_verts <= 1:
133
+ return
134
+
135
+ # Allocate arrays
136
+ self.mem = MemoryAllocator()
137
+ self.degree = <int *> self.mem.malloc(self.num_verts * sizeof(int))
138
+ self.face_map = <int *> self.mem.malloc(self.num_darts * sizeof(int))
139
+ self.visited = <int *> self.mem.malloc(self.num_darts * sizeof(int))
140
+ self.face_freeze = <int *> self.mem.malloc(self.num_darts * sizeof(int))
141
+ self.vertex_darts = <int **>self.mem.malloc(self.num_verts * sizeof(int *))
142
+ self.swappers = <int **>self.mem.malloc(self.num_verts * sizeof(int *))
143
+ cdef int *w = <int *> self.mem.malloc((self.num_verts + self.num_darts) * sizeof(int))
144
+ cdef int *s = <int *> self.mem.malloc(2 * (self.num_darts - self.num_verts) * sizeof(int))
145
+
146
+ cdef int i, j, dv, u, v
147
+
148
+ for v in range(self.num_verts):
149
+ if not G.has_vertex(v):
150
+ raise ValueError("please relabel G so vertices are 0, ..., n-1")
151
+
152
+ dv = G.in_degrees[v]
153
+ self.degree[v] = 0
154
+ self.vertex_darts[v] = w
155
+ w += dv + 1
156
+
157
+ self.swappers[v] = s
158
+ s += 2 * (dv - 1)
159
+
160
+ i = 0
161
+ for v in range(self.num_verts):
162
+ dv = self.degree[v]
163
+
164
+ # we use self.face_map as a temporary int array to hold
165
+ # neighbors of v since it will be overwritten shortly.
166
+ G.in_neighbors_unsafe(v, self.face_map, G.in_degrees[v])
167
+ for j in range(G.in_degrees[v]):
168
+ u = self.face_map[j]
169
+ if u < v:
170
+ # edge hasn't been seen yet
171
+ self.vertex_darts[u][self.degree[u]] = i
172
+ self.vertex_darts[v][dv] = i + 1
173
+ self.degree[u] += 1
174
+ dv += 1
175
+ i += 2
176
+
177
+ self.degree[v] = dv
178
+
179
+ for v in range(self.num_verts):
180
+ dv = self.degree[v]
181
+ w = self.vertex_darts[v]
182
+ w[dv] = w[0]
183
+ for i in range(dv):
184
+ u = w[i]
185
+ self.face_map[edge_map(u)] = w[i + 1]
186
+
187
+ self.freeze_face()
188
+
189
+ # good for debugging
190
+ # def dump(self):
191
+ # cdef int v, j
192
+ # print("vertex darts:", end="")
193
+ # for v in range(self.num_verts):
194
+ # print('(', end="")
195
+ # for j in range(self.degree[v] + 1):
196
+ # print(self.vertex_darts[v][j], end="")
197
+ # print(')', end="")
198
+ # print("\n")
199
+
200
+ # print("face map: [", end="")
201
+ # for v in range(self.num_darts):
202
+ # print(self.face_map[v], end="")
203
+ # print(']')
204
+
205
+ cdef inline void freeze_face(self) noexcept:
206
+ """
207
+ Quickly store the current face_map so we can recover
208
+ the embedding it corresponds to later.
209
+ """
210
+ memcpy(self.face_freeze, self.face_map, self.num_darts * sizeof(int))
211
+
212
+ def get_embedding(self):
213
+ """
214
+ Return an embedding for the graph.
215
+
216
+ If ``min_genus_backtrack`` has been called with ``record_embedding =
217
+ True``, then this will return the first minimal embedding that we found.
218
+ Otherwise, this returns the first embedding considered.
219
+
220
+ EXAMPLES::
221
+
222
+ sage: import sage.graphs.genus
223
+ sage: G = Graph(graphs.CompleteGraph(5), sparse=False)
224
+ sage: gb = sage.graphs.genus.simple_connected_genus_backtracker(G._backend.c_graph()[0])
225
+ sage: gb.genus(record_embedding=True)
226
+ 1
227
+ sage: gb.get_embedding()
228
+ {0: [1, 2, 3, 4], 1: [0, 2, 3, 4], 2: [0, 1, 4, 3], 3: [0, 2, 1, 4], 4: [0, 3, 1, 2]}
229
+ sage: G = Graph(sparse=False)
230
+ sage: G.add_edge(0,1)
231
+ sage: gb = sage.graphs.genus.simple_connected_genus_backtracker(G._backend.c_graph()[0])
232
+ sage: gb.get_embedding()
233
+ {0: [1], 1: [0]}
234
+ sage: G = Graph(sparse=False)
235
+ sage: gb = sage.graphs.genus.simple_connected_genus_backtracker(G._backend.c_graph()[0])
236
+ sage: gb.get_embedding()
237
+ {}
238
+ """
239
+ if not self.num_verts:
240
+ return {}
241
+ elif self.num_verts == 1:
242
+ return {0: []}
243
+
244
+ cdef int i, j, v
245
+ cdef int *w
246
+ cdef int *face_map = self.face_freeze
247
+ cdef list darts_to_verts = [0 for i in range(self.num_darts)]
248
+ cdef dict embedding = {}
249
+ cdef list orbit_v
250
+
251
+ for v in range(self.num_verts):
252
+ w = self.vertex_darts[v]
253
+ for i in range(self.degree[v]):
254
+ darts_to_verts[w[i]] = v
255
+
256
+ for v in range(self.num_verts):
257
+ w = self.vertex_darts[v]
258
+ i = w[0]
259
+ orbit_v = [darts_to_verts[edge_map(i)]]
260
+
261
+ j = face_map[edge_map(i)]
262
+ while j != i:
263
+ orbit_v.append(darts_to_verts[edge_map(j)])
264
+ j = face_map[edge_map(j)]
265
+
266
+ embedding[v] = orbit_v
267
+
268
+ return embedding
269
+
270
+ cdef int run_cycle(self, int i) noexcept:
271
+ r"""
272
+ Mark off the orbit of `i` under face_map.
273
+
274
+ If `i` has been visited recently, bail immediately and return 0.
275
+ Otherwise, visit each vertex in the orbit of `i` and set
276
+ `self.visited[j] = k`, where `j` is the `k`-th element in the orbit
277
+ of `i`. Then, return 1.
278
+
279
+ In this manner, we are able to quickly check if a particular element has
280
+ been visited recently. Moreover, we are able to distinguish what order
281
+ three elements of a single orbit come in. This is important for
282
+ `self.flip()`, and discussed in more detail there.
283
+ """
284
+ if self.visited[i]:
285
+ return 0
286
+
287
+ cdef int counter = 1
288
+ cdef int j = self.face_map[i]
289
+
290
+ self.visited[i] = 1
291
+ counter += 1
292
+ while i != j:
293
+ self.visited[j] = 1 + counter
294
+ counter += 1
295
+ j = self.face_map[j]
296
+ return 1
297
+
298
+ cdef void flip(self, int v, int i) noexcept:
299
+ r"""
300
+ This is where the real work happens. Once cycles have been counted for
301
+ the initial face_map, we make small local changes, and look at their
302
+ effect on the number of cycles.
303
+
304
+ Consider a vertex whose embedding is given by the cycle
305
+
306
+ `self.vertex_darts[v] = [..., v0, v1, v2, ... ]`.
307
+
308
+ which implies that the vertex map has the cycle
309
+
310
+ `... -> v0 -> v1 -> v2 -> ... `
311
+
312
+ and say we'd like to exchange a1 and a2. Then, we'll change the vertex
313
+ map to
314
+
315
+ `... -> v0 -> v2 -> v1 -> ...`
316
+
317
+ and when this happens, we change the face map orbit of `e0 = e(av)`,
318
+ `e1 = e(v1)`, and `e2 = e(v2)`, where `e` denotes the edge map.
319
+
320
+ In fact, the only orbits that can change are those of `e0`, `e1`, and
321
+ `e2`. Thus, to determine the effect of the flip on the cycle structure,
322
+ we need only consider these orbits.
323
+
324
+ We find that the set of possibilities for a flip to change the number of
325
+ orbits among these three elements is very small. In particular,
326
+
327
+ * If the three elements belong to distinct orbits, a flip joins them
328
+ into a single orbit.
329
+
330
+ * If the three elements are among exactly two orbits, a flip does not
331
+ change that fact (though it does break the paired elements and make a
332
+ new pair, all we care about is the number of cycles)
333
+
334
+ * If all three elements are in the same orbit, a flip either disconnects
335
+ them into three distinct orbits, or maintains status quo.
336
+
337
+ To differentiate these situations, we need only to look at the order of
338
+ `v0`, `v1`, and `v2` under the orbit. If `e0 -> ... -> e2 -> ... -> e1`
339
+ before the flip, the cycle breaks into three. Otherwise, the number of
340
+ cycles stays the same.
341
+ """
342
+ cdef int *w = self.vertex_darts[v]
343
+ cdef int *face_map = self.face_map
344
+
345
+ cdef int v0, v1, v2, e0, e1, e2, f0, f1, f2, j, k
346
+
347
+ v0 = w[i - 1]
348
+ v1 = w[i]
349
+ v2 = w[i + 1]
350
+
351
+ e0 = edge_map(v0)
352
+ e1 = edge_map(v1)
353
+ e2 = edge_map(v2)
354
+
355
+ f0 = face_map[e0]
356
+ f1 = face_map[e1]
357
+ f2 = face_map[e2]
358
+
359
+ face_map[e0] = -1
360
+ face_map[e1] = -2
361
+ face_map[e2] = -3
362
+
363
+ j = face_map[f0]
364
+ while j >= 0:
365
+ j = face_map[j]
366
+ if j != -2:
367
+ k = face_map[f1]
368
+ while k >= 0:
369
+ k = face_map[k]
370
+
371
+ # Magic function follows. There are only four possibilities for j
372
+ # and k since j != -2. We use magic to avoid branching.
373
+ # j | k | MF(j,k)
374
+ # ---+----+--------
375
+ # -1 | -2 | -2
376
+ # -1 | -3 | 0
377
+ # -3 | -1 | 2
378
+ # -3 | -2 | 0
379
+
380
+ self.num_cycles += (2 * k + 1 - j) % 4
381
+
382
+ face_map[e0] = v2
383
+ face_map[e1] = f2
384
+ face_map[e2] = v1
385
+
386
+ w[i] = v2
387
+ w[i + 1] = v1
388
+
389
+ cdef int count_cycles(self) noexcept:
390
+ """
391
+ Count all cycles.
392
+ """
393
+ cdef int i
394
+ self.num_cycles = 0
395
+
396
+ for i in range(self.num_darts):
397
+ self.visited[i] = 0
398
+
399
+ for i in range(self.num_darts):
400
+ self.num_cycles += self.run_cycle(i)
401
+
402
+ def genus(self, int style=1, int cutoff=0, bint record_embedding=False):
403
+ r"""
404
+ Compute the minimal or maximal genus of ``self``'s graph.
405
+
406
+ Note, this is a remarkably naive algorithm for a very difficult problem.
407
+ Most interesting cases will take millennia to finish, with the exception
408
+ of graphs with max degree 3.
409
+
410
+ INPUT:
411
+
412
+ - ``style`` -- integer (default: `1`); find minimum genus if 1,
413
+ maximum genus if 2
414
+
415
+ - ``cutoff`` -- integer (default: `0`); stop searching if search style
416
+ is 1 and ``genus`` `\leq` ``cutoff``, or if style is 2 and ``genus``
417
+ `\geq` ``cutoff``. This is useful where the genus of the graph has a
418
+ known bound.
419
+
420
+ - ``record_embedding`` -- boolean (default: ``False``); whether or not
421
+ to remember the best embedding seen. This embedding can be retrieved
422
+ with ``self.get_embedding()``.
423
+
424
+ OUTPUT: the minimal or maximal genus for ``self``'s graph
425
+
426
+ EXAMPLES::
427
+
428
+ sage: import sage.graphs.genus
429
+ sage: G = Graph(graphs.CompleteGraph(5), sparse=False)
430
+ sage: gb = sage.graphs.genus.simple_connected_genus_backtracker(G._backend.c_graph()[0])
431
+ sage: gb.genus(cutoff=2, record_embedding=True)
432
+ 2
433
+ sage: E = gb.get_embedding()
434
+ sage: gb.genus(record_embedding=False)
435
+ 1
436
+ sage: gb.get_embedding() == E
437
+ True
438
+ sage: gb.genus(style=2, cutoff=5)
439
+ 3
440
+ sage: G = Graph(sparse=False)
441
+ sage: gb = sage.graphs.genus.simple_connected_genus_backtracker(G._backend.c_graph()[0])
442
+ sage: gb.genus()
443
+ 0
444
+ """
445
+ cdef int g
446
+
447
+ # in the original genus implementation, this case resulted in infinite
448
+ # recursion. oops. Let's skip that.
449
+ if self.num_verts <= 0:
450
+ return 0
451
+ sig_on()
452
+ if style == 1:
453
+ g = self.genus_backtrack(cutoff, record_embedding, &min_genus_check)
454
+ elif style == 2:
455
+ g = self.genus_backtrack(cutoff, record_embedding, &max_genus_check)
456
+ sig_off()
457
+ return g
458
+
459
+ cdef void reset_swap(self, int v) noexcept:
460
+ """
461
+ Reset the swapper associated with vertex ``v``.
462
+ """
463
+ cdef int d = self.degree[v] - 1
464
+ reset_swap(d, self.swappers[v], self.swappers[v] + d)
465
+
466
+ cdef int next_swap(self, int v) noexcept:
467
+ """
468
+ Compute and return the next swap associated with the vertex ``v``.
469
+ """
470
+ cdef int d = self.degree[v] - 1
471
+ return next_swap(d, self.swappers[v], self.swappers[v] + d)
472
+
473
+ cdef int genus_backtrack(self,
474
+ int cutoff,
475
+ bint record_embedding,
476
+ (int (*)(simple_connected_genus_backtracker, int, bint, int) noexcept) check_embedding) noexcept:
477
+ """
478
+ Here's the main backtracking routine.
479
+
480
+ We iterate over all embeddings of ``self``'s graph by considering all
481
+ cyclic orderings of ``self.vertex_darts``. We use the Steinhaus-
482
+ Johnson-Trotter algorithm to enumerate these by walking over a poly-ary
483
+ Gray code, and each time the Gray code would flip a bit, we apply the
484
+ next adjacent transposition from S-J-T at that vertex.
485
+
486
+ We start by counting the number of cycles for our initial embedding.
487
+ From that point forward, we compute the amount that each flip changes
488
+ the number of cycles.
489
+ """
490
+ cdef int next_swap, vertex
491
+
492
+ for vertex in range(self.num_verts):
493
+ self.reset_swap(vertex)
494
+
495
+ vertex = self.num_verts - 1
496
+
497
+ self.count_cycles()
498
+
499
+ if check_embedding(self, cutoff, record_embedding, 1):
500
+ return self.record_genus
501
+
502
+ next_swap = self.next_swap(vertex)
503
+ while True:
504
+ while next_swap == -1:
505
+ self.reset_swap(vertex)
506
+ vertex -= 1
507
+ if vertex < 0:
508
+ return self.record_genus
509
+ next_swap = self.next_swap(vertex)
510
+ self.flip(vertex, next_swap + 1)
511
+
512
+ if check_embedding(self, cutoff, record_embedding, 0):
513
+ return self.record_genus
514
+
515
+ vertex = self.num_verts-1
516
+ next_swap = self.next_swap(vertex)
517
+
518
+ cdef int min_genus_check(simple_connected_genus_backtracker self,
519
+ int cutoff,
520
+ bint record_embedding,
521
+ int initial) noexcept:
522
+ """
523
+ Search for the minimal genus.
524
+
525
+ If we find a genus <= cutoff, return 1 to quit entirely.
526
+ If we find a better genus than previously recorded, keep track of that, and
527
+ if record_embedding is set, record the face map with self.freeze_face()
528
+ """
529
+ cdef int g = 1 - (self.num_verts - self.num_darts / 2 + self.num_cycles) / 2
530
+ if g < self.record_genus or initial == 1:
531
+ self.record_genus = g
532
+ if record_embedding:
533
+ self.freeze_face()
534
+ if g <= cutoff:
535
+ return 1
536
+ return 0
537
+
538
+ cdef int max_genus_check(simple_connected_genus_backtracker self,
539
+ int cutoff,
540
+ bint record_embedding,
541
+ int initial) noexcept:
542
+ """
543
+ Same as min_genus_check, but search for a maximum.
544
+ """
545
+ cdef int g = 1 - (self.num_verts - self.num_darts / 2 + self.num_cycles) / 2
546
+ if g > self.record_genus or initial == 1:
547
+ self.record_genus = g
548
+ if record_embedding:
549
+ self.freeze_face()
550
+ if g >= cutoff:
551
+ return 1
552
+ return 0
553
+
554
+
555
+ def simple_connected_graph_genus(G, set_embedding=False, check=True, minimal=True):
556
+ """
557
+ Compute the genus of a simple connected graph.
558
+
559
+ .. WARNING::
560
+
561
+ THIS MAY SEGFAULT OR HANG ON:
562
+ * DISCONNECTED GRAPHS
563
+ * DIRECTED GRAPHS
564
+ * LOOPED GRAPHS
565
+ * MULTIGRAPHS
566
+
567
+ DO NOT CALL WITH ``check = False`` UNLESS YOU ARE CERTAIN.
568
+
569
+ EXAMPLES::
570
+
571
+ sage: # needs planarity
572
+ sage: import sage.graphs.genus
573
+ sage: from sage.graphs.genus import simple_connected_graph_genus as genus
574
+ sage: [genus(g) for g in graphs(6) if g.is_connected()].count(1)
575
+ 13
576
+ sage: G = graphs.FlowerSnark()
577
+ sage: genus(G) # see [1]
578
+ 2
579
+ sage: G = graphs.BubbleSortGraph(4)
580
+ sage: genus(G)
581
+ 0
582
+ sage: G = graphs.OddGraph(3)
583
+ sage: genus(G)
584
+ 1
585
+
586
+ REFERENCES:
587
+
588
+ [1] :doi:`10.1007/s00373-007-0729-9`
589
+ """
590
+ cdef int style, cutoff
591
+ oG = G # original graph
592
+
593
+ if minimal and G.is_planar(set_embedding=set_embedding):
594
+ return 0
595
+
596
+ if check:
597
+ if not G.is_connected():
598
+ raise ValueError("cannot compute the genus of a disconnected graph")
599
+
600
+ if G.is_directed() or G.has_multiple_edges() or G.has_loops():
601
+ G = G.to_simple()
602
+
603
+ G, vmap = G.relabel(inplace=False, return_map=True)
604
+ backmap = {u: v for v, u in vmap.items()}
605
+ G = Graph(G, sparse=False)
606
+ GG = simple_connected_genus_backtracker(G._backend.c_graph()[0])
607
+
608
+ if minimal:
609
+ style = 1
610
+ cutoff = 1
611
+ else:
612
+ style = 2
613
+ cutoff = 1 + (G.num_edges() - G.num_verts()) / 2 # rounding here is ok
614
+
615
+ g = GG.genus(style=style, cutoff=cutoff, record_embedding=set_embedding)
616
+ if set_embedding:
617
+ oE = {}
618
+ E = GG.get_embedding()
619
+ for v in E:
620
+ oE[backmap[v]] = [backmap[x] for x in E[v]]
621
+ oG.set_embedding(oE)
622
+ return g