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,713 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ r"""
3
+ Tutte polynomial
4
+
5
+ This module implements a deletion-contraction algorithm for computing
6
+ the Tutte polynomial as described in the paper [HPR2010]_.
7
+
8
+ .. csv-table::
9
+ :class: contentstable
10
+ :widths: 30, 70
11
+ :delim: |
12
+
13
+ :func:`tutte_polynomial` | Compute the Tutte polynomial of the input graph
14
+
15
+ Authors:
16
+
17
+ - Mike Hansen (06-2013), Implemented the algorithm.
18
+ - Jernej Azarija (06-2013), Tweaked the code, added documentation
19
+
20
+ Definition
21
+ -----------
22
+
23
+ Given a graph `G`, with `n` vertices and `m` edges and `k(G)`
24
+ connected components we define the Tutte polynomial of `G` as
25
+
26
+ .. MATH::
27
+
28
+ \sum_H (x-1) ^{k(H) - c} (y-1)^{k(H) - |E(H)|-n}
29
+
30
+ where the sum ranges over all induced subgraphs `H` of `G`.
31
+
32
+ Functions
33
+ ---------
34
+ """
35
+
36
+ from contextlib import contextmanager
37
+ from sage.misc.lazy_attribute import lazy_attribute
38
+ from sage.misc.misc_c import prod
39
+ from sage.rings.integer_ring import ZZ
40
+ from sage.misc.decorators import sage_wraps
41
+
42
+ ######################
43
+ # Graph Modification #
44
+ ######################
45
+
46
+
47
+ @contextmanager
48
+ def removed_multiedge(G, unlabeled_edge):
49
+ r"""
50
+ A context manager which removes an edge with multiplicity from the
51
+ graph `G` and restores it upon exiting.
52
+
53
+ EXAMPLES::
54
+
55
+ sage: from sage.graphs.tutte_polynomial import removed_multiedge
56
+ sage: G = Graph(multiedges=True)
57
+ sage: G.add_edges([(0,1,'a'),(0,1,'b')])
58
+ sage: G.edges(sort=True)
59
+ [(0, 1, 'a'), (0, 1, 'b')]
60
+ sage: with removed_multiedge(G,(0,1)) as Y:
61
+ ....: G.edges(sort=True)
62
+ []
63
+ sage: G.edges(sort=True)
64
+ [(0, 1, 'a'), (0, 1, 'b')]
65
+ """
66
+ u, v = unlabeled_edge
67
+ edges = G.edge_boundary([u], [v], labels=True)
68
+ G.delete_multiedge(u, v)
69
+ try:
70
+ yield
71
+ finally:
72
+ G.add_edges(edges)
73
+
74
+
75
+ @contextmanager
76
+ def removed_edge(G, edge):
77
+ r"""
78
+ A context manager which removes an edge from the graph `G` and
79
+ restores it upon exiting.
80
+
81
+ EXAMPLES::
82
+
83
+ sage: from sage.graphs.tutte_polynomial import removed_edge
84
+ sage: G = Graph()
85
+ sage: G.add_edge(0,1)
86
+ sage: G.edges(sort=True)
87
+ [(0, 1, None)]
88
+ sage: with removed_edge(G,(0,1)) as Y:
89
+ ....: G.edges(sort=True); G.vertices(sort=True)
90
+ []
91
+ [0, 1]
92
+ sage: G.edges(sort=True)
93
+ [(0, 1, None)]
94
+ """
95
+ G.delete_edge(edge)
96
+ try:
97
+ yield
98
+ finally:
99
+ G.add_edge(edge)
100
+
101
+
102
+ @contextmanager
103
+ def contracted_edge(G, unlabeled_edge):
104
+ r"""
105
+ Delete the first vertex in the edge, and make all the edges that
106
+ went from it go to the second vertex.
107
+
108
+ EXAMPLES::
109
+
110
+ sage: from sage.graphs.tutte_polynomial import contracted_edge
111
+ sage: G = Graph(multiedges=True)
112
+ sage: G.add_edges([(0,1,'a'),(1,2,'b'),(0,3,'c')])
113
+ sage: G.edges(sort=True)
114
+ [(0, 1, 'a'), (0, 3, 'c'), (1, 2, 'b')]
115
+ sage: with contracted_edge(G,(0,1)) as Y:
116
+ ....: G.edges(sort=True); G.vertices(sort=True)
117
+ [(1, 2, 'b'), (1, 3, 'c')]
118
+ [1, 2, 3]
119
+ sage: G.edges(sort=True)
120
+ [(0, 1, 'a'), (0, 3, 'c'), (1, 2, 'b')]
121
+ """
122
+ v1, v2 = unlabeled_edge
123
+ loops = G.allows_loops()
124
+
125
+ v1_edges = G.edges_incident(v1)
126
+ G.delete_vertex(v1)
127
+ added_edges = []
128
+
129
+ for start, end, label in v1_edges:
130
+ other_vertex = start if start != v1 else end
131
+ edge = (other_vertex, v2, label)
132
+ if loops or other_vertex != v2:
133
+ G.add_edge(edge)
134
+ added_edges.append(edge)
135
+
136
+ try:
137
+ yield
138
+ finally:
139
+ for edge in added_edges:
140
+ G.delete_edge(edge)
141
+ for edge in v1_edges:
142
+ G.add_edge(edge)
143
+
144
+
145
+ @contextmanager
146
+ def removed_loops(G):
147
+ r"""
148
+ A context manager which removes all the loops in the graph `G`.
149
+ It yields a list of the loops, and restores the loops upon
150
+ exiting.
151
+
152
+ EXAMPLES::
153
+
154
+ sage: from sage.graphs.tutte_polynomial import removed_loops
155
+ sage: G = Graph(multiedges=True, loops=True)
156
+ sage: G.add_edges([(0,1,'a'),(1,2,'b'),(0,0,'c')])
157
+ sage: G.edges(sort=True)
158
+ [(0, 0, 'c'), (0, 1, 'a'), (1, 2, 'b')]
159
+ sage: with removed_loops(G) as Y:
160
+ ....: G.edges(sort=True); G.vertices(sort=True); Y
161
+ [(0, 1, 'a'), (1, 2, 'b')]
162
+ [0, 1, 2]
163
+ [(0, 0, 'c')]
164
+ sage: G.edges(sort=True)
165
+ [(0, 0, 'c'), (0, 1, 'a'), (1, 2, 'b')]
166
+ """
167
+ loops = G.loops()
168
+ for edge in loops:
169
+ G.delete_edge(edge)
170
+ try:
171
+ yield loops
172
+ finally:
173
+ for edge in loops:
174
+ G.add_edge(edge)
175
+
176
+
177
+ def underlying_graph(G):
178
+ r"""
179
+ Given a graph `G` with multi-edges, returns a graph where all the
180
+ multi-edges are replaced with a single edge.
181
+
182
+ EXAMPLES::
183
+
184
+ sage: from sage.graphs.tutte_polynomial import underlying_graph
185
+ sage: G = Graph(multiedges=True)
186
+ sage: G.add_edges([(0,1,'a'),(0,1,'b')])
187
+ sage: G.edges(sort=True)
188
+ [(0, 1, 'a'), (0, 1, 'b')]
189
+ sage: underlying_graph(G).edges(sort=True)
190
+ [(0, 1, None)]
191
+ """
192
+ from sage.graphs.graph import Graph
193
+ g = Graph()
194
+ g.allow_loops(True)
195
+ for edge in set(G.edges(sort=False, labels=False)):
196
+ g.add_edge(edge)
197
+ return g
198
+
199
+
200
+ def edge_multiplicities(G):
201
+ r"""
202
+ Return the dictionary of multiplicities of the edges in the
203
+ graph `G`.
204
+
205
+ EXAMPLES::
206
+
207
+ sage: from sage.graphs.tutte_polynomial import edge_multiplicities
208
+ sage: G = Graph({1: [2,2,3], 2: [2], 3: [4,4], 4: [2,2,2]})
209
+ sage: sorted(edge_multiplicities(G).items())
210
+ [((1, 2), 2), ((1, 3), 1), ((2, 2), 1), ((2, 4), 3), ((3, 4), 2)]
211
+ """
212
+ d = {}
213
+ for edge in G.edges(sort=False, labels=False):
214
+ d[edge] = d.setdefault(edge, 0) + 1
215
+ return d
216
+
217
+ ########
218
+ # Ears #
219
+ ########
220
+
221
+
222
+ class Ear:
223
+ r"""
224
+ An ear is a sequence of vertices
225
+
226
+ Here is the definition from [HPR2010]_:
227
+
228
+ An ear in a graph is a path `v_1 - v_2 - \dots - v_n - v_{n+1}`
229
+ where `d(v_1) > 2`, `d(v_{n+1}) > 2` and
230
+ `d(v_2) = d(v_3) = \dots = d(v_n) = 2`.
231
+
232
+ A cycle is viewed as a special ear where `v_1 = v_{n+1}` and the
233
+ restriction on the degree of this vertex is lifted.
234
+
235
+ INPUT:
236
+ """
237
+ def __init__(self, graph, end_points, interior, is_cycle):
238
+ """
239
+ EXAMPLES::
240
+
241
+ sage: G = graphs.PathGraph(4)
242
+ sage: G.add_edges([(0,4),(0,5),(3,6),(3,7)])
243
+ sage: from sage.graphs.tutte_polynomial import Ear
244
+ sage: E = Ear(G,[0,3],[1,2],False)
245
+ """
246
+ self.end_points = end_points
247
+ self.interior = interior
248
+ self.is_cycle = is_cycle
249
+ self.graph = graph
250
+
251
+ @property
252
+ def s(self):
253
+ """
254
+ Return the number of distinct edges in this ear.
255
+
256
+ EXAMPLES::
257
+
258
+ sage: G = graphs.PathGraph(4)
259
+ sage: G.add_edges([(0,4),(0,5),(3,6),(3,7)])
260
+ sage: from sage.graphs.tutte_polynomial import Ear
261
+ sage: E = Ear(G,[0,3],[1,2],False)
262
+ sage: E.s
263
+ 3
264
+ """
265
+ return len(self.interior) + 1
266
+
267
+ @property
268
+ def vertices(self):
269
+ """
270
+ Return the vertices of this ear.
271
+
272
+ EXAMPLES::
273
+
274
+ sage: G = graphs.PathGraph(4)
275
+ sage: G.add_edges([(0,4),(0,5),(3,6),(3,7)])
276
+ sage: from sage.graphs.tutte_polynomial import Ear
277
+ sage: E = Ear(G,[0,3],[1,2],False)
278
+ sage: E.vertices
279
+ [0, 1, 2, 3]
280
+ """
281
+ return sorted(self.end_points + self.interior)
282
+
283
+ @lazy_attribute
284
+ def unlabeled_edges(self):
285
+ """
286
+ Return the edges in this ear.
287
+
288
+ EXAMPLES::
289
+
290
+ sage: G = graphs.PathGraph(4)
291
+ sage: G.add_edges([(0,4),(0,5),(3,6),(3,7)])
292
+ sage: from sage.graphs.tutte_polynomial import Ear
293
+ sage: E = Ear(G,[0,3],[1,2],False)
294
+ sage: E.unlabeled_edges
295
+ [(0, 1), (1, 2), (2, 3)]
296
+ """
297
+ return self.graph.edges_incident(vertices=self.interior, labels=False)
298
+
299
+ @staticmethod
300
+ def find_ear(g):
301
+ """
302
+ Finds the first ear in a graph.
303
+
304
+ EXAMPLES::
305
+
306
+ sage: G = graphs.PathGraph(4)
307
+ sage: G.add_edges([(0,4),(0,5),(3,6),(3,7)])
308
+ sage: from sage.graphs.tutte_polynomial import Ear
309
+ sage: E = Ear.find_ear(G)
310
+ sage: E.s
311
+ 3
312
+ sage: E.unlabeled_edges
313
+ [(0, 1), (1, 2), (2, 3)]
314
+ sage: E.vertices
315
+ [0, 1, 2, 3]
316
+ """
317
+ degree_two_vertices = [v for v, degree
318
+ in g.degree_iterator(labels=True)
319
+ if degree == 2]
320
+ subgraph = g.subgraph(degree_two_vertices)
321
+ for component in subgraph.connected_components(sort=False):
322
+ edges = g.edges_incident(vertices=component, labels=True)
323
+ all_vertices = sorted(set(sum([e[:2] for e in edges], ())))
324
+ if len(all_vertices) < 3:
325
+ continue
326
+ end_points = [v for v in all_vertices if v not in component]
327
+ if not end_points:
328
+ end_points = [component[0]]
329
+
330
+ ear_is_cycle = end_points[0] == end_points[-1]
331
+
332
+ if ear_is_cycle:
333
+ for e in end_points:
334
+ if e in component:
335
+ component.remove(e)
336
+
337
+ return Ear(g, end_points, component, ear_is_cycle)
338
+
339
+ @contextmanager
340
+ def removed_from(self, G):
341
+ r"""
342
+ A context manager which removes the ear from the graph `G`.
343
+
344
+ EXAMPLES::
345
+
346
+ sage: G = graphs.PathGraph(4)
347
+ sage: G.add_edges([(0,4),(0,5),(3,6),(3,7)])
348
+ sage: len(G.edges(sort=True))
349
+ 7
350
+ sage: from sage.graphs.tutte_polynomial import Ear
351
+ sage: E = Ear.find_ear(G)
352
+ sage: with E.removed_from(G) as Y:
353
+ ....: G.edges(sort=True)
354
+ [(0, 4, None), (0, 5, None), (3, 6, None), (3, 7, None)]
355
+ sage: len(G.edges(sort=True))
356
+ 7
357
+ """
358
+ deleted_edges = []
359
+ for edge in G.edges_incident(vertices=self.interior, labels=True):
360
+ G.delete_edge(edge)
361
+ deleted_edges.append(edge)
362
+ for v in self.interior:
363
+ G.delete_vertex(v)
364
+
365
+ try:
366
+ yield
367
+ finally:
368
+ for edge in deleted_edges:
369
+ G.add_edge(edge)
370
+
371
+ ##################
372
+ # Edge Selection #
373
+ ##################
374
+
375
+
376
+ class EdgeSelection:
377
+ pass
378
+
379
+
380
+ class VertexOrder(EdgeSelection):
381
+ def __init__(self, order):
382
+ """
383
+ EXAMPLES::
384
+
385
+ sage: from sage.graphs.tutte_polynomial import VertexOrder
386
+ sage: A = VertexOrder([4,6,3,2,1,7])
387
+ sage: A.order
388
+ [4, 6, 3, 2, 1, 7]
389
+ sage: A.inverse_order
390
+ {1: 4, 2: 3, 3: 2, 4: 0, 6: 1, 7: 5}
391
+ """
392
+ self.order = list(order)
393
+ self.inverse_order = dict([reversed(_) for _ in enumerate(order)])
394
+
395
+ def __call__(self, graph):
396
+ """
397
+ EXAMPLES::
398
+
399
+ sage: from sage.graphs.tutte_polynomial import VertexOrder
400
+ sage: A = VertexOrder([4,0,3,2,1,5])
401
+ sage: G = graphs.PathGraph(6)
402
+ sage: A(G)
403
+ (3, 4, None)
404
+ """
405
+ for v in self.order:
406
+ edges = graph.edges_incident([v])
407
+ if edges:
408
+ edges.sort(key=lambda x: self.inverse_order[x[0] if x[0] != v else x[1]])
409
+ return edges[0]
410
+ raise RuntimeError("no edges left to select")
411
+
412
+
413
+ class MinimizeSingleDegree(EdgeSelection):
414
+ def __call__(self, graph):
415
+ """
416
+ EXAMPLES::
417
+
418
+ sage: from sage.graphs.tutte_polynomial import MinimizeSingleDegree
419
+ sage: G = graphs.PathGraph(6)
420
+ sage: MinimizeSingleDegree()(G)
421
+ (0, 1, None)
422
+ """
423
+ degrees = list(graph.degree_iterator(labels=True))
424
+ degrees.sort(key=lambda x: x[1]) # Sort by degree
425
+ for v, degree in degrees:
426
+ for e in graph.edges_incident([v], labels=True):
427
+ return e
428
+ raise RuntimeError("no edges left to select")
429
+
430
+
431
+ class MinimizeDegree(EdgeSelection):
432
+ def __call__(self, graph):
433
+ """
434
+ EXAMPLES::
435
+
436
+ sage: from sage.graphs.tutte_polynomial import MinimizeDegree
437
+ sage: G = graphs.PathGraph(6)
438
+ sage: MinimizeDegree()(G)
439
+ (0, 1, None)
440
+ """
441
+ degrees = dict(graph.degree_iterator(labels=True))
442
+ edges = graph.edges(labels=True, sort=False)
443
+ if edges:
444
+ return min(edges, key=lambda x: degrees[x[0]] + degrees[x[1]])
445
+ raise RuntimeError("no edges left to select")
446
+
447
+
448
+ class MaximizeDegree(EdgeSelection):
449
+ def __call__(self, graph):
450
+ """
451
+ EXAMPLES::
452
+
453
+ sage: from sage.graphs.tutte_polynomial import MaximizeDegree
454
+ sage: G = graphs.PathGraph(6)
455
+ sage: MaximizeDegree()(G)
456
+ (1, 2, None)
457
+ """
458
+ degrees = dict(graph.degree_iterator(labels=True))
459
+ edges = graph.edges(labels=True, sort=False)
460
+ if edges:
461
+ return max(edges, key=lambda x: degrees[x[0]] + degrees[x[1]])
462
+ raise RuntimeError("no edges left to select")
463
+
464
+
465
+ ###########
466
+ # Caching #
467
+ ###########
468
+
469
+
470
+ def _cache_key(G):
471
+ """
472
+ Return the key used to cache the result for the graph G.
473
+
474
+ This is used by the decorator :func:`_cached`.
475
+
476
+ EXAMPLES::
477
+
478
+ sage: from sage.graphs.tutte_polynomial import _cache_key
479
+ sage: G = graphs.DiamondGraph()
480
+ sage: print(_cache_key(G))
481
+ ((0, 2), (0, 3), (1, 2), (1, 3), (2, 3))
482
+ """
483
+ return tuple(G.canonical_label().edges(labels=False, sort=True))
484
+
485
+
486
+ def _cached(func):
487
+ """
488
+ Wrapper used to cache results of the function `func`.
489
+
490
+ This uses the function :func:`_cache_key`.
491
+
492
+ EXAMPLES::
493
+
494
+ sage: from sage.graphs.tutte_polynomial import tutte_polynomial
495
+ sage: G = graphs.PetersenGraph()
496
+ sage: T = tutte_polynomial(G) #indirect doctest
497
+ sage: tutte_polynomial(G)(1,1) #indirect doctest
498
+ 2000
499
+ """
500
+ @sage_wraps(func)
501
+ def wrapper(G, *args, **kwds):
502
+ cache = kwds.setdefault('cache', {})
503
+ key = _cache_key(G)
504
+ if key in cache:
505
+ return cache[key]
506
+ result = func(G, *args, **kwds)
507
+ cache[key] = result
508
+ return result
509
+ wrapper.original_func = func
510
+ return wrapper
511
+
512
+
513
+ ####################
514
+ # Tutte Polynomial #
515
+ ####################
516
+
517
+ @_cached
518
+ def tutte_polynomial(G, edge_selector=None, cache=None):
519
+ r"""
520
+ Return the Tutte polynomial of the graph `G`.
521
+
522
+ INPUT:
523
+
524
+ - ``edge_selector`` -- method (optional); this argument allows the user
525
+ to specify his own heuristic for selecting edges used in the deletion
526
+ contraction recurrence
527
+
528
+ - ``cache`` -- (optional) dictionary to cache the Tutte
529
+ polynomials generated in the recursive process. One will be
530
+ created automatically if not provided.
531
+
532
+ EXAMPLES:
533
+
534
+ The Tutte polynomial of any tree of order `n` is `x^{n-1}`::
535
+
536
+ sage: all(T.tutte_polynomial() == x**9 for T in graphs.trees(10)) # needs sage.symbolic
537
+ True
538
+
539
+ The Tutte polynomial of the Petersen graph is::
540
+
541
+ sage: P = graphs.PetersenGraph()
542
+ sage: P.tutte_polynomial()
543
+ x^9 + 6*x^8 + 21*x^7 + 56*x^6 + 12*x^5*y + y^6 + 114*x^5 + 70*x^4*y
544
+ + 30*x^3*y^2 + 15*x^2*y^3 + 10*x*y^4 + 9*y^5 + 170*x^4 + 170*x^3*y
545
+ + 105*x^2*y^2 + 65*x*y^3 + 35*y^4 + 180*x^3 + 240*x^2*y + 171*x*y^2
546
+ + 75*y^3 + 120*x^2 + 168*x*y + 84*y^2 + 36*x + 36*y
547
+
548
+ The Tutte polynomial of a connected graph `G` evaluated at (1,1) is the number of
549
+ spanning trees of `G`::
550
+
551
+ sage: G = graphs.RandomGNP(10,0.6)
552
+ sage: while not G.is_connected():
553
+ ....: G = graphs.RandomGNP(10,0.6)
554
+ sage: G.tutte_polynomial()(1,1) == G.spanning_trees_count() # needs sage.modules
555
+ True
556
+
557
+ Given that `T(x,y)` is the Tutte polynomial of a graph `G` with
558
+ `n` vertices and `c` connected components, then `(-1)^{n-c} x^k
559
+ T(1-x,0)` is the chromatic polynomial of `G`. ::
560
+
561
+ sage: G = graphs.OctahedralGraph()
562
+ sage: T = G.tutte_polynomial()
563
+ sage: R = PolynomialRing(ZZ, 'x')
564
+ sage: R((-1)^5*x*T(1-x,0)).factor() # needs sage.symbolic
565
+ (x - 2) * (x - 1) * x * (x^3 - 9*x^2 + 29*x - 32)
566
+ sage: G.chromatic_polynomial().factor() # needs sage.libs.flint
567
+ (x - 2) * (x - 1) * x * (x^3 - 9*x^2 + 29*x - 32)
568
+
569
+ TESTS:
570
+
571
+ Providing an external cache::
572
+
573
+ sage: cache = {}
574
+ sage: _ = graphs.RandomGNP(7,.5).tutte_polynomial(cache=cache)
575
+ sage: len(cache) > 0
576
+ True
577
+
578
+ Verify that :issue:`18366` is fixed::
579
+
580
+ sage: g = Graph(multiedges=True)
581
+ sage: g.add_edges([(0,1,1),(1,5,2),(5,3,3),(5,2,4),(2,4,5),(0,2,6),(0,3,7),(0,4,8),(0,5,9)])
582
+ sage: g.tutte_polynomial()(1,1)
583
+ 52
584
+ sage: g.spanning_trees_count() # needs sage.modules
585
+ 52
586
+ """
587
+ R = ZZ['x, y']
588
+ if G.num_edges() == 0:
589
+ return R.one()
590
+
591
+ G = G.relabel(inplace=False, immutable=False) # making sure the vertices are integers
592
+ G.allow_loops(True)
593
+ G.allow_multiple_edges(True)
594
+
595
+ if edge_selector is None:
596
+ edge_selector = MinimizeSingleDegree()
597
+ x, y = R.gens()
598
+ return _tutte_polynomial_internal(G, x, y, edge_selector, cache=cache)
599
+
600
+
601
+ @_cached
602
+ def _tutte_polynomial_internal(G, x, y, edge_selector, cache=None):
603
+ """
604
+ Do the recursive computation of the Tutte polynomial.
605
+
606
+ INPUT:
607
+
608
+ - ``G`` -- the graph
609
+ - ``x``, ``y`` -- the variables `x`, `y` respectively
610
+ - ``edge_selector`` -- the heuristic for selecting edges used in the
611
+ deletion contraction recurrence
612
+
613
+ TESTS::
614
+
615
+ sage: P = graphs.CycleGraph(5)
616
+ sage: P.tutte_polynomial() # indirect doctest
617
+ x^4 + x^3 + x^2 + x + y
618
+ """
619
+ if not G.num_edges():
620
+ return x.parent().one()
621
+
622
+ def recursive_tp(graph=None):
623
+ """
624
+ The recursive call -- used so that we do not have to specify
625
+ the same arguments everywhere.
626
+ """
627
+ if graph is None:
628
+ graph = G
629
+ return _tutte_polynomial_internal(graph, x, y, edge_selector, cache=cache)
630
+
631
+ # Remove loops
632
+ with removed_loops(G) as loops:
633
+ if loops:
634
+ return y**len(loops) * recursive_tp()
635
+
636
+ uG = underlying_graph(G)
637
+ em = edge_multiplicities(G)
638
+ d = list(em.values())
639
+
640
+ def yy(start, end):
641
+ return sum(y**i for i in range(start, end+1))
642
+
643
+ # Lemma 1
644
+ if G.is_forest():
645
+ return prod(x + yy(1, d_i-1) for d_i in d)
646
+
647
+ # Theorem 1: from Haggard, Pearce, Royle 2008
648
+ blocks, cut_vertices = G.blocks_and_cut_vertices()
649
+ if len(blocks) > 1:
650
+ return prod([recursive_tp(G.subgraph(block)) for block in blocks])
651
+
652
+ components = G.connected_components_number()
653
+ edge = edge_selector(G)
654
+ unlabeled_edge = edge[:2]
655
+
656
+ with removed_edge(G, edge):
657
+ if G.connected_components_number() > components:
658
+ with contracted_edge(G, unlabeled_edge):
659
+ return x*recursive_tp()
660
+
661
+ ##################################
662
+ # We are in the biconnected case #
663
+ ##################################
664
+
665
+ # Theorem 4: from Haggard, Pearce, and Royle Note that the formula
666
+ # at https://web.archive.org/web/20110401195911/http://homepages.ecs.vuw.ac.nz/~djp/files/TOMS10.pdf is
667
+ # slightly incorrect. The initial sum should only go to n-2
668
+ # instead of n (allowing for the last part of the recursion).
669
+ # Additionally, the first operand of the final product should be
670
+ # (x+y^{1...(d_n+d_{n-1}-1)}) instead of just (x+y^(d_n+d_{n-1}-1)
671
+ if uG.num_verts() == uG.num_edges(): # G is a multi-cycle
672
+ n = len(d)
673
+ result = 0
674
+ for i in range(n - 2):
675
+ term = (prod((x + yy(1, d_j-1)) for d_j in d[i+1:]) *
676
+ prod((yy(0, d_k-1)) for d_k in d[:i]))
677
+ result += term
678
+ # The last part of the recursion
679
+ result += (x + yy(1, d[-1] + d[-2] - 1))*prod(yy(0, d_i-1)
680
+ for d_i in d[:-2])
681
+ return result
682
+
683
+ # Theorem 3 from Haggard, Pearce, and Royle, adapted to multi-ears
684
+ ear = Ear.find_ear(uG)
685
+ if ear is not None:
686
+ if (ear.is_cycle and ear.vertices == G.vertices(sort=True)):
687
+ # The graph is an ear (cycle) We should never be in this
688
+ # case since we check for multi-cycles above
689
+ return y + sum(x**i for i in range(1, ear.s))
690
+ else:
691
+ with ear.removed_from(G):
692
+ # result = sum(x^i for i in range(ear.s)) #single ear case
693
+ result = sum((prod(x + yy(1, em[e]-1) for e in ear.unlabeled_edges[i+1:])
694
+ * prod(yy(0, em[e]-1) for e in ear.unlabeled_edges[:i]))
695
+ for i in range(len(ear.unlabeled_edges)))
696
+ result *= recursive_tp()
697
+
698
+ with contracted_edge(G, [ear.end_points[0],
699
+ ear.end_points[-1]]):
700
+ result += prod(yy(0, em[e]-1)
701
+ for e in ear.unlabeled_edges)*recursive_tp()
702
+
703
+ return result
704
+
705
+ # Theorem 2
706
+ if len(em) == 1: # the graph is just a multiedge
707
+ return x + sum(y**i for i in range(1, em[unlabeled_edge]))
708
+ else:
709
+ with removed_multiedge(G, unlabeled_edge):
710
+ result = recursive_tp()
711
+ with contracted_edge(G, unlabeled_edge):
712
+ result += sum(y**i for i in range(em[unlabeled_edge]))*recursive_tp()
713
+ return result