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,3314 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
r"""
|
3
|
+
Common graphs
|
4
|
+
|
5
|
+
All graphs in Sage can be built through the ``graphs`` object. In order to
|
6
|
+
build a complete graph on 15 elements, one can do::
|
7
|
+
|
8
|
+
sage: g = graphs.CompleteGraph(15)
|
9
|
+
|
10
|
+
To get a path with 4 vertices, and the house graph::
|
11
|
+
|
12
|
+
sage: p = graphs.PathGraph(4)
|
13
|
+
sage: h = graphs.HouseGraph()
|
14
|
+
|
15
|
+
More interestingly, one can get the list of all graphs that Sage knows how to
|
16
|
+
build by typing ``graphs.`` in Sage and then hitting :kbd:`Tab`.
|
17
|
+
"""
|
18
|
+
|
19
|
+
import subprocess
|
20
|
+
|
21
|
+
|
22
|
+
# This method appends a list of methods to the doc as a 3xN table.
|
23
|
+
|
24
|
+
# Here's the point :
|
25
|
+
#
|
26
|
+
# we just have to insert the method's name in this file to add it to
|
27
|
+
# the tab, and in exchange the doc contains a table of width 3 with
|
28
|
+
# all methods listed, so that the reading order is Column1, then
|
29
|
+
# Column2, then Column3. Doing this by hand is hell with Sphinx when
|
30
|
+
# you need to insert a new method inside of the list !
|
31
|
+
|
32
|
+
def __append_to_doc(methods):
|
33
|
+
global __doc__
|
34
|
+
__doc__ += ("\n.. csv-table::\n"
|
35
|
+
" :class: contentstable\n"
|
36
|
+
" :widths: 33, 33, 33\n"
|
37
|
+
" :delim: |\n\n")
|
38
|
+
|
39
|
+
h = (len(methods) + 2) // 3
|
40
|
+
# Reorders the list of methods for horizontal reading, the only one Sphinx understands
|
41
|
+
reordered_methods = [0] * (3 * h)
|
42
|
+
for i, m in enumerate(methods):
|
43
|
+
reordered_methods[3 * (i % h) + (i // h)] = m
|
44
|
+
methods = reordered_methods
|
45
|
+
|
46
|
+
# Adding the list to the __doc__ string
|
47
|
+
def wrap_name(x):
|
48
|
+
if x:
|
49
|
+
return ":meth:`" + str(x) + " <GraphGenerators." + str(x) + ">`"
|
50
|
+
return ""
|
51
|
+
|
52
|
+
while methods:
|
53
|
+
a = methods.pop(0)
|
54
|
+
b = methods.pop(0)
|
55
|
+
c = methods.pop(0)
|
56
|
+
__doc__ += " " + wrap_name(a) + " | " + wrap_name(b) + " | " + wrap_name(c) + "\n"
|
57
|
+
|
58
|
+
|
59
|
+
__doc__ += """
|
60
|
+
**Basic structures**
|
61
|
+
"""
|
62
|
+
|
63
|
+
__append_to_doc(
|
64
|
+
["BullGraph",
|
65
|
+
"ButterflyGraph",
|
66
|
+
"CircularLadderGraph",
|
67
|
+
"ClawGraph",
|
68
|
+
"CycleGraph",
|
69
|
+
"CompleteBipartiteGraph",
|
70
|
+
"CompleteGraph",
|
71
|
+
"CompleteMultipartiteGraph",
|
72
|
+
"CorrelationGraph",
|
73
|
+
"DiamondGraph",
|
74
|
+
"GemGraph",
|
75
|
+
"DartGraph",
|
76
|
+
"ForkGraph",
|
77
|
+
"DipoleGraph",
|
78
|
+
"EmptyGraph",
|
79
|
+
"Grid2dGraph",
|
80
|
+
"GridGraph",
|
81
|
+
"HouseGraph",
|
82
|
+
"HouseXGraph",
|
83
|
+
"LadderGraph",
|
84
|
+
"LollipopGraph",
|
85
|
+
"MoebiusLadderGraph",
|
86
|
+
"PathGraph",
|
87
|
+
"StarGraph",
|
88
|
+
"TadpoleGraph",
|
89
|
+
"ToroidalGrid2dGraph",
|
90
|
+
"Toroidal6RegularGrid2dGraph"]
|
91
|
+
)
|
92
|
+
|
93
|
+
__doc__ += """
|
94
|
+
**Small Graphs**
|
95
|
+
|
96
|
+
A small graph is just a single graph and has no parameter influencing
|
97
|
+
the number of edges or vertices.
|
98
|
+
"""
|
99
|
+
|
100
|
+
__append_to_doc(
|
101
|
+
["Balaban10Cage",
|
102
|
+
"Balaban11Cage",
|
103
|
+
"BidiakisCube",
|
104
|
+
"BiggsSmithGraph",
|
105
|
+
"BlanusaFirstSnarkGraph",
|
106
|
+
"BlanusaSecondSnarkGraph",
|
107
|
+
"BrinkmannGraph",
|
108
|
+
"BrouwerHaemersGraph",
|
109
|
+
"BuckyBall",
|
110
|
+
"CameronGraph",
|
111
|
+
"Cell600",
|
112
|
+
"Cell120",
|
113
|
+
"ChvatalGraph",
|
114
|
+
"ClebschGraph",
|
115
|
+
"cocliques_HoffmannSingleton",
|
116
|
+
"ConwaySmith_for_3S7",
|
117
|
+
"CoxeterGraph",
|
118
|
+
"CubeplexGraph",
|
119
|
+
"DesarguesGraph",
|
120
|
+
"DejterGraph",
|
121
|
+
"distance_3_doubly_truncated_Golay_code_graph",
|
122
|
+
"DoubleStarSnark",
|
123
|
+
"DoublyTruncatedWittGraph",
|
124
|
+
"DurerGraph",
|
125
|
+
"DyckGraph",
|
126
|
+
"EllinghamHorton54Graph",
|
127
|
+
"EllinghamHorton78Graph",
|
128
|
+
"ErreraGraph",
|
129
|
+
"F26AGraph",
|
130
|
+
"FlowerSnark",
|
131
|
+
"FolkmanGraph",
|
132
|
+
"FosterGraph",
|
133
|
+
"FosterGraph3S6",
|
134
|
+
"FranklinGraph",
|
135
|
+
"FruchtGraph",
|
136
|
+
"GoldnerHararyGraph",
|
137
|
+
"GolombGraph",
|
138
|
+
"GossetGraph",
|
139
|
+
"graph_3O73",
|
140
|
+
"GrayGraph",
|
141
|
+
"GritsenkoGraph",
|
142
|
+
"GrotzschGraph",
|
143
|
+
"HallJankoGraph",
|
144
|
+
"HarborthGraph",
|
145
|
+
"HarriesGraph",
|
146
|
+
"HarriesWongGraph",
|
147
|
+
"HeawoodGraph",
|
148
|
+
"HerschelGraph",
|
149
|
+
"HigmanSimsGraph",
|
150
|
+
"HoffmanGraph",
|
151
|
+
"HoffmanSingletonGraph",
|
152
|
+
"HoltGraph",
|
153
|
+
"HortonGraph",
|
154
|
+
"IoninKharaghani765Graph",
|
155
|
+
"IvanovIvanovFaradjevGraph",
|
156
|
+
"J2Graph",
|
157
|
+
"JankoKharaghaniGraph",
|
158
|
+
"JankoKharaghaniTonchevGraph",
|
159
|
+
"KittellGraph",
|
160
|
+
"KrackhardtKiteGraph",
|
161
|
+
"Klein3RegularGraph",
|
162
|
+
"Klein7RegularGraph",
|
163
|
+
"LargeWittGraph",
|
164
|
+
"LeonardGraph",
|
165
|
+
"LjubljanaGraph",
|
166
|
+
"vanLintSchrijverGraph",
|
167
|
+
"LivingstoneGraph",
|
168
|
+
"locally_GQ42_distance_transitive_graph",
|
169
|
+
"LocalMcLaughlinGraph",
|
170
|
+
"M22Graph",
|
171
|
+
"MarkstroemGraph",
|
172
|
+
"MathonStronglyRegularGraph",
|
173
|
+
"McGeeGraph",
|
174
|
+
"McLaughlinGraph",
|
175
|
+
"MeredithGraph",
|
176
|
+
"MoebiusKantorGraph",
|
177
|
+
"MoserSpindle",
|
178
|
+
"MurtyGraph",
|
179
|
+
"NauruGraph",
|
180
|
+
"PappusGraph",
|
181
|
+
"PoussinGraph",
|
182
|
+
"PerkelGraph",
|
183
|
+
"PetersenGraph",
|
184
|
+
"RobertsonGraph",
|
185
|
+
"SchlaefliGraph",
|
186
|
+
"shortened_00_11_binary_Golay_code_graph",
|
187
|
+
"shortened_000_111_extended_binary_Golay_code_graph",
|
188
|
+
"ShrikhandeGraph",
|
189
|
+
"SimsGewirtzGraph",
|
190
|
+
"SousselierGraph",
|
191
|
+
"SylvesterGraph",
|
192
|
+
"SzekeresSnarkGraph",
|
193
|
+
"ThomsenGraph",
|
194
|
+
"TietzeGraph",
|
195
|
+
"TricornGraph",
|
196
|
+
"TruncatedIcosidodecahedralGraph",
|
197
|
+
"TruncatedTetrahedralGraph",
|
198
|
+
"TruncatedWittGraph",
|
199
|
+
"Tutte12Cage",
|
200
|
+
"TutteCoxeterGraph",
|
201
|
+
"TutteGraph",
|
202
|
+
"TwinplexGraph",
|
203
|
+
"U42Graph216",
|
204
|
+
"U42Graph540",
|
205
|
+
"WagnerGraph",
|
206
|
+
"WatkinsSnarkGraph",
|
207
|
+
"WellsGraph",
|
208
|
+
"WienerArayaGraph",
|
209
|
+
"SuzukiGraph"])
|
210
|
+
|
211
|
+
__doc__ += """
|
212
|
+
**Platonic solids** (ordered ascending by number of vertices)
|
213
|
+
"""
|
214
|
+
|
215
|
+
__append_to_doc(
|
216
|
+
["TetrahedralGraph",
|
217
|
+
"OctahedralGraph",
|
218
|
+
"HexahedralGraph",
|
219
|
+
"IcosahedralGraph",
|
220
|
+
"DodecahedralGraph"])
|
221
|
+
|
222
|
+
__doc__ += """
|
223
|
+
**Families of graphs**
|
224
|
+
|
225
|
+
A family of graph is an infinite set of graphs which can be indexed by fixed
|
226
|
+
number of parameters, e.g. two integer parameters. (A method whose name starts
|
227
|
+
with a small letter does not return a single graph object but a graph iterator
|
228
|
+
or a list of graphs or ...)
|
229
|
+
"""
|
230
|
+
|
231
|
+
__append_to_doc(
|
232
|
+
["AlternatingFormsGraph",
|
233
|
+
"AztecDiamondGraph",
|
234
|
+
"BalancedTree",
|
235
|
+
"BarbellGraph",
|
236
|
+
"BilinearFormsGraph",
|
237
|
+
"BiwheelGraph",
|
238
|
+
"BubbleSortGraph",
|
239
|
+
"CaiFurerImmermanGraph",
|
240
|
+
"chang_graphs",
|
241
|
+
"CirculantGraph",
|
242
|
+
"cographs",
|
243
|
+
"cospectral_graphs",
|
244
|
+
"CubeGraph",
|
245
|
+
"CubeConnectedCycle",
|
246
|
+
"distance_regular_graph",
|
247
|
+
"DorogovtsevGoltsevMendesGraph",
|
248
|
+
"DoubleGrassmannGraph",
|
249
|
+
"DoubleOddGraph",
|
250
|
+
"EgawaGraph",
|
251
|
+
"FibonacciTree",
|
252
|
+
"FoldedCubeGraph",
|
253
|
+
"FriendshipGraph",
|
254
|
+
"fullerenes",
|
255
|
+
"FurerGadget",
|
256
|
+
"fusenes",
|
257
|
+
"FuzzyBallGraph",
|
258
|
+
"GeneralisedDodecagonGraph",
|
259
|
+
"GeneralisedHexagonGraph",
|
260
|
+
"GeneralisedOctagonGraph",
|
261
|
+
"GeneralizedPetersenGraph",
|
262
|
+
"GeneralizedSierpinskiGraph",
|
263
|
+
"GoethalsSeidelGraph",
|
264
|
+
"GrassmannGraph",
|
265
|
+
"HalfCube",
|
266
|
+
"HammingGraph",
|
267
|
+
"HanoiTowerGraph",
|
268
|
+
"HararyGraph",
|
269
|
+
"HermitianFormsGraph",
|
270
|
+
"HyperStarGraph",
|
271
|
+
"JohnsonGraph",
|
272
|
+
"KneserGraph",
|
273
|
+
"LCFGraph",
|
274
|
+
"line_graph_forbidden_subgraphs",
|
275
|
+
"MathonPseudocyclicMergingGraph",
|
276
|
+
"MathonPseudocyclicStronglyRegularGraph",
|
277
|
+
"MuzychukS6Graph",
|
278
|
+
"MycielskiGraph",
|
279
|
+
"MycielskiStep",
|
280
|
+
"nauty_geng",
|
281
|
+
"nauty_genbg",
|
282
|
+
"NKStarGraph",
|
283
|
+
"NStarGraph",
|
284
|
+
"OddGraph",
|
285
|
+
"PaleyGraph",
|
286
|
+
"PasechnikGraph",
|
287
|
+
"petersen_family",
|
288
|
+
"planar_graphs",
|
289
|
+
"plantri_gen",
|
290
|
+
"quadrangulations",
|
291
|
+
"RingedTree",
|
292
|
+
"SierpinskiGasketGraph",
|
293
|
+
"SquaredSkewHadamardMatrixGraph",
|
294
|
+
"SwitchedSquaredSkewHadamardMatrixGraph",
|
295
|
+
"StaircaseGraph",
|
296
|
+
"strongly_regular_graph",
|
297
|
+
"trees",
|
298
|
+
"TruncatedBiwheelGraph",
|
299
|
+
"nauty_gentreeg",
|
300
|
+
"triangulations",
|
301
|
+
"TuranGraph",
|
302
|
+
"UstimenkoGraph",
|
303
|
+
"WheelGraph",
|
304
|
+
"WindmillGraph"])
|
305
|
+
|
306
|
+
|
307
|
+
__doc__ += """
|
308
|
+
**Graphs from classical geometries over finite fields**
|
309
|
+
|
310
|
+
A number of classes of graphs related to geometries over finite fields and
|
311
|
+
quadrics and Hermitean varieties there.
|
312
|
+
"""
|
313
|
+
|
314
|
+
__append_to_doc(
|
315
|
+
["AffineOrthogonalPolarGraph",
|
316
|
+
"AhrensSzekeresGeneralizedQuadrangleGraph",
|
317
|
+
"NonisotropicOrthogonalPolarGraph",
|
318
|
+
"NonisotropicUnitaryPolarGraph",
|
319
|
+
"OrthogonalDualPolarGraph",
|
320
|
+
"OrthogonalPolarGraph",
|
321
|
+
"SymplecticDualPolarGraph",
|
322
|
+
"SymplecticPolarGraph",
|
323
|
+
"TaylorTwographDescendantSRG",
|
324
|
+
"TaylorTwographSRG",
|
325
|
+
"T2starGeneralizedQuadrangleGraph",
|
326
|
+
"Nowhere0WordsTwoWeightCodeGraph",
|
327
|
+
"HaemersGraph",
|
328
|
+
"CossidentePenttilaGraph",
|
329
|
+
"UnitaryDualPolarGraph",
|
330
|
+
"UnitaryPolarGraph"])
|
331
|
+
|
332
|
+
__doc__ += """
|
333
|
+
**Chessboard Graphs**
|
334
|
+
"""
|
335
|
+
|
336
|
+
__append_to_doc(
|
337
|
+
["BishopGraph",
|
338
|
+
"KingGraph",
|
339
|
+
"KnightGraph",
|
340
|
+
"QueenGraph",
|
341
|
+
"RookGraph"])
|
342
|
+
|
343
|
+
__doc__ += """
|
344
|
+
**Intersection graphs**
|
345
|
+
|
346
|
+
These graphs are generated by geometric representations. The objects of
|
347
|
+
the representation correspond to the graph vertices and the intersections
|
348
|
+
of objects yield the graph edges.
|
349
|
+
"""
|
350
|
+
|
351
|
+
__append_to_doc(
|
352
|
+
["IntersectionGraph",
|
353
|
+
"IntervalGraph",
|
354
|
+
"OrthogonalArrayBlockGraph",
|
355
|
+
"PermutationGraph",
|
356
|
+
"ToleranceGraph"])
|
357
|
+
|
358
|
+
__doc__ += """
|
359
|
+
**Random graphs**
|
360
|
+
"""
|
361
|
+
|
362
|
+
__append_to_doc(
|
363
|
+
["RandomBarabasiAlbert",
|
364
|
+
"RandomBicubicPlanar",
|
365
|
+
"RandomBipartite",
|
366
|
+
"RandomRegularBipartite",
|
367
|
+
"RandomBlockGraph",
|
368
|
+
"RandomBoundedToleranceGraph",
|
369
|
+
"RandomGNM",
|
370
|
+
"RandomGNP",
|
371
|
+
"RandomHolmeKim",
|
372
|
+
"RandomChordalGraph",
|
373
|
+
"RandomIntervalGraph",
|
374
|
+
"RandomKTree",
|
375
|
+
"RandomPartialKTree",
|
376
|
+
"RandomLobster",
|
377
|
+
"RandomNewmanWattsStrogatz",
|
378
|
+
"RandomProperIntervalGraph",
|
379
|
+
"RandomRegular",
|
380
|
+
"RandomShell",
|
381
|
+
"RandomToleranceGraph",
|
382
|
+
"RandomTree",
|
383
|
+
"RandomTreePowerlaw",
|
384
|
+
"RandomTriangulation",
|
385
|
+
"RandomUnitDiskGraph"])
|
386
|
+
|
387
|
+
__doc__ += """
|
388
|
+
**Graphs with a given degree sequence**
|
389
|
+
"""
|
390
|
+
|
391
|
+
__append_to_doc(
|
392
|
+
["DegreeSequence",
|
393
|
+
"DegreeSequenceBipartite",
|
394
|
+
"DegreeSequenceConfigurationModel",
|
395
|
+
"DegreeSequenceExpected",
|
396
|
+
"DegreeSequenceTree"])
|
397
|
+
|
398
|
+
__doc__ += """
|
399
|
+
**Miscellaneous**
|
400
|
+
"""
|
401
|
+
|
402
|
+
__append_to_doc(
|
403
|
+
["WorldMap",
|
404
|
+
"EuropeMap",
|
405
|
+
"AfricaMap",
|
406
|
+
"USAMap"]
|
407
|
+
)
|
408
|
+
|
409
|
+
__doc__ += """
|
410
|
+
|
411
|
+
AUTHORS:
|
412
|
+
|
413
|
+
- Robert Miller (2006-11-05): initial version, empty, random, petersen
|
414
|
+
|
415
|
+
- Emily Kirkman (2006-11-12): basic structures, node positioning for
|
416
|
+
all constructors
|
417
|
+
|
418
|
+
- Emily Kirkman (2006-11-19): docstrings, examples
|
419
|
+
|
420
|
+
- William Stein (2006-12-05): Editing.
|
421
|
+
|
422
|
+
- Robert Miller (2007-01-16): Cube generation and plotting
|
423
|
+
|
424
|
+
- Emily Kirkman (2007-01-16): more basic structures, docstrings
|
425
|
+
|
426
|
+
- Emily Kirkman (2007-02-14): added more named graphs
|
427
|
+
|
428
|
+
- Robert Miller (2007-06-08-11): Platonic solids, random graphs,
|
429
|
+
graphs with a given degree sequence, random directed graphs
|
430
|
+
|
431
|
+
- Robert Miller (2007-10-24): Isomorph free exhaustive generation
|
432
|
+
|
433
|
+
- Nathann Cohen (2009-08-12): WorldMap
|
434
|
+
|
435
|
+
- Michael Yurko (2009-9-01): added hyperstar, (n,k)-star, n-star, and
|
436
|
+
bubblesort graphs
|
437
|
+
|
438
|
+
- Anders Jonsson (2009-10-15): added generalized Petersen graphs
|
439
|
+
|
440
|
+
- Harald Schilly and Yann Laigle-Chapuy (2010-03-24): added Fibonacci Tree
|
441
|
+
|
442
|
+
- Jason Grout (2010-06-04): cospectral_graphs
|
443
|
+
|
444
|
+
- Edward Scheinerman (2010-08-11): RandomTree
|
445
|
+
|
446
|
+
- Ed Scheinerman (2010-08-21): added Grotzsch graph and Mycielski graphs
|
447
|
+
|
448
|
+
- Ed Scheinerman (2010-11-15): added RandomTriangulation
|
449
|
+
|
450
|
+
- Minh Van Nguyen (2010-11-26): added more named graphs
|
451
|
+
|
452
|
+
- Keshav Kini (2011-02-16): added Shrikhande and Dyck graphs
|
453
|
+
|
454
|
+
- David Coudert (2012-02-10): new RandomGNP generator
|
455
|
+
|
456
|
+
- David Coudert (2012-08-02): added chessboard graphs: Queen, King,
|
457
|
+
Knight, Bishop, and Rook graphs
|
458
|
+
|
459
|
+
- Nico Van Cleemput (2013-05-26): added fullerenes
|
460
|
+
|
461
|
+
- Nico Van Cleemput (2013-07-01): added benzenoids
|
462
|
+
|
463
|
+
- Birk Eisermann (2013-07-29): new section 'intersection graphs',
|
464
|
+
added (random, bounded) tolerance graphs
|
465
|
+
|
466
|
+
- Marco Cognetta (2016-03-03): added TuranGraph
|
467
|
+
|
468
|
+
- Janmenjaya Panda (2024-05-26): added MoebiusLadderGraph
|
469
|
+
|
470
|
+
- Janmenjaya Panda (2024-06-09): added StaircaseGraph, BiwheelGraph and
|
471
|
+
TruncatedBiwheelGraph
|
472
|
+
|
473
|
+
|
474
|
+
Functions and methods
|
475
|
+
---------------------
|
476
|
+
"""
|
477
|
+
|
478
|
+
# ****************************************************************************
|
479
|
+
# Copyright (C) 2006 Robert L. Miller <rlmillster@gmail.com>
|
480
|
+
# Emily A. Kirkman
|
481
|
+
# 2009 Michael C. Yurko <myurko@gmail.com>
|
482
|
+
#
|
483
|
+
# This program is free software: you can redistribute it and/or modify
|
484
|
+
# it under the terms of the GNU General Public License as published by
|
485
|
+
# the Free Software Foundation, either version 2 of the License, or
|
486
|
+
# (at your option) any later version.
|
487
|
+
# https://www.gnu.org/licenses/
|
488
|
+
# ****************************************************************************
|
489
|
+
|
490
|
+
from . import graph
|
491
|
+
|
492
|
+
|
493
|
+
class GraphGenerators:
|
494
|
+
r"""
|
495
|
+
A class consisting of constructors for several common graphs, as well as
|
496
|
+
orderly generation of isomorphism class representatives. See the
|
497
|
+
:mod:`module's help <sage.graphs.graph_generators>` for a list of supported
|
498
|
+
constructors.
|
499
|
+
|
500
|
+
A list of all graphs and graph structures (other than isomorphism class
|
501
|
+
representatives) in this database is available via tab completion. Type
|
502
|
+
"graphs." and then hit the :kbd:`Tab` key to see which graphs are available.
|
503
|
+
|
504
|
+
The docstrings include educational information about each named
|
505
|
+
graph with the hopes that this class can be used as a reference.
|
506
|
+
|
507
|
+
For all the constructors in this class (except the octahedral,
|
508
|
+
dodecahedral, random and empty graphs), the position dictionary is
|
509
|
+
filled to override the spring-layout algorithm.
|
510
|
+
|
511
|
+
|
512
|
+
ORDERLY GENERATION::
|
513
|
+
|
514
|
+
graphs(vertices, property=lambda x: True, augment='edges', size=None)
|
515
|
+
|
516
|
+
This syntax accesses the generator of isomorphism class
|
517
|
+
representatives. Iterates over distinct, exhaustive
|
518
|
+
representatives.
|
519
|
+
|
520
|
+
Also: see the use of the nauty package for generating graphs
|
521
|
+
at the :meth:`nauty_geng` method.
|
522
|
+
|
523
|
+
INPUT:
|
524
|
+
|
525
|
+
- ``vertices`` -- a natural number or ``None`` to infinitely generate
|
526
|
+
bigger and bigger graphs
|
527
|
+
|
528
|
+
- ``property`` -- (default: ``lambda x: True``) any property to be
|
529
|
+
tested on graphs before generation, but note that in general the
|
530
|
+
graphs produced are not the same as those produced by using the
|
531
|
+
property function to filter a list of graphs produced by using
|
532
|
+
the ``lambda x: True`` default. The generation process assumes
|
533
|
+
the property has certain characteristics set by the ``augment``
|
534
|
+
argument, and only in the case of inherited properties such that
|
535
|
+
all subgraphs of the relevant kind (for ``augment='edges'`` or
|
536
|
+
``augment='vertices'``) of a graph with the property also
|
537
|
+
possess the property will there be no missing graphs. (The
|
538
|
+
``property`` argument is ignored if ``degree_sequence`` is
|
539
|
+
specified.)
|
540
|
+
|
541
|
+
- ``augment`` -- (default: ``'edges'``) possible values:
|
542
|
+
|
543
|
+
- ``'edges'`` -- augments a fixed number of vertices by
|
544
|
+
adding one edge. In this case, all graphs on *exactly* ``n=vertices`` are
|
545
|
+
generated. If for any graph G satisfying the property, every
|
546
|
+
subgraph, obtained from G by deleting one edge but not the vertices
|
547
|
+
incident to that edge, satisfies the property, then this will
|
548
|
+
generate all graphs with that property. If this does not hold, then
|
549
|
+
all the graphs generated will satisfy the property, but there will
|
550
|
+
be some missing.
|
551
|
+
|
552
|
+
- ``'vertices'`` -- augments by adding a vertex and
|
553
|
+
edges incident to that vertex. In this case, all graphs *up to*
|
554
|
+
``n=vertices`` are generated. If for any graph G satisfying the
|
555
|
+
property, every subgraph, obtained from G by deleting one vertex
|
556
|
+
and only edges incident to that vertex, satisfies the property,
|
557
|
+
then this will generate all graphs with that property. If this does
|
558
|
+
not hold, then all the graphs generated will satisfy the property,
|
559
|
+
but there will be some missing.
|
560
|
+
|
561
|
+
- ``size`` -- (default: ``None``) the size of the graph to be generated
|
562
|
+
|
563
|
+
- ``degree_sequence`` -- (default: ``None``) a sequence of nonnegative integers,
|
564
|
+
or ``None``. If specified, the generated graphs will have these
|
565
|
+
integers for degrees. In this case, property and size are both
|
566
|
+
ignored.
|
567
|
+
|
568
|
+
- ``loops`` -- boolean (default: ``False``); whether to allow loops in the graph
|
569
|
+
or not
|
570
|
+
|
571
|
+
- ``sparse`` -- (default: ``True``) whether to use a sparse or dense data
|
572
|
+
structure. See the documentation of :class:`~sage.graphs.graph.Graph`.
|
573
|
+
|
574
|
+
- ``copy`` -- boolean (default: ``True``); whether to return copies. If set
|
575
|
+
to ``False`` the method returns the graph it is working on. The second
|
576
|
+
alternative is faster, but modifying any of the graph instances returned
|
577
|
+
by the method may break the function's behaviour, as it is using these
|
578
|
+
graphs to compute the next ones: only use ``copy=False`` when you stick
|
579
|
+
to *reading* the graphs returned.
|
580
|
+
|
581
|
+
This parameter is ignored when ``immutable`` is set to ``True``, in which
|
582
|
+
case returned graphs are always copies.
|
583
|
+
|
584
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return immutable
|
585
|
+
or mutable graphs. When set to ``True``, this parameter implies
|
586
|
+
``copy=True``.
|
587
|
+
|
588
|
+
EXAMPLES:
|
589
|
+
|
590
|
+
Print graphs on 3 or less vertices::
|
591
|
+
|
592
|
+
sage: for G in graphs(3, augment='vertices'):
|
593
|
+
....: print(G)
|
594
|
+
Graph on 0 vertices
|
595
|
+
Graph on 1 vertex
|
596
|
+
Graph on 2 vertices
|
597
|
+
Graph on 3 vertices
|
598
|
+
Graph on 3 vertices
|
599
|
+
Graph on 3 vertices
|
600
|
+
Graph on 2 vertices
|
601
|
+
Graph on 3 vertices
|
602
|
+
|
603
|
+
Print graphs on 3 vertices.
|
604
|
+
|
605
|
+
::
|
606
|
+
|
607
|
+
sage: # needs nauty
|
608
|
+
sage: for G in graphs(3):
|
609
|
+
....: print(G)
|
610
|
+
Graph on 3 vertices
|
611
|
+
Graph on 3 vertices
|
612
|
+
Graph on 3 vertices
|
613
|
+
Graph on 3 vertices
|
614
|
+
|
615
|
+
Generate all graphs with 5 vertices and 4 edges.
|
616
|
+
|
617
|
+
::
|
618
|
+
|
619
|
+
sage: # needs nauty
|
620
|
+
sage: L = graphs(5, size=4)
|
621
|
+
sage: len(list(L))
|
622
|
+
6
|
623
|
+
|
624
|
+
Generate all graphs with 5 vertices and up to 4 edges.
|
625
|
+
|
626
|
+
::
|
627
|
+
|
628
|
+
sage: # needs nauty
|
629
|
+
sage: L = list(graphs(5, lambda G: G.size() <= 4))
|
630
|
+
sage: len(L)
|
631
|
+
14
|
632
|
+
sage: graphs_list.show_graphs(L) # long time # needs sage.plot
|
633
|
+
|
634
|
+
Generate all graphs with up to 5 vertices and up to 4 edges.
|
635
|
+
|
636
|
+
::
|
637
|
+
|
638
|
+
sage: # needs nauty
|
639
|
+
sage: L = list(graphs(5, lambda G: G.size() <= 4, augment='vertices'))
|
640
|
+
sage: len(L)
|
641
|
+
31
|
642
|
+
sage: graphs_list.show_graphs(L) # long time # needs sage.plot
|
643
|
+
|
644
|
+
Generate all graphs with degree at most 2, up to 6 vertices.
|
645
|
+
|
646
|
+
::
|
647
|
+
|
648
|
+
sage: # needs nauty
|
649
|
+
sage: property = lambda G: ( max([G.degree(v) for v in G] + [0]) <= 2 )
|
650
|
+
sage: L = list(graphs(6, property, augment='vertices'))
|
651
|
+
sage: len(L)
|
652
|
+
45
|
653
|
+
|
654
|
+
Generate all bipartite graphs on up to 7 vertices: (see
|
655
|
+
:oeis:`A033995`)
|
656
|
+
|
657
|
+
::
|
658
|
+
|
659
|
+
sage: # needs nauty
|
660
|
+
sage: L = list( graphs(7, lambda G: G.is_bipartite(), augment='vertices') )
|
661
|
+
sage: [len([g for g in L if g.order() == i]) for i in [1..7]]
|
662
|
+
[1, 2, 3, 7, 13, 35, 88]
|
663
|
+
|
664
|
+
Generate all bipartite graphs on exactly 7 vertices::
|
665
|
+
|
666
|
+
sage: # needs nauty
|
667
|
+
sage: L = list( graphs(7, lambda G: G.is_bipartite()) )
|
668
|
+
sage: len(L)
|
669
|
+
88
|
670
|
+
|
671
|
+
Generate all bipartite graphs on exactly 8 vertices::
|
672
|
+
|
673
|
+
sage: # needs nauty
|
674
|
+
sage: L = list( graphs(8, lambda G: G.is_bipartite()) ) # long time
|
675
|
+
sage: len(L) # long time
|
676
|
+
303
|
677
|
+
|
678
|
+
Remember that the property argument does not behave as a filter,
|
679
|
+
except for appropriately inheritable properties::
|
680
|
+
|
681
|
+
sage: # needs nauty
|
682
|
+
sage: property = lambda G: G.is_vertex_transitive()
|
683
|
+
sage: len(list(graphs(4, property))) # needs sage.groups
|
684
|
+
1
|
685
|
+
sage: sum(1 for g in graphs(4) if property(g)) # needs sage.groups
|
686
|
+
4
|
687
|
+
|
688
|
+
sage: # needs nauty
|
689
|
+
sage: property = lambda G: G.is_bipartite()
|
690
|
+
sage: len(list(graphs(4, property)))
|
691
|
+
7
|
692
|
+
sage: sum(1 for g in graphs(4) if property(g))
|
693
|
+
7
|
694
|
+
|
695
|
+
Generate graphs on the fly: (see :oeis:`A000088`)
|
696
|
+
|
697
|
+
::
|
698
|
+
|
699
|
+
sage: # needs nauty
|
700
|
+
sage: for i in range(7):
|
701
|
+
....: print(len(list(graphs(i))))
|
702
|
+
1
|
703
|
+
1
|
704
|
+
2
|
705
|
+
4
|
706
|
+
11
|
707
|
+
34
|
708
|
+
156
|
709
|
+
|
710
|
+
Generate all simple graphs, allowing loops: (see :oeis:`A000666`)
|
711
|
+
|
712
|
+
::
|
713
|
+
|
714
|
+
sage: L = list(graphs(5,augment='vertices',loops=True)) # long time
|
715
|
+
sage: for i in [0..5]: # long time
|
716
|
+
....: print((i, len([g for g in L if g.order() == i])))
|
717
|
+
(0, 1)
|
718
|
+
(1, 2)
|
719
|
+
(2, 6)
|
720
|
+
(3, 20)
|
721
|
+
(4, 90)
|
722
|
+
(5, 544)
|
723
|
+
|
724
|
+
Generate all graphs with a specified degree sequence (see :oeis:`A002851`)::
|
725
|
+
|
726
|
+
sage: # needs nauty
|
727
|
+
sage: for i in [4,6,8]: # long time (4s on sage.math, 2012)
|
728
|
+
....: print((i, len([g for g in graphs(i, degree_sequence=[3]*i) if g.is_connected()])))
|
729
|
+
(4, 1)
|
730
|
+
(6, 2)
|
731
|
+
(8, 5)
|
732
|
+
sage: for i in [4,6,8]: # long time (7s on sage.math, 2012)
|
733
|
+
....: print((i, len([g for g in graphs(i, augment='vertices', degree_sequence=[3]*i) if g.is_connected()])))
|
734
|
+
(4, 1)
|
735
|
+
(6, 2)
|
736
|
+
(8, 5)
|
737
|
+
|
738
|
+
::
|
739
|
+
|
740
|
+
sage: # needs nauty
|
741
|
+
sage: print((10, len([g for g in graphs(10,degree_sequence=[3]*10) if g.is_connected()]))) # not tested
|
742
|
+
(10, 19)
|
743
|
+
|
744
|
+
Make sure that the graphs are really independent and the generator
|
745
|
+
survives repeated vertex removal (:issue:`8458`)::
|
746
|
+
|
747
|
+
sage: # needs nauty
|
748
|
+
sage: for G in graphs(3):
|
749
|
+
....: G.delete_vertex(0)
|
750
|
+
....: print(G.order())
|
751
|
+
2
|
752
|
+
2
|
753
|
+
2
|
754
|
+
2
|
755
|
+
|
756
|
+
Returned graphs can be mutable or immutable::
|
757
|
+
|
758
|
+
sage: # needs nauty
|
759
|
+
sage: G = next(graphs(3, immutable=False))
|
760
|
+
sage: G.delete_vertex(0)
|
761
|
+
sage: G = next(graphs(3, immutable=True))
|
762
|
+
sage: G.delete_vertex(0)
|
763
|
+
Traceback (most recent call last):
|
764
|
+
...
|
765
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
766
|
+
sage: G = next(graphs(4, degree_sequence=[3]*4))
|
767
|
+
sage: G.delete_vertex(0)
|
768
|
+
sage: G = next(graphs(4, degree_sequence=[3]*4, immutable=True))
|
769
|
+
sage: G.delete_vertex(0)
|
770
|
+
Traceback (most recent call last):
|
771
|
+
...
|
772
|
+
ValueError: graph is immutable; please change a copy instead (use function copy())
|
773
|
+
|
774
|
+
REFERENCE:
|
775
|
+
|
776
|
+
- Brendan D. McKay, Isomorph-Free Exhaustive generation. *Journal
|
777
|
+
of Algorithms*, Volume 26, Issue 2, February 1998, pages 306-324.
|
778
|
+
"""
|
779
|
+
|
780
|
+
###########################################################################
|
781
|
+
# Graph Iterators
|
782
|
+
###########################################################################
|
783
|
+
|
784
|
+
def __call__(self, vertices=None, property=None, augment='edges', size=None,
|
785
|
+
degree_sequence=None, loops=False, sparse=True, copy=True,
|
786
|
+
immutable=False):
|
787
|
+
"""
|
788
|
+
Access the generator of isomorphism class representatives.
|
789
|
+
Iterates over distinct, exhaustive representatives. See the docstring
|
790
|
+
of this class for full documentation.
|
791
|
+
|
792
|
+
EXAMPLES:
|
793
|
+
|
794
|
+
Print graphs on 3 or less vertices::
|
795
|
+
|
796
|
+
sage: # needs nauty
|
797
|
+
sage: for G in graphs(3, augment='vertices'):
|
798
|
+
....: print(G)
|
799
|
+
Graph on 0 vertices
|
800
|
+
Graph on 1 vertex
|
801
|
+
Graph on 2 vertices
|
802
|
+
Graph on 3 vertices
|
803
|
+
Graph on 3 vertices
|
804
|
+
Graph on 3 vertices
|
805
|
+
Graph on 2 vertices
|
806
|
+
Graph on 3 vertices
|
807
|
+
|
808
|
+
::
|
809
|
+
|
810
|
+
sage: # needs nauty
|
811
|
+
sage: for g in graphs():
|
812
|
+
....: if g.num_verts() > 3: break
|
813
|
+
....: print(g)
|
814
|
+
Graph on 0 vertices
|
815
|
+
Graph on 1 vertex
|
816
|
+
Graph on 2 vertices
|
817
|
+
Graph on 2 vertices
|
818
|
+
Graph on 3 vertices
|
819
|
+
Graph on 3 vertices
|
820
|
+
Graph on 3 vertices
|
821
|
+
Graph on 3 vertices
|
822
|
+
|
823
|
+
For more examples, see the class level documentation, or type::
|
824
|
+
|
825
|
+
sage: graphs? # not tested
|
826
|
+
|
827
|
+
REFERENCE:
|
828
|
+
|
829
|
+
- Brendan D. McKay, Isomorph-Free Exhaustive generation.
|
830
|
+
Journal of Algorithms Volume 26, Issue 2, February 1998,
|
831
|
+
pages 306-324.
|
832
|
+
"""
|
833
|
+
# Use nauty for the basic case, as it is much faster.
|
834
|
+
if (vertices and property is None and size is None and
|
835
|
+
degree_sequence is None and not loops and augment == 'edges' and
|
836
|
+
sparse and (copy or immutable)):
|
837
|
+
yield from graphs.nauty_geng(vertices, immutable=immutable)
|
838
|
+
return
|
839
|
+
|
840
|
+
if property is None:
|
841
|
+
def property(x):
|
842
|
+
return True
|
843
|
+
|
844
|
+
if degree_sequence is not None:
|
845
|
+
if vertices is None:
|
846
|
+
raise NotImplementedError
|
847
|
+
if (len(degree_sequence) != vertices or sum(degree_sequence) % 2
|
848
|
+
or sum(degree_sequence) > vertices * (vertices - 1)):
|
849
|
+
raise ValueError("Invalid degree sequence.")
|
850
|
+
degree_sequence = sorted(degree_sequence)
|
851
|
+
if augment == 'edges':
|
852
|
+
def property(x):
|
853
|
+
D = sorted(x.degree())
|
854
|
+
return all(degree_sequence[i] >= d for i, d in enumerate(D))
|
855
|
+
|
856
|
+
def extra_property(x):
|
857
|
+
return degree_sequence == sorted(x.degree())
|
858
|
+
else:
|
859
|
+
def property(x):
|
860
|
+
D = sorted(x.degree() + [0] * (vertices - x.num_verts()))
|
861
|
+
return all(degree_sequence[i] >= d for i, d in enumerate(D))
|
862
|
+
|
863
|
+
def extra_property(x):
|
864
|
+
if x.num_verts() != vertices:
|
865
|
+
return False
|
866
|
+
return degree_sequence == sorted(x.degree())
|
867
|
+
elif size is not None:
|
868
|
+
def extra_property(x):
|
869
|
+
return x.size() == size
|
870
|
+
else:
|
871
|
+
def extra_property(x):
|
872
|
+
return True
|
873
|
+
|
874
|
+
if augment == 'vertices':
|
875
|
+
if vertices is None:
|
876
|
+
raise NotImplementedError
|
877
|
+
g = graph.Graph(loops=loops, sparse=sparse)
|
878
|
+
for gg in canaug_traverse_vert(g, [], vertices, property, loops=loops, sparse=sparse):
|
879
|
+
if extra_property(gg):
|
880
|
+
yield gg.copy(immutable=immutable) if copy or immutable else gg
|
881
|
+
elif augment == 'edges':
|
882
|
+
if vertices is None:
|
883
|
+
from sage.rings.integer import Integer
|
884
|
+
vertices = Integer(0)
|
885
|
+
while True:
|
886
|
+
for g in self(vertices, loops=loops, sparse=sparse):
|
887
|
+
yield g.copy(immutable=immutable) if copy or immutable else g
|
888
|
+
vertices += 1
|
889
|
+
g = graph.Graph(vertices, loops=loops, sparse=sparse)
|
890
|
+
gens = []
|
891
|
+
for i in range(vertices - 1):
|
892
|
+
gen = list(range(i))
|
893
|
+
gen.append(i + 1)
|
894
|
+
gen.append(i)
|
895
|
+
gen += list(range(i + 2, vertices))
|
896
|
+
gens.append(gen)
|
897
|
+
for gg in canaug_traverse_edge(g, gens, property, loops=loops, sparse=sparse):
|
898
|
+
if extra_property(gg):
|
899
|
+
yield gg.copy(immutable=immutable) if copy or immutable else gg
|
900
|
+
else:
|
901
|
+
raise NotImplementedError
|
902
|
+
|
903
|
+
def nauty_geng(self, options='', debug=False, immutable=False):
|
904
|
+
r"""
|
905
|
+
Return a generator which creates graphs from nauty's geng program.
|
906
|
+
|
907
|
+
INPUT:
|
908
|
+
|
909
|
+
- ``options`` -- string (default: ``''``); a string passed to ``geng``
|
910
|
+
as if it was run at a system command line. At a minimum, you *must*
|
911
|
+
pass the number of vertices you desire. Sage expects the graphs to be
|
912
|
+
in nauty's "graph6" format, do not set an option to change this
|
913
|
+
default or results will be unpredictable.
|
914
|
+
|
915
|
+
- ``debug`` -- boolean (default: ``False``); if ``True`` the first line
|
916
|
+
of ``geng``'s output to standard error is captured and the first call
|
917
|
+
to the generator's ``next()`` function will return this line as a
|
918
|
+
string. A line leading with ">A" indicates a successful initiation of
|
919
|
+
the program with some information on the arguments, while a line
|
920
|
+
beginning with ">E" indicates an error with the input.
|
921
|
+
|
922
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
923
|
+
immutable or mutable graphs
|
924
|
+
|
925
|
+
The possible options, obtained as output of ``geng --help``::
|
926
|
+
|
927
|
+
n : the number of vertices
|
928
|
+
mine:maxe : <int>:<int> a range for the number of edges
|
929
|
+
<int>:0 means '<int> or more' except in the case 0:0
|
930
|
+
res/mod : only generate subset res out of subsets 0..mod-1
|
931
|
+
|
932
|
+
-c : only write connected graphs
|
933
|
+
-C : only write biconnected graphs
|
934
|
+
-t : only generate triangle-free graphs
|
935
|
+
-f : only generate 4-cycle-free graphs
|
936
|
+
-b : only generate bipartite graphs
|
937
|
+
(-t, -f and -b can be used in any combination)
|
938
|
+
-m : save memory at the expense of time (only makes a
|
939
|
+
difference in the absence of -b, -t, -f and n <= 28).
|
940
|
+
-d<int> : a lower bound for the minimum degree
|
941
|
+
-D<int> : a upper bound for the maximum degree
|
942
|
+
-v : display counts by number of edges
|
943
|
+
-l : canonically label output graphs
|
944
|
+
|
945
|
+
-q : suppress auxiliary output (except from -v)
|
946
|
+
|
947
|
+
Options which cause ``geng`` to use an output format different than the
|
948
|
+
graph6 format are not listed above (-u, -g, -s, -y, -h) as they will
|
949
|
+
confuse the creation of a Sage graph. The res/mod option can be useful
|
950
|
+
when using the output in a routine run several times in parallel.
|
951
|
+
|
952
|
+
OUTPUT:
|
953
|
+
|
954
|
+
A generator which will produce the graphs as Sage graphs.
|
955
|
+
These will be simple graphs: no loops, no multiple edges, no
|
956
|
+
directed edges.
|
957
|
+
|
958
|
+
.. SEEALSO::
|
959
|
+
|
960
|
+
:meth:`Graph.is_strongly_regular` -- tests whether a graph is
|
961
|
+
strongly regular and/or returns its parameters.
|
962
|
+
|
963
|
+
EXAMPLES:
|
964
|
+
|
965
|
+
The generator can be used to construct graphs for testing,
|
966
|
+
one at a time (usually inside a loop). Or it can be used to
|
967
|
+
create an entire list all at once if there is sufficient memory
|
968
|
+
to contain it. ::
|
969
|
+
|
970
|
+
sage: # needs nauty
|
971
|
+
sage: gen = graphs.nauty_geng("2")
|
972
|
+
sage: next(gen)
|
973
|
+
Graph on 2 vertices
|
974
|
+
sage: next(gen)
|
975
|
+
Graph on 2 vertices
|
976
|
+
sage: next(gen)
|
977
|
+
Traceback (most recent call last):
|
978
|
+
...
|
979
|
+
StopIteration
|
980
|
+
|
981
|
+
A list of all graphs on 7 vertices. This agrees with
|
982
|
+
:oeis:`A000088`. ::
|
983
|
+
|
984
|
+
sage: # needs nauty
|
985
|
+
sage: gen = graphs.nauty_geng("7")
|
986
|
+
sage: len(list(gen))
|
987
|
+
1044
|
988
|
+
|
989
|
+
A list of just the connected graphs on 7 vertices. This agrees with
|
990
|
+
:oeis:`A001349`. ::
|
991
|
+
|
992
|
+
sage: # needs nauty
|
993
|
+
sage: gen = graphs.nauty_geng("7 -c")
|
994
|
+
sage: len(list(gen))
|
995
|
+
853
|
996
|
+
|
997
|
+
A list of connected degree exactly 2 graphs on 5 vertices. ::
|
998
|
+
|
999
|
+
sage: # needs nauty
|
1000
|
+
sage: gen = graphs.nauty_geng("5 -c -d2 -D2")
|
1001
|
+
sage: len(list(gen))
|
1002
|
+
1
|
1003
|
+
|
1004
|
+
The ``debug`` switch can be used to examine ``geng``'s reaction to the
|
1005
|
+
input in the ``options`` string. We illustrate success. (A failure
|
1006
|
+
will be a string beginning with ">E".) Passing the "-q" switch to
|
1007
|
+
``geng`` will suppress the indicator of a successful initiation, and so
|
1008
|
+
the first returned value might be an empty string if ``debug`` is
|
1009
|
+
``True``::
|
1010
|
+
|
1011
|
+
sage: # needs nauty
|
1012
|
+
sage: gen = graphs.nauty_geng("4", debug=True)
|
1013
|
+
sage: print(next(gen))
|
1014
|
+
>A ...geng -d0D3 n=4 e=0-6
|
1015
|
+
sage: gen = graphs.nauty_geng("4 -q", debug=True)
|
1016
|
+
sage: next(gen)
|
1017
|
+
''
|
1018
|
+
|
1019
|
+
TESTS:
|
1020
|
+
|
1021
|
+
Wrong input, ``"-c3"`` instead of ``"-c 3"`` (:issue:`14068`)::
|
1022
|
+
|
1023
|
+
sage: # needs nauty
|
1024
|
+
sage: list(graphs.nauty_geng("-c3", debug=False))
|
1025
|
+
Traceback (most recent call last):
|
1026
|
+
...
|
1027
|
+
ValueError: wrong format of parameter option
|
1028
|
+
sage: list(graphs.nauty_geng("-c3", debug=True))
|
1029
|
+
['>E Usage: ...geng ...\n']
|
1030
|
+
sage: list(graphs.nauty_geng("-c 3", debug=True))
|
1031
|
+
['>A ...geng -cd1D2 n=3 e=2-3\n', Graph on 3 vertices, Graph on 3 vertices]
|
1032
|
+
"""
|
1033
|
+
import shlex
|
1034
|
+
from sage.features.nauty import NautyExecutable
|
1035
|
+
geng_path = NautyExecutable("geng").absolute_filename()
|
1036
|
+
sp = subprocess.Popen(shlex.quote(geng_path) + " {0}".format(options), shell=True,
|
1037
|
+
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
1038
|
+
stderr=subprocess.PIPE, close_fds=True,
|
1039
|
+
encoding='latin-1')
|
1040
|
+
msg = sp.stderr.readline()
|
1041
|
+
if debug:
|
1042
|
+
yield msg
|
1043
|
+
elif msg.startswith('>E'):
|
1044
|
+
raise ValueError('wrong format of parameter option')
|
1045
|
+
gen = sp.stdout
|
1046
|
+
while True:
|
1047
|
+
try:
|
1048
|
+
s = next(gen)
|
1049
|
+
except StopIteration:
|
1050
|
+
# Exhausted list of graphs from nauty geng
|
1051
|
+
return
|
1052
|
+
yield graph.Graph(s[:-1], format='graph6', immutable=immutable)
|
1053
|
+
|
1054
|
+
def nauty_genbg(self, options='', debug=False, immutable=False):
|
1055
|
+
r"""
|
1056
|
+
Return a generator which creates bipartite graphs from nauty's ``genbgL``
|
1057
|
+
program.
|
1058
|
+
|
1059
|
+
INPUT:
|
1060
|
+
|
1061
|
+
- ``options`` -- string (default: ``""``); a string passed to ``genbgL``
|
1062
|
+
as if it was run at a system command line. At a minimum, you *must*
|
1063
|
+
pass the number of vertices you desire in each side. Sage expects the
|
1064
|
+
bipartite graphs to be in nauty's "graph6" format, do not set an
|
1065
|
+
option to change this default or results will be unpredictable.
|
1066
|
+
|
1067
|
+
- ``debug`` -- boolean (default: ``False``); if ``True`` the first line
|
1068
|
+
of ``geng``'s output to standard error is captured and the first call
|
1069
|
+
to the generator's ``next()`` function will return this line as a
|
1070
|
+
string. A line leading with ">A" indicates a successful initiation of
|
1071
|
+
the program with some information on the arguments, while a line
|
1072
|
+
beginning with ">E" indicates an error with the input.
|
1073
|
+
|
1074
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
1075
|
+
immutable or mutable graphs
|
1076
|
+
|
1077
|
+
The possible options, obtained as output of ``genbgL --help``::
|
1078
|
+
|
1079
|
+
n1 : the number of vertices in the first class.
|
1080
|
+
We must have n1=1..30.
|
1081
|
+
n2 : the number of vertices in the second class.
|
1082
|
+
We must have n2=0..64 and n1+n2=1..64.
|
1083
|
+
mine:maxe : <int>:<int> a range for the number of edges
|
1084
|
+
<int>:0 means '<int> or more' except in the case 0:0
|
1085
|
+
res/mod : only generate subset res out of subsets 0..mod-1
|
1086
|
+
-c : only write connected graphs
|
1087
|
+
-z : all the vertices in the second class must have
|
1088
|
+
different neighbourhoods
|
1089
|
+
-F : the vertices in the second class must have at least
|
1090
|
+
two neighbours of degree at least 2
|
1091
|
+
-L : there is no vertex in the first class whose removal
|
1092
|
+
leaves the vertices in the second class unreachable
|
1093
|
+
from each other
|
1094
|
+
-Y<int> : two vertices in the second class must have at least
|
1095
|
+
<int> common neighbours
|
1096
|
+
-Z<int> : two vertices in the second class must have at most
|
1097
|
+
<int> common neighbours
|
1098
|
+
-A : no vertex in the second class has a neighbourhood
|
1099
|
+
which is a subset of another vertex's neighbourhood
|
1100
|
+
in the second class
|
1101
|
+
-D<int> : specify an upper bound for the maximum degree
|
1102
|
+
Example: -D6. You can also give separate maxima for
|
1103
|
+
the two parts, for example: -D5:6
|
1104
|
+
-d<int> : specify a lower bound for the minimum degree
|
1105
|
+
Again, you can specify it separately for the two parts,
|
1106
|
+
for example -d1:2
|
1107
|
+
-v : display counts by number of edges to stderr
|
1108
|
+
-l : canonically label output graphs
|
1109
|
+
|
1110
|
+
Options which cause ``genbgL`` to use an output format different than
|
1111
|
+
the ``graph6`` format are not listed above (``-s``, ``-a``) as they will
|
1112
|
+
confuse the creation of a Sage graph. Option ``-q`` which suppress
|
1113
|
+
auxiliary output (except from ``-v``) should never be used as we are
|
1114
|
+
unable to recover the partition of the vertices of the bipartite graph
|
1115
|
+
without the auxiliary output. Hence the partition of the vertices of
|
1116
|
+
returned bipartite graphs might not respect the requirement.
|
1117
|
+
|
1118
|
+
The res/mod option can be useful when using the output in a routine run
|
1119
|
+
several times in parallel.
|
1120
|
+
|
1121
|
+
OUTPUT:
|
1122
|
+
|
1123
|
+
A generator which will produce the graphs as
|
1124
|
+
:class:`~sage/graphs.bipartite_graph.BipartiteGraph`. These will be
|
1125
|
+
simple bipartite graphs: no loops, no multiple edges, no directed edges.
|
1126
|
+
|
1127
|
+
EXAMPLES:
|
1128
|
+
|
1129
|
+
The generator can be used to construct bipartite graphs for testing,
|
1130
|
+
one at a time (usually inside a loop). Or it can be used to
|
1131
|
+
create an entire list all at once if there is sufficient memory
|
1132
|
+
to contain it::
|
1133
|
+
|
1134
|
+
sage: # needs nauty
|
1135
|
+
sage: gen = graphs.nauty_genbg("1 1")
|
1136
|
+
sage: next(gen)
|
1137
|
+
Bipartite graph on 2 vertices
|
1138
|
+
sage: next(gen)
|
1139
|
+
Bipartite graph on 2 vertices
|
1140
|
+
sage: next(gen)
|
1141
|
+
Traceback (most recent call last):
|
1142
|
+
...
|
1143
|
+
StopIteration
|
1144
|
+
|
1145
|
+
Connected bipartite graphs of order 6 with different number of vertices
|
1146
|
+
in each side::
|
1147
|
+
|
1148
|
+
sage: # needs nauty
|
1149
|
+
sage: gen = graphs.nauty_genbg("1 5 -c")
|
1150
|
+
sage: len(list(gen))
|
1151
|
+
1
|
1152
|
+
sage: gen = graphs.nauty_genbg("2 4 -c")
|
1153
|
+
sage: len(list(gen))
|
1154
|
+
6
|
1155
|
+
sage: gen = graphs.nauty_genbg("3 3 -c")
|
1156
|
+
sage: len(list(gen))
|
1157
|
+
13
|
1158
|
+
|
1159
|
+
Use :meth:`nauty_geng` instead if you want the list of all bipartite
|
1160
|
+
graphs of order `n`. For instance, the list of all connected bipartite
|
1161
|
+
graphs of order 6, which agrees with :oeis:`A005142`::
|
1162
|
+
|
1163
|
+
sage: # needs nauty
|
1164
|
+
sage: gen = graphs.nauty_geng("-b -c 6")
|
1165
|
+
sage: len(list(gen))
|
1166
|
+
17
|
1167
|
+
|
1168
|
+
The ``debug`` switch can be used to examine ``genbgL``'s reaction to the
|
1169
|
+
input in the ``options`` string. A message starting with ">A" indicates
|
1170
|
+
success and a message starting with ">E" indicates a failure::
|
1171
|
+
|
1172
|
+
sage: # needs nauty
|
1173
|
+
sage: gen = graphs.nauty_genbg("2 3", debug=True)
|
1174
|
+
sage: print(next(gen))
|
1175
|
+
>A ...genbg... n=2+3 e=0:6 d=0:0 D=3:2
|
1176
|
+
sage: gen = graphs.nauty_genbg("-c2 3", debug=True)
|
1177
|
+
sage: next(gen)
|
1178
|
+
'>E Usage: ...genbg... [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2...
|
1179
|
+
|
1180
|
+
Check that the partition of the bipartite graph is consistent::
|
1181
|
+
|
1182
|
+
sage: # needs nauty
|
1183
|
+
sage: gen = graphs.nauty_genbg("3 3")
|
1184
|
+
sage: left = set(range(3))
|
1185
|
+
sage: for g in gen:
|
1186
|
+
....: if g.left != left:
|
1187
|
+
....: raise ValueError('wrong partition')
|
1188
|
+
|
1189
|
+
TESTS:
|
1190
|
+
|
1191
|
+
Wrong input::
|
1192
|
+
|
1193
|
+
sage: # needs nauty
|
1194
|
+
sage: list(graphs.nauty_genbg("-c1 2", debug=False))
|
1195
|
+
Traceback (most recent call last):
|
1196
|
+
...
|
1197
|
+
ValueError: wrong format of parameter options
|
1198
|
+
sage: list(graphs.nauty_genbg("-c1 2", debug=True))
|
1199
|
+
['>E Usage: ...genbg... [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2...
|
1200
|
+
sage: list(graphs.nauty_genbg("-c 1 2", debug=True))
|
1201
|
+
['>A ...genbg... n=1+2 e=2:2 d=1:1 D=2:1 c...\n', Bipartite graph on 3 vertices]
|
1202
|
+
|
1203
|
+
We must have n1=1..30, n2=0..64 and n1+n2=1..64 (:issue:`34179`,
|
1204
|
+
:issue:`38618`)::
|
1205
|
+
|
1206
|
+
sage: # needs nauty
|
1207
|
+
sage: next(graphs.nauty_genbg("31 1", debug=False))
|
1208
|
+
Traceback (most recent call last):
|
1209
|
+
...
|
1210
|
+
ValueError: wrong format of parameter options
|
1211
|
+
sage: next(graphs.nauty_genbg("31 1", debug=True))
|
1212
|
+
'>E ...genbg...: must have n1=1..30, n1+n2=1..64...
|
1213
|
+
sage: next(graphs.nauty_genbg("30 40", debug=True))
|
1214
|
+
'>E ...genbg...: must have n1=1..30, n1+n2=1..64...
|
1215
|
+
sage: next(graphs.nauty_genbg("1 63", debug=False))
|
1216
|
+
Bipartite graph on 64 vertices
|
1217
|
+
sage: next(graphs.nauty_genbg("1 64", debug=True))
|
1218
|
+
'>E ...genbg...: must have n1=1..30, n1+n2=1..64...
|
1219
|
+
sage: next(graphs.nauty_genbg("0 2", debug=True))
|
1220
|
+
'>E ...genbg...: must have n1=1..30, n1+n2=1..64...
|
1221
|
+
sage: next(graphs.nauty_genbg("2 0", debug=False))
|
1222
|
+
Bipartite graph on 2 vertices
|
1223
|
+
sage: next(graphs.nauty_genbg("2 -1", debug=True))
|
1224
|
+
'>E Usage: ...genbg... [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2...
|
1225
|
+
"""
|
1226
|
+
import shlex
|
1227
|
+
from sage.features.nauty import NautyExecutable
|
1228
|
+
genbg_path = NautyExecutable("genbgL").absolute_filename()
|
1229
|
+
sp = subprocess.Popen(shlex.quote(genbg_path) + " {0}".format(options), shell=True,
|
1230
|
+
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
1231
|
+
stderr=subprocess.PIPE, close_fds=True,
|
1232
|
+
encoding='latin-1')
|
1233
|
+
msg = sp.stderr.readline()
|
1234
|
+
if debug:
|
1235
|
+
yield msg
|
1236
|
+
elif msg.startswith('>E'):
|
1237
|
+
raise ValueError('wrong format of parameter options')
|
1238
|
+
|
1239
|
+
if msg.startswith('>A'):
|
1240
|
+
# We extract the partition of the vertices from the msg string
|
1241
|
+
for s in msg.split(' '):
|
1242
|
+
if s.startswith('n='):
|
1243
|
+
from sage.rings.integer import Integer
|
1244
|
+
n1, n2 = (Integer(t) for t in s[2:].split('+') if t.isdigit())
|
1245
|
+
partition = [set(range(n1)), set(range(n1, n1 + n2))]
|
1246
|
+
break
|
1247
|
+
else:
|
1248
|
+
# should never happen
|
1249
|
+
raise ValueError('unable to recover the partition')
|
1250
|
+
else:
|
1251
|
+
# Either msg starts with >E or option -q has been given
|
1252
|
+
partition = None
|
1253
|
+
|
1254
|
+
gen = sp.stdout
|
1255
|
+
from sage.graphs.bipartite_graph import BipartiteGraph
|
1256
|
+
while True:
|
1257
|
+
try:
|
1258
|
+
s = next(gen)
|
1259
|
+
except StopIteration:
|
1260
|
+
# Exhausted list of bipartite graphs from nauty genbgL
|
1261
|
+
return
|
1262
|
+
yield BipartiteGraph(s[:-1], format='graph6', partition=partition,
|
1263
|
+
immutable=immutable)
|
1264
|
+
|
1265
|
+
def nauty_genktreeg(self, options='', debug=False, immutable=False):
|
1266
|
+
r"""
|
1267
|
+
Return a generator which creates all `k`-trees using nauty..
|
1268
|
+
|
1269
|
+
A `k`-tree is an undirected graph formed by starting with a complete
|
1270
|
+
graph on `k + 1` vertices and then repeatedly add vertices in such a
|
1271
|
+
way that each added vertex `v` has exactly `k` neighbors `U` such that,
|
1272
|
+
together, the `k + 1` vertices formed by `v` and `U` form a clique.
|
1273
|
+
See the :wikipedia:`K-tree` for more details.
|
1274
|
+
|
1275
|
+
INPUT:
|
1276
|
+
|
1277
|
+
- ``options`` -- string (default: ``""``); a string passed to
|
1278
|
+
``genktreeg`` as if it was run at a system command line. At a minimum,
|
1279
|
+
you *must* pass the number of vertices you desire. Sage expects the
|
1280
|
+
graphs to be in nauty's "graph6" format, do not set an option to
|
1281
|
+
change this default or results will be unpredictable.
|
1282
|
+
|
1283
|
+
- ``debug`` -- boolean (default: ``False``); if ``True`` the first line
|
1284
|
+
of ``genktreeg``'s output to standard error is captured and the first
|
1285
|
+
call to the generator's ``next()`` function will return this line as a
|
1286
|
+
string. A line leading with ">A" indicates a successful initiation of
|
1287
|
+
the program with some information on the arguments, while a line
|
1288
|
+
beginning with ">E" indicates an error with the input.
|
1289
|
+
|
1290
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
1291
|
+
immutable or mutable graphs
|
1292
|
+
|
1293
|
+
The possible options, obtained as output of ``genktreeg --help``::
|
1294
|
+
|
1295
|
+
n : the number of vertices
|
1296
|
+
-k<int> : the value of `k`(default: 2)
|
1297
|
+
res/mod : only generate subset res out of subsets 0..mod-1
|
1298
|
+
-l : canonically label output graphs
|
1299
|
+
|
1300
|
+
Options which cause ``genktreeg`` to use an output format different than
|
1301
|
+
the graph6 format are not listed above (-u, -s, -h) as they will confuse
|
1302
|
+
the creation of a Sage graph. The res/mod option can be useful when
|
1303
|
+
using the output in a routine run several times in parallel.
|
1304
|
+
|
1305
|
+
OUTPUT:
|
1306
|
+
|
1307
|
+
A generator which will produce the graphs as Sage graphs.
|
1308
|
+
These will be simple graphs: no loops, no multiple edges, no
|
1309
|
+
directed edges.
|
1310
|
+
|
1311
|
+
EXAMPLES:
|
1312
|
+
|
1313
|
+
A `k`-tree is a maximal graph with treewidth `k`::
|
1314
|
+
|
1315
|
+
sage: # needs nauty
|
1316
|
+
sage: gen = graphs.nauty_genktreeg("10 -k4")
|
1317
|
+
sage: G = next(gen); G
|
1318
|
+
Graph on 10 vertices
|
1319
|
+
sage: G.treewidth() # needs cliquer
|
1320
|
+
4
|
1321
|
+
|
1322
|
+
A list of all 2-trees with 6, 7 and 8 vertices. This agrees with
|
1323
|
+
:oeis:`A054581`::
|
1324
|
+
|
1325
|
+
sage: # needs nauty
|
1326
|
+
sage: gen = graphs.nauty_genktreeg("6")
|
1327
|
+
sage: len(list(gen))
|
1328
|
+
5
|
1329
|
+
sage: gen = graphs.nauty_genktreeg("7")
|
1330
|
+
sage: len(list(gen))
|
1331
|
+
12
|
1332
|
+
sage: gen = graphs.nauty_genktreeg("8")
|
1333
|
+
sage: len(list(gen))
|
1334
|
+
39
|
1335
|
+
|
1336
|
+
The ``debug`` switch can be used to examine ``geng``'s reaction to the
|
1337
|
+
input in the ``options`` string. We illustrate success. (A failure
|
1338
|
+
will be a string beginning with ">E".) Passing the "-q" switch to
|
1339
|
+
``geng`` will suppress the indicator of a successful initiation, and so
|
1340
|
+
the first returned value might be an empty string if ``debug`` is
|
1341
|
+
``True``::
|
1342
|
+
|
1343
|
+
sage: gen = graphs.nauty_genktreeg("7", debug=True) # needs nauty
|
1344
|
+
sage: print(next(gen)) # needs nauty
|
1345
|
+
>A ...genktreeg k=2 n=7
|
1346
|
+
|
1347
|
+
TESTS:
|
1348
|
+
|
1349
|
+
Wrong input::
|
1350
|
+
|
1351
|
+
sage: # needs nauty
|
1352
|
+
sage: list(graphs.nauty_genktreeg("4 -k5", debug=True))
|
1353
|
+
['>E genktreeg: n cannot be less than k\n']
|
1354
|
+
sage: list(graphs.nauty_genktreeg("10 -k 4", debug=True))
|
1355
|
+
['>E genktreeg -k: missing argument value\n']
|
1356
|
+
sage: list(graphs.nauty_genktreeg("-c3", debug=False))
|
1357
|
+
Traceback (most recent call last):
|
1358
|
+
...
|
1359
|
+
ValueError: wrong format of parameter option
|
1360
|
+
"""
|
1361
|
+
import shlex
|
1362
|
+
from sage.features.nauty import NautyExecutable
|
1363
|
+
geng_path = NautyExecutable("genktreeg").absolute_filename()
|
1364
|
+
sp = subprocess.Popen(shlex.quote(geng_path) + " {0}".format(options), shell=True,
|
1365
|
+
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
1366
|
+
stderr=subprocess.PIPE, close_fds=True,
|
1367
|
+
encoding='latin-1')
|
1368
|
+
msg = sp.stderr.readline()
|
1369
|
+
if debug:
|
1370
|
+
yield msg
|
1371
|
+
elif msg.startswith('>E'):
|
1372
|
+
raise ValueError('wrong format of parameter option')
|
1373
|
+
gen = sp.stdout
|
1374
|
+
while True:
|
1375
|
+
try:
|
1376
|
+
s = next(gen)
|
1377
|
+
except StopIteration:
|
1378
|
+
# Exhausted list of graphs from nauty geng
|
1379
|
+
return
|
1380
|
+
yield graph.Graph(s[:-1], format='graph6', immutable=immutable)
|
1381
|
+
|
1382
|
+
def cospectral_graphs(self, vertices, matrix_function=None, graphs=None,
|
1383
|
+
immutable=False):
|
1384
|
+
r"""
|
1385
|
+
Find all sets of graphs on ``vertices`` vertices (with
|
1386
|
+
possible restrictions) which are cospectral with respect to a
|
1387
|
+
constructed matrix.
|
1388
|
+
|
1389
|
+
INPUT:
|
1390
|
+
|
1391
|
+
- ``vertices`` -- the number of vertices in the graphs to be tested
|
1392
|
+
|
1393
|
+
- ``matrix_function`` -- a function taking a graph and giving back
|
1394
|
+
a matrix. This defaults to the adjacency matrix. The spectra
|
1395
|
+
examined are the spectra of these matrices.
|
1396
|
+
|
1397
|
+
- ``graphs`` -- one of three things:
|
1398
|
+
|
1399
|
+
- ``None`` -- default; test all graphs having ``vertices``
|
1400
|
+
vertices
|
1401
|
+
|
1402
|
+
- a function taking a graph and returning ``True`` or ``False``
|
1403
|
+
- test only the graphs on ``vertices`` vertices for which
|
1404
|
+
the function returns ``True``
|
1405
|
+
|
1406
|
+
- a list of graphs (or other iterable object) -- these graphs
|
1407
|
+
are tested for cospectral sets. In this case,
|
1408
|
+
``vertices`` is ignored.
|
1409
|
+
|
1410
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
1411
|
+
immutable or mutable graphs
|
1412
|
+
|
1413
|
+
OUTPUT:
|
1414
|
+
|
1415
|
+
A list of lists of graphs. Each sublist will be a list of
|
1416
|
+
cospectral graphs (lists of cardinality 1 being omitted).
|
1417
|
+
|
1418
|
+
.. SEEALSO::
|
1419
|
+
|
1420
|
+
:meth:`Graph.is_strongly_regular` -- tests whether a graph is
|
1421
|
+
strongly regular and/or returns its parameters.
|
1422
|
+
|
1423
|
+
EXAMPLES::
|
1424
|
+
|
1425
|
+
sage: g = graphs.cospectral_graphs(5) # needs sage.modules
|
1426
|
+
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g) # needs sage.modules
|
1427
|
+
[['Dr?', 'Ds_']]
|
1428
|
+
sage: g[0][1].am().charpoly()==g[0][1].am().charpoly() # needs sage.modules
|
1429
|
+
True
|
1430
|
+
|
1431
|
+
There are two sets of cospectral graphs on six vertices with no isolated vertices::
|
1432
|
+
|
1433
|
+
sage: # needs sage.modules
|
1434
|
+
sage: g = graphs.cospectral_graphs(6, graphs=lambda x: min(x.degree())>0)
|
1435
|
+
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
|
1436
|
+
[['Ep__', 'Er?G'], ['ExGg', 'ExoG']]
|
1437
|
+
sage: g[0][1].am().charpoly()==g[0][1].am().charpoly()
|
1438
|
+
True
|
1439
|
+
sage: g[1][1].am().charpoly()==g[1][1].am().charpoly()
|
1440
|
+
True
|
1441
|
+
|
1442
|
+
There is one pair of cospectral trees on eight vertices::
|
1443
|
+
|
1444
|
+
sage: g = graphs.cospectral_graphs(6, graphs=graphs.trees(8)) # needs sage.modules
|
1445
|
+
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g) # needs sage.modules
|
1446
|
+
[['GiPC?C', 'GiQCC?']]
|
1447
|
+
sage: g[0][1].am().charpoly()==g[0][1].am().charpoly() # needs sage.modules
|
1448
|
+
True
|
1449
|
+
|
1450
|
+
There are two sets of cospectral graphs (with respect to the
|
1451
|
+
Laplacian matrix) on six vertices::
|
1452
|
+
|
1453
|
+
sage: # needs sage.modules
|
1454
|
+
sage: g = graphs.cospectral_graphs(6, matrix_function=lambda g: g.laplacian_matrix())
|
1455
|
+
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g)
|
1456
|
+
[['Edq_', 'ErcG'], ['Exoo', 'EzcG']]
|
1457
|
+
sage: g[0][1].laplacian_matrix().charpoly()==g[0][1].laplacian_matrix().charpoly()
|
1458
|
+
True
|
1459
|
+
sage: g[1][1].laplacian_matrix().charpoly()==g[1][1].laplacian_matrix().charpoly()
|
1460
|
+
True
|
1461
|
+
|
1462
|
+
To find cospectral graphs with respect to the normalized
|
1463
|
+
Laplacian, assuming the graphs do not have an isolated vertex, it
|
1464
|
+
is enough to check the spectrum of the matrix `D^{-1}A`, where `D`
|
1465
|
+
is the diagonal matrix of vertex degrees, and A is the adjacency
|
1466
|
+
matrix. We find two such cospectral graphs (for the normalized
|
1467
|
+
Laplacian) on five vertices::
|
1468
|
+
|
1469
|
+
sage: def DinverseA(g):
|
1470
|
+
....: A = g.adjacency_matrix().change_ring(QQ)
|
1471
|
+
....: for i in range(g.order()):
|
1472
|
+
....: A.rescale_row(i, 1 / len(A.nonzero_positions_in_row(i)))
|
1473
|
+
....: return A
|
1474
|
+
sage: g = graphs.cospectral_graphs(5, matrix_function=DinverseA, # needs sage.libs.pari sage.modules
|
1475
|
+
....: graphs=lambda g: min(g.degree()) > 0)
|
1476
|
+
sage: sorted(sorted(g.graph6_string() for g in glist) for glist in g) # needs sage.modules
|
1477
|
+
[['Dlg', 'Ds_']]
|
1478
|
+
sage: (g[0][1].laplacian_matrix(normalized=True).charpoly() # needs sage.modules sage.symbolic
|
1479
|
+
....: == g[0][1].laplacian_matrix(normalized=True).charpoly())
|
1480
|
+
True
|
1481
|
+
"""
|
1482
|
+
if matrix_function is None:
|
1483
|
+
matrix_function = lambda g: g.adjacency_matrix()
|
1484
|
+
|
1485
|
+
def prop(x):
|
1486
|
+
return True
|
1487
|
+
|
1488
|
+
from sage.graphs.graph_generators import graphs as graph_gen
|
1489
|
+
if graphs is None:
|
1490
|
+
graph_list = graph_gen(vertices, property=prop, immutable=immutable)
|
1491
|
+
elif callable(graphs):
|
1492
|
+
graph_list = (g for g in graph_gen(vertices, property=prop,
|
1493
|
+
immutable=immutable) if graphs(g))
|
1494
|
+
else:
|
1495
|
+
graph_list = iter(graphs)
|
1496
|
+
|
1497
|
+
from collections import defaultdict
|
1498
|
+
charpolys = defaultdict(list)
|
1499
|
+
for g in graph_list:
|
1500
|
+
cp = matrix_function(g).charpoly()
|
1501
|
+
charpolys[cp].append(g)
|
1502
|
+
|
1503
|
+
cospectral_graphs = []
|
1504
|
+
for cp, g_list in charpolys.items():
|
1505
|
+
if len(g_list) > 1:
|
1506
|
+
cospectral_graphs.append(g_list)
|
1507
|
+
|
1508
|
+
return cospectral_graphs
|
1509
|
+
|
1510
|
+
def _read_planar_code(self, code_input, immutable=False):
|
1511
|
+
r"""
|
1512
|
+
Return a generator for the plane graphs in planar code format in
|
1513
|
+
the binary file ``code_input`` (see [BM2016]_).
|
1514
|
+
|
1515
|
+
A file with planar code starts with a header ``>>planar_code<<``.
|
1516
|
+
After the header each graph is stored in the following way :
|
1517
|
+
|
1518
|
+
The first character is the number of vertices, followed by
|
1519
|
+
n11,...,n1k,null character,n21,...,n2k',null character, ...
|
1520
|
+
|
1521
|
+
where the n1* are all neighbors of n1 and all n2* are the
|
1522
|
+
neighbors of n2, ...
|
1523
|
+
Besides, these neighbors are enumerated in clockwise order.
|
1524
|
+
|
1525
|
+
INPUT:
|
1526
|
+
|
1527
|
+
- ``code_input`` -- a binary file containing valid planar code data
|
1528
|
+
|
1529
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
1530
|
+
immutable or mutable graphs
|
1531
|
+
|
1532
|
+
OUTPUT:
|
1533
|
+
|
1534
|
+
A generator which will produce the plane graphs as Sage graphs
|
1535
|
+
with an embedding set. These will be simple graphs: no loops, no
|
1536
|
+
multiple edges, no directed edges (unless plantri is asked to give
|
1537
|
+
the dual graphs instead).
|
1538
|
+
|
1539
|
+
.. SEEALSO::
|
1540
|
+
|
1541
|
+
- :meth:`~sage.graphs.generic_graph.GenericGraph.set_embedding`,
|
1542
|
+
:meth:`~sage.graphs.generic_graph.GenericGraph.get_embedding` --
|
1543
|
+
get/set methods for embeddings.
|
1544
|
+
|
1545
|
+
EXAMPLES:
|
1546
|
+
|
1547
|
+
The following example creates a small planar code binary
|
1548
|
+
file in memory and reads it using the ``_read_planar_code`` method::
|
1549
|
+
|
1550
|
+
sage: from io import BytesIO
|
1551
|
+
sage: code_input = BytesIO()
|
1552
|
+
sage: n = code_input.write(b'>>planar_code<<')
|
1553
|
+
sage: for c in [4,2,3,4,0,1,4,3,0,1,2,4,0,1,3,2,0]:
|
1554
|
+
....: n = code_input.write(bytes('{:c}'.format(c),'ascii'))
|
1555
|
+
sage: n = code_input.seek(0)
|
1556
|
+
sage: gen = graphs._read_planar_code(code_input)
|
1557
|
+
sage: l = list(gen); l
|
1558
|
+
[Graph on 4 vertices]
|
1559
|
+
sage: l[0].is_isomorphic(graphs.CompleteGraph(4))
|
1560
|
+
True
|
1561
|
+
sage: l[0].get_embedding()
|
1562
|
+
{1: [2, 3, 4],
|
1563
|
+
2: [1, 4, 3],
|
1564
|
+
3: [1, 2, 4],
|
1565
|
+
4: [1, 3, 2]}
|
1566
|
+
|
1567
|
+
TESTS::
|
1568
|
+
|
1569
|
+
sage: from io import StringIO
|
1570
|
+
sage: code_input = StringIO()
|
1571
|
+
sage: n = code_input.write('>>planar_code<<')
|
1572
|
+
sage: n = code_input.seek(0)
|
1573
|
+
sage: list(graphs._read_planar_code(code_input))
|
1574
|
+
Traceback (most recent call last):
|
1575
|
+
...
|
1576
|
+
TypeError: not a binary file
|
1577
|
+
|
1578
|
+
sage: from io import BytesIO
|
1579
|
+
sage: code_input = BytesIO()
|
1580
|
+
sage: n = code_input.write(b'>>wrong header<<')
|
1581
|
+
sage: n = code_input.seek(0)
|
1582
|
+
sage: list(graphs._read_planar_code(code_input))
|
1583
|
+
Traceback (most recent call last):
|
1584
|
+
...
|
1585
|
+
TypeError: file has no valid planar code header
|
1586
|
+
"""
|
1587
|
+
# start of code to read planar code
|
1588
|
+
header = code_input.read(15)
|
1589
|
+
if not isinstance(header, bytes):
|
1590
|
+
raise TypeError('not a binary file')
|
1591
|
+
if header != b'>>planar_code<<':
|
1592
|
+
raise TypeError('file has no valid planar code header')
|
1593
|
+
|
1594
|
+
# read graph per graph
|
1595
|
+
while True:
|
1596
|
+
c = code_input.read(1)
|
1597
|
+
if not c:
|
1598
|
+
return
|
1599
|
+
|
1600
|
+
# Each graph is stored in the following way :
|
1601
|
+
#
|
1602
|
+
# The first character is the number of vertices, followed by
|
1603
|
+
# n11,...,n1k,null character,n21,...,n2k',null character, ...
|
1604
|
+
#
|
1605
|
+
# where the n1* are all neighbors of n1 and all n2* are the
|
1606
|
+
# neighbors of n2, ...
|
1607
|
+
#
|
1608
|
+
# Besides, these neighbors are enumerated in clockwise order.
|
1609
|
+
order = ord(c)
|
1610
|
+
|
1611
|
+
zeroCount = 0
|
1612
|
+
|
1613
|
+
g = [[] for i in range(order)]
|
1614
|
+
|
1615
|
+
while zeroCount < order:
|
1616
|
+
c = code_input.read(1)
|
1617
|
+
if ord(c) == 0:
|
1618
|
+
zeroCount += 1
|
1619
|
+
else:
|
1620
|
+
g[zeroCount].append(ord(c))
|
1621
|
+
|
1622
|
+
# construct graph based on g
|
1623
|
+
|
1624
|
+
# first taking care that every edge is given twice
|
1625
|
+
edges_g = {i + 1: [j for j in di if j < i + 1]
|
1626
|
+
for i, di in enumerate(g)}
|
1627
|
+
|
1628
|
+
# then adding half of the loops (if any)
|
1629
|
+
has_loops = False
|
1630
|
+
for i, di in enumerate(g):
|
1631
|
+
Ni = di.count(i + 1)
|
1632
|
+
if Ni > 1:
|
1633
|
+
edges_g[i + 1] += [i + 1] * (Ni // 2)
|
1634
|
+
has_loops = True
|
1635
|
+
G = graph.Graph(edges_g, loops=has_loops, immutable=immutable)
|
1636
|
+
|
1637
|
+
if not (G.has_multiple_edges() or has_loops):
|
1638
|
+
embed_g = {i + 1: di for i, di in enumerate(g)}
|
1639
|
+
G.set_embedding(embed_g)
|
1640
|
+
yield G
|
1641
|
+
|
1642
|
+
def fullerenes(self, order, ipr=False, immutable=False):
|
1643
|
+
r"""
|
1644
|
+
Return a generator which creates fullerene graphs using
|
1645
|
+
the buckygen generator (see [BGM2012]_).
|
1646
|
+
|
1647
|
+
INPUT:
|
1648
|
+
|
1649
|
+
- ``order`` -- a positive even integer smaller than or equal to 254
|
1650
|
+
This specifies the number of vertices in the generated fullerenes
|
1651
|
+
|
1652
|
+
- ``ipr`` -- boolean (default: ``False``); if ``True`` only fullerenes
|
1653
|
+
that satisfy the Isolated Pentagon Rule are generated. This means that
|
1654
|
+
no pentagonal faces share an edge.
|
1655
|
+
|
1656
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
1657
|
+
immutable or mutable graphs
|
1658
|
+
|
1659
|
+
OUTPUT:
|
1660
|
+
|
1661
|
+
A generator which will produce the fullerene graphs as Sage graphs
|
1662
|
+
with an embedding set. These will be simple graphs: no loops, no
|
1663
|
+
multiple edges, no directed edges.
|
1664
|
+
|
1665
|
+
.. SEEALSO::
|
1666
|
+
|
1667
|
+
- :meth:`~sage.graphs.generic_graph.GenericGraph.set_embedding`,
|
1668
|
+
:meth:`~sage.graphs.generic_graph.GenericGraph.get_embedding` --
|
1669
|
+
get/set methods for embeddings.
|
1670
|
+
|
1671
|
+
EXAMPLES:
|
1672
|
+
|
1673
|
+
There are 1812 isomers of `\textrm{C}_{60}`, i.e., 1812 fullerene graphs
|
1674
|
+
on 60 vertices::
|
1675
|
+
|
1676
|
+
sage: gen = graphs.fullerenes(60) # optional - buckygen
|
1677
|
+
sage: len(list(gen)) # optional - buckygen
|
1678
|
+
1812
|
1679
|
+
|
1680
|
+
However, there is only one IPR fullerene graph on 60 vertices: the famous
|
1681
|
+
Buckminster Fullerene::
|
1682
|
+
|
1683
|
+
sage: gen = graphs.fullerenes(60, ipr=True) # optional - buckygen
|
1684
|
+
sage: next(gen) # optional - buckygen
|
1685
|
+
Graph on 60 vertices
|
1686
|
+
sage: next(gen) # optional - buckygen
|
1687
|
+
Traceback (most recent call last):
|
1688
|
+
...
|
1689
|
+
StopIteration
|
1690
|
+
|
1691
|
+
The unique fullerene graph on 20 vertices is isomorphic to the dodecahedron
|
1692
|
+
graph. ::
|
1693
|
+
|
1694
|
+
sage: # optional - buckygen
|
1695
|
+
sage: gen = graphs.fullerenes(20)
|
1696
|
+
sage: g = next(gen)
|
1697
|
+
sage: g.is_isomorphic(graphs.DodecahedralGraph())
|
1698
|
+
True
|
1699
|
+
sage: g.get_embedding()
|
1700
|
+
{1: [2, 3, 4],
|
1701
|
+
2: [1, 5, 6],
|
1702
|
+
3: [1, 7, 8],
|
1703
|
+
4: [1, 9, 10],
|
1704
|
+
5: [2, 10, 11],
|
1705
|
+
6: [2, 12, 7],
|
1706
|
+
7: [3, 6, 13],
|
1707
|
+
8: [3, 14, 9],
|
1708
|
+
9: [4, 8, 15],
|
1709
|
+
10: [4, 16, 5],
|
1710
|
+
11: [5, 17, 12],
|
1711
|
+
12: [6, 11, 18],
|
1712
|
+
13: [7, 18, 14],
|
1713
|
+
14: [8, 13, 19],
|
1714
|
+
15: [9, 19, 16],
|
1715
|
+
16: [10, 15, 17],
|
1716
|
+
17: [11, 16, 20],
|
1717
|
+
18: [12, 20, 13],
|
1718
|
+
19: [14, 20, 15],
|
1719
|
+
20: [17, 19, 18]}
|
1720
|
+
sage: g.plot3d(layout='spring') # needs sage.plot
|
1721
|
+
Graphics3d Object
|
1722
|
+
"""
|
1723
|
+
# number of vertices should be positive
|
1724
|
+
if order < 0:
|
1725
|
+
raise ValueError("number of vertices should be nonnegative")
|
1726
|
+
|
1727
|
+
# buckygen can only output fullerenes on up to 254 vertices
|
1728
|
+
if order > 254:
|
1729
|
+
raise ValueError("number of vertices should be at most 254")
|
1730
|
+
|
1731
|
+
# fullerenes only exist for an even number of vertices, larger than 20
|
1732
|
+
# and different from 22
|
1733
|
+
if order % 2 == 1 or order < 20 or order == 22:
|
1734
|
+
return
|
1735
|
+
|
1736
|
+
from sage.features.graph_generators import Buckygen
|
1737
|
+
Buckygen().require()
|
1738
|
+
|
1739
|
+
import shlex
|
1740
|
+
command = shlex.quote(Buckygen().absolute_filename())
|
1741
|
+
command += ' -' + ('I' if ipr else '') + 'd {0}d'.format(order)
|
1742
|
+
|
1743
|
+
sp = subprocess.Popen(command, shell=True,
|
1744
|
+
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
1745
|
+
stderr=subprocess.PIPE, close_fds=True)
|
1746
|
+
|
1747
|
+
yield from graphs._read_planar_code(sp.stdout, immutable=immutable)
|
1748
|
+
|
1749
|
+
def fusenes(self, hexagon_count, benzenoids=False, immutable=False):
|
1750
|
+
r"""
|
1751
|
+
Return a generator which creates fusenes and benzenoids using
|
1752
|
+
the benzene generator (see [BCH2002]_). Fusenes are planar
|
1753
|
+
polycyclic hydrocarbons with all bounded faces hexagons. Benzenoids
|
1754
|
+
are fusenes that are subgraphs of the hexagonal lattice.
|
1755
|
+
|
1756
|
+
INPUT:
|
1757
|
+
|
1758
|
+
- ``hexagon_count`` -- positive integer smaller than or equal to 30;
|
1759
|
+
this specifies the number of hexagons in the generated benzenoids
|
1760
|
+
|
1761
|
+
- ``benzenoids`` -- boolean (default: ``False``); if ``True`` only
|
1762
|
+
benzenoids are generated
|
1763
|
+
|
1764
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
1765
|
+
immutable or mutable graphs
|
1766
|
+
|
1767
|
+
OUTPUT:
|
1768
|
+
|
1769
|
+
A generator which will produce the fusenes as Sage graphs
|
1770
|
+
with an embedding set. These will be simple graphs: no loops, no
|
1771
|
+
multiple edges, no directed edges.
|
1772
|
+
|
1773
|
+
.. SEEALSO::
|
1774
|
+
|
1775
|
+
- :meth:`~sage.graphs.generic_graph.GenericGraph.set_embedding`,
|
1776
|
+
:meth:`~sage.graphs.generic_graph.GenericGraph.get_embedding` --
|
1777
|
+
get/set methods for embeddings.
|
1778
|
+
|
1779
|
+
EXAMPLES:
|
1780
|
+
|
1781
|
+
There is a unique fusene with 2 hexagons::
|
1782
|
+
|
1783
|
+
sage: gen = graphs.fusenes(2) # optional - benzene
|
1784
|
+
sage: len(list(gen)) # optional - benzene
|
1785
|
+
1
|
1786
|
+
|
1787
|
+
This fusene is naphthalene (`\textrm{C}_{10}\textrm{H}_{8}`).
|
1788
|
+
In the fusene graph the H-atoms are not stored, so this is
|
1789
|
+
a graph on just 10 vertices::
|
1790
|
+
|
1791
|
+
sage: gen = graphs.fusenes(2) # optional - benzene
|
1792
|
+
sage: next(gen) # optional - benzene
|
1793
|
+
Graph on 10 vertices
|
1794
|
+
sage: next(gen) # optional - benzene
|
1795
|
+
Traceback (most recent call last):
|
1796
|
+
...
|
1797
|
+
StopIteration
|
1798
|
+
|
1799
|
+
There are 6505 benzenoids with 9 hexagons::
|
1800
|
+
|
1801
|
+
sage: gen = graphs.fusenes(9, benzenoids=True) # optional - benzene
|
1802
|
+
sage: len(list(gen)) # optional - benzene
|
1803
|
+
6505
|
1804
|
+
"""
|
1805
|
+
if hexagon_count < 0:
|
1806
|
+
raise ValueError("number of hexagons should be nonnegative")
|
1807
|
+
|
1808
|
+
# benzene is only built for fusenes with up to 30 hexagons
|
1809
|
+
if hexagon_count > 30:
|
1810
|
+
raise ValueError("number of hexagons should be at most 30")
|
1811
|
+
|
1812
|
+
# there are no fusenes with 0 hexagons
|
1813
|
+
if hexagon_count == 0:
|
1814
|
+
return
|
1815
|
+
|
1816
|
+
# there is only one unique fusene with 1 hexagon (and benzene doesn't generate it)
|
1817
|
+
if hexagon_count == 1:
|
1818
|
+
g = {1: [6, 2], 2: [1, 3], 3: [2, 4], 4: [3, 5], 5: [4, 6], 6: [5, 1]}
|
1819
|
+
G = graph.Graph(g)
|
1820
|
+
G.set_embedding(g)
|
1821
|
+
yield G
|
1822
|
+
return
|
1823
|
+
|
1824
|
+
from sage.features.graph_generators import Benzene
|
1825
|
+
Benzene().require()
|
1826
|
+
|
1827
|
+
import shlex
|
1828
|
+
command = shlex.quote(Benzene().absolute_filename())
|
1829
|
+
command += (' b' if benzenoids else '') + ' {0} p'.format(hexagon_count)
|
1830
|
+
|
1831
|
+
sp = subprocess.Popen(command, shell=True,
|
1832
|
+
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
1833
|
+
stderr=subprocess.PIPE, close_fds=True)
|
1834
|
+
|
1835
|
+
yield from graphs._read_planar_code(sp.stdout, immutable=immutable)
|
1836
|
+
|
1837
|
+
def plantri_gen(self, options="", immutable=False):
|
1838
|
+
r"""
|
1839
|
+
Iterator over planar graphs created using the ``plantri`` generator.
|
1840
|
+
|
1841
|
+
``plantri`` is a (optional) program that generates certain types of
|
1842
|
+
graphs that are embedded on the sphere. It outputs exactly one member of
|
1843
|
+
each isomorphism class, using an amount of memory almost independent of
|
1844
|
+
the number of graphs produced. Isomorphisms are defined with respect to
|
1845
|
+
the embeddings, so in some cases outputs may be isomorphic as abstract
|
1846
|
+
graphs.
|
1847
|
+
|
1848
|
+
This method allows for passing command directly to ``plantry``,
|
1849
|
+
similarly to method :meth:`nauty_geng`, provide that the output format
|
1850
|
+
is not changed.
|
1851
|
+
|
1852
|
+
INPUT:
|
1853
|
+
|
1854
|
+
- ``options`` -- string (default: ``""``); a string passed to
|
1855
|
+
``plantri`` as if it was run at a system command line. At a minimum,
|
1856
|
+
you *must* pass the number of vertices you desire. Sage expects the
|
1857
|
+
output of plantri to be in "planar code" format, so do not set an
|
1858
|
+
option to change this default or results will be unpredictable.
|
1859
|
+
|
1860
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
1861
|
+
immutable or mutable graphs
|
1862
|
+
|
1863
|
+
The possible options are::
|
1864
|
+
|
1865
|
+
n : the number of vertices (the only compulsory parameter).
|
1866
|
+
This number must be in range `3\cdots 64`.
|
1867
|
+
It can also be given as "nd", where the suffix "d" means
|
1868
|
+
"dual", in which case it is converted by adding 4 then
|
1869
|
+
dividing by 2, i.e., `(28+4)/2 = 16`. In the case of
|
1870
|
+
triangulations, this calculation yields the number of
|
1871
|
+
faces, which is the number of vertices in the dual cubic
|
1872
|
+
graph.
|
1873
|
+
|
1874
|
+
-d : output the dual instead of the original graph.
|
1875
|
+
Note that it is applied only at the output stage. All
|
1876
|
+
other switches refer to the original graph before the dual
|
1877
|
+
is taken.
|
1878
|
+
|
1879
|
+
-o : Normally, one member of each isomorphism class is written.
|
1880
|
+
If this switch is given, one member of each O-P
|
1881
|
+
isomorphism class is written.
|
1882
|
+
|
1883
|
+
-V : output only graphs with non-trivial group. If -o is
|
1884
|
+
given the O-P group is used, the full group otherwise.
|
1885
|
+
|
1886
|
+
-m<int> : lower bound on the minimum degree. The default is -m3.
|
1887
|
+
In the dual graph, this means a lower bound on the minimum
|
1888
|
+
face size.
|
1889
|
+
|
1890
|
+
-c<int> : lower bound on the connectivity. The default is -c3.
|
1891
|
+
|
1892
|
+
-x : when used in combination with -cN, the connectivity must
|
1893
|
+
be exactly N rather than at least N.
|
1894
|
+
|
1895
|
+
-e : used to specify bounds on the number of edges.
|
1896
|
+
There are four possible forms:
|
1897
|
+
-e<int> exactly <int> edges
|
1898
|
+
-e:<int> at most <int> edges
|
1899
|
+
-e<int>: at least <int> edges
|
1900
|
+
-e<int>:<int> between <int> and <int> edges
|
1901
|
+
|
1902
|
+
-f<int> : upper bound on the size of a face, and so on the maximum
|
1903
|
+
degree of the dual.
|
1904
|
+
|
1905
|
+
-b but not -p : select eulerian triangulations, where "eulerian"
|
1906
|
+
means that every vertex has even degree.
|
1907
|
+
This parameter can be used in combination with
|
1908
|
+
parameters -c and -x.
|
1909
|
+
|
1910
|
+
-p but not -b : select general planar simple graphs.
|
1911
|
+
This parameter can be used in combination with
|
1912
|
+
parameters -m, -c, -x, -e and -f.
|
1913
|
+
|
1914
|
+
-bp or -pb : select general planar simple bipartite graphs.
|
1915
|
+
This parameter can be used in combination with
|
1916
|
+
parameters -m, -c, -x, -e and -f, except -c4, -m4,
|
1917
|
+
-m5 and -f3.
|
1918
|
+
|
1919
|
+
-P<int> : select triangulations of a disk. These are embedded simple
|
1920
|
+
graphs with a distinguished "outer" face. The outer face
|
1921
|
+
can be of any size (here called the disk size) but the
|
1922
|
+
other faces must be triangles. The argument <int> to -P
|
1923
|
+
is the disk size. If no argument (or 0) is given, all disk
|
1924
|
+
sizes are permitted.
|
1925
|
+
This parameter can be used in combination with
|
1926
|
+
parameters -m, -c, and -x.
|
1927
|
+
|
1928
|
+
-q : select simple quadrangulations. These are planar simple
|
1929
|
+
graphs for which every face has length 4.
|
1930
|
+
This parameter can be used in combination with parameters
|
1931
|
+
-c and -m.
|
1932
|
+
|
1933
|
+
-A : select Appolonian networks. These are simple planar
|
1934
|
+
triangulations that can be formed starting with `K_4` then
|
1935
|
+
repeatedly dividing a face into three by addition of a new
|
1936
|
+
vertex. They all have minimum degree and connectivity
|
1937
|
+
equal to 3.
|
1938
|
+
|
1939
|
+
res/mod : only generate subset res out of subsets 0..mod-1.
|
1940
|
+
The set of objects is divided into mod disjoint classes
|
1941
|
+
and only the res-th class is generated.
|
1942
|
+
|
1943
|
+
If -b, -q, -p, -P and -A are absent, the graphs found are triangulations
|
1944
|
+
only restricted by connectivity and minimum degree. In this case,
|
1945
|
+
there is the possibility of connectivity lower than 3.
|
1946
|
+
|
1947
|
+
Other options listed in the ``plantri`` guide might cause unpredictable
|
1948
|
+
behavior, in particular those changing the output format of ``plantri``
|
1949
|
+
as they will confuse the creation of a Sage graph.
|
1950
|
+
|
1951
|
+
OUTPUT:
|
1952
|
+
|
1953
|
+
An iterator which yields the graphs generated by ``plantri`` as Sage
|
1954
|
+
:class:`~sage.graphs.graph.Graph`.
|
1955
|
+
|
1956
|
+
.. SEEALSO::
|
1957
|
+
|
1958
|
+
- :meth:`planar_graphs` -- iterator over connected planar graphs
|
1959
|
+
using the ``plantri`` generator
|
1960
|
+
- :meth:`triangulations` -- iterator over connected planar
|
1961
|
+
triangulations using the ``plantri`` generator
|
1962
|
+
- :meth:`quadrangulations` -- iterator over connected planar
|
1963
|
+
quadrangulations using the ``plantri`` generator
|
1964
|
+
|
1965
|
+
EXAMPLES:
|
1966
|
+
|
1967
|
+
The generator can be used to construct graphs for testing, one at a time
|
1968
|
+
(usually inside a loop). Or it can be used to create an entire list all
|
1969
|
+
at once if there is sufficient memory to contain it::
|
1970
|
+
|
1971
|
+
sage: # optional - plantri
|
1972
|
+
sage: gen = graphs.plantri_gen("6")
|
1973
|
+
sage: next(gen)
|
1974
|
+
Graph on 6 vertices
|
1975
|
+
sage: next(gen)
|
1976
|
+
Graph on 6 vertices
|
1977
|
+
sage: next(gen)
|
1978
|
+
Traceback (most recent call last):
|
1979
|
+
...
|
1980
|
+
StopIteration
|
1981
|
+
|
1982
|
+
An overview of the number of quadrangulations on up to 12 vertices. This
|
1983
|
+
agrees with :oeis:`A113201`::
|
1984
|
+
|
1985
|
+
sage: for i in range(4, 13): # optional - plantri
|
1986
|
+
....: cmd = '-qm2c2 {}'.format(i)
|
1987
|
+
....: L = len(list(graphs.plantri_gen(cmd)))
|
1988
|
+
....: print("{:2d} {:3d}".format(i, L))
|
1989
|
+
4 1
|
1990
|
+
5 1
|
1991
|
+
6 2
|
1992
|
+
7 3
|
1993
|
+
8 9
|
1994
|
+
9 18
|
1995
|
+
10 62
|
1996
|
+
11 198
|
1997
|
+
12 803
|
1998
|
+
|
1999
|
+
TESTS:
|
2000
|
+
|
2001
|
+
Wrong input, ``"-c=3"`` instead of ``"-c3"``::
|
2002
|
+
|
2003
|
+
sage: list(graphs.plantri_gen("6 -c3")) # optional - plantri
|
2004
|
+
[Graph on 6 vertices, Graph on 6 vertices]
|
2005
|
+
sage: list(graphs.plantri_gen("6 -c=3")) # optional - plantri
|
2006
|
+
Traceback (most recent call last):
|
2007
|
+
...
|
2008
|
+
AttributeError: invalid options '6 -c=3'
|
2009
|
+
"""
|
2010
|
+
from sage.features.graph_generators import Plantri
|
2011
|
+
Plantri().require()
|
2012
|
+
|
2013
|
+
import shlex
|
2014
|
+
command = '{} {}'.format(shlex.quote(Plantri().absolute_filename()),
|
2015
|
+
options)
|
2016
|
+
sp = subprocess.Popen(command, shell=True,
|
2017
|
+
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
2018
|
+
stderr=subprocess.PIPE, close_fds=True)
|
2019
|
+
|
2020
|
+
try:
|
2021
|
+
yield from graphs._read_planar_code(sp.stdout, immutable=immutable)
|
2022
|
+
except (TypeError, AssertionError):
|
2023
|
+
raise AttributeError("invalid options '{}'".format(options))
|
2024
|
+
|
2025
|
+
def planar_graphs(self, order, minimum_degree=None,
|
2026
|
+
minimum_connectivity=None,
|
2027
|
+
exact_connectivity=False,
|
2028
|
+
minimum_edges=None,
|
2029
|
+
maximum_edges=None,
|
2030
|
+
maximum_face_size=None,
|
2031
|
+
only_bipartite=False,
|
2032
|
+
dual=False,
|
2033
|
+
immutable=False):
|
2034
|
+
r"""
|
2035
|
+
An iterator over connected planar graphs using the plantri generator.
|
2036
|
+
|
2037
|
+
This uses the plantri generator (see [BM2007]_) which is available
|
2038
|
+
through the optional package plantri.
|
2039
|
+
|
2040
|
+
.. NOTE::
|
2041
|
+
|
2042
|
+
The non-3-connected graphs will be returned several times, with all
|
2043
|
+
its possible embeddings.
|
2044
|
+
|
2045
|
+
INPUT:
|
2046
|
+
|
2047
|
+
- ``order`` -- positive integer smaller than or equal to 64;
|
2048
|
+
this specifies the number of vertices in the generated graphs
|
2049
|
+
|
2050
|
+
- ``minimum_degree`` -- (default: ``None``) a value `\geq 1` and `\leq
|
2051
|
+
5`, or ``None``. This specifies the minimum degree of the generated
|
2052
|
+
graphs. If this is ``None`` and the order is 1, then this is set to
|
2053
|
+
0. If this is ``None`` and the minimum connectivity is specified, then
|
2054
|
+
this is set to the same value as the minimum connectivity. If the
|
2055
|
+
minimum connectivity is also equal to ``None``, then this is set to 1.
|
2056
|
+
|
2057
|
+
- ``minimum_connectivity`` -- (default: ``None``) a value `\geq 1`
|
2058
|
+
and `\leq 3`, or ``None``. This specifies the minimum connectivity of the
|
2059
|
+
generated graphs. If this is ``None`` and the minimum degree is
|
2060
|
+
specified, then this is set to the minimum of the minimum degree
|
2061
|
+
and 3. If the minimum degree is also equal to ``None``, then this
|
2062
|
+
is set to 1.
|
2063
|
+
|
2064
|
+
- ``exact_connectivity`` -- (default: ``False``) if ``True`` only
|
2065
|
+
graphs with exactly the specified connectivity will be generated.
|
2066
|
+
This option cannot be used with ``minimum_connectivity=3``, or if
|
2067
|
+
the minimum connectivity is not explicitly set.
|
2068
|
+
|
2069
|
+
- ``minimum_edges`` -- integer (default: ``None``); lower bound on the
|
2070
|
+
number of edges
|
2071
|
+
|
2072
|
+
- ``maximum_edges`` -- integer (default: ``None``); upper bound on the
|
2073
|
+
number of edges
|
2074
|
+
|
2075
|
+
- ``maximum_face_size`` -- integer (default: ``None``); upper bound on
|
2076
|
+
the size of a face and so on the maximum degree of the dual graph
|
2077
|
+
|
2078
|
+
- ``only_bipartite`` -- (default: ``False``) if ``True`` only bipartite
|
2079
|
+
graphs will be generated. This option cannot be used for graphs with
|
2080
|
+
a minimum degree larger than 3.
|
2081
|
+
|
2082
|
+
- ``dual`` -- (default: ``False``) if ``True`` return instead the
|
2083
|
+
planar duals of the generated graphs
|
2084
|
+
|
2085
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
2086
|
+
immutable or mutable graphs
|
2087
|
+
|
2088
|
+
OUTPUT:
|
2089
|
+
|
2090
|
+
An iterator which will produce all planar graphs with the given
|
2091
|
+
number of vertices as Sage graphs with an embedding set. These will be
|
2092
|
+
simple graphs (no loops, no multiple edges, no directed edges)
|
2093
|
+
unless the option ``dual=True`` is used.
|
2094
|
+
|
2095
|
+
.. SEEALSO::
|
2096
|
+
|
2097
|
+
- :meth:`~sage.graphs.generic_graph.GenericGraph.set_embedding`,
|
2098
|
+
:meth:`~sage.graphs.generic_graph.GenericGraph.get_embedding` --
|
2099
|
+
get/set methods for embeddings.
|
2100
|
+
|
2101
|
+
EXAMPLES:
|
2102
|
+
|
2103
|
+
There are 6 planar graphs on 4 vertices::
|
2104
|
+
|
2105
|
+
sage: gen = graphs.planar_graphs(4) # optional - plantri
|
2106
|
+
sage: len(list(gen)) # optional - plantri
|
2107
|
+
6
|
2108
|
+
|
2109
|
+
Three of these planar graphs are bipartite::
|
2110
|
+
|
2111
|
+
sage: gen = graphs.planar_graphs(4, only_bipartite=True) # optional - plantri
|
2112
|
+
sage: len(list(gen)) # optional - plantri
|
2113
|
+
3
|
2114
|
+
|
2115
|
+
Setting ``dual=True`` gives the planar dual graphs::
|
2116
|
+
|
2117
|
+
sage: gen = graphs.planar_graphs(4, dual=True) # optional - plantri
|
2118
|
+
sage: [u for u in list(gen)] # optional - plantri
|
2119
|
+
[Graph on 4 vertices,
|
2120
|
+
Multi-graph on 3 vertices,
|
2121
|
+
Multi-graph on 2 vertices,
|
2122
|
+
Looped multi-graph on 2 vertices,
|
2123
|
+
Looped multi-graph on 1 vertex,
|
2124
|
+
Looped multi-graph on 1 vertex]
|
2125
|
+
|
2126
|
+
The cycle of length 4 is the only 2-connected bipartite planar graph
|
2127
|
+
on 4 vertices::
|
2128
|
+
|
2129
|
+
sage: l = list(graphs.planar_graphs(4, minimum_connectivity=2, only_bipartite=True)) # optional - plantri
|
2130
|
+
sage: l[0].get_embedding() # optional - plantri
|
2131
|
+
{1: [2, 3],
|
2132
|
+
2: [1, 4],
|
2133
|
+
3: [1, 4],
|
2134
|
+
4: [2, 3]}
|
2135
|
+
|
2136
|
+
There is one planar graph with one vertex. This graph obviously has
|
2137
|
+
minimum degree equal to 0::
|
2138
|
+
|
2139
|
+
sage: list(graphs.planar_graphs(1)) # optional - plantri
|
2140
|
+
[Graph on 1 vertex]
|
2141
|
+
sage: list(graphs.planar_graphs(1, minimum_degree=1)) # optional - plantri
|
2142
|
+
[]
|
2143
|
+
|
2144
|
+
Specifying lower and upper bounds on the number of edges::
|
2145
|
+
|
2146
|
+
sage: # optional - plantri
|
2147
|
+
sage: len(list(graphs.planar_graphs(4)))
|
2148
|
+
6
|
2149
|
+
sage: len(list(graphs.planar_graphs(4, minimum_edges=4)))
|
2150
|
+
4
|
2151
|
+
sage: len(list(graphs.planar_graphs(4, maximum_edges=4)))
|
2152
|
+
4
|
2153
|
+
sage: len(list(graphs.planar_graphs(4, minimum_edges=4, maximum_edges=4)))
|
2154
|
+
2
|
2155
|
+
|
2156
|
+
Specifying the maximum size of a face::
|
2157
|
+
|
2158
|
+
sage: len(list(graphs.planar_graphs(4, maximum_face_size=3))) # optional - plantri
|
2159
|
+
1
|
2160
|
+
sage: len(list(graphs.planar_graphs(4, maximum_face_size=4))) # optional - plantri
|
2161
|
+
3
|
2162
|
+
|
2163
|
+
TESTS:
|
2164
|
+
|
2165
|
+
The number of edges in a planar graph is equal to the number of edges in
|
2166
|
+
its dual::
|
2167
|
+
|
2168
|
+
sage: # optional - plantri
|
2169
|
+
sage: planar = list(graphs.planar_graphs(5,dual=True))
|
2170
|
+
sage: dual_planar = list(graphs.planar_graphs(5,dual=False))
|
2171
|
+
sage: planar_sizes = [g.size() for g in planar]
|
2172
|
+
sage: dual_planar_sizes = [g.size() for g in dual_planar]
|
2173
|
+
sage: planar_sizes == dual_planar_sizes
|
2174
|
+
True
|
2175
|
+
"""
|
2176
|
+
if order < 0:
|
2177
|
+
raise ValueError("number of vertices should be nonnegative")
|
2178
|
+
|
2179
|
+
# plantri can only output general planar graphs on up to 64 vertices
|
2180
|
+
if order > 64:
|
2181
|
+
raise ValueError("number of vertices should be at most 64")
|
2182
|
+
|
2183
|
+
if exact_connectivity and minimum_connectivity is None:
|
2184
|
+
raise ValueError("Minimum connectivity must be specified to use the exact_connectivity option.")
|
2185
|
+
|
2186
|
+
if minimum_connectivity is not None and not (1 <= minimum_connectivity <= 3):
|
2187
|
+
raise ValueError("Minimum connectivity should be a number between 1 and 3.")
|
2188
|
+
|
2189
|
+
# minimum degree should be None or a number between 1 and 5
|
2190
|
+
if minimum_degree == 0:
|
2191
|
+
if order != 1:
|
2192
|
+
raise ValueError("Minimum degree equal to 0 is only possible if the graphs have 1 vertex.")
|
2193
|
+
elif minimum_degree is not None and not (1 <= minimum_degree <= 5):
|
2194
|
+
raise ValueError("Minimum degree should be a number between 1 and 5 if the order is greater than 1.")
|
2195
|
+
elif minimum_degree is None and order == 1:
|
2196
|
+
minimum_degree = 0
|
2197
|
+
|
2198
|
+
# check combination of values of minimum degree and minimum connectivity
|
2199
|
+
if minimum_connectivity is None:
|
2200
|
+
if minimum_degree is not None:
|
2201
|
+
minimum_connectivity = min(3, minimum_degree)
|
2202
|
+
elif minimum_degree is None:
|
2203
|
+
minimum_degree, minimum_connectivity = 1, 1
|
2204
|
+
else:
|
2205
|
+
if minimum_degree is None:
|
2206
|
+
minimum_degree = minimum_connectivity
|
2207
|
+
elif (minimum_degree < minimum_connectivity and
|
2208
|
+
minimum_degree > 0):
|
2209
|
+
raise ValueError("Minimum connectivity can be at most the minimum degree.")
|
2210
|
+
|
2211
|
+
# exact connectivity is not implemented for minimum connectivity 3
|
2212
|
+
if exact_connectivity and minimum_connectivity == 3:
|
2213
|
+
raise NotImplementedError("Generation of planar graphs with connectivity exactly 3 is not implemented.")
|
2214
|
+
|
2215
|
+
if only_bipartite and minimum_degree > 3:
|
2216
|
+
raise NotImplementedError("Generation of bipartite planar graphs with minimum degree 4 or 5 is not implemented.")
|
2217
|
+
|
2218
|
+
edges = ''
|
2219
|
+
if minimum_edges is None:
|
2220
|
+
if maximum_edges is not None:
|
2221
|
+
if maximum_edges < order - 1:
|
2222
|
+
raise ValueError("the number of edges cannot be less than order - 1")
|
2223
|
+
edges = '-e:{}'.format(maximum_edges)
|
2224
|
+
else:
|
2225
|
+
if minimum_edges > 3 * order - 6:
|
2226
|
+
raise ValueError("the number of edges cannot be more than 3*order - 6")
|
2227
|
+
if maximum_edges is None:
|
2228
|
+
edges = '-e{}:'.format(minimum_edges)
|
2229
|
+
elif minimum_edges > maximum_edges:
|
2230
|
+
raise ValueError("the maximum number of edges must be larger "
|
2231
|
+
"or equal to the minimum number of edges")
|
2232
|
+
elif minimum_edges == maximum_edges:
|
2233
|
+
edges = '-e{}'.format(minimum_edges)
|
2234
|
+
else:
|
2235
|
+
edges = '-e{}:{}'.format(minimum_edges, maximum_edges)
|
2236
|
+
|
2237
|
+
faces = ''
|
2238
|
+
if maximum_face_size is not None:
|
2239
|
+
if maximum_face_size < 3:
|
2240
|
+
raise ValueError("the upper bound on the size of a face must be at least 3")
|
2241
|
+
faces = '-f{}'.format(maximum_face_size)
|
2242
|
+
|
2243
|
+
if order == 0:
|
2244
|
+
return
|
2245
|
+
|
2246
|
+
minimum_order = {0: 1, 1: 2, 2: 3, 3: 4, 4: 6, 5: 12}[minimum_degree]
|
2247
|
+
|
2248
|
+
if order < minimum_order:
|
2249
|
+
return
|
2250
|
+
|
2251
|
+
if order == 1:
|
2252
|
+
if minimum_degree == 0:
|
2253
|
+
G = graph.Graph(1, immutable=immutable)
|
2254
|
+
G.set_embedding({0: []})
|
2255
|
+
yield G
|
2256
|
+
return
|
2257
|
+
|
2258
|
+
cmd = '-p{}m{}c{}{}{} {} {} {}'
|
2259
|
+
command = cmd.format('b' if only_bipartite else '',
|
2260
|
+
minimum_degree,
|
2261
|
+
minimum_connectivity,
|
2262
|
+
'x' if exact_connectivity else '',
|
2263
|
+
'd' if dual else '',
|
2264
|
+
edges, faces,
|
2265
|
+
order)
|
2266
|
+
|
2267
|
+
yield from graphs.plantri_gen(command, immutable=immutable)
|
2268
|
+
|
2269
|
+
def triangulations(self, order, minimum_degree=None, minimum_connectivity=None,
|
2270
|
+
exact_connectivity=False, only_eulerian=False, dual=False,
|
2271
|
+
immutable=False):
|
2272
|
+
r"""
|
2273
|
+
An iterator over connected planar triangulations using the plantri generator.
|
2274
|
+
|
2275
|
+
This uses the plantri generator (see [BM2007]_) which is available
|
2276
|
+
through the optional package plantri.
|
2277
|
+
|
2278
|
+
INPUT:
|
2279
|
+
|
2280
|
+
- ``order`` -- positive integer smaller than or equal to 64;
|
2281
|
+
this specifies the number of vertices in the generated triangulations
|
2282
|
+
|
2283
|
+
- ``minimum_degree`` -- (default: ``None``) a value `\geq 3` and `\leq 5`,
|
2284
|
+
or ``None``. This specifies the minimum degree of the generated
|
2285
|
+
triangulations. If this is ``None`` and the minimum connectivity
|
2286
|
+
is specified, then this is set to the same value as the minimum
|
2287
|
+
connectivity. If the minimum connectivity is also equal to ``None``,
|
2288
|
+
then this is set to 3.
|
2289
|
+
|
2290
|
+
- ``minimum_connectivity`` -- (default: ``None``) a value `\geq 3` and
|
2291
|
+
`\leq 5`, or ``None``. This specifies the minimum connectivity of the
|
2292
|
+
generated triangulations. If this is ``None`` and the minimum degree
|
2293
|
+
is specified, then this is set to the minimum of the minimum degree
|
2294
|
+
and 3. If the minimum degree is also equal to ``None``, then this is
|
2295
|
+
set to 3.
|
2296
|
+
|
2297
|
+
- ``exact_connectivity`` -- (default: ``False``) if ``True`` only
|
2298
|
+
triangulations with exactly the specified connectivity will be generated.
|
2299
|
+
This option cannot be used with ``minimum_connectivity=3``, or if
|
2300
|
+
the minimum connectivity is not explicitly set.
|
2301
|
+
|
2302
|
+
- ``only_eulerian`` -- (default: ``False``) if ``True`` only Eulerian
|
2303
|
+
triangulations will be generated. This option cannot be used if the
|
2304
|
+
minimum degree is explicitly set to anything else than 4.
|
2305
|
+
|
2306
|
+
- ``dual`` -- (default: ``False``) if ``True`` return instead the
|
2307
|
+
planar duals of the generated graphs
|
2308
|
+
|
2309
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
2310
|
+
immutable or mutable graphs
|
2311
|
+
|
2312
|
+
OUTPUT:
|
2313
|
+
|
2314
|
+
An iterator which will produce all planar triangulations with the given
|
2315
|
+
number of vertices as Sage graphs with an embedding set. These will be
|
2316
|
+
simple graphs (no loops, no multiple edges, no directed edges).
|
2317
|
+
|
2318
|
+
.. SEEALSO::
|
2319
|
+
|
2320
|
+
- :meth:`~sage.graphs.generic_graph.GenericGraph.set_embedding`,
|
2321
|
+
:meth:`~sage.graphs.generic_graph.GenericGraph.get_embedding` --
|
2322
|
+
get/set methods for embeddings.
|
2323
|
+
|
2324
|
+
- :meth:`~sage.graphs.graph_generators.GraphGenerators.RandomTriangulation`
|
2325
|
+
-- build a random triangulation.
|
2326
|
+
|
2327
|
+
EXAMPLES:
|
2328
|
+
|
2329
|
+
The unique planar embedding of the `K_4` is the only planar triangulations
|
2330
|
+
on 4 vertices::
|
2331
|
+
|
2332
|
+
sage: gen = graphs.triangulations(4) # optional - plantri
|
2333
|
+
sage: [g.get_embedding() for g in gen] # optional - plantri
|
2334
|
+
[{1: [2, 3, 4], 2: [1, 4, 3], 3: [1, 2, 4], 4: [1, 3, 2]}]
|
2335
|
+
|
2336
|
+
but, of course, this graph is not Eulerian::
|
2337
|
+
|
2338
|
+
sage: gen = graphs.triangulations(4, only_eulerian=True) # optional - plantri
|
2339
|
+
sage: len(list(gen)) # optional - plantri
|
2340
|
+
0
|
2341
|
+
|
2342
|
+
The unique Eulerian triangulation on 6 vertices is isomorphic to the octahedral
|
2343
|
+
graph. ::
|
2344
|
+
|
2345
|
+
sage: gen = graphs.triangulations(6, only_eulerian=True) # optional - plantri
|
2346
|
+
sage: g = next(gen) # optional - plantri
|
2347
|
+
sage: g.is_isomorphic(graphs.OctahedralGraph()) # optional - plantri
|
2348
|
+
True
|
2349
|
+
|
2350
|
+
The minimum degree of a triangulation is 3, so the method can not output
|
2351
|
+
a triangle::
|
2352
|
+
|
2353
|
+
sage: list(graphs.triangulations(3)) # optional - plantri
|
2354
|
+
[]
|
2355
|
+
|
2356
|
+
An overview of the number of 5-connected triangulations on up to 22 vertices. This
|
2357
|
+
agrees with :oeis:`A081621`::
|
2358
|
+
|
2359
|
+
sage: for i in range(12, 23): # optional - plantri
|
2360
|
+
....: L = len(list(graphs.triangulations(i, minimum_connectivity=5)))
|
2361
|
+
....: print("{} {:3d}".format(i,L))
|
2362
|
+
12 1
|
2363
|
+
13 0
|
2364
|
+
14 1
|
2365
|
+
15 1
|
2366
|
+
16 3
|
2367
|
+
17 4
|
2368
|
+
18 12
|
2369
|
+
19 23
|
2370
|
+
20 71
|
2371
|
+
21 187
|
2372
|
+
22 627
|
2373
|
+
|
2374
|
+
The minimum connectivity can be at most the minimum degree::
|
2375
|
+
|
2376
|
+
sage: gen = next(graphs.triangulations(10, minimum_degree=3, # optional - plantri
|
2377
|
+
....: minimum_connectivity=5))
|
2378
|
+
Traceback (most recent call last):
|
2379
|
+
...
|
2380
|
+
ValueError: Minimum connectivity can be at most the minimum degree.
|
2381
|
+
|
2382
|
+
There are 5 triangulations with 9 vertices and minimum degree equal to 4
|
2383
|
+
that are 3-connected, but only one of them is not 4-connected::
|
2384
|
+
|
2385
|
+
sage: len([g for g in graphs.triangulations(9, minimum_degree=4, # optional - plantri
|
2386
|
+
....: minimum_connectivity=3)])
|
2387
|
+
5
|
2388
|
+
sage: len([g for g in graphs.triangulations(9, minimum_degree=4, # optional - plantri
|
2389
|
+
....: minimum_connectivity=3,
|
2390
|
+
....: exact_connectivity=True)])
|
2391
|
+
1
|
2392
|
+
|
2393
|
+
Setting ``dual=True`` gives the planar dual graphs::
|
2394
|
+
|
2395
|
+
sage: [len(g) for g in graphs.triangulations(9, minimum_degree=4, # optional plantri
|
2396
|
+
....: minimum_connectivity=3, dual=True)]
|
2397
|
+
[14, 14, 14, 14, 14]
|
2398
|
+
|
2399
|
+
TESTS::
|
2400
|
+
|
2401
|
+
sage: [g.size() for g in graphs.triangulations(6, minimum_connectivity=3)] # optional - plantri
|
2402
|
+
[12, 12]
|
2403
|
+
"""
|
2404
|
+
if order < 0:
|
2405
|
+
raise ValueError("number of vertices should be nonnegative")
|
2406
|
+
|
2407
|
+
# plantri can only output planar triangulations on up to 64 vertices
|
2408
|
+
if order > 64:
|
2409
|
+
raise ValueError("number of vertices should be at most 64")
|
2410
|
+
|
2411
|
+
if exact_connectivity and minimum_connectivity is None:
|
2412
|
+
raise ValueError("Minimum connectivity must be specified to use the exact_connectivity option.")
|
2413
|
+
|
2414
|
+
if minimum_connectivity is not None and not (3 <= minimum_connectivity <= 5):
|
2415
|
+
raise ValueError("Minimum connectivity should be None or a number between 3 and 5.")
|
2416
|
+
|
2417
|
+
if minimum_degree is not None and not (3 <= minimum_degree <= 5):
|
2418
|
+
raise ValueError("Minimum degree should be None or a number between 3 and 5.")
|
2419
|
+
|
2420
|
+
# for Eulerian triangulations the minimum degree is set to 4 (unless it was already specifically set)
|
2421
|
+
if only_eulerian and minimum_degree is None:
|
2422
|
+
minimum_degree = 4
|
2423
|
+
|
2424
|
+
# check combination of values of minimum degree and minimum connectivity
|
2425
|
+
if minimum_connectivity is None:
|
2426
|
+
if minimum_degree is not None:
|
2427
|
+
minimum_connectivity = min(3, minimum_degree)
|
2428
|
+
else:
|
2429
|
+
minimum_degree, minimum_connectivity = 3, 3
|
2430
|
+
else:
|
2431
|
+
if minimum_degree is None:
|
2432
|
+
minimum_degree = minimum_connectivity
|
2433
|
+
elif minimum_degree < minimum_connectivity:
|
2434
|
+
raise ValueError("Minimum connectivity can be at most the minimum degree.")
|
2435
|
+
|
2436
|
+
# exact connectivity is not implemented for minimum connectivity equal
|
2437
|
+
# to minimum degree
|
2438
|
+
if exact_connectivity and minimum_connectivity == minimum_degree:
|
2439
|
+
raise NotImplementedError("Generation of triangulations with minimum connectivity equal to minimum degree is not implemented.")
|
2440
|
+
|
2441
|
+
minimum_order = {3: 4, 4: 6, 5: 12}[minimum_degree]
|
2442
|
+
|
2443
|
+
if order < minimum_order:
|
2444
|
+
return
|
2445
|
+
|
2446
|
+
if only_eulerian and order < 6:
|
2447
|
+
return
|
2448
|
+
|
2449
|
+
cmd = '-{}m{}c{}{}{} {}'
|
2450
|
+
command = cmd.format('b' if only_eulerian else '',
|
2451
|
+
minimum_degree,
|
2452
|
+
minimum_connectivity,
|
2453
|
+
'x' if exact_connectivity else '',
|
2454
|
+
'd' if dual else '',
|
2455
|
+
order)
|
2456
|
+
|
2457
|
+
yield from graphs.plantri_gen(command, immutable=immutable)
|
2458
|
+
|
2459
|
+
def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None,
|
2460
|
+
no_nonfacial_quadrangles=False, dual=False,
|
2461
|
+
immutable=False):
|
2462
|
+
r"""
|
2463
|
+
An iterator over planar quadrangulations using the plantri generator.
|
2464
|
+
|
2465
|
+
This uses the plantri generator (see [BM2007]_) which is available
|
2466
|
+
through the optional package plantri.
|
2467
|
+
|
2468
|
+
INPUT:
|
2469
|
+
|
2470
|
+
- ``order`` -- positive integer smaller than or equal to 64;
|
2471
|
+
this specifies the number of vertices in the generated quadrangulations
|
2472
|
+
|
2473
|
+
- ``minimum_degree`` -- (default: ``None``) a value `\geq 2` and `\leq
|
2474
|
+
3`, or ``None``. This specifies the minimum degree of the generated
|
2475
|
+
quadrangulations. If this is ``None`` and the minimum connectivity is
|
2476
|
+
specified, then this is set to the same value as the minimum
|
2477
|
+
connectivity. If the minimum connectivity is also equal to ``None``,
|
2478
|
+
then this is set to 2.
|
2479
|
+
|
2480
|
+
- ``minimum_connectivity`` -- (default: ``None``) a value `\geq 2` and
|
2481
|
+
`\leq 3`, or ``None``. This specifies the minimum connectivity of the
|
2482
|
+
generated quadrangulations. If this is ``None`` and the option
|
2483
|
+
``no_nonfacial_quadrangles`` is set to ``True``, then this is set to
|
2484
|
+
3. Otherwise if this is ``None`` and the minimum degree is specified,
|
2485
|
+
then this is set to the minimum degree. If the minimum degree is also
|
2486
|
+
equal to ``None``, then this is set to 3.
|
2487
|
+
|
2488
|
+
- ``no_nonfacial_quadrangles`` -- (default: ``False``) if ``True`` only
|
2489
|
+
quadrangulations with no non-facial quadrangles are generated. This
|
2490
|
+
option cannot be used if ``minimum_connectivity`` is set to 2.
|
2491
|
+
|
2492
|
+
- ``dual`` -- (default: ``False``) if ``True`` return instead the
|
2493
|
+
planar duals of the generated graphs
|
2494
|
+
|
2495
|
+
- ``immutable`` -- boolean (default: ``False``); whether to return
|
2496
|
+
immutable or mutable graphs
|
2497
|
+
|
2498
|
+
OUTPUT:
|
2499
|
+
|
2500
|
+
An iterator which will produce all planar quadrangulations with the given
|
2501
|
+
number of vertices as Sage graphs with an embedding set. These will be
|
2502
|
+
simple graphs (no loops, no multiple edges, no directed edges).
|
2503
|
+
|
2504
|
+
.. SEEALSO::
|
2505
|
+
|
2506
|
+
- :meth:`~sage.graphs.generic_graph.GenericGraph.set_embedding`,
|
2507
|
+
:meth:`~sage.graphs.generic_graph.GenericGraph.get_embedding` --
|
2508
|
+
get/set methods for embeddings.
|
2509
|
+
|
2510
|
+
EXAMPLES:
|
2511
|
+
|
2512
|
+
The cube is the only 3-connected planar quadrangulation on 8 vertices::
|
2513
|
+
|
2514
|
+
sage: # optional - plantri
|
2515
|
+
sage: gen = graphs.quadrangulations(8, minimum_connectivity=3)
|
2516
|
+
sage: g = next(gen)
|
2517
|
+
sage: g.is_isomorphic(graphs.CubeGraph(3))
|
2518
|
+
True
|
2519
|
+
sage: next(gen)
|
2520
|
+
Traceback (most recent call last):
|
2521
|
+
...
|
2522
|
+
StopIteration
|
2523
|
+
|
2524
|
+
An overview of the number of quadrangulations on up to 12 vertices. This
|
2525
|
+
agrees with :oeis:`A113201`::
|
2526
|
+
|
2527
|
+
sage: for i in range(4,13): # optional - plantri
|
2528
|
+
....: L = len(list(graphs.quadrangulations(i)))
|
2529
|
+
....: print("{:2d} {:3d}".format(i,L))
|
2530
|
+
4 1
|
2531
|
+
5 1
|
2532
|
+
6 2
|
2533
|
+
7 3
|
2534
|
+
8 9
|
2535
|
+
9 18
|
2536
|
+
10 62
|
2537
|
+
11 198
|
2538
|
+
12 803
|
2539
|
+
|
2540
|
+
There are 2 planar quadrangulation on 12 vertices that do not have a
|
2541
|
+
non-facial quadrangle::
|
2542
|
+
|
2543
|
+
sage: len([g for g in graphs.quadrangulations(12, no_nonfacial_quadrangles=True)]) # optional - plantri
|
2544
|
+
2
|
2545
|
+
|
2546
|
+
Setting ``dual=True`` gives the planar dual graphs::
|
2547
|
+
|
2548
|
+
sage: [len(g) for g in graphs.quadrangulations(12, no_nonfacial_quadrangles=True, dual=True)] # optional - plantri
|
2549
|
+
[10, 10]
|
2550
|
+
"""
|
2551
|
+
if order < 0:
|
2552
|
+
raise ValueError("number of vertices should be nonnegative")
|
2553
|
+
|
2554
|
+
# plantri can only output planar quadrangulations on up to 64 vertices
|
2555
|
+
if order > 64:
|
2556
|
+
raise ValueError("number of vertices should be at most 64")
|
2557
|
+
|
2558
|
+
if minimum_connectivity not in {None, 2, 3}:
|
2559
|
+
raise ValueError("Minimum connectivity should be None, 2 or 3.")
|
2560
|
+
|
2561
|
+
if minimum_degree not in {None, 2, 3}:
|
2562
|
+
raise ValueError("Minimum degree should be None, 2 or 3.")
|
2563
|
+
|
2564
|
+
if (no_nonfacial_quadrangles and
|
2565
|
+
minimum_connectivity == 2):
|
2566
|
+
raise NotImplementedError("Generation of no non-facial quadrangles "
|
2567
|
+
"and minimum connectivity 2 is not implemented")
|
2568
|
+
|
2569
|
+
# check combination of values of minimum degree and minimum connectivity
|
2570
|
+
if minimum_connectivity is None:
|
2571
|
+
if minimum_degree is not None:
|
2572
|
+
minimum_connectivity = min(2, minimum_degree)
|
2573
|
+
else:
|
2574
|
+
minimum_degree, minimum_connectivity = 2, 2
|
2575
|
+
else:
|
2576
|
+
if minimum_degree is None:
|
2577
|
+
minimum_degree = minimum_connectivity
|
2578
|
+
elif minimum_degree < minimum_connectivity:
|
2579
|
+
raise ValueError("Minimum connectivity can be at most the minimum degree.")
|
2580
|
+
|
2581
|
+
minimum_order = {2: 4, 3: 8}[minimum_degree]
|
2582
|
+
|
2583
|
+
if order < minimum_order:
|
2584
|
+
return
|
2585
|
+
|
2586
|
+
if no_nonfacial_quadrangles:
|
2587
|
+
# for plantri -q the option -c4 means 3-connected with no non-facial quadrangles
|
2588
|
+
minimum_connectivity = 4
|
2589
|
+
|
2590
|
+
cmd = '-qm{}c{}{} {}'
|
2591
|
+
command = cmd.format(minimum_degree,
|
2592
|
+
minimum_connectivity,
|
2593
|
+
'd' if dual else '',
|
2594
|
+
order)
|
2595
|
+
|
2596
|
+
yield from graphs.plantri_gen(command, immutable=immutable)
|
2597
|
+
|
2598
|
+
###########################################################################
|
2599
|
+
# Basic Graphs
|
2600
|
+
###########################################################################
|
2601
|
+
from .generators import basic
|
2602
|
+
BullGraph = staticmethod(basic.BullGraph)
|
2603
|
+
ButterflyGraph = staticmethod(basic.ButterflyGraph)
|
2604
|
+
CircularLadderGraph = staticmethod(basic.CircularLadderGraph)
|
2605
|
+
ClawGraph = staticmethod(basic.ClawGraph)
|
2606
|
+
CycleGraph = staticmethod(basic.CycleGraph)
|
2607
|
+
CompleteGraph = staticmethod(basic.CompleteGraph)
|
2608
|
+
CompleteBipartiteGraph = staticmethod(basic.CompleteBipartiteGraph)
|
2609
|
+
CompleteMultipartiteGraph = staticmethod(basic.CompleteMultipartiteGraph)
|
2610
|
+
CorrelationGraph = staticmethod(basic.CorrelationGraph)
|
2611
|
+
DiamondGraph = staticmethod(basic.DiamondGraph)
|
2612
|
+
GemGraph = staticmethod(basic.GemGraph)
|
2613
|
+
DartGraph = staticmethod(basic.DartGraph)
|
2614
|
+
ForkGraph = staticmethod(basic.ForkGraph)
|
2615
|
+
EmptyGraph = staticmethod(basic.EmptyGraph)
|
2616
|
+
Grid2dGraph = staticmethod(basic.Grid2dGraph)
|
2617
|
+
GridGraph = staticmethod(basic.GridGraph)
|
2618
|
+
HouseGraph = staticmethod(basic.HouseGraph)
|
2619
|
+
HouseXGraph = staticmethod(basic.HouseXGraph)
|
2620
|
+
LadderGraph = staticmethod(basic.LadderGraph)
|
2621
|
+
MoebiusLadderGraph = staticmethod(basic.MoebiusLadderGraph)
|
2622
|
+
PathGraph = staticmethod(basic.PathGraph)
|
2623
|
+
StarGraph = staticmethod(basic.StarGraph)
|
2624
|
+
Toroidal6RegularGrid2dGraph = staticmethod(basic.Toroidal6RegularGrid2dGraph)
|
2625
|
+
ToroidalGrid2dGraph = staticmethod(basic.ToroidalGrid2dGraph)
|
2626
|
+
|
2627
|
+
###########################################################################
|
2628
|
+
# Small Graphs
|
2629
|
+
###########################################################################
|
2630
|
+
from .generators import smallgraphs, distance_regular
|
2631
|
+
Balaban10Cage = staticmethod(smallgraphs.Balaban10Cage)
|
2632
|
+
Balaban11Cage = staticmethod(smallgraphs.Balaban11Cage)
|
2633
|
+
BidiakisCube = staticmethod(smallgraphs.BidiakisCube)
|
2634
|
+
BiggsSmithGraph = staticmethod(smallgraphs.BiggsSmithGraph)
|
2635
|
+
BlanusaFirstSnarkGraph = staticmethod(smallgraphs.BlanusaFirstSnarkGraph)
|
2636
|
+
BlanusaSecondSnarkGraph = staticmethod(smallgraphs.BlanusaSecondSnarkGraph)
|
2637
|
+
BrinkmannGraph = staticmethod(smallgraphs.BrinkmannGraph)
|
2638
|
+
BrouwerHaemersGraph = staticmethod(smallgraphs.BrouwerHaemersGraph)
|
2639
|
+
BuckyBall = staticmethod(smallgraphs.BuckyBall)
|
2640
|
+
CameronGraph = staticmethod(smallgraphs.CameronGraph)
|
2641
|
+
Cell600 = staticmethod(smallgraphs.Cell600)
|
2642
|
+
Cell120 = staticmethod(smallgraphs.Cell120)
|
2643
|
+
ChvatalGraph = staticmethod(smallgraphs.ChvatalGraph)
|
2644
|
+
ClebschGraph = staticmethod(smallgraphs.ClebschGraph)
|
2645
|
+
cocliques_HoffmannSingleton = staticmethod(distance_regular.cocliques_HoffmannSingleton)
|
2646
|
+
ConwaySmith_for_3S7 = staticmethod(distance_regular.ConwaySmith_for_3S7)
|
2647
|
+
CoxeterGraph = staticmethod(smallgraphs.CoxeterGraph)
|
2648
|
+
CubeplexGraph = staticmethod(smallgraphs.CubeplexGraph)
|
2649
|
+
DejterGraph = staticmethod(smallgraphs.DejterGraph)
|
2650
|
+
DesarguesGraph = staticmethod(smallgraphs.DesarguesGraph)
|
2651
|
+
distance_3_doubly_truncated_Golay_code_graph = staticmethod(distance_regular.distance_3_doubly_truncated_Golay_code_graph)
|
2652
|
+
DoubleStarSnark = staticmethod(smallgraphs.DoubleStarSnark)
|
2653
|
+
DoublyTruncatedWittGraph = staticmethod(distance_regular.DoublyTruncatedWittGraph)
|
2654
|
+
DurerGraph = staticmethod(smallgraphs.DurerGraph)
|
2655
|
+
DyckGraph = staticmethod(smallgraphs.DyckGraph)
|
2656
|
+
EllinghamHorton54Graph = staticmethod(smallgraphs.EllinghamHorton54Graph)
|
2657
|
+
EllinghamHorton78Graph = staticmethod(smallgraphs.EllinghamHorton78Graph)
|
2658
|
+
ErreraGraph = staticmethod(smallgraphs.ErreraGraph)
|
2659
|
+
F26AGraph = staticmethod(smallgraphs.F26AGraph)
|
2660
|
+
FlowerSnark = staticmethod(smallgraphs.FlowerSnark)
|
2661
|
+
FolkmanGraph = staticmethod(smallgraphs.FolkmanGraph)
|
2662
|
+
FosterGraph = staticmethod(smallgraphs.FosterGraph)
|
2663
|
+
FosterGraph3S6 = staticmethod(distance_regular.FosterGraph3S6)
|
2664
|
+
FranklinGraph = staticmethod(smallgraphs.FranklinGraph)
|
2665
|
+
FruchtGraph = staticmethod(smallgraphs.FruchtGraph)
|
2666
|
+
GoldnerHararyGraph = staticmethod(smallgraphs.GoldnerHararyGraph)
|
2667
|
+
GolombGraph = staticmethod(smallgraphs.GolombGraph)
|
2668
|
+
GossetGraph = staticmethod(smallgraphs.GossetGraph)
|
2669
|
+
graph_3O73 = staticmethod(distance_regular.graph_3O73)
|
2670
|
+
GrayGraph = staticmethod(smallgraphs.GrayGraph)
|
2671
|
+
GritsenkoGraph = staticmethod(smallgraphs.GritsenkoGraph)
|
2672
|
+
GrotzschGraph = staticmethod(smallgraphs.GrotzschGraph)
|
2673
|
+
HallJankoGraph = staticmethod(smallgraphs.HallJankoGraph)
|
2674
|
+
WellsGraph = staticmethod(smallgraphs.WellsGraph)
|
2675
|
+
HarborthGraph = staticmethod(smallgraphs.HarborthGraph)
|
2676
|
+
HarriesGraph = staticmethod(smallgraphs.HarriesGraph)
|
2677
|
+
HarriesWongGraph = staticmethod(smallgraphs.HarriesWongGraph)
|
2678
|
+
HeawoodGraph = staticmethod(smallgraphs.HeawoodGraph)
|
2679
|
+
HerschelGraph = staticmethod(smallgraphs.HerschelGraph)
|
2680
|
+
HigmanSimsGraph = staticmethod(smallgraphs.HigmanSimsGraph)
|
2681
|
+
HoffmanGraph = staticmethod(smallgraphs.HoffmanGraph)
|
2682
|
+
HoffmanSingletonGraph = staticmethod(smallgraphs.HoffmanSingletonGraph)
|
2683
|
+
HoltGraph = staticmethod(smallgraphs.HoltGraph)
|
2684
|
+
HortonGraph = staticmethod(smallgraphs.HortonGraph)
|
2685
|
+
IoninKharaghani765Graph = staticmethod(smallgraphs.IoninKharaghani765Graph)
|
2686
|
+
IvanovIvanovFaradjevGraph = staticmethod(distance_regular.IvanovIvanovFaradjevGraph)
|
2687
|
+
J2Graph = staticmethod(distance_regular.J2Graph)
|
2688
|
+
JankoKharaghaniGraph = staticmethod(smallgraphs.JankoKharaghaniGraph)
|
2689
|
+
JankoKharaghaniTonchevGraph = staticmethod(smallgraphs.JankoKharaghaniTonchevGraph)
|
2690
|
+
KittellGraph = staticmethod(smallgraphs.KittellGraph)
|
2691
|
+
KrackhardtKiteGraph = staticmethod(smallgraphs.KrackhardtKiteGraph)
|
2692
|
+
Klein3RegularGraph = staticmethod(smallgraphs.Klein3RegularGraph)
|
2693
|
+
Klein7RegularGraph = staticmethod(smallgraphs.Klein7RegularGraph)
|
2694
|
+
LargeWittGraph = staticmethod(distance_regular.LargeWittGraph)
|
2695
|
+
LeonardGraph = staticmethod(distance_regular.LeonardGraph)
|
2696
|
+
LjubljanaGraph = staticmethod(smallgraphs.LjubljanaGraph)
|
2697
|
+
vanLintSchrijverGraph = staticmethod(distance_regular.vanLintSchrijverGraph)
|
2698
|
+
LivingstoneGraph = staticmethod(smallgraphs.LivingstoneGraph)
|
2699
|
+
locally_GQ42_distance_transitive_graph = staticmethod(distance_regular.locally_GQ42_distance_transitive_graph)
|
2700
|
+
LocalMcLaughlinGraph = staticmethod(smallgraphs.LocalMcLaughlinGraph)
|
2701
|
+
M22Graph = staticmethod(smallgraphs.M22Graph)
|
2702
|
+
MarkstroemGraph = staticmethod(smallgraphs.MarkstroemGraph)
|
2703
|
+
MathonStronglyRegularGraph = staticmethod(smallgraphs.MathonStronglyRegularGraph)
|
2704
|
+
McGeeGraph = staticmethod(smallgraphs.McGeeGraph)
|
2705
|
+
McLaughlinGraph = staticmethod(smallgraphs.McLaughlinGraph)
|
2706
|
+
MeredithGraph = staticmethod(smallgraphs.MeredithGraph)
|
2707
|
+
MoebiusKantorGraph = staticmethod(smallgraphs.MoebiusKantorGraph)
|
2708
|
+
MoserSpindle = staticmethod(smallgraphs.MoserSpindle)
|
2709
|
+
MurtyGraph = staticmethod(smallgraphs.MurtyGraph)
|
2710
|
+
NauruGraph = staticmethod(smallgraphs.NauruGraph)
|
2711
|
+
PappusGraph = staticmethod(smallgraphs.PappusGraph)
|
2712
|
+
PoussinGraph = staticmethod(smallgraphs.PoussinGraph)
|
2713
|
+
PerkelGraph = staticmethod(smallgraphs.PerkelGraph)
|
2714
|
+
PetersenGraph = staticmethod(smallgraphs.PetersenGraph)
|
2715
|
+
RobertsonGraph = staticmethod(smallgraphs.RobertsonGraph)
|
2716
|
+
SchlaefliGraph = staticmethod(smallgraphs.SchlaefliGraph)
|
2717
|
+
shortened_00_11_binary_Golay_code_graph = staticmethod(distance_regular.shortened_00_11_binary_Golay_code_graph)
|
2718
|
+
shortened_000_111_extended_binary_Golay_code_graph = staticmethod(distance_regular.shortened_000_111_extended_binary_Golay_code_graph)
|
2719
|
+
ShrikhandeGraph = staticmethod(smallgraphs.ShrikhandeGraph)
|
2720
|
+
SimsGewirtzGraph = staticmethod(smallgraphs.SimsGewirtzGraph)
|
2721
|
+
SousselierGraph = staticmethod(smallgraphs.SousselierGraph)
|
2722
|
+
SylvesterGraph = staticmethod(smallgraphs.SylvesterGraph)
|
2723
|
+
SzekeresSnarkGraph = staticmethod(smallgraphs.SzekeresSnarkGraph)
|
2724
|
+
ThomsenGraph = staticmethod(smallgraphs.ThomsenGraph)
|
2725
|
+
TietzeGraph = staticmethod(smallgraphs.TietzeGraph)
|
2726
|
+
TricornGraph = staticmethod(smallgraphs.TricornGraph)
|
2727
|
+
Tutte12Cage = staticmethod(smallgraphs.Tutte12Cage)
|
2728
|
+
TruncatedIcosidodecahedralGraph = staticmethod(smallgraphs.TruncatedIcosidodecahedralGraph)
|
2729
|
+
TruncatedTetrahedralGraph = staticmethod(smallgraphs.TruncatedTetrahedralGraph)
|
2730
|
+
TruncatedWittGraph = staticmethod(distance_regular.TruncatedWittGraph)
|
2731
|
+
TutteCoxeterGraph = staticmethod(smallgraphs.TutteCoxeterGraph)
|
2732
|
+
TutteGraph = staticmethod(smallgraphs.TutteGraph)
|
2733
|
+
TwinplexGraph = staticmethod(smallgraphs.TwinplexGraph)
|
2734
|
+
U42Graph216 = staticmethod(smallgraphs.U42Graph216)
|
2735
|
+
U42Graph540 = staticmethod(smallgraphs.U42Graph540)
|
2736
|
+
WagnerGraph = staticmethod(smallgraphs.WagnerGraph)
|
2737
|
+
WatkinsSnarkGraph = staticmethod(smallgraphs.WatkinsSnarkGraph)
|
2738
|
+
WienerArayaGraph = staticmethod(smallgraphs.WienerArayaGraph)
|
2739
|
+
SuzukiGraph = staticmethod(smallgraphs.SuzukiGraph)
|
2740
|
+
|
2741
|
+
###########################################################################
|
2742
|
+
# Platonic Solids
|
2743
|
+
###########################################################################
|
2744
|
+
from .generators import platonic_solids
|
2745
|
+
DodecahedralGraph = staticmethod(platonic_solids.DodecahedralGraph)
|
2746
|
+
HexahedralGraph = staticmethod(platonic_solids.HexahedralGraph)
|
2747
|
+
IcosahedralGraph = staticmethod(platonic_solids.IcosahedralGraph)
|
2748
|
+
OctahedralGraph = staticmethod(platonic_solids.OctahedralGraph)
|
2749
|
+
TetrahedralGraph = staticmethod(platonic_solids.TetrahedralGraph)
|
2750
|
+
|
2751
|
+
###########################################################################
|
2752
|
+
# Families
|
2753
|
+
###########################################################################
|
2754
|
+
from . import cographs as cographs_module
|
2755
|
+
from .generators import families
|
2756
|
+
from . import strongly_regular_db
|
2757
|
+
AlternatingFormsGraph = staticmethod(distance_regular.AlternatingFormsGraph)
|
2758
|
+
AztecDiamondGraph = staticmethod(families.AztecDiamondGraph)
|
2759
|
+
BalancedTree = staticmethod(families.BalancedTree)
|
2760
|
+
BarbellGraph = staticmethod(families.BarbellGraph)
|
2761
|
+
BilinearFormsGraph = staticmethod(distance_regular.BilinearFormsGraph)
|
2762
|
+
BiwheelGraph = staticmethod(families.BiwheelGraph)
|
2763
|
+
BubbleSortGraph = staticmethod(families.BubbleSortGraph)
|
2764
|
+
CaiFurerImmermanGraph = staticmethod(families.CaiFurerImmermanGraph)
|
2765
|
+
chang_graphs = staticmethod(families.chang_graphs)
|
2766
|
+
CirculantGraph = staticmethod(families.CirculantGraph)
|
2767
|
+
cographs = staticmethod(cographs_module.cographs)
|
2768
|
+
CubeGraph = staticmethod(families.CubeGraph)
|
2769
|
+
CubeConnectedCycle = staticmethod(families.CubeConnectedCycle)
|
2770
|
+
DipoleGraph = staticmethod(families.DipoleGraph)
|
2771
|
+
distance_regular_graph = staticmethod(distance_regular.distance_regular_graph)
|
2772
|
+
DorogovtsevGoltsevMendesGraph = staticmethod(families.DorogovtsevGoltsevMendesGraph)
|
2773
|
+
DoubleGeneralizedPetersenGraph = staticmethod(families.DoubleGeneralizedPetersenGraph)
|
2774
|
+
DoubleGrassmannGraph = staticmethod(distance_regular.DoubleGrassmannGraph)
|
2775
|
+
DoubleOddGraph = staticmethod(distance_regular.DoubleOddGraph)
|
2776
|
+
EgawaGraph = staticmethod(families.EgawaGraph)
|
2777
|
+
FibonacciTree = staticmethod(families.FibonacciTree)
|
2778
|
+
FoldedCubeGraph = staticmethod(families.FoldedCubeGraph)
|
2779
|
+
FriendshipGraph = staticmethod(families.FriendshipGraph)
|
2780
|
+
FurerGadget = staticmethod(families.FurerGadget)
|
2781
|
+
FuzzyBallGraph = staticmethod(families.FuzzyBallGraph)
|
2782
|
+
GeneralisedDodecagonGraph = staticmethod(distance_regular.GeneralisedDodecagonGraph)
|
2783
|
+
GeneralisedHexagonGraph = staticmethod(distance_regular.GeneralisedHexagonGraph)
|
2784
|
+
GeneralisedOctagonGraph = staticmethod(distance_regular.GeneralisedOctagonGraph)
|
2785
|
+
GeneralizedPetersenGraph = staticmethod(families.GeneralizedPetersenGraph)
|
2786
|
+
GeneralizedSierpinskiGraph = staticmethod(families.GeneralizedSierpinskiGraph)
|
2787
|
+
GoethalsSeidelGraph = staticmethod(families.GoethalsSeidelGraph)
|
2788
|
+
GrassmannGraph = staticmethod(distance_regular.GrassmannGraph)
|
2789
|
+
HalfCube = staticmethod(distance_regular.HalfCube)
|
2790
|
+
HammingGraph = staticmethod(families.HammingGraph)
|
2791
|
+
HanoiTowerGraph = staticmethod(families.HanoiTowerGraph)
|
2792
|
+
HararyGraph = staticmethod(families.HararyGraph)
|
2793
|
+
HermitianFormsGraph = staticmethod(distance_regular.HermitianFormsGraph)
|
2794
|
+
HyperStarGraph = staticmethod(families.HyperStarGraph)
|
2795
|
+
IGraph = staticmethod(families.IGraph)
|
2796
|
+
JohnsonGraph = staticmethod(families.JohnsonGraph)
|
2797
|
+
KneserGraph = staticmethod(families.KneserGraph)
|
2798
|
+
LCFGraph = staticmethod(families.LCFGraph)
|
2799
|
+
line_graph_forbidden_subgraphs = staticmethod(families.line_graph_forbidden_subgraphs)
|
2800
|
+
LollipopGraph = staticmethod(families.LollipopGraph)
|
2801
|
+
MathonPseudocyclicMergingGraph = staticmethod(families.MathonPseudocyclicMergingGraph)
|
2802
|
+
MathonPseudocyclicStronglyRegularGraph = staticmethod(families.MathonPseudocyclicStronglyRegularGraph)
|
2803
|
+
MuzychukS6Graph = staticmethod(families.MuzychukS6Graph)
|
2804
|
+
MycielskiGraph = staticmethod(families.MycielskiGraph)
|
2805
|
+
MycielskiStep = staticmethod(families.MycielskiStep)
|
2806
|
+
NKStarGraph = staticmethod(families.NKStarGraph)
|
2807
|
+
NStarGraph = staticmethod(families.NStarGraph)
|
2808
|
+
OddGraph = staticmethod(families.OddGraph)
|
2809
|
+
PaleyGraph = staticmethod(families.PaleyGraph)
|
2810
|
+
PasechnikGraph = staticmethod(families.PasechnikGraph)
|
2811
|
+
petersen_family = staticmethod(families.petersen_family)
|
2812
|
+
RingedTree = staticmethod(families.RingedTree)
|
2813
|
+
RoseWindowGraph = staticmethod(families.RoseWindowGraph)
|
2814
|
+
SierpinskiGasketGraph = staticmethod(families.SierpinskiGasketGraph)
|
2815
|
+
SquaredSkewHadamardMatrixGraph = staticmethod(families.SquaredSkewHadamardMatrixGraph)
|
2816
|
+
SwitchedSquaredSkewHadamardMatrixGraph = staticmethod(families.SwitchedSquaredSkewHadamardMatrixGraph)
|
2817
|
+
StaircaseGraph = staticmethod(families.StaircaseGraph)
|
2818
|
+
strongly_regular_graph = staticmethod(strongly_regular_db.strongly_regular_graph)
|
2819
|
+
TabacjnGraph = staticmethod(families.TabacjnGraph)
|
2820
|
+
TadpoleGraph = staticmethod(families.TadpoleGraph)
|
2821
|
+
trees = staticmethod(families.trees)
|
2822
|
+
TruncatedBiwheelGraph = staticmethod(families.TruncatedBiwheelGraph)
|
2823
|
+
nauty_gentreeg = staticmethod(families.nauty_gentreeg)
|
2824
|
+
TuranGraph = staticmethod(families.TuranGraph)
|
2825
|
+
UstimenkoGraph = staticmethod(distance_regular.UstimenkoGraph)
|
2826
|
+
WheelGraph = staticmethod(families.WheelGraph)
|
2827
|
+
WindmillGraph = staticmethod(families.WindmillGraph)
|
2828
|
+
|
2829
|
+
###########################################################################
|
2830
|
+
# Graphs from classical geometries over `F_q`
|
2831
|
+
###########################################################################
|
2832
|
+
from .generators import classical_geometries
|
2833
|
+
AffineOrthogonalPolarGraph = staticmethod(classical_geometries.AffineOrthogonalPolarGraph)
|
2834
|
+
AhrensSzekeresGeneralizedQuadrangleGraph = staticmethod(classical_geometries.AhrensSzekeresGeneralizedQuadrangleGraph)
|
2835
|
+
NonisotropicOrthogonalPolarGraph = staticmethod(classical_geometries.NonisotropicOrthogonalPolarGraph)
|
2836
|
+
NonisotropicUnitaryPolarGraph = staticmethod(classical_geometries.NonisotropicUnitaryPolarGraph)
|
2837
|
+
OrthogonalDualPolarGraph = staticmethod(classical_geometries.OrthogonalDualPolarGraph)
|
2838
|
+
OrthogonalPolarGraph = staticmethod(classical_geometries.OrthogonalPolarGraph)
|
2839
|
+
SymplecticDualPolarGraph = staticmethod(classical_geometries.SymplecticDualPolarGraph)
|
2840
|
+
SymplecticPolarGraph = staticmethod(classical_geometries.SymplecticPolarGraph)
|
2841
|
+
TaylorTwographDescendantSRG = staticmethod(classical_geometries.TaylorTwographDescendantSRG)
|
2842
|
+
TaylorTwographSRG = staticmethod(classical_geometries.TaylorTwographSRG)
|
2843
|
+
T2starGeneralizedQuadrangleGraph = staticmethod(classical_geometries.T2starGeneralizedQuadrangleGraph)
|
2844
|
+
Nowhere0WordsTwoWeightCodeGraph = staticmethod(classical_geometries.Nowhere0WordsTwoWeightCodeGraph)
|
2845
|
+
HaemersGraph = staticmethod(classical_geometries.HaemersGraph)
|
2846
|
+
CossidentePenttilaGraph = staticmethod(classical_geometries.CossidentePenttilaGraph)
|
2847
|
+
UnitaryDualPolarGraph = staticmethod(classical_geometries.UnitaryDualPolarGraph)
|
2848
|
+
UnitaryPolarGraph = staticmethod(classical_geometries.UnitaryPolarGraph)
|
2849
|
+
|
2850
|
+
###########################################################################
|
2851
|
+
# Chessboard Graphs
|
2852
|
+
###########################################################################
|
2853
|
+
from .generators import chessboard
|
2854
|
+
ChessboardGraphGenerator = staticmethod(chessboard.ChessboardGraphGenerator)
|
2855
|
+
BishopGraph = staticmethod(chessboard.BishopGraph)
|
2856
|
+
KingGraph = staticmethod(chessboard.KingGraph)
|
2857
|
+
KnightGraph = staticmethod(chessboard.KnightGraph)
|
2858
|
+
QueenGraph = staticmethod(chessboard.QueenGraph)
|
2859
|
+
RookGraph = staticmethod(chessboard.RookGraph)
|
2860
|
+
|
2861
|
+
###########################################################################
|
2862
|
+
# Intersection graphs
|
2863
|
+
###########################################################################
|
2864
|
+
from .generators import intersection
|
2865
|
+
IntervalGraph = staticmethod(intersection.IntervalGraph)
|
2866
|
+
IntersectionGraph = staticmethod(intersection.IntersectionGraph)
|
2867
|
+
PermutationGraph = staticmethod(intersection.PermutationGraph)
|
2868
|
+
OrthogonalArrayBlockGraph = staticmethod(intersection.OrthogonalArrayBlockGraph)
|
2869
|
+
ToleranceGraph = staticmethod(intersection.ToleranceGraph)
|
2870
|
+
|
2871
|
+
###########################################################################
|
2872
|
+
# Random Graphs
|
2873
|
+
###########################################################################
|
2874
|
+
from .generators import random
|
2875
|
+
RandomBarabasiAlbert = staticmethod(random.RandomBarabasiAlbert)
|
2876
|
+
RandomBipartite = staticmethod(random.RandomBipartite)
|
2877
|
+
RandomRegularBipartite = staticmethod(random.RandomRegularBipartite)
|
2878
|
+
RandomBicubicPlanar = staticmethod(random.RandomBicubicPlanar)
|
2879
|
+
RandomBlockGraph = staticmethod(random.RandomBlockGraph)
|
2880
|
+
RandomBoundedToleranceGraph = staticmethod(random.RandomBoundedToleranceGraph)
|
2881
|
+
RandomChordalGraph = staticmethod(random.RandomChordalGraph)
|
2882
|
+
RandomGNM = staticmethod(random.RandomGNM)
|
2883
|
+
RandomGNP = staticmethod(random.RandomGNP)
|
2884
|
+
RandomHolmeKim = staticmethod(random.RandomHolmeKim)
|
2885
|
+
RandomIntervalGraph = staticmethod(random.RandomIntervalGraph)
|
2886
|
+
RandomLobster = staticmethod(random.RandomLobster)
|
2887
|
+
RandomNewmanWattsStrogatz = staticmethod(random.RandomNewmanWattsStrogatz)
|
2888
|
+
RandomProperIntervalGraph = staticmethod(random.RandomProperIntervalGraph)
|
2889
|
+
RandomRegular = staticmethod(random.RandomRegular)
|
2890
|
+
RandomShell = staticmethod(random.RandomShell)
|
2891
|
+
RandomKTree = staticmethod(random.RandomKTree)
|
2892
|
+
RandomPartialKTree = staticmethod(random.RandomPartialKTree)
|
2893
|
+
RandomToleranceGraph = staticmethod(random.RandomToleranceGraph)
|
2894
|
+
RandomTreePowerlaw = staticmethod(random.RandomTreePowerlaw)
|
2895
|
+
RandomTree = staticmethod(random.RandomTree)
|
2896
|
+
RandomTriangulation = staticmethod(random.RandomTriangulation)
|
2897
|
+
RandomUnitDiskGraph = staticmethod(random.RandomUnitDiskGraph)
|
2898
|
+
|
2899
|
+
###########################################################################
|
2900
|
+
# Maps
|
2901
|
+
###########################################################################
|
2902
|
+
from .generators import world_map
|
2903
|
+
WorldMap = staticmethod(world_map.WorldMap)
|
2904
|
+
EuropeMap = staticmethod(world_map.EuropeMap)
|
2905
|
+
AfricaMap = staticmethod(world_map.AfricaMap)
|
2906
|
+
USAMap = staticmethod(world_map.USAMap)
|
2907
|
+
|
2908
|
+
###########################################################################
|
2909
|
+
# Degree Sequence
|
2910
|
+
###########################################################################
|
2911
|
+
from .generators import degree_sequence
|
2912
|
+
DegreeSequence = staticmethod(degree_sequence.DegreeSequence)
|
2913
|
+
DegreeSequenceBipartite = staticmethod(degree_sequence.DegreeSequenceBipartite)
|
2914
|
+
DegreeSequenceConfigurationModel = staticmethod(degree_sequence.DegreeSequenceConfigurationModel)
|
2915
|
+
DegreeSequenceTree = staticmethod(degree_sequence.DegreeSequenceTree)
|
2916
|
+
DegreeSequenceExpected = staticmethod(degree_sequence.DegreeSequenceExpected)
|
2917
|
+
|
2918
|
+
|
2919
|
+
def canaug_traverse_vert(g, aut_gens, max_verts, property, dig=False, loops=False, sparse=True):
|
2920
|
+
"""
|
2921
|
+
Main function for exhaustive generation. Recursive traversal of a
|
2922
|
+
canonically generated tree of isomorph free (di)graphs satisfying a
|
2923
|
+
given property.
|
2924
|
+
|
2925
|
+
INPUT:
|
2926
|
+
|
2927
|
+
- ``g`` -- current position on the tree
|
2928
|
+
|
2929
|
+
- ``aut_gens`` -- list of generators of Aut(g), in list notation
|
2930
|
+
|
2931
|
+
- ``max_verts`` -- when to retreat
|
2932
|
+
|
2933
|
+
- ``property`` -- check before traversing below g
|
2934
|
+
|
2935
|
+
- ``degree_sequence`` -- specify a degree sequence to try to obtain
|
2936
|
+
|
2937
|
+
EXAMPLES::
|
2938
|
+
|
2939
|
+
sage: from sage.graphs.graph_generators import canaug_traverse_vert
|
2940
|
+
sage: list(canaug_traverse_vert(Graph(), [], 3, lambda x: True))
|
2941
|
+
[Graph on 0 vertices, ... Graph on 3 vertices]
|
2942
|
+
|
2943
|
+
The best way to access this function is through the graphs()
|
2944
|
+
iterator:
|
2945
|
+
|
2946
|
+
Print graphs on 3 or less vertices.
|
2947
|
+
|
2948
|
+
::
|
2949
|
+
|
2950
|
+
sage: for G in graphs(3, augment='vertices'):
|
2951
|
+
....: print(G)
|
2952
|
+
Graph on 0 vertices
|
2953
|
+
Graph on 1 vertex
|
2954
|
+
Graph on 2 vertices
|
2955
|
+
Graph on 3 vertices
|
2956
|
+
Graph on 3 vertices
|
2957
|
+
Graph on 3 vertices
|
2958
|
+
Graph on 2 vertices
|
2959
|
+
Graph on 3 vertices
|
2960
|
+
|
2961
|
+
Print digraphs on 2 or less vertices.
|
2962
|
+
|
2963
|
+
::
|
2964
|
+
|
2965
|
+
sage: for D in digraphs(2, augment='vertices'):
|
2966
|
+
....: print(D)
|
2967
|
+
Digraph on 0 vertices
|
2968
|
+
Digraph on 1 vertex
|
2969
|
+
Digraph on 2 vertices
|
2970
|
+
Digraph on 2 vertices
|
2971
|
+
Digraph on 2 vertices
|
2972
|
+
"""
|
2973
|
+
from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree
|
2974
|
+
if not property(g):
|
2975
|
+
return
|
2976
|
+
yield g
|
2977
|
+
|
2978
|
+
n = g.order()
|
2979
|
+
if n < max_verts:
|
2980
|
+
|
2981
|
+
# build a list representing C(g) - the vertex to be added
|
2982
|
+
# is at the end, so only specify which edges...
|
2983
|
+
# in the case of graphs, there are n possibilities,
|
2984
|
+
# and in the case of digraphs, there are 2*n.
|
2985
|
+
if dig:
|
2986
|
+
possibilities = 2 * n
|
2987
|
+
else:
|
2988
|
+
possibilities = n
|
2989
|
+
num_roots = 2**possibilities
|
2990
|
+
children = [-1]*num_roots
|
2991
|
+
|
2992
|
+
# union-find C(g) under Aut(g)
|
2993
|
+
for gen in aut_gens:
|
2994
|
+
for i in range(len(children)):
|
2995
|
+
k = 0
|
2996
|
+
for j in range(possibilities):
|
2997
|
+
if (1 << j) & i:
|
2998
|
+
if dig and j >= n:
|
2999
|
+
k += (1 << (gen[j - n] + n))
|
3000
|
+
else:
|
3001
|
+
k += (1 << gen[j])
|
3002
|
+
while children[k] != -1:
|
3003
|
+
k = children[k]
|
3004
|
+
while children[i] != -1:
|
3005
|
+
i = children[i]
|
3006
|
+
if i != k:
|
3007
|
+
# union i & k
|
3008
|
+
smaller, larger = sorted([i, k])
|
3009
|
+
children[larger] = smaller
|
3010
|
+
num_roots -= 1
|
3011
|
+
|
3012
|
+
# find representatives of orbits of C(g)
|
3013
|
+
roots = []
|
3014
|
+
found_roots = 0
|
3015
|
+
i = 0
|
3016
|
+
while found_roots < num_roots:
|
3017
|
+
if children[i] == -1:
|
3018
|
+
found_roots += 1
|
3019
|
+
roots.append(i)
|
3020
|
+
i += 1
|
3021
|
+
for i in roots:
|
3022
|
+
# construct a z for each number in roots...
|
3023
|
+
z = g.copy(sparse=sparse)
|
3024
|
+
z.add_vertex(n)
|
3025
|
+
edges = []
|
3026
|
+
if dig:
|
3027
|
+
index = 0
|
3028
|
+
while 2 * index < possibilities:
|
3029
|
+
if (1 << index) & i:
|
3030
|
+
edges.append((index, n))
|
3031
|
+
index += 1
|
3032
|
+
while index < possibilities:
|
3033
|
+
if (1 << index) & i:
|
3034
|
+
edges.append((n, index - n))
|
3035
|
+
index += 1
|
3036
|
+
else:
|
3037
|
+
index = 0
|
3038
|
+
while (1 << index) <= i:
|
3039
|
+
if (1 << index) & i:
|
3040
|
+
edges.append((index, n))
|
3041
|
+
index += 1
|
3042
|
+
z.add_edges(edges)
|
3043
|
+
z_s = []
|
3044
|
+
if property(z):
|
3045
|
+
z_s.append(z)
|
3046
|
+
if loops:
|
3047
|
+
z = z.copy(sparse=sparse)
|
3048
|
+
z.add_edge((n, n))
|
3049
|
+
if property(z):
|
3050
|
+
z_s.append(z)
|
3051
|
+
for z in z_s:
|
3052
|
+
z_aut_gens, _, canonical_relabeling = search_tree(z, [z.vertices(sort=True)], certificate=True, dig=(dig or loops))
|
3053
|
+
cut_vert = 0
|
3054
|
+
while canonical_relabeling[cut_vert] != n:
|
3055
|
+
cut_vert += 1
|
3056
|
+
sub_verts = [v for v in z if v != cut_vert]
|
3057
|
+
m_z = z.subgraph(sub_verts)
|
3058
|
+
|
3059
|
+
if m_z == g:
|
3060
|
+
for a in canaug_traverse_vert(z, z_aut_gens, max_verts, property, dig=dig, loops=loops, sparse=sparse):
|
3061
|
+
yield a
|
3062
|
+
else:
|
3063
|
+
for possibility in check_aut(z_aut_gens, cut_vert, n):
|
3064
|
+
if m_z.relabel(dict(enumerate(possibility)), check_input=False, inplace=False) == g:
|
3065
|
+
for a in canaug_traverse_vert(z, z_aut_gens, max_verts, property, dig=dig, loops=loops, sparse=sparse):
|
3066
|
+
yield a
|
3067
|
+
break
|
3068
|
+
|
3069
|
+
|
3070
|
+
def check_aut(aut_gens, cut_vert, n):
|
3071
|
+
"""
|
3072
|
+
Helper function for exhaustive generation.
|
3073
|
+
|
3074
|
+
At the start, check_aut is given a set of generators for the
|
3075
|
+
automorphism group, aut_gens. We already know we are looking for
|
3076
|
+
an element of the auto- morphism group that sends cut_vert to n,
|
3077
|
+
and check_aut generates these for the canaug_traverse function.
|
3078
|
+
|
3079
|
+
EXAMPLES:
|
3080
|
+
|
3081
|
+
Note that the last two entries indicate that none of the
|
3082
|
+
automorphism group has yet been searched - we are starting at the
|
3083
|
+
identity [0, 1, 2, 3] and so far that is all we have seen. We
|
3084
|
+
return automorphisms mapping 2 to 3::
|
3085
|
+
|
3086
|
+
sage: from sage.graphs.graph_generators import check_aut
|
3087
|
+
sage: list( check_aut( [ [0, 3, 2, 1], [1, 0, 3, 2], [2, 1, 0, 3] ], 2, 3))
|
3088
|
+
[[1, 0, 3, 2], [1, 2, 3, 0]]
|
3089
|
+
"""
|
3090
|
+
from copy import copy
|
3091
|
+
perm = list(range(n + 1))
|
3092
|
+
seen_perms = [perm]
|
3093
|
+
unchecked_perms = [perm]
|
3094
|
+
while unchecked_perms:
|
3095
|
+
perm = unchecked_perms.pop(0)
|
3096
|
+
for gen in aut_gens:
|
3097
|
+
new_perm = copy(perm)
|
3098
|
+
for i in range(len(perm)):
|
3099
|
+
new_perm[i] = gen[perm[i]]
|
3100
|
+
if new_perm not in seen_perms:
|
3101
|
+
seen_perms.append(new_perm)
|
3102
|
+
unchecked_perms.append(new_perm)
|
3103
|
+
if new_perm[cut_vert] == n:
|
3104
|
+
yield new_perm
|
3105
|
+
|
3106
|
+
|
3107
|
+
def canaug_traverse_edge(g, aut_gens, property, dig=False, loops=False, sparse=True):
|
3108
|
+
"""
|
3109
|
+
Main function for exhaustive generation. Recursive traversal of a
|
3110
|
+
canonically generated tree of isomorph free graphs satisfying a
|
3111
|
+
given property.
|
3112
|
+
|
3113
|
+
INPUT:
|
3114
|
+
|
3115
|
+
- ``g`` -- current position on the tree
|
3116
|
+
|
3117
|
+
- ``aut_gens`` -- list of generators of Aut(g), in list notation
|
3118
|
+
|
3119
|
+
- ``property`` -- check before traversing below g
|
3120
|
+
|
3121
|
+
EXAMPLES::
|
3122
|
+
|
3123
|
+
sage: from sage.graphs.graph_generators import canaug_traverse_edge
|
3124
|
+
sage: G = Graph(3)
|
3125
|
+
sage: list(canaug_traverse_edge(G, [], lambda x: True))
|
3126
|
+
[Graph on 3 vertices, ... Graph on 3 vertices]
|
3127
|
+
|
3128
|
+
The best way to access this function is through the graphs()
|
3129
|
+
iterator:
|
3130
|
+
|
3131
|
+
Print graphs on 3 or less vertices.
|
3132
|
+
|
3133
|
+
::
|
3134
|
+
|
3135
|
+
sage: for G in graphs(3): # needs nauty
|
3136
|
+
....: print(G)
|
3137
|
+
Graph on 3 vertices
|
3138
|
+
Graph on 3 vertices
|
3139
|
+
Graph on 3 vertices
|
3140
|
+
Graph on 3 vertices
|
3141
|
+
|
3142
|
+
Print digraphs on 3 or less vertices.
|
3143
|
+
|
3144
|
+
::
|
3145
|
+
|
3146
|
+
sage: for G in digraphs(3): # needs nauty
|
3147
|
+
....: print(G)
|
3148
|
+
Digraph on 3 vertices
|
3149
|
+
Digraph on 3 vertices
|
3150
|
+
...
|
3151
|
+
Digraph on 3 vertices
|
3152
|
+
Digraph on 3 vertices
|
3153
|
+
"""
|
3154
|
+
from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree
|
3155
|
+
|
3156
|
+
if not property(g):
|
3157
|
+
return
|
3158
|
+
yield g
|
3159
|
+
n = g.order()
|
3160
|
+
if dig:
|
3161
|
+
max_size = n * (n - 1)
|
3162
|
+
else:
|
3163
|
+
max_size = (n * (n - 1)) >> 1 # >> 1 is just / 2 (this is n choose 2)
|
3164
|
+
if loops:
|
3165
|
+
max_size += n
|
3166
|
+
if g.size() < max_size:
|
3167
|
+
# build a list representing C(g) - the edge to be added
|
3168
|
+
# is one of max_size choices
|
3169
|
+
if dig:
|
3170
|
+
children = [[(j, i) for i in range(n)] for j in range(n)]
|
3171
|
+
else:
|
3172
|
+
children = [[(j, i) for i in range(j)] for j in range(n)]
|
3173
|
+
# union-find C(g) under Aut(g)
|
3174
|
+
orbits = list(range(n))
|
3175
|
+
for gen in aut_gens:
|
3176
|
+
for iii in range(n):
|
3177
|
+
if orbits[gen[iii]] != orbits[iii]:
|
3178
|
+
temp = orbits[gen[iii]]
|
3179
|
+
for jjj in range(n):
|
3180
|
+
if orbits[jjj] == temp:
|
3181
|
+
orbits[jjj] = orbits[iii]
|
3182
|
+
if dig:
|
3183
|
+
jjj_range = list(range(iii)) + list(range(iii + 1, n))
|
3184
|
+
else:
|
3185
|
+
jjj_range = list(range(iii)) # iii > jjj
|
3186
|
+
for jjj in jjj_range:
|
3187
|
+
i, j = iii, jjj
|
3188
|
+
if dig:
|
3189
|
+
x, y = gen[i], gen[j]
|
3190
|
+
else:
|
3191
|
+
y, x = sorted([gen[i], gen[j]])
|
3192
|
+
if children[i][j] != children[x][y]:
|
3193
|
+
x_val, y_val = x, y
|
3194
|
+
i_val, j_val = i, j
|
3195
|
+
if dig:
|
3196
|
+
while (x_val, y_val) != children[x_val][y_val]:
|
3197
|
+
x_val, y_val = children[x_val][y_val]
|
3198
|
+
while (i_val, j_val) != children[i_val][j_val]:
|
3199
|
+
i_val, j_val = children[i_val][j_val]
|
3200
|
+
else:
|
3201
|
+
while (x_val, y_val) != children[x_val][y_val]:
|
3202
|
+
y_val, x_val = sorted(children[x_val][y_val])
|
3203
|
+
while (i_val, j_val) != children[i_val][j_val]:
|
3204
|
+
j_val, i_val = sorted(children[i_val][j_val])
|
3205
|
+
while (x, y) != (x_val, y_val):
|
3206
|
+
xx, yy = x, y
|
3207
|
+
x, y = children[x][y]
|
3208
|
+
children[xx][yy] = (x_val, y_val)
|
3209
|
+
while (i, j) != (i_val, j_val):
|
3210
|
+
ii, jj = i, j
|
3211
|
+
i, j = children[i][j]
|
3212
|
+
children[ii][jj] = (i_val, j_val)
|
3213
|
+
if x < i:
|
3214
|
+
children[i][j] = (x, y)
|
3215
|
+
elif x > i:
|
3216
|
+
children[x][y] = (i, j)
|
3217
|
+
elif y < j:
|
3218
|
+
children[i][j] = (x, y)
|
3219
|
+
elif y > j:
|
3220
|
+
children[x][y] = (i, j)
|
3221
|
+
else:
|
3222
|
+
continue
|
3223
|
+
# find representatives of orbits of C(g)
|
3224
|
+
roots = []
|
3225
|
+
for i in range(n):
|
3226
|
+
if dig:
|
3227
|
+
j_range = list(range(i)) + list(range(i + 1, n))
|
3228
|
+
else:
|
3229
|
+
j_range = list(range(i))
|
3230
|
+
for j in j_range:
|
3231
|
+
if children[i][j] == (i, j):
|
3232
|
+
roots.append((i, j))
|
3233
|
+
if loops:
|
3234
|
+
seen = []
|
3235
|
+
for i in range(n):
|
3236
|
+
if orbits[i] not in seen:
|
3237
|
+
roots.append((i, i))
|
3238
|
+
seen.append(orbits[i])
|
3239
|
+
for i, j in roots:
|
3240
|
+
if g.has_edge(i, j):
|
3241
|
+
continue
|
3242
|
+
# construct a z for each edge in roots...
|
3243
|
+
z = g.copy(sparse=sparse)
|
3244
|
+
z.add_edge(i, j)
|
3245
|
+
if not property(z):
|
3246
|
+
continue
|
3247
|
+
z_aut_gens, _, canonical_relabeling = search_tree(z, [z.vertices(sort=True)], certificate=True, dig=(dig or loops))
|
3248
|
+
relabel_inverse = [0]*n
|
3249
|
+
for ii in range(n):
|
3250
|
+
relabel_inverse[canonical_relabeling[ii]] = ii
|
3251
|
+
z_can = z.relabel(canonical_relabeling, inplace=False)
|
3252
|
+
cut_edge_can = z_can.edges(labels=False, sort=True)[-1]
|
3253
|
+
cut_edge = [relabel_inverse[cut_edge_can[0]], relabel_inverse[cut_edge_can[1]]]
|
3254
|
+
if dig:
|
3255
|
+
cut_edge = tuple(cut_edge)
|
3256
|
+
else:
|
3257
|
+
cut_edge = tuple(sorted(cut_edge))
|
3258
|
+
|
3259
|
+
from copy import copy
|
3260
|
+
m_z = copy(z)
|
3261
|
+
m_z.delete_edge(cut_edge)
|
3262
|
+
if m_z == g:
|
3263
|
+
for a in canaug_traverse_edge(z, z_aut_gens, property, dig=dig, loops=loops, sparse=sparse):
|
3264
|
+
yield a
|
3265
|
+
else:
|
3266
|
+
for possibility in check_aut_edge(z_aut_gens, cut_edge, i, j, n, dig=dig):
|
3267
|
+
if m_z.relabel(possibility, inplace=False) == g:
|
3268
|
+
for a in canaug_traverse_edge(z, z_aut_gens, property, dig=dig, loops=loops, sparse=sparse):
|
3269
|
+
yield a
|
3270
|
+
break
|
3271
|
+
|
3272
|
+
|
3273
|
+
def check_aut_edge(aut_gens, cut_edge, i, j, n, dig=False):
|
3274
|
+
"""
|
3275
|
+
Helper function for exhaustive generation.
|
3276
|
+
|
3277
|
+
At the start, check_aut_edge is given a set of generators for the
|
3278
|
+
automorphism group, aut_gens. We already know we are looking for
|
3279
|
+
an element of the auto- morphism group that sends cut_edge to {i,
|
3280
|
+
j}, and check_aut generates these for the canaug_traverse
|
3281
|
+
function.
|
3282
|
+
|
3283
|
+
EXAMPLES:
|
3284
|
+
|
3285
|
+
Note that the last two entries indicate that none of the
|
3286
|
+
automorphism group has yet been searched - we are starting at the
|
3287
|
+
identity [0, 1, 2, 3] and so far that is all we have seen. We
|
3288
|
+
return automorphisms mapping 2 to 3::
|
3289
|
+
|
3290
|
+
sage: from sage.graphs.graph_generators import check_aut
|
3291
|
+
sage: list( check_aut( [ [0, 3, 2, 1], [1, 0, 3, 2], [2, 1, 0, 3] ], 2, 3))
|
3292
|
+
[[1, 0, 3, 2], [1, 2, 3, 0]]
|
3293
|
+
"""
|
3294
|
+
from copy import copy
|
3295
|
+
perm = list(range(n))
|
3296
|
+
seen_perms = [perm]
|
3297
|
+
unchecked_perms = [perm]
|
3298
|
+
while unchecked_perms:
|
3299
|
+
perm = unchecked_perms.pop(0)
|
3300
|
+
for gen in aut_gens:
|
3301
|
+
new_perm = copy(perm)
|
3302
|
+
for ii in range(n):
|
3303
|
+
new_perm[ii] = gen[perm[ii]]
|
3304
|
+
if new_perm not in seen_perms:
|
3305
|
+
seen_perms.append(new_perm)
|
3306
|
+
unchecked_perms.append(new_perm)
|
3307
|
+
if new_perm[cut_edge[0]] == i and new_perm[cut_edge[1]] == j:
|
3308
|
+
yield new_perm
|
3309
|
+
if not dig and new_perm[cut_edge[0]] == j and new_perm[cut_edge[1]] == i:
|
3310
|
+
yield new_perm
|
3311
|
+
|
3312
|
+
|
3313
|
+
# Easy access to the graph generators from the command line:
|
3314
|
+
graphs = GraphGenerators()
|