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.
- passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
- passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
- passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
- passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
- passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
- passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
- passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- sage/all__sagemath_graphs.py +39 -0
- sage/combinat/abstract_tree.py +2723 -0
- sage/combinat/all__sagemath_graphs.py +34 -0
- sage/combinat/binary_tree.py +5306 -0
- sage/combinat/cluster_algebra_quiver/all.py +22 -0
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
- sage/combinat/cluster_algebra_quiver/interact.py +124 -0
- sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
- sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
- sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
- sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
- sage/combinat/designs/MOLS_handbook_data.py +570 -0
- sage/combinat/designs/all.py +58 -0
- sage/combinat/designs/bibd.py +1655 -0
- sage/combinat/designs/block_design.py +1071 -0
- sage/combinat/designs/covering_array.py +269 -0
- sage/combinat/designs/covering_design.py +530 -0
- sage/combinat/designs/database.py +5615 -0
- sage/combinat/designs/design_catalog.py +122 -0
- sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/designs_pyx.pxd +21 -0
- sage/combinat/designs/designs_pyx.pyx +993 -0
- sage/combinat/designs/difference_family.py +3951 -0
- sage/combinat/designs/difference_matrices.py +279 -0
- sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
- sage/combinat/designs/ext_rep.py +1064 -0
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
- sage/combinat/designs/group_divisible_designs.py +361 -0
- sage/combinat/designs/incidence_structures.py +2357 -0
- sage/combinat/designs/latin_squares.py +581 -0
- sage/combinat/designs/orthogonal_arrays.py +2244 -0
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
- sage/combinat/designs/resolvable_bibd.py +815 -0
- sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
- sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +530 -0
- sage/combinat/designs/twographs.py +306 -0
- sage/combinat/finite_state_machine.py +14874 -0
- sage/combinat/finite_state_machine_generators.py +2006 -0
- sage/combinat/graph_path.py +448 -0
- sage/combinat/interval_posets.py +3908 -0
- sage/combinat/nu_tamari_lattice.py +269 -0
- sage/combinat/ordered_tree.py +1446 -0
- sage/combinat/posets/all.py +46 -0
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/cartesian_product.py +493 -0
- sage/combinat/posets/d_complete.py +182 -0
- sage/combinat/posets/elements.py +273 -0
- sage/combinat/posets/forest.py +30 -0
- sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +174 -0
- sage/combinat/posets/hasse_diagram.py +3672 -0
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +794 -0
- sage/combinat/posets/lattices.py +5117 -0
- sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +292 -0
- sage/combinat/posets/linear_extensions.py +1037 -0
- sage/combinat/posets/mobile.py +275 -0
- sage/combinat/posets/moebius_algebra.py +776 -0
- sage/combinat/posets/poset_examples.py +2178 -0
- sage/combinat/posets/posets.py +9360 -0
- sage/combinat/rooted_tree.py +1070 -0
- sage/combinat/shard_order.py +239 -0
- sage/combinat/tamari_lattices.py +384 -0
- sage/combinat/yang_baxter_graph.py +923 -0
- sage/databases/all__sagemath_graphs.py +1 -0
- sage/databases/knotinfo_db.py +1231 -0
- sage/ext_data/all__sagemath_graphs.py +1 -0
- sage/ext_data/graphs/graph_plot_js.html +330 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/graphs/all.py +42 -0
- sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/asteroidal_triples.pyx +320 -0
- sage/graphs/base/all.py +1 -0
- sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/boost_graph.pxd +106 -0
- sage/graphs/base/boost_graph.pyx +3045 -0
- sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/c_graph.pxd +106 -0
- sage/graphs/base/c_graph.pyx +5096 -0
- sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/dense_graph.pxd +28 -0
- sage/graphs/base/dense_graph.pyx +801 -0
- sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/graph_backends.pxd +5 -0
- sage/graphs/base/graph_backends.pyx +797 -0
- sage/graphs/base/overview.py +85 -0
- sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/sparse_graph.pxd +90 -0
- sage/graphs/base/sparse_graph.pyx +1653 -0
- sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_dense_graph.pxd +5 -0
- sage/graphs/base/static_dense_graph.pyx +1032 -0
- sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_backend.pxd +27 -0
- sage/graphs/base/static_sparse_backend.pyx +1583 -0
- sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_graph.pxd +37 -0
- sage/graphs/base/static_sparse_graph.pyx +1375 -0
- sage/graphs/bipartite_graph.py +2732 -0
- sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/centrality.pyx +1038 -0
- sage/graphs/cographs.py +519 -0
- sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/comparability.pyx +851 -0
- sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/connectivity.pxd +157 -0
- sage/graphs/connectivity.pyx +4813 -0
- sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/convexity_properties.pxd +16 -0
- sage/graphs/convexity_properties.pyx +870 -0
- sage/graphs/digraph.py +4754 -0
- sage/graphs/digraph_generators.py +1993 -0
- sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/distances_all_pairs.pxd +12 -0
- sage/graphs/distances_all_pairs.pyx +2938 -0
- sage/graphs/domination.py +1363 -0
- sage/graphs/dot2tex_utils.py +100 -0
- sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/edge_connectivity.pyx +1215 -0
- sage/graphs/generators/all.py +1 -0
- sage/graphs/generators/basic.py +1769 -0
- sage/graphs/generators/chessboard.py +538 -0
- sage/graphs/generators/classical_geometries.py +1611 -0
- sage/graphs/generators/degree_sequence.py +235 -0
- sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generators/distance_regular.pyx +2846 -0
- sage/graphs/generators/families.py +4759 -0
- sage/graphs/generators/intersection.py +565 -0
- sage/graphs/generators/platonic_solids.py +262 -0
- sage/graphs/generators/random.py +2623 -0
- sage/graphs/generators/smallgraphs.py +5741 -0
- sage/graphs/generators/world_map.py +724 -0
- sage/graphs/generic_graph.py +26867 -0
- sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generic_graph_pyx.pxd +34 -0
- sage/graphs/generic_graph_pyx.pyx +1673 -0
- sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/genus.pyx +622 -0
- sage/graphs/graph.py +9645 -0
- sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_coloring.pyx +2284 -0
- sage/graphs/graph_database.py +1177 -0
- sage/graphs/graph_decompositions/all.py +1 -0
- sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
- sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +508 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
- sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
- sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
- sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
- sage/graphs/graph_editor.py +82 -0
- sage/graphs/graph_generators.py +3314 -0
- sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +95 -0
- sage/graphs/graph_input.py +812 -0
- sage/graphs/graph_latex.py +2064 -0
- sage/graphs/graph_list.py +410 -0
- sage/graphs/graph_plot.py +1756 -0
- sage/graphs/graph_plot_js.py +338 -0
- sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/hyperbolicity.pyx +1704 -0
- sage/graphs/hypergraph_generators.py +364 -0
- sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/independent_sets.pxd +13 -0
- sage/graphs/independent_sets.pyx +402 -0
- sage/graphs/isgci.py +1033 -0
- sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +489 -0
- sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/line_graph.pyx +743 -0
- sage/graphs/lovasz_theta.py +77 -0
- sage/graphs/matching.py +1633 -0
- sage/graphs/matching_covered_graph.py +3590 -0
- sage/graphs/orientations.py +1489 -0
- sage/graphs/partial_cube.py +459 -0
- sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/path_enumeration.pyx +2040 -0
- sage/graphs/pq_trees.py +1129 -0
- sage/graphs/print_graphs.py +201 -0
- sage/graphs/schnyder.py +865 -0
- sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/spanning_tree.pyx +1457 -0
- sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/strongly_regular_db.pyx +3340 -0
- sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/traversals.pxd +9 -0
- sage/graphs/traversals.pyx +1872 -0
- sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/trees.pxd +15 -0
- sage/graphs/trees.pyx +310 -0
- sage/graphs/tutte_polynomial.py +713 -0
- sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/views.pyx +794 -0
- sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/weakly_chordal.pyx +604 -0
- sage/groups/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
- sage/knots/all.py +6 -0
- sage/knots/free_knotinfo_monoid.py +507 -0
- sage/knots/gauss_code.py +291 -0
- sage/knots/knot.py +682 -0
- sage/knots/knot_table.py +284 -0
- sage/knots/knotinfo.py +2900 -0
- sage/knots/link.py +4715 -0
- sage/sandpiles/all.py +13 -0
- sage/sandpiles/examples.py +225 -0
- sage/sandpiles/sandpile.py +6365 -0
- sage/topology/all.py +22 -0
- sage/topology/cell_complex.py +1214 -0
- sage/topology/cubical_complex.py +1976 -0
- sage/topology/delta_complex.py +1806 -0
- sage/topology/filtered_simplicial_complex.py +744 -0
- sage/topology/moment_angle_complex.py +823 -0
- sage/topology/simplicial_complex.py +5160 -0
- sage/topology/simplicial_complex_catalog.py +92 -0
- sage/topology/simplicial_complex_examples.py +1680 -0
- sage/topology/simplicial_complex_homset.py +205 -0
- sage/topology/simplicial_complex_morphism.py +836 -0
- sage/topology/simplicial_set.py +4102 -0
- sage/topology/simplicial_set_catalog.py +55 -0
- sage/topology/simplicial_set_constructions.py +2954 -0
- sage/topology/simplicial_set_examples.py +865 -0
- 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
|
Binary file
|