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,1666 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
"""
|
3
|
+
Graph-theoretic partition backtrack functions
|
4
|
+
|
5
|
+
EXAMPLES::
|
6
|
+
|
7
|
+
sage: import sage.groups.perm_gps.partn_ref.refinement_graphs
|
8
|
+
|
9
|
+
REFERENCE:
|
10
|
+
|
11
|
+
- [1] McKay, Brendan D. *Practical Graph Isomorphism*. Congressus Numerantium,
|
12
|
+
Vol. 30 (1981), pp. 45-87.
|
13
|
+
"""
|
14
|
+
# ****************************************************************************
|
15
|
+
# Copyright (C) 2006 - 2011 Robert L. Miller <rlmillster@gmail.com>
|
16
|
+
#
|
17
|
+
# This program is free software: you can redistribute it and/or modify
|
18
|
+
# it under the terms of the GNU General Public License as published by
|
19
|
+
# the Free Software Foundation, either version 2 of the License, or
|
20
|
+
# (at your option) any later version.
|
21
|
+
# https://www.gnu.org/licenses/
|
22
|
+
# ****************************************************************************
|
23
|
+
|
24
|
+
from sage.groups.perm_gps.partn_ref.data_structures cimport *
|
25
|
+
from sage.data_structures.bitset_base cimport *
|
26
|
+
from sage.rings.integer cimport Integer
|
27
|
+
from sage.graphs.base.sparse_graph cimport SparseGraph
|
28
|
+
from sage.graphs.base.dense_graph cimport DenseGraph, copy_dense_graph
|
29
|
+
from sage.groups.perm_gps.partn_ref.double_coset cimport double_coset
|
30
|
+
|
31
|
+
|
32
|
+
def isomorphic(G1, G2, partn, ordering2, dig, use_indicator_function, sparse=False):
|
33
|
+
"""
|
34
|
+
Test whether two graphs are isomorphic.
|
35
|
+
|
36
|
+
EXAMPLES::
|
37
|
+
|
38
|
+
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import isomorphic
|
39
|
+
|
40
|
+
sage: G = Graph(2)
|
41
|
+
sage: H = Graph(2)
|
42
|
+
sage: isomorphic(G, H, [[0,1]], [0,1], 0, 1)
|
43
|
+
{0: 0, 1: 1}
|
44
|
+
sage: isomorphic(G, H, [[0,1]], [0,1], 0, 1)
|
45
|
+
{0: 0, 1: 1}
|
46
|
+
sage: isomorphic(G, H, [[0],[1]], [0,1], 0, 1)
|
47
|
+
{0: 0, 1: 1}
|
48
|
+
sage: isomorphic(G, H, [[0],[1]], [1,0], 0, 1)
|
49
|
+
{0: 1, 1: 0}
|
50
|
+
|
51
|
+
sage: G = Graph(3)
|
52
|
+
sage: H = Graph(3)
|
53
|
+
sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1)
|
54
|
+
{0: 0, 1: 1, 2: 2}
|
55
|
+
sage: G.add_edge(0,1)
|
56
|
+
sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1)
|
57
|
+
False
|
58
|
+
sage: H.add_edge(1,2)
|
59
|
+
sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1)
|
60
|
+
{0: 1, 1: 2, 2: 0}
|
61
|
+
"""
|
62
|
+
cdef PartitionStack *part
|
63
|
+
cdef int *output
|
64
|
+
cdef int *ordering
|
65
|
+
cdef CGraph G
|
66
|
+
cdef GraphStruct GS1 = GraphStruct()
|
67
|
+
cdef GraphStruct GS2 = GraphStruct()
|
68
|
+
cdef GraphStruct GS
|
69
|
+
cdef int i, j, n = -1
|
70
|
+
cdef list partition, cell
|
71
|
+
cdef bint loops = 0
|
72
|
+
|
73
|
+
from sage.graphs.graph import Graph
|
74
|
+
from sage.graphs.generic_graph import GenericGraph
|
75
|
+
from copy import copy
|
76
|
+
which_G = 1
|
77
|
+
for G_in in [G1, G2]:
|
78
|
+
if which_G == 1:
|
79
|
+
GS = GS1
|
80
|
+
first = True
|
81
|
+
else:
|
82
|
+
GS = GS2
|
83
|
+
first = False
|
84
|
+
if isinstance(G_in, GenericGraph):
|
85
|
+
if G_in.has_loops():
|
86
|
+
loops = 1
|
87
|
+
if n == -1:
|
88
|
+
n = G_in.num_verts()
|
89
|
+
elif n != G_in.num_verts():
|
90
|
+
return False
|
91
|
+
if G_in.vertices(sort=True) != list(range(n)):
|
92
|
+
G_in = copy(G_in)
|
93
|
+
to = G_in.relabel(return_map=True)
|
94
|
+
frm = {}
|
95
|
+
for v in to:
|
96
|
+
frm[to[v]] = v
|
97
|
+
if first:
|
98
|
+
partition = [[to[v] for v in cell] for cell in partn]
|
99
|
+
else:
|
100
|
+
if first:
|
101
|
+
partition = partn
|
102
|
+
to = list(range(n))
|
103
|
+
frm = to
|
104
|
+
if sparse:
|
105
|
+
G = SparseGraph(n)
|
106
|
+
else:
|
107
|
+
G = DenseGraph(n)
|
108
|
+
if G_in.is_directed():
|
109
|
+
for i, j in G_in.edge_iterator(labels=False):
|
110
|
+
G.add_arc(i, j)
|
111
|
+
else:
|
112
|
+
for i, j in G_in.edge_iterator(labels=False):
|
113
|
+
G.add_arc(i, j)
|
114
|
+
G.add_arc(j, i)
|
115
|
+
elif isinstance(G_in, CGraph):
|
116
|
+
G = <CGraph> G_in
|
117
|
+
if n == -1:
|
118
|
+
n = G.num_verts
|
119
|
+
elif n != <int>G.num_verts:
|
120
|
+
return False
|
121
|
+
if not loops:
|
122
|
+
for i in range(n):
|
123
|
+
if G.has_arc_unsafe(i,i):
|
124
|
+
loops = 1
|
125
|
+
to = {}
|
126
|
+
for a in G.verts():
|
127
|
+
to[a] = a
|
128
|
+
frm = to
|
129
|
+
if first:
|
130
|
+
partition = partn
|
131
|
+
else:
|
132
|
+
raise TypeError("G must be a Sage graph")
|
133
|
+
if first:
|
134
|
+
frm1 = frm
|
135
|
+
else:
|
136
|
+
frm2 = frm
|
137
|
+
to2 = to
|
138
|
+
GS.G = G
|
139
|
+
GS.directed = 1 if dig else 0
|
140
|
+
GS.loops = 1
|
141
|
+
GS.use_indicator = 1 if use_indicator_function else 0
|
142
|
+
which_G += 1
|
143
|
+
|
144
|
+
if n == 0:
|
145
|
+
return {}
|
146
|
+
|
147
|
+
part = PS_from_list(partition)
|
148
|
+
ordering = <int *> sig_malloc(n * sizeof(int))
|
149
|
+
output = <int *> sig_malloc(n * sizeof(int))
|
150
|
+
if part is NULL or ordering is NULL or output is NULL:
|
151
|
+
PS_dealloc(part)
|
152
|
+
sig_free(ordering)
|
153
|
+
sig_free(output)
|
154
|
+
raise MemoryError
|
155
|
+
for i from 0 <= i < n:
|
156
|
+
ordering[i] = to2[ordering2[i]]
|
157
|
+
|
158
|
+
GS1.scratch = <int *> sig_malloc((3*n+1) * sizeof(int))
|
159
|
+
GS2.scratch = <int *> sig_malloc((3*n+1) * sizeof(int))
|
160
|
+
if GS1.scratch is NULL or GS2.scratch is NULL:
|
161
|
+
sig_free(GS1.scratch)
|
162
|
+
sig_free(GS2.scratch)
|
163
|
+
PS_dealloc(part)
|
164
|
+
sig_free(ordering)
|
165
|
+
raise MemoryError
|
166
|
+
|
167
|
+
cdef bint isomorphic = double_coset(<void *>GS1, <void *>GS2, part, ordering, n, &all_children_are_equivalent, &refine_by_degree, &compare_graphs, NULL, NULL, output)
|
168
|
+
|
169
|
+
PS_dealloc(part)
|
170
|
+
sig_free(ordering)
|
171
|
+
sig_free(GS1.scratch)
|
172
|
+
sig_free(GS2.scratch)
|
173
|
+
if isomorphic:
|
174
|
+
output_py = dict([[frm1[i], frm2[output[i]]] for i from 0 <= i < n])
|
175
|
+
else:
|
176
|
+
output_py = False
|
177
|
+
sig_free(output)
|
178
|
+
return output_py
|
179
|
+
|
180
|
+
|
181
|
+
def search_tree(G_in, partition, lab=True, dig=False, dict_rep=False, certificate=False,
|
182
|
+
verbosity=0, use_indicator_function=True, sparse=True,
|
183
|
+
base=False, order=False):
|
184
|
+
"""
|
185
|
+
Compute canonical labels and automorphism groups of graphs.
|
186
|
+
|
187
|
+
INPUT:
|
188
|
+
|
189
|
+
- ``G_in`` -- a Sage graph
|
190
|
+
- ``partition`` -- list of lists representing a partition of the vertices
|
191
|
+
- ``lab`` -- if ``True``, compute and return the canonical label in addition to the
|
192
|
+
automorphism group
|
193
|
+
- ``dig`` -- set to ``True`` for digraphs and graphs with loops; if ``True``, does not
|
194
|
+
use optimizations based on Lemma 2.25 in [1] that are valid only for
|
195
|
+
simple graphs
|
196
|
+
- ``dict_rep`` -- if ``True``, return a dictionary with keys the vertices of the
|
197
|
+
input graph G_in and values elements of the set the permutation group
|
198
|
+
acts on. (The point is that graphs are arbitrarily labelled, often
|
199
|
+
0..n-1, and permutation groups always act on 1..n. This dictionary
|
200
|
+
maps vertex labels (such as 0..n-1) to the domain of the permutations.)
|
201
|
+
- ``certificate`` -- if ``True``, return the permutation from G to its canonical label
|
202
|
+
- ``verbosity`` -- currently ignored
|
203
|
+
- ``use_indicator_function`` -- option to turn off indicator function
|
204
|
+
(``True`` is generally faster)
|
205
|
+
- ``sparse`` -- whether to use sparse or dense representation of the graph
|
206
|
+
(ignored if G is already a CGraph - see sage.graphs.base)
|
207
|
+
- ``base`` -- whether to return the first sequence of split vertices (used in
|
208
|
+
computing the order of the group)
|
209
|
+
- ``order`` -- whether to return the order of the automorphism group
|
210
|
+
|
211
|
+
OUTPUT:
|
212
|
+
|
213
|
+
Depends on the options. If more than one thing is returned, they are in a
|
214
|
+
tuple in the following order:
|
215
|
+
|
216
|
+
- list of generators in list-permutation format -- always
|
217
|
+
- dict -- if dict_rep
|
218
|
+
- graph -- if lab
|
219
|
+
- dict -- if certificate
|
220
|
+
- list -- if base
|
221
|
+
- integer -- if order
|
222
|
+
|
223
|
+
EXAMPLES::
|
224
|
+
|
225
|
+
sage: st = sage.groups.perm_gps.partn_ref.refinement_graphs.search_tree
|
226
|
+
sage: from sage.graphs.base.dense_graph import DenseGraph
|
227
|
+
sage: from sage.graphs.base.sparse_graph import SparseGraph
|
228
|
+
|
229
|
+
Graphs on zero vertices::
|
230
|
+
|
231
|
+
sage: G = Graph()
|
232
|
+
sage: st(G, [[]], order=True)
|
233
|
+
([], Graph on 0 vertices, 1)
|
234
|
+
|
235
|
+
Graphs on one vertex::
|
236
|
+
|
237
|
+
sage: G = Graph(1)
|
238
|
+
sage: st(G, [[0]], order=True)
|
239
|
+
([], Graph on 1 vertex, 1)
|
240
|
+
|
241
|
+
Graphs on two vertices::
|
242
|
+
|
243
|
+
sage: G = Graph(2)
|
244
|
+
sage: st(G, [[0,1]], order=True)
|
245
|
+
([[1, 0]], Graph on 2 vertices, 2)
|
246
|
+
sage: st(G, [[0],[1]], order=True)
|
247
|
+
([], Graph on 2 vertices, 1)
|
248
|
+
sage: G.add_edge(0,1)
|
249
|
+
sage: st(G, [[0,1]], order=True)
|
250
|
+
([[1, 0]], Graph on 2 vertices, 2)
|
251
|
+
sage: st(G, [[0],[1]], order=True)
|
252
|
+
([], Graph on 2 vertices, 1)
|
253
|
+
|
254
|
+
Graphs on three vertices::
|
255
|
+
|
256
|
+
sage: G = Graph(3)
|
257
|
+
sage: st(G, [[0,1,2]], order=True)
|
258
|
+
([[0, 2, 1], [1, 0, 2]], Graph on 3 vertices, 6)
|
259
|
+
sage: st(G, [[0],[1,2]], order=True)
|
260
|
+
([[0, 2, 1]], Graph on 3 vertices, 2)
|
261
|
+
sage: st(G, [[0],[1],[2]], order=True)
|
262
|
+
([], Graph on 3 vertices, 1)
|
263
|
+
sage: G.add_edge(0,1)
|
264
|
+
sage: st(G, [range(3)], order=True)
|
265
|
+
([[1, 0, 2]], Graph on 3 vertices, 2)
|
266
|
+
sage: st(G, [[0],[1,2]], order=True)
|
267
|
+
([], Graph on 3 vertices, 1)
|
268
|
+
sage: st(G, [[0,1],[2]], order=True)
|
269
|
+
([[1, 0, 2]], Graph on 3 vertices, 2)
|
270
|
+
|
271
|
+
The Dodecahedron has automorphism group of size 120::
|
272
|
+
|
273
|
+
sage: G = graphs.DodecahedralGraph()
|
274
|
+
sage: Pi = [range(20)]
|
275
|
+
sage: st(G, Pi, order=True)[2]
|
276
|
+
120
|
277
|
+
|
278
|
+
The three-cube has automorphism group of size 48::
|
279
|
+
|
280
|
+
sage: G = graphs.CubeGraph(3)
|
281
|
+
sage: G.relabel()
|
282
|
+
sage: Pi = [G.vertices(sort=False)]
|
283
|
+
sage: st(G, Pi, order=True)[2]
|
284
|
+
48
|
285
|
+
|
286
|
+
We obtain the same output using different types of Sage graphs::
|
287
|
+
|
288
|
+
sage: G = graphs.DodecahedralGraph()
|
289
|
+
sage: GD = DenseGraph(20)
|
290
|
+
sage: GS = SparseGraph(20)
|
291
|
+
sage: for i,j,_ in G.edge_iterator():
|
292
|
+
....: GD.add_arc(i,j); GD.add_arc(j,i)
|
293
|
+
....: GS.add_arc(i,j); GS.add_arc(j,i)
|
294
|
+
sage: Pi = [range(20)]
|
295
|
+
sage: a,b = st(G, Pi)
|
296
|
+
sage: asp,bsp = st(GS, Pi)
|
297
|
+
sage: ade,bde = st(GD, Pi)
|
298
|
+
sage: bsg = Graph()
|
299
|
+
sage: bdg = Graph()
|
300
|
+
sage: for i in range(20):
|
301
|
+
....: for j in range(20):
|
302
|
+
....: if bsp.has_arc(i,j):
|
303
|
+
....: bsg.add_edge(i,j)
|
304
|
+
....: if bde.has_arc(i,j):
|
305
|
+
....: bdg.add_edge(i,j)
|
306
|
+
sage: a, b.graph6_string()
|
307
|
+
([[0, 19, 3, 2, 6, 5, 4, 17, 18, 11, 10, 9, 13, 12, 16, 15, 14, 7, 8, 1],
|
308
|
+
[0, 1, 8, 9, 13, 14, 7, 6, 2, 3, 19, 18, 17, 4, 5, 15, 16, 12, 11, 10],
|
309
|
+
[1, 8, 9, 10, 11, 12, 13, 14, 7, 6, 2, 3, 4, 5, 15, 16, 17, 18, 19, 0]],
|
310
|
+
'S?[PG__OQ@?_?_?P?CO?_?AE?EC?Ac?@O')
|
311
|
+
sage: a == asp
|
312
|
+
True
|
313
|
+
sage: a == ade
|
314
|
+
True
|
315
|
+
sage: b == bsg
|
316
|
+
True
|
317
|
+
sage: b == bdg
|
318
|
+
True
|
319
|
+
|
320
|
+
Cubes!::
|
321
|
+
|
322
|
+
sage: C = graphs.CubeGraph(1)
|
323
|
+
sage: gens, order = st(C, [C.vertices(sort=False)], lab=False, order=True); order
|
324
|
+
2
|
325
|
+
sage: C = graphs.CubeGraph(2)
|
326
|
+
sage: gens, order = st(C, [C.vertices(sort=False)], lab=False, order=True); order
|
327
|
+
8
|
328
|
+
sage: C = graphs.CubeGraph(3)
|
329
|
+
sage: gens, order = st(C, [C.vertices(sort=False)], lab=False, order=True); order
|
330
|
+
48
|
331
|
+
sage: C = graphs.CubeGraph(4)
|
332
|
+
sage: gens, order = st(C, [C.vertices(sort=False)], lab=False, order=True); order
|
333
|
+
384
|
334
|
+
sage: C = graphs.CubeGraph(5)
|
335
|
+
sage: gens, order = st(C, [C.vertices(sort=False)], lab=False, order=True); order
|
336
|
+
3840
|
337
|
+
sage: C = graphs.CubeGraph(6)
|
338
|
+
sage: gens, order = st(C, [C.vertices(sort=False)], lab=False, order=True); order
|
339
|
+
46080
|
340
|
+
|
341
|
+
One can also turn off the indicator function (note: this will take longer)::
|
342
|
+
|
343
|
+
sage: D1 = DiGraph({0:[2],2:[0],1:[1]}, loops=True)
|
344
|
+
sage: D2 = DiGraph({1:[2],2:[1],0:[0]}, loops=True)
|
345
|
+
sage: a,b = st(D1, [D1.vertices(sort=False)], dig=True, use_indicator_function=False)
|
346
|
+
sage: c,d = st(D2, [D2.vertices(sort=False)], dig=True, use_indicator_function=False)
|
347
|
+
sage: b==d
|
348
|
+
True
|
349
|
+
|
350
|
+
This example is due to Chris Godsil::
|
351
|
+
|
352
|
+
sage: # needs cliquer
|
353
|
+
sage: HS = graphs.HoffmanSingletonGraph()
|
354
|
+
sage: alqs = [Set(c) for c in (HS.complement()).cliques_maximum()]
|
355
|
+
sage: Y = Graph([alqs, lambda s,t: len(s.intersection(t))==0])
|
356
|
+
sage: Y0,Y1 = Y.connected_components_subgraphs()
|
357
|
+
sage: st(Y0, [Y0.vertices(sort=False)])[1] == st(Y1, [Y1.vertices(sort=False)])[1]
|
358
|
+
True
|
359
|
+
sage: st(Y0, [Y0.vertices(sort=False)])[1] == st(HS, [HS.vertices(sort=False)])[1]
|
360
|
+
True
|
361
|
+
sage: st(HS, [HS.vertices(sort=False)])[1] == st(Y1, [Y1.vertices(sort=False)])[1]
|
362
|
+
True
|
363
|
+
|
364
|
+
Certain border cases need to be tested as well::
|
365
|
+
|
366
|
+
sage: G = Graph('Fll^G')
|
367
|
+
sage: a,b,c = st(G, [range(G.num_verts())], order=True); b
|
368
|
+
Graph on 7 vertices
|
369
|
+
sage: c
|
370
|
+
48
|
371
|
+
sage: G = Graph(21)
|
372
|
+
sage: st(G, [range(G.num_verts())], order=True)[2] == factorial(21)
|
373
|
+
True
|
374
|
+
|
375
|
+
sage: G = Graph('^????????????????????{??N??@w??FaGa?PCO@CP?AGa?_QO?Q@G?CcA??cc????Bo????{????F_')
|
376
|
+
sage: perm = {3:15, 15:3}
|
377
|
+
sage: H = G.relabel(perm, inplace=False)
|
378
|
+
sage: st(G, [range(G.num_verts())])[1] == st(H, [range(H.num_verts())])[1]
|
379
|
+
True
|
380
|
+
|
381
|
+
sage: st(Graph(':Dkw'), [range(5)], lab=False, dig=True)
|
382
|
+
[[4, 1, 2, 3, 0], [0, 2, 1, 3, 4]]
|
383
|
+
"""
|
384
|
+
cdef CGraph G
|
385
|
+
cdef int i, j, n
|
386
|
+
cdef Integer I
|
387
|
+
cdef bint loops
|
388
|
+
cdef aut_gp_and_can_lab *output
|
389
|
+
cdef PartitionStack *part
|
390
|
+
from sage.graphs.graph import Graph
|
391
|
+
from sage.graphs.generic_graph import GenericGraph
|
392
|
+
from copy import copy
|
393
|
+
if isinstance(G_in, GenericGraph):
|
394
|
+
loops = G_in.has_loops()
|
395
|
+
n = G_in.num_verts()
|
396
|
+
if G_in.vertices(sort=False) != list(range(n)):
|
397
|
+
G_in = copy(G_in)
|
398
|
+
to = G_in.relabel(return_map=True)
|
399
|
+
frm = {}
|
400
|
+
for v in to:
|
401
|
+
frm[to[v]] = v
|
402
|
+
partition = [[to[v] for v in cell] for cell in partition]
|
403
|
+
else:
|
404
|
+
to = dict(enumerate(range(n)))
|
405
|
+
frm = to
|
406
|
+
if sparse:
|
407
|
+
G = SparseGraph(n)
|
408
|
+
else:
|
409
|
+
G = DenseGraph(n)
|
410
|
+
if G_in.is_directed():
|
411
|
+
for i, j in G_in.edge_iterator(labels=False):
|
412
|
+
G.add_arc(i, j)
|
413
|
+
else:
|
414
|
+
for i, j in G_in.edge_iterator(labels=False):
|
415
|
+
G.add_arc(i, j)
|
416
|
+
G.add_arc(j, i)
|
417
|
+
elif isinstance(G_in, CGraph):
|
418
|
+
G = <CGraph> G_in
|
419
|
+
n = G.num_verts
|
420
|
+
loops = 0
|
421
|
+
for i in range(n):
|
422
|
+
if G.has_arc_unsafe(i, i):
|
423
|
+
loops = 1
|
424
|
+
to = {}
|
425
|
+
for a in G.verts():
|
426
|
+
to[a] = a
|
427
|
+
frm = to
|
428
|
+
else:
|
429
|
+
raise TypeError("G must be a Sage graph")
|
430
|
+
|
431
|
+
cdef GraphStruct GS = GraphStruct()
|
432
|
+
GS.G = G
|
433
|
+
GS.directed = 1 if dig else 0
|
434
|
+
GS.loops = loops
|
435
|
+
GS.use_indicator = 1 if use_indicator_function else 0
|
436
|
+
|
437
|
+
if n == 0:
|
438
|
+
return_tuple = [[]]
|
439
|
+
if dict_rep:
|
440
|
+
return_tuple.append({})
|
441
|
+
if lab:
|
442
|
+
if isinstance(G_in, GenericGraph):
|
443
|
+
G_C = copy(G_in)
|
444
|
+
else:
|
445
|
+
if isinstance(G, SparseGraph):
|
446
|
+
G_C = SparseGraph(n)
|
447
|
+
else:
|
448
|
+
G_C = DenseGraph(n)
|
449
|
+
return_tuple.append(G_C)
|
450
|
+
if certificate:
|
451
|
+
return_tuple.append({})
|
452
|
+
if base:
|
453
|
+
return_tuple.append([])
|
454
|
+
if order:
|
455
|
+
return_tuple.append(Integer(1))
|
456
|
+
if len(return_tuple) == 1:
|
457
|
+
return return_tuple[0]
|
458
|
+
else:
|
459
|
+
return tuple(return_tuple)
|
460
|
+
|
461
|
+
GS.scratch = <int *> sig_malloc( (3*G.num_verts + 1) * sizeof(int) )
|
462
|
+
part = PS_from_list(partition)
|
463
|
+
if GS.scratch is NULL or part is NULL:
|
464
|
+
PS_dealloc(part)
|
465
|
+
sig_free(GS.scratch)
|
466
|
+
raise MemoryError
|
467
|
+
|
468
|
+
output = get_aut_gp_and_can_lab(<void *>GS, part, G.num_verts, &all_children_are_equivalent, &refine_by_degree, &compare_graphs, lab, NULL, NULL, NULL)
|
469
|
+
sig_free( GS.scratch )
|
470
|
+
# prepare output
|
471
|
+
list_of_gens = []
|
472
|
+
for i from 0 <= i < output.num_gens:
|
473
|
+
list_of_gens.append([output.generators[j+i*G.num_verts] for j from 0 <= j < <int>G.num_verts])
|
474
|
+
return_tuple = [list_of_gens]
|
475
|
+
if dict_rep:
|
476
|
+
ddd = {}
|
477
|
+
for v in frm:
|
478
|
+
ddd[frm[v]] = v if v != 0 else n
|
479
|
+
return_tuple.append(ddd)
|
480
|
+
if lab:
|
481
|
+
if isinstance(G_in, GenericGraph):
|
482
|
+
G_C = copy(G_in)
|
483
|
+
G_C.relabel([output.relabeling[i] for i from 0 <= i < n])
|
484
|
+
else:
|
485
|
+
if isinstance(G, SparseGraph):
|
486
|
+
G_C = SparseGraph(n)
|
487
|
+
else:
|
488
|
+
G_C = DenseGraph(n)
|
489
|
+
for i from 0 <= i < n:
|
490
|
+
for j in G.out_neighbors(i):
|
491
|
+
G_C.add_arc(output.relabeling[i],output.relabeling[j])
|
492
|
+
return_tuple.append(G_C)
|
493
|
+
if certificate:
|
494
|
+
dd = {}
|
495
|
+
for i from 0 <= i < <int>G.num_verts:
|
496
|
+
dd[frm[i]] = output.relabeling[i]
|
497
|
+
return_tuple.append(dd)
|
498
|
+
if base:
|
499
|
+
return_tuple.append([output.group.base_orbits[i][0] for i from 0 <= i < output.group.base_size])
|
500
|
+
if order:
|
501
|
+
I = Integer()
|
502
|
+
SC_order(output.group, 0, I.value)
|
503
|
+
return_tuple.append(I)
|
504
|
+
PS_dealloc(part)
|
505
|
+
deallocate_agcl_output(output)
|
506
|
+
if len(return_tuple) == 1:
|
507
|
+
return return_tuple[0]
|
508
|
+
else:
|
509
|
+
return tuple(return_tuple)
|
510
|
+
|
511
|
+
|
512
|
+
cdef int refine_by_degree(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len) noexcept:
|
513
|
+
r"""
|
514
|
+
Refine the input partition by checking degrees of vertices to the given
|
515
|
+
cells.
|
516
|
+
|
517
|
+
INPUT:
|
518
|
+
|
519
|
+
- ``PS`` -- a partition stack, whose finest partition is the partition to be
|
520
|
+
refined
|
521
|
+
- ``S`` -- a graph struct object, which contains scratch space, the graph in
|
522
|
+
question, and some flags
|
523
|
+
- ``cells_to_refine_by`` -- list of pointers to cells to check degrees against
|
524
|
+
in refining the other cells (updated in place). Must be allocated to
|
525
|
+
length at least the degree of PS, since the array may grow
|
526
|
+
- ``ctrb_len`` -- how many cells in cells_to_refine_by
|
527
|
+
|
528
|
+
OUTPUT:
|
529
|
+
|
530
|
+
An integer invariant under the orbits of $S_n$. That is, if $\gamma$ is a
|
531
|
+
permutation of the vertices, then
|
532
|
+
$$ I(G, PS, cells_to_refine_by) = I( \gamma(G), \gamma(PS), \gamma(cells_to_refine_by) ) .$$
|
533
|
+
"""
|
534
|
+
cdef GraphStruct GS = <GraphStruct> S
|
535
|
+
cdef CGraph G = GS.G
|
536
|
+
cdef int current_cell_against = 0
|
537
|
+
cdef int current_cell, i, r
|
538
|
+
cdef int first_largest_subcell
|
539
|
+
cdef int invariant = 1
|
540
|
+
cdef int max_degree
|
541
|
+
cdef int *degrees = GS.scratch # length 3n+1
|
542
|
+
cdef bint necessary_to_split_cell
|
543
|
+
cdef int against_index
|
544
|
+
if <int>G.num_verts != PS.degree and PS.depth == 0:
|
545
|
+
# should be less verts, then, so place the "nonverts" in separate cell at the end
|
546
|
+
current_cell = 0
|
547
|
+
while current_cell < PS.degree:
|
548
|
+
i = current_cell
|
549
|
+
r = 0
|
550
|
+
while True:
|
551
|
+
if G.has_vertex(PS.entries[i]):
|
552
|
+
degrees[i-current_cell] = 0
|
553
|
+
else:
|
554
|
+
r = 1
|
555
|
+
degrees[i-current_cell] = 1
|
556
|
+
i += 1
|
557
|
+
if PS.levels[i-1] <= PS.depth:
|
558
|
+
break
|
559
|
+
if r != 0:
|
560
|
+
sort_by_function(PS, current_cell, degrees)
|
561
|
+
current_cell = i
|
562
|
+
while not PS_is_discrete(PS) and current_cell_against < ctrb_len:
|
563
|
+
invariant += 1
|
564
|
+
current_cell = 0
|
565
|
+
while current_cell < PS.degree:
|
566
|
+
invariant += 50
|
567
|
+
i = current_cell
|
568
|
+
necessary_to_split_cell = 0
|
569
|
+
max_degree = 0
|
570
|
+
while True:
|
571
|
+
degrees[i-current_cell] = degree(PS, G, i, cells_to_refine_by[current_cell_against], 0)
|
572
|
+
if degrees[i-current_cell] != degrees[0]:
|
573
|
+
necessary_to_split_cell = 1
|
574
|
+
if degrees[i-current_cell] > max_degree:
|
575
|
+
max_degree = degrees[i-current_cell]
|
576
|
+
i += 1
|
577
|
+
if PS.levels[i-1] <= PS.depth:
|
578
|
+
break
|
579
|
+
# now, i points to the next cell (before refinement)
|
580
|
+
if necessary_to_split_cell:
|
581
|
+
invariant += 10
|
582
|
+
first_largest_subcell = sort_by_function(PS, current_cell, degrees)
|
583
|
+
invariant += first_largest_subcell + max_degree
|
584
|
+
against_index = current_cell_against
|
585
|
+
while against_index < ctrb_len:
|
586
|
+
if cells_to_refine_by[against_index] == current_cell:
|
587
|
+
cells_to_refine_by[against_index] = first_largest_subcell
|
588
|
+
break
|
589
|
+
against_index += 1
|
590
|
+
r = current_cell
|
591
|
+
while True:
|
592
|
+
if r == current_cell or PS.levels[r-1] == PS.depth:
|
593
|
+
if r != first_largest_subcell:
|
594
|
+
cells_to_refine_by[ctrb_len] = r
|
595
|
+
ctrb_len += 1
|
596
|
+
r += 1
|
597
|
+
if r >= i:
|
598
|
+
break
|
599
|
+
invariant += (i - current_cell)
|
600
|
+
current_cell = i
|
601
|
+
if GS.directed:
|
602
|
+
# if we are looking at a digraph, also compute
|
603
|
+
# the reverse degrees and sort by them
|
604
|
+
current_cell = 0
|
605
|
+
while current_cell < PS.degree: # current_cell is still a valid cell
|
606
|
+
invariant += 20
|
607
|
+
i = current_cell
|
608
|
+
necessary_to_split_cell = 0
|
609
|
+
max_degree = 0
|
610
|
+
while True:
|
611
|
+
degrees[i-current_cell] = degree(PS, G, i, cells_to_refine_by[current_cell_against], 1)
|
612
|
+
if degrees[i-current_cell] != degrees[0]:
|
613
|
+
necessary_to_split_cell = 1
|
614
|
+
if degrees[i-current_cell] > max_degree:
|
615
|
+
max_degree = degrees[i-current_cell]
|
616
|
+
i += 1
|
617
|
+
if PS.levels[i-1] <= PS.depth:
|
618
|
+
break
|
619
|
+
# now, i points to the next cell (before refinement)
|
620
|
+
if necessary_to_split_cell:
|
621
|
+
invariant += 7
|
622
|
+
first_largest_subcell = sort_by_function(PS, current_cell, degrees)
|
623
|
+
invariant += first_largest_subcell + max_degree
|
624
|
+
against_index = current_cell_against
|
625
|
+
while against_index < ctrb_len:
|
626
|
+
if cells_to_refine_by[against_index] == current_cell:
|
627
|
+
cells_to_refine_by[against_index] = first_largest_subcell
|
628
|
+
break
|
629
|
+
against_index += 1
|
630
|
+
against_index = ctrb_len
|
631
|
+
r = current_cell
|
632
|
+
while True:
|
633
|
+
if r == current_cell or PS.levels[r-1] == PS.depth:
|
634
|
+
if r != first_largest_subcell:
|
635
|
+
cells_to_refine_by[against_index] = r
|
636
|
+
against_index += 1
|
637
|
+
ctrb_len += 1
|
638
|
+
r += 1
|
639
|
+
if r >= i:
|
640
|
+
break
|
641
|
+
invariant += (i - current_cell)
|
642
|
+
current_cell = i
|
643
|
+
current_cell_against += 1
|
644
|
+
if GS.use_indicator:
|
645
|
+
return invariant
|
646
|
+
else:
|
647
|
+
return 0
|
648
|
+
|
649
|
+
cdef int compare_graphs(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree) noexcept:
|
650
|
+
r"""
|
651
|
+
Compare gamma_1(S1) and gamma_2(S2).
|
652
|
+
|
653
|
+
Return -1 if gamma_1(S1) < gamma_2(S2), 0 if gamma_1(S1) ==
|
654
|
+
gamma_2(S2), 1 if gamma_1(S1) > gamma_2(S2).
|
655
|
+
|
656
|
+
INPUT:
|
657
|
+
|
658
|
+
- ``gamma_1``, ``gamma_2`` -- list permutations (inverse)
|
659
|
+
- ``S1``, ``S2`` -- graph struct objects
|
660
|
+
"""
|
661
|
+
cdef size_t i, j
|
662
|
+
cdef GraphStruct GS1 = <GraphStruct> S1
|
663
|
+
cdef GraphStruct GS2 = <GraphStruct> S2
|
664
|
+
cdef CGraph G1 = GS1.G
|
665
|
+
cdef CGraph G2 = GS2.G
|
666
|
+
if G1.active_vertices.size != G2.active_vertices.size or \
|
667
|
+
not bitset_cmp(G1.active_vertices, G2.active_vertices):
|
668
|
+
for i from 0 <= i < <size_t>degree:
|
669
|
+
if G1.has_vertex(gamma_1[i]) != G2.has_vertex(gamma_2[i]):
|
670
|
+
return G1.has_vertex(gamma_1[i]) - G2.has_vertex(gamma_2[i])
|
671
|
+
for i from 0 <= i < G1.num_verts:
|
672
|
+
for j from 0 <= j < G1.num_verts:
|
673
|
+
if G1.has_arc_unsafe(gamma_1[i], gamma_1[j]):
|
674
|
+
if not G2.has_arc_unsafe(gamma_2[i], gamma_2[j]):
|
675
|
+
return 1
|
676
|
+
elif G2.has_arc_unsafe(gamma_2[i], gamma_2[j]):
|
677
|
+
return -1
|
678
|
+
return 0
|
679
|
+
|
680
|
+
cdef bint all_children_are_equivalent(PartitionStack *PS, void *S) noexcept:
|
681
|
+
"""
|
682
|
+
Return ``True`` if every refinement of the current partition results in the
|
683
|
+
same structure.
|
684
|
+
|
685
|
+
.. WARNING::
|
686
|
+
|
687
|
+
Converse does not hold in general! See Lemma 2.25 of [1] for details.
|
688
|
+
|
689
|
+
INPUT:
|
690
|
+
|
691
|
+
- ``PS`` -- the partition stack to be checked
|
692
|
+
- ``S`` -- a graph struct object
|
693
|
+
"""
|
694
|
+
cdef GraphStruct GS = <GraphStruct> S
|
695
|
+
if GS.directed or GS.loops:
|
696
|
+
return 0
|
697
|
+
cdef int i, n = PS.degree
|
698
|
+
cdef bint in_cell = 0
|
699
|
+
cdef int nontrivial_cells = 0
|
700
|
+
cdef int total_cells = PS_num_cells(PS)
|
701
|
+
if n <= total_cells + 4:
|
702
|
+
return 1
|
703
|
+
for i from 0 <= i < n-1:
|
704
|
+
if PS.levels[i] <= PS.depth:
|
705
|
+
if in_cell:
|
706
|
+
nontrivial_cells += 1
|
707
|
+
in_cell = 0
|
708
|
+
else:
|
709
|
+
in_cell = 1
|
710
|
+
if in_cell:
|
711
|
+
nontrivial_cells += 1
|
712
|
+
if n == total_cells + nontrivial_cells:
|
713
|
+
return 1
|
714
|
+
if n == total_cells + nontrivial_cells + 1:
|
715
|
+
return 1
|
716
|
+
return 0
|
717
|
+
|
718
|
+
cdef inline int degree(PartitionStack *PS, CGraph G, int entry, int cell_index, bint reverse) noexcept:
|
719
|
+
"""
|
720
|
+
Return the number of edges from the vertex corresponding to entry to
|
721
|
+
vertices in the cell corresponding to cell_index.
|
722
|
+
|
723
|
+
INPUT:
|
724
|
+
|
725
|
+
- ``PS`` -- the partition stack to be checked
|
726
|
+
- ``S`` -- a graph struct object
|
727
|
+
- ``entry`` -- the position of the vertex in question in the entries of PS
|
728
|
+
- ``cell_index`` -- the starting position of the cell in question in the entries
|
729
|
+
of PS
|
730
|
+
- ``reverse`` -- whether to check for arcs in the other direction
|
731
|
+
"""
|
732
|
+
cdef int num_arcs = 0
|
733
|
+
entry = PS.entries[entry]
|
734
|
+
if not reverse:
|
735
|
+
while True:
|
736
|
+
if G.has_arc_unsafe(PS.entries[cell_index], entry):
|
737
|
+
num_arcs += 1
|
738
|
+
if PS.levels[cell_index] > PS.depth:
|
739
|
+
cell_index += 1
|
740
|
+
else:
|
741
|
+
break
|
742
|
+
else:
|
743
|
+
while True:
|
744
|
+
if G.has_arc_unsafe(entry, PS.entries[cell_index]):
|
745
|
+
num_arcs += 1
|
746
|
+
if PS.levels[cell_index] > PS.depth:
|
747
|
+
cell_index += 1
|
748
|
+
else:
|
749
|
+
break
|
750
|
+
return num_arcs
|
751
|
+
|
752
|
+
|
753
|
+
def all_labeled_graphs(n):
|
754
|
+
"""
|
755
|
+
Return all labeled graphs on n vertices {0,1,...,n-1}.
|
756
|
+
|
757
|
+
Used in classifying isomorphism types (naive approach), and more
|
758
|
+
importantly in benchmarking the search algorithm.
|
759
|
+
|
760
|
+
EXAMPLES::
|
761
|
+
|
762
|
+
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import all_labeled_graphs
|
763
|
+
sage: st = sage.groups.perm_gps.partn_ref.refinement_graphs.search_tree
|
764
|
+
sage: Glist = {}
|
765
|
+
sage: Giso = {}
|
766
|
+
sage: for n in [1..5]: # long time (4s on sage.math, 2011)
|
767
|
+
....: Glist[n] = all_labeled_graphs(n)
|
768
|
+
....: Giso[n] = []
|
769
|
+
....: for g in Glist[n]:
|
770
|
+
....: a, b = st(g, [range(n)])
|
771
|
+
....: inn = False
|
772
|
+
....: for gi in Giso[n]:
|
773
|
+
....: if b == gi:
|
774
|
+
....: inn = True
|
775
|
+
....: if not inn:
|
776
|
+
....: Giso[n].append(b)
|
777
|
+
sage: for n in Giso: # long time
|
778
|
+
....: print("{} {}".format(n, len(Giso[n])))
|
779
|
+
1 1
|
780
|
+
2 2
|
781
|
+
3 4
|
782
|
+
4 11
|
783
|
+
5 34
|
784
|
+
"""
|
785
|
+
from sage.graphs.graph import Graph
|
786
|
+
TE = []
|
787
|
+
for i in range(n):
|
788
|
+
for j in range(i):
|
789
|
+
TE.append((i, j))
|
790
|
+
m = len(TE)
|
791
|
+
Glist= []
|
792
|
+
for i in range(2**m):
|
793
|
+
G = Graph(n)
|
794
|
+
b = Integer(i).binary()
|
795
|
+
b = '0'*(m-len(b)) + b
|
796
|
+
for i in range(m):
|
797
|
+
if int(b[i]):
|
798
|
+
G.add_edge(TE[i])
|
799
|
+
Glist.append(G)
|
800
|
+
return Glist
|
801
|
+
|
802
|
+
|
803
|
+
def random_tests(num=10, n_max=60, perms_per_graph=5):
|
804
|
+
"""
|
805
|
+
Test to make sure that C(gamma(G)) == C(G) for random permutations gamma
|
806
|
+
and random graphs G, and that isomorphic returns an isomorphism.
|
807
|
+
|
808
|
+
INPUT:
|
809
|
+
|
810
|
+
- ``num`` -- run tests for this many graphs
|
811
|
+
- ``n_max`` -- test graphs with at most this many vertices
|
812
|
+
- ``perms_per_graph`` -- test each graph with this many random permutations
|
813
|
+
|
814
|
+
DISCUSSION:
|
815
|
+
|
816
|
+
This code generates num random graphs G on at most n_max vertices. The
|
817
|
+
density of edges is chosen randomly between 0 and 1.
|
818
|
+
|
819
|
+
For each graph G generated, we uniformly generate perms_per_graph random
|
820
|
+
permutations and verify that the canonical labels of G and the image of G
|
821
|
+
under the generated permutation are equal, and that the isomorphic function
|
822
|
+
returns an isomorphism.
|
823
|
+
|
824
|
+
TESTS::
|
825
|
+
|
826
|
+
sage: import sage.groups.perm_gps.partn_ref.refinement_graphs
|
827
|
+
sage: sage.groups.perm_gps.partn_ref.refinement_graphs.random_tests() # long time
|
828
|
+
All passed: 200 random tests on 20 graphs.
|
829
|
+
"""
|
830
|
+
from sage.misc.prandom import random, randint
|
831
|
+
from sage.graphs.graph_generators import GraphGenerators
|
832
|
+
from sage.graphs.digraph_generators import DiGraphGenerators
|
833
|
+
from sage.combinat.permutation import Permutations
|
834
|
+
from copy import copy
|
835
|
+
cdef int i, j, num_tests = 0, num_graphs = 0
|
836
|
+
GG = GraphGenerators()
|
837
|
+
DGG = DiGraphGenerators()
|
838
|
+
for mmm in range(num):
|
839
|
+
p = random()
|
840
|
+
n = randint(1, n_max)
|
841
|
+
S = Permutations(n)
|
842
|
+
|
843
|
+
G = GG.RandomGNP(n, p)
|
844
|
+
H = copy(G)
|
845
|
+
for i from 0 <= i < perms_per_graph:
|
846
|
+
G = copy(H)
|
847
|
+
G1 = search_tree(G, [G.vertices(sort=False)])[1]
|
848
|
+
perm = list(S.random_element())
|
849
|
+
perm = [perm[j]-1 for j from 0 <= j < n]
|
850
|
+
G.relabel(perm)
|
851
|
+
G2 = search_tree(G, [G.vertices(sort=False)])[1]
|
852
|
+
if G1 != G2:
|
853
|
+
print("search_tree FAILURE: graph6-")
|
854
|
+
print(H.graph6_string())
|
855
|
+
print(perm)
|
856
|
+
return
|
857
|
+
isom = isomorphic(G, H, [list(range(n))], list(range(n)), 0, 1)
|
858
|
+
if not isom or G.relabel(isom, inplace=False) != H:
|
859
|
+
print("isom FAILURE: graph6-")
|
860
|
+
print(H.graph6_string())
|
861
|
+
print(perm)
|
862
|
+
return
|
863
|
+
|
864
|
+
D = DGG.RandomDirectedGNP(n, p)
|
865
|
+
D.allow_loops(True)
|
866
|
+
for i from 0 <= i < n:
|
867
|
+
if random() < p:
|
868
|
+
D.add_edge(i,i)
|
869
|
+
E = copy(D)
|
870
|
+
for i from 0 <= i < perms_per_graph:
|
871
|
+
D = copy(E)
|
872
|
+
D1 = search_tree(D, [D.vertices(sort=False)], dig=True)[1]
|
873
|
+
perm = list(S.random_element())
|
874
|
+
perm = [perm[j]-1 for j from 0 <= j < n]
|
875
|
+
D.relabel(perm)
|
876
|
+
D2 = search_tree(D, [D.vertices(sort=False)], dig=True)[1]
|
877
|
+
if D1 != D2:
|
878
|
+
print("search_tree FAILURE: dig6-")
|
879
|
+
print(E.dig6_string())
|
880
|
+
print(perm)
|
881
|
+
return
|
882
|
+
isom = isomorphic(D, E, [list(range(n))], list(range(n)), 1, 1)
|
883
|
+
if not isom or D.relabel(isom, inplace=False) != E:
|
884
|
+
print("isom FAILURE: dig6-")
|
885
|
+
print(E.dig6_string())
|
886
|
+
print(perm)
|
887
|
+
print(isom)
|
888
|
+
return
|
889
|
+
num_tests += 4*perms_per_graph
|
890
|
+
num_graphs += 2
|
891
|
+
print("All passed: %d random tests on %d graphs." % (num_tests, num_graphs))
|
892
|
+
|
893
|
+
|
894
|
+
def orbit_partition(gamma, list_perm=False):
|
895
|
+
r"""
|
896
|
+
Assuming that G is a graph on vertices 0,1,...,n-1, and gamma is an
|
897
|
+
element of SymmetricGroup(n), returns the partition of the vertex
|
898
|
+
set determined by the orbits of gamma, considered as action on the
|
899
|
+
set 1,2,...,n where we take 0 = n. In other words, returns the
|
900
|
+
partition determined by a cyclic representation of gamma.
|
901
|
+
|
902
|
+
INPUT:
|
903
|
+
|
904
|
+
- ``list_perm`` -- if ``True``, assumes
|
905
|
+
``gamma`` is a list representing the map
|
906
|
+
`i \mapsto ``gamma``[i]`
|
907
|
+
|
908
|
+
EXAMPLES::
|
909
|
+
|
910
|
+
sage: # needs sage.groups
|
911
|
+
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import orbit_partition
|
912
|
+
sage: G = graphs.PetersenGraph()
|
913
|
+
sage: S = SymmetricGroup(10)
|
914
|
+
sage: gamma = S('(10,1,2,3,4)(5,6,7)(8,9)')
|
915
|
+
sage: orbit_partition(gamma)
|
916
|
+
[[1, 2, 3, 4, 0], [5, 6, 7], [8, 9]]
|
917
|
+
sage: gamma = S('(10,5)(1,6)(2,7)(3,8)(4,9)')
|
918
|
+
sage: orbit_partition(gamma)
|
919
|
+
[[1, 6], [2, 7], [3, 8], [4, 9], [5, 0]]
|
920
|
+
"""
|
921
|
+
if list_perm:
|
922
|
+
n = len(gamma)
|
923
|
+
seen = [1] + [0]*(n-1)
|
924
|
+
i = 0
|
925
|
+
p = 0
|
926
|
+
partition = [[0]]
|
927
|
+
while sum(seen) < n:
|
928
|
+
if gamma[i] != partition[p][0]:
|
929
|
+
partition[p].append(gamma[i])
|
930
|
+
i = gamma[i]
|
931
|
+
seen[i] = 1
|
932
|
+
else:
|
933
|
+
for j in range(n):
|
934
|
+
if seen[j] == 0:
|
935
|
+
i = j
|
936
|
+
break
|
937
|
+
partition.append([i])
|
938
|
+
p += 1
|
939
|
+
seen[i] = 1
|
940
|
+
return partition
|
941
|
+
else:
|
942
|
+
n = len(gamma.domain())
|
943
|
+
l = []
|
944
|
+
for i in range(1,n+1):
|
945
|
+
orb = gamma.orbit(i)
|
946
|
+
if orb not in l:
|
947
|
+
l.append(orb)
|
948
|
+
for i in l:
|
949
|
+
for j in range(len(i)):
|
950
|
+
if i[j] == n:
|
951
|
+
i[j] = 0
|
952
|
+
return l
|
953
|
+
|
954
|
+
|
955
|
+
def coarsest_equitable_refinement(CGraph G, list partition, bint directed):
|
956
|
+
"""
|
957
|
+
Return the coarsest equitable refinement of ``partition`` for ``G``.
|
958
|
+
|
959
|
+
This is a helper function for the graph function of the same name.
|
960
|
+
|
961
|
+
DOCTEST (More thorough testing in ``sage/graphs/graph.py``)::
|
962
|
+
|
963
|
+
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import coarsest_equitable_refinement
|
964
|
+
sage: from sage.graphs.base.sparse_graph import SparseGraph
|
965
|
+
sage: coarsest_equitable_refinement(SparseGraph(7), [[0], [1,2,3,4], [5,6]], 0)
|
966
|
+
[[0], [1, 2, 3, 4], [5, 6]]
|
967
|
+
"""
|
968
|
+
cdef int i, j = 0, k = 0, n = G.num_verts
|
969
|
+
|
970
|
+
# set up partition stack and graph struct
|
971
|
+
cdef PartitionStack *nu = PS_new(n, 0)
|
972
|
+
for cell in partition:
|
973
|
+
for i in cell:
|
974
|
+
nu.entries[j] = i
|
975
|
+
nu.levels[j] = n
|
976
|
+
j += 1
|
977
|
+
nu.levels[j-1] = 0
|
978
|
+
PS_move_min_to_front(nu, k, j-1)
|
979
|
+
k = j
|
980
|
+
|
981
|
+
cdef GraphStruct GS = GraphStruct()
|
982
|
+
GS.G = G
|
983
|
+
GS.scratch = <int *> sig_malloc((3*n+1) * sizeof(int))
|
984
|
+
if GS.scratch is NULL:
|
985
|
+
PS_dealloc(nu)
|
986
|
+
raise MemoryError
|
987
|
+
GS.directed = directed
|
988
|
+
GS.use_indicator = 0
|
989
|
+
|
990
|
+
# set up cells to refine by
|
991
|
+
cdef int num_cells = len(partition)
|
992
|
+
cdef int *alpha = <int *>sig_malloc(n * sizeof(int))
|
993
|
+
if alpha is NULL:
|
994
|
+
PS_dealloc(nu)
|
995
|
+
sig_free(GS.scratch)
|
996
|
+
raise MemoryError
|
997
|
+
j = 0
|
998
|
+
for i from 0 <= i < num_cells:
|
999
|
+
alpha[i] = j
|
1000
|
+
j += len(partition[i])
|
1001
|
+
|
1002
|
+
# refine, and get the result
|
1003
|
+
refine_by_degree(nu, <void *>GS, alpha, num_cells)
|
1004
|
+
|
1005
|
+
eq_part = []
|
1006
|
+
cell = []
|
1007
|
+
for i from 0 <= i < n:
|
1008
|
+
cell.append(nu.entries[i])
|
1009
|
+
if nu.levels[i] <= 0:
|
1010
|
+
eq_part.append(cell)
|
1011
|
+
cell = []
|
1012
|
+
|
1013
|
+
PS_dealloc(nu)
|
1014
|
+
sig_free(GS.scratch)
|
1015
|
+
sig_free(alpha)
|
1016
|
+
|
1017
|
+
return eq_part
|
1018
|
+
|
1019
|
+
|
1020
|
+
def get_orbits(list gens, int n):
|
1021
|
+
"""
|
1022
|
+
Compute orbits given a list of generators of a permutation group, in list
|
1023
|
+
format.
|
1024
|
+
|
1025
|
+
This is a helper function for automorphism groups of graphs.
|
1026
|
+
|
1027
|
+
DOCTEST (More thorough testing in ``sage/graphs/graph.py``)::
|
1028
|
+
|
1029
|
+
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import get_orbits
|
1030
|
+
sage: get_orbits([[1,2,3,0,4,5], [0,1,2,3,5,4]], 6)
|
1031
|
+
[[0, 1, 2, 3], [4, 5]]
|
1032
|
+
"""
|
1033
|
+
cdef int i, j
|
1034
|
+
if len(gens) == 0:
|
1035
|
+
return [[i] for i from 0 <= i < n]
|
1036
|
+
|
1037
|
+
cdef OrbitPartition *OP = OP_new(n)
|
1038
|
+
cdef int *perm_ints = <int *> sig_malloc(n * sizeof(int))
|
1039
|
+
if perm_ints is NULL:
|
1040
|
+
OP_dealloc(OP)
|
1041
|
+
raise MemoryError
|
1042
|
+
|
1043
|
+
for gen in gens:
|
1044
|
+
for i from 0 <= i < n:
|
1045
|
+
perm_ints[i] = gen[i]
|
1046
|
+
OP_merge_list_perm(OP, perm_ints)
|
1047
|
+
|
1048
|
+
orbit_dict = {}
|
1049
|
+
for i from 0 <= i < n:
|
1050
|
+
j = OP_find(OP, i)
|
1051
|
+
if j in orbit_dict:
|
1052
|
+
orbit_dict[j].append(i)
|
1053
|
+
else:
|
1054
|
+
orbit_dict[j] = [i]
|
1055
|
+
|
1056
|
+
OP_dealloc(OP)
|
1057
|
+
sig_free(perm_ints)
|
1058
|
+
|
1059
|
+
return list(orbit_dict.itervalues())
|
1060
|
+
|
1061
|
+
|
1062
|
+
# Canonical augmentation
|
1063
|
+
from cpython.ref cimport *
|
1064
|
+
|
1065
|
+
# Dense graphs: adding edges
|
1066
|
+
|
1067
|
+
# This implements an augmentation scheme as follows:
|
1068
|
+
# * Seed objects are graphs with n vertices and no edges.
|
1069
|
+
# * Augmentations consist of adding a single edge, or a loop.
|
1070
|
+
|
1071
|
+
cdef void *dg_edge_gen_next(void *data, int *degree, bint *mem_err) noexcept:
|
1072
|
+
r"""
|
1073
|
+
The ``next`` function in an edge iterator. The iterator generates unique
|
1074
|
+
representatives under the action of the automorphism group of the parent
|
1075
|
+
graph on edges not in the graph, which are to considered for adding to the
|
1076
|
+
graph.
|
1077
|
+
"""
|
1078
|
+
cdef dg_edge_gen_data *degd = <dg_edge_gen_data *> data
|
1079
|
+
cdef GraphStruct graph = <GraphStruct> degd.graph
|
1080
|
+
cdef subset *edge_candidate
|
1081
|
+
cdef int u, v, reject
|
1082
|
+
cdef bint mem_err_sub = 0
|
1083
|
+
if mem_err[0]:
|
1084
|
+
(<canonical_generator_data *> degd.edge_iterator.data).mem_err = 1
|
1085
|
+
while True:
|
1086
|
+
edge_candidate = <subset *> degd.edge_iterator.next(degd.edge_iterator.data, NULL, &mem_err_sub)
|
1087
|
+
if edge_candidate is NULL:
|
1088
|
+
break
|
1089
|
+
reject = 0
|
1090
|
+
if bitset_len(&edge_candidate.bits) < (1 if graph.loops else 2):
|
1091
|
+
reject = 1
|
1092
|
+
else:
|
1093
|
+
u = bitset_first(&edge_candidate.bits)
|
1094
|
+
v = bitset_next(&edge_candidate.bits, u+1)
|
1095
|
+
if v == -1:
|
1096
|
+
v = u
|
1097
|
+
if graph.G.has_arc_unsafe(u, v):
|
1098
|
+
reject = 1
|
1099
|
+
if not reject:
|
1100
|
+
break
|
1101
|
+
if mem_err_sub:
|
1102
|
+
mem_err[0] = 1
|
1103
|
+
return edge_candidate
|
1104
|
+
|
1105
|
+
cdef void *allocate_degd(int degree) noexcept:
|
1106
|
+
r"""
|
1107
|
+
Allocate the data part of the iterator over edges to add to the graph.
|
1108
|
+
"""
|
1109
|
+
cdef dg_edge_gen_data *degd = <dg_edge_gen_data *> sig_malloc(sizeof(dg_edge_gen_data))
|
1110
|
+
cdef iterator *edge_iterator = allocate_subset_gen(degree, 2)
|
1111
|
+
if degd is NULL or edge_iterator is NULL:
|
1112
|
+
sig_free(degd)
|
1113
|
+
free_subset_gen(edge_iterator)
|
1114
|
+
return NULL
|
1115
|
+
edge_iterator = setup_set_gen(edge_iterator, degree, 2)
|
1116
|
+
if edge_iterator is NULL:
|
1117
|
+
sig_free(degd)
|
1118
|
+
return NULL
|
1119
|
+
degd.edge_iterator = edge_iterator
|
1120
|
+
return degd
|
1121
|
+
|
1122
|
+
cdef void deallocate_degd(void *data) noexcept:
|
1123
|
+
r"""
|
1124
|
+
Deallocate the data part of the iterator over edges to add to the graph.
|
1125
|
+
"""
|
1126
|
+
cdef dg_edge_gen_data *degd = <dg_edge_gen_data *> data
|
1127
|
+
free_subset_gen(degd.edge_iterator)
|
1128
|
+
sig_free(degd)
|
1129
|
+
|
1130
|
+
cdef int gen_children_dg_edge(void *S, aut_gp_and_can_lab *group, iterator *it) noexcept:
|
1131
|
+
r"""
|
1132
|
+
Setup an iterator over edges to be added.
|
1133
|
+
"""
|
1134
|
+
cdef GraphStruct GS = <GraphStruct> S
|
1135
|
+
cdef int n = GS.G.num_verts
|
1136
|
+
(<dg_edge_gen_data *> it.data).graph = <void *> GS
|
1137
|
+
cdef iterator *edge_iterator = setup_set_gen((<dg_edge_gen_data *> it.data).edge_iterator, n, 2)
|
1138
|
+
if edge_iterator is not NULL:
|
1139
|
+
start_canonical_generator(group.group, NULL, n, edge_iterator)
|
1140
|
+
return (edge_iterator is NULL)
|
1141
|
+
|
1142
|
+
cdef void *apply_dg_edge_aug(void *parent, void *aug, void *child, int *degree, bint *mem_err) noexcept:
|
1143
|
+
r"""
|
1144
|
+
Apply the augmentation to ``parent`` storing the result in ``child``. Here
|
1145
|
+
``aug`` represents an edge to be added.
|
1146
|
+
"""
|
1147
|
+
cdef GraphStruct GS_child = <GraphStruct> child, GS_par = <GraphStruct> parent
|
1148
|
+
cdef DenseGraph DG = <DenseGraph> GS_child.G, DG_par = <DenseGraph> GS_par.G
|
1149
|
+
cdef subset *edge = <subset *> aug
|
1150
|
+
cdef int u, v
|
1151
|
+
|
1152
|
+
# copy DG_par edges to DG
|
1153
|
+
copy_dense_graph(DG, DG_par)
|
1154
|
+
|
1155
|
+
# add the edge
|
1156
|
+
u = bitset_first(&edge.bits)
|
1157
|
+
v = bitset_next(&edge.bits, u+1)
|
1158
|
+
if v == -1:
|
1159
|
+
DG.add_arc_unsafe(u, u)
|
1160
|
+
else:
|
1161
|
+
DG.add_arc_unsafe(u, v)
|
1162
|
+
DG.add_arc_unsafe(v, u)
|
1163
|
+
|
1164
|
+
degree[0] = DG.num_verts
|
1165
|
+
return <void *> GS_child
|
1166
|
+
|
1167
|
+
cdef void *allocate_dg_edge(int n, bint loops) noexcept:
|
1168
|
+
r"""
|
1169
|
+
Allocate an object for this augmentation scheme.
|
1170
|
+
"""
|
1171
|
+
cdef GraphStruct GS
|
1172
|
+
cdef DenseGraph G
|
1173
|
+
cdef int *scratch
|
1174
|
+
try:
|
1175
|
+
GS = GraphStruct()
|
1176
|
+
G = DenseGraph(n)
|
1177
|
+
scratch = <int *> sig_malloc((3*n+1) * sizeof(int))
|
1178
|
+
if scratch is NULL:
|
1179
|
+
raise MemoryError
|
1180
|
+
except MemoryError:
|
1181
|
+
return NULL
|
1182
|
+
Py_INCREF(GS)
|
1183
|
+
Py_INCREF(G)
|
1184
|
+
GS.G = G
|
1185
|
+
GS.directed = 0
|
1186
|
+
GS.loops = loops
|
1187
|
+
GS.use_indicator = 1
|
1188
|
+
GS.scratch = scratch
|
1189
|
+
return <void *> GS
|
1190
|
+
|
1191
|
+
cdef void free_dg_edge(void *child) noexcept:
|
1192
|
+
r"""
|
1193
|
+
Deallocate an object for this augmentation scheme.
|
1194
|
+
"""
|
1195
|
+
cdef GraphStruct GS = <GraphStruct> child
|
1196
|
+
sig_free(GS.scratch)
|
1197
|
+
Py_DECREF(GS.G)
|
1198
|
+
Py_DECREF(GS)
|
1199
|
+
|
1200
|
+
cdef void *canonical_dg_edge_parent(void *child, void *parent, int *permutation, int *degree, bint *mem_err) noexcept:
|
1201
|
+
r"""
|
1202
|
+
Apply ``permutation`` to ``child``, determine an arbitrary parent by
|
1203
|
+
deleting the lexicographically largest edge, apply the inverse of
|
1204
|
+
``permutation`` to the result and store the result in ``parent``.
|
1205
|
+
"""
|
1206
|
+
cdef GraphStruct GS_par = <GraphStruct> parent, GS = <GraphStruct> child
|
1207
|
+
cdef DenseGraph DG_par = <DenseGraph> GS_par.G, DG = <DenseGraph> GS.G
|
1208
|
+
cdef int u, v, n = DG.num_verts
|
1209
|
+
cdef int *scratch = GS_par.scratch
|
1210
|
+
|
1211
|
+
# copy DG edges to DG_par
|
1212
|
+
copy_dense_graph(DG_par, DG)
|
1213
|
+
|
1214
|
+
# remove the right edge
|
1215
|
+
for u from 0 <= u < n:
|
1216
|
+
scratch[permutation[u]] = u
|
1217
|
+
for u from n > u >= 0:
|
1218
|
+
if DG.in_degrees[scratch[u]] != 0:
|
1219
|
+
break
|
1220
|
+
for v from u >= v >= 0:
|
1221
|
+
if DG.has_arc_unsafe(scratch[u], scratch[v]):
|
1222
|
+
break
|
1223
|
+
DG_par.del_arc_unsafe(scratch[u], scratch[v])
|
1224
|
+
if u != v:
|
1225
|
+
DG_par.del_arc_unsafe(scratch[v], scratch[u])
|
1226
|
+
|
1227
|
+
degree[0] = n
|
1228
|
+
return <void *> GS_par
|
1229
|
+
|
1230
|
+
cdef iterator *allocate_dg_edge_gen(int degree, int depth, bint loops) noexcept:
|
1231
|
+
r"""
|
1232
|
+
Allocate the iterator for generating graphs.
|
1233
|
+
"""
|
1234
|
+
cdef iterator *dg_edge_gen = <iterator *> sig_malloc(sizeof(iterator))
|
1235
|
+
cdef canonical_generator_data *cgd = allocate_cgd(depth, degree)
|
1236
|
+
if dg_edge_gen is NULL or cgd is NULL:
|
1237
|
+
sig_free(dg_edge_gen)
|
1238
|
+
deallocate_cgd(cgd)
|
1239
|
+
return NULL
|
1240
|
+
cdef int i, j
|
1241
|
+
for i from 0 <= i < depth:
|
1242
|
+
cgd.object_stack[i] = allocate_dg_edge(degree, loops)
|
1243
|
+
cgd.parent_stack[i] = allocate_dg_edge(degree, loops)
|
1244
|
+
cgd.iterator_stack[i].data = allocate_degd(degree)
|
1245
|
+
cgd.iterator_stack[i].next = &dg_edge_gen_next
|
1246
|
+
if cgd.iterator_stack[i].data is NULL or \
|
1247
|
+
cgd.object_stack[i] is NULL or \
|
1248
|
+
cgd.parent_stack[i] is NULL:
|
1249
|
+
for j from 0 <= j <= i:
|
1250
|
+
deallocate_degd(cgd.iterator_stack[j].data)
|
1251
|
+
free_dg_edge(cgd.object_stack[j])
|
1252
|
+
free_dg_edge(cgd.parent_stack[j])
|
1253
|
+
sig_free(dg_edge_gen)
|
1254
|
+
deallocate_cgd(cgd)
|
1255
|
+
return NULL
|
1256
|
+
dg_edge_gen.data = <void *> cgd
|
1257
|
+
dg_edge_gen.next = canonical_generator_next
|
1258
|
+
return dg_edge_gen
|
1259
|
+
|
1260
|
+
cdef void free_dg_edge_gen(iterator *dg_edge_gen) noexcept:
|
1261
|
+
r"""
|
1262
|
+
Deallocate the iterator for generating graphs.
|
1263
|
+
"""
|
1264
|
+
cdef canonical_generator_data *cgd = <canonical_generator_data *> dg_edge_gen.data
|
1265
|
+
deallocate_cgd(cgd)
|
1266
|
+
sig_free(dg_edge_gen)
|
1267
|
+
|
1268
|
+
|
1269
|
+
def generate_dense_graphs_edge_addition(int n, bint loops, G=None, depth=None,
|
1270
|
+
bint construct=False,
|
1271
|
+
bint indicate_mem_err=True):
|
1272
|
+
r"""
|
1273
|
+
|
1274
|
+
EXAMPLES::
|
1275
|
+
|
1276
|
+
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_edge_addition
|
1277
|
+
|
1278
|
+
::
|
1279
|
+
|
1280
|
+
sage: for n in [0..6]:
|
1281
|
+
....: print(generate_dense_graphs_edge_addition(n,1))
|
1282
|
+
1
|
1283
|
+
2
|
1284
|
+
6
|
1285
|
+
20
|
1286
|
+
90
|
1287
|
+
544
|
1288
|
+
5096
|
1289
|
+
|
1290
|
+
::
|
1291
|
+
|
1292
|
+
sage: for n in [0..7]:
|
1293
|
+
....: print(generate_dense_graphs_edge_addition(n,0))
|
1294
|
+
1
|
1295
|
+
1
|
1296
|
+
2
|
1297
|
+
4
|
1298
|
+
11
|
1299
|
+
34
|
1300
|
+
156
|
1301
|
+
1044
|
1302
|
+
sage: generate_dense_graphs_edge_addition(8,0) # long time (about 14 seconds at 2.4 GHz)
|
1303
|
+
12346
|
1304
|
+
"""
|
1305
|
+
from sage.graphs.graph import Graph
|
1306
|
+
cdef iterator *graph_iterator
|
1307
|
+
cdef DenseGraph DG, ODG
|
1308
|
+
cdef GraphStruct GS
|
1309
|
+
if n < 0:
|
1310
|
+
return [] if construct else Integer(0)
|
1311
|
+
if n == 0:
|
1312
|
+
return [Graph(0, implementation='c_graph', sparse=False, loops=loops)] if construct else Integer(1)
|
1313
|
+
if n == 1:
|
1314
|
+
if not loops:
|
1315
|
+
return [Graph(1, implementation='c_graph', sparse=False, loops=loops)] if construct else Integer(1)
|
1316
|
+
else:
|
1317
|
+
if construct:
|
1318
|
+
G = Graph(1, implementation='c_graph', sparse=False, loops=loops)
|
1319
|
+
(<CGraph>G._backend._cg).add_arc_unsafe(0,0)
|
1320
|
+
return [G, Graph(1, implementation='c_graph', sparse=False, loops=loops)]
|
1321
|
+
else:
|
1322
|
+
return Integer(2)
|
1323
|
+
|
1324
|
+
if depth is None:
|
1325
|
+
depth = n*n
|
1326
|
+
|
1327
|
+
graph_iterator = allocate_dg_edge_gen(n, depth, loops)
|
1328
|
+
if graph_iterator is NULL:
|
1329
|
+
raise MemoryError
|
1330
|
+
|
1331
|
+
GS = (<GraphStruct> (<canonical_generator_data *> graph_iterator.data).object_stack[0])
|
1332
|
+
if G is not None:
|
1333
|
+
DG = GS.G
|
1334
|
+
for u,v in G.edges(sort=False, labels=False):
|
1335
|
+
DG.add_arc(u,v)
|
1336
|
+
if u != v:
|
1337
|
+
DG.add_arc(v,u)
|
1338
|
+
|
1339
|
+
graph_iterator = setup_canonical_generator(n,
|
1340
|
+
all_children_are_equivalent,
|
1341
|
+
refine_by_degree,
|
1342
|
+
compare_graphs,
|
1343
|
+
gen_children_dg_edge,
|
1344
|
+
apply_dg_edge_aug,
|
1345
|
+
free_dg_edge,
|
1346
|
+
deallocate_degd,
|
1347
|
+
free_subset,
|
1348
|
+
canonical_dg_edge_parent,
|
1349
|
+
depth, 0, graph_iterator)
|
1350
|
+
|
1351
|
+
start_canonical_generator(NULL, <void *> GS, n, graph_iterator)
|
1352
|
+
|
1353
|
+
cdef list out_list
|
1354
|
+
cdef void *thing
|
1355
|
+
cdef Integer number
|
1356
|
+
cdef bint mem_err = 0
|
1357
|
+
if construct:
|
1358
|
+
out_list = []
|
1359
|
+
else:
|
1360
|
+
number = Integer(0)
|
1361
|
+
if construct:
|
1362
|
+
while True:
|
1363
|
+
thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)
|
1364
|
+
if thing is NULL:
|
1365
|
+
break
|
1366
|
+
ODG = (<GraphStruct>thing).G
|
1367
|
+
G = Graph(0, implementation='c_graph', sparse=False)
|
1368
|
+
DG = DenseGraph(ODG.active_vertices.size, extra_vertices=0)
|
1369
|
+
copy_dense_graph(DG, ODG)
|
1370
|
+
G._backend._cg = DG
|
1371
|
+
out_list.append(G)
|
1372
|
+
else:
|
1373
|
+
while True:
|
1374
|
+
thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)
|
1375
|
+
if thing is NULL:
|
1376
|
+
break
|
1377
|
+
number += 1
|
1378
|
+
|
1379
|
+
free_dg_edge_gen(graph_iterator)
|
1380
|
+
if mem_err:
|
1381
|
+
if indicate_mem_err:
|
1382
|
+
raise MemoryError
|
1383
|
+
else:
|
1384
|
+
out_list.append(MemoryError())
|
1385
|
+
if construct:
|
1386
|
+
return out_list
|
1387
|
+
else:
|
1388
|
+
return number
|
1389
|
+
|
1390
|
+
|
1391
|
+
# Dense graphs: adding vertices
|
1392
|
+
|
1393
|
+
# This implements an augmentation scheme as follows:
|
1394
|
+
# * Seed objects are graphs with one vertex and no edges.
|
1395
|
+
# * Augmentations consist of adding a single vertex connected to some subset of
|
1396
|
+
# the previous vertices.
|
1397
|
+
|
1398
|
+
cdef int gen_children_dg_vert(void *S, aut_gp_and_can_lab *group, iterator *it) noexcept:
|
1399
|
+
r"""
|
1400
|
+
Setup an iterator over subsets to join a new vertex to.
|
1401
|
+
"""
|
1402
|
+
cdef GraphStruct GS = <GraphStruct> S
|
1403
|
+
cdef int n = GS.G.num_verts
|
1404
|
+
cdef iterator *subset_iterator = setup_set_gen(it, n, n)
|
1405
|
+
if subset_iterator is not NULL:
|
1406
|
+
start_canonical_generator(group.group, NULL, n, subset_iterator)
|
1407
|
+
return (subset_iterator is NULL)
|
1408
|
+
|
1409
|
+
cdef void *apply_dg_vert_aug(void *parent, void *aug, void *child, int *degree, bint *mem_err) noexcept:
|
1410
|
+
r"""
|
1411
|
+
Apply the augmentation to ``parent`` storing the result in ``child``. Here
|
1412
|
+
``aug`` represents a subset to join to a new vertex.
|
1413
|
+
"""
|
1414
|
+
cdef GraphStruct GS_child = <GraphStruct> child, GS_par = <GraphStruct> parent
|
1415
|
+
cdef DenseGraph DG = <DenseGraph> GS_child.G, DG_par = <DenseGraph> GS_par.G
|
1416
|
+
cdef subset *set1 = <subset *> aug
|
1417
|
+
cdef int u, n = DG_par.num_verts
|
1418
|
+
|
1419
|
+
# copy DG_par edges to DG
|
1420
|
+
copy_dense_graph(DG, DG_par)
|
1421
|
+
DG.add_vertex_unsafe(n)
|
1422
|
+
|
1423
|
+
# add the edges
|
1424
|
+
u = bitset_first(&set1.bits)
|
1425
|
+
while u != -1:
|
1426
|
+
DG.add_arc_unsafe(u, n)
|
1427
|
+
DG.add_arc_unsafe(n, u)
|
1428
|
+
u = bitset_next(&set1.bits, u+1)
|
1429
|
+
|
1430
|
+
degree[0] = n+1
|
1431
|
+
return <void *> GS_child
|
1432
|
+
|
1433
|
+
cdef void *allocate_dg_vert(int n, int depth) noexcept:
|
1434
|
+
r"""
|
1435
|
+
Allocate an object for this augmentation scheme.
|
1436
|
+
"""
|
1437
|
+
cdef GraphStruct GS
|
1438
|
+
cdef DenseGraph G
|
1439
|
+
cdef int *scratch
|
1440
|
+
try:
|
1441
|
+
GS = GraphStruct()
|
1442
|
+
G = DenseGraph(0, extra_vertices=depth)
|
1443
|
+
bitset_set_first_n(G.active_vertices, n)
|
1444
|
+
G.num_verts = n
|
1445
|
+
scratch = <int *> sig_malloc((3*depth+1) * sizeof(int))
|
1446
|
+
if scratch is NULL:
|
1447
|
+
raise MemoryError
|
1448
|
+
except MemoryError:
|
1449
|
+
return NULL
|
1450
|
+
Py_INCREF(GS)
|
1451
|
+
Py_INCREF(G)
|
1452
|
+
GS.G = G
|
1453
|
+
GS.directed = 0
|
1454
|
+
GS.loops = 0
|
1455
|
+
GS.use_indicator = 1
|
1456
|
+
GS.scratch = scratch
|
1457
|
+
return <void *> GS
|
1458
|
+
|
1459
|
+
cdef void free_dg_vert(void *child) noexcept:
|
1460
|
+
r"""
|
1461
|
+
Deallocate an object for this augmentation scheme.
|
1462
|
+
"""
|
1463
|
+
cdef GraphStruct GS = <GraphStruct> child
|
1464
|
+
sig_free(GS.scratch)
|
1465
|
+
Py_DECREF(GS.G)
|
1466
|
+
Py_DECREF(GS)
|
1467
|
+
|
1468
|
+
cdef void *canonical_dg_vert_parent(void *child, void *parent, int *permutation, int *degree, bint *mem_err) noexcept:
|
1469
|
+
r"""
|
1470
|
+
Apply ``permutation`` to ``child``, determines an arbitrary parent by
|
1471
|
+
deleting the lexicographically largest vertex, apply the inverse of
|
1472
|
+
``permutation`` to the result and store the result in ``parent``.
|
1473
|
+
"""
|
1474
|
+
cdef GraphStruct GS_par = <GraphStruct> parent, GS = <GraphStruct> child
|
1475
|
+
cdef DenseGraph DG_par = <DenseGraph> GS_par.G, DG = <DenseGraph> GS.G
|
1476
|
+
cdef int u, n = DG_par.num_verts
|
1477
|
+
cdef int *scratch = GS.scratch
|
1478
|
+
|
1479
|
+
# copy DG edges to DG_par
|
1480
|
+
copy_dense_graph(DG_par, DG)
|
1481
|
+
|
1482
|
+
# remove the right vertex
|
1483
|
+
for u from 0 <= u <= n:
|
1484
|
+
scratch[permutation[u]] = u
|
1485
|
+
DG_par.del_vertex_unsafe(scratch[n])
|
1486
|
+
|
1487
|
+
degree[0] = n
|
1488
|
+
return <void *> GS_par
|
1489
|
+
|
1490
|
+
cdef iterator *allocate_dg_vert_gen(int degree, int depth) noexcept:
|
1491
|
+
r"""
|
1492
|
+
Allocate the iterator for generating graphs.
|
1493
|
+
"""
|
1494
|
+
cdef iterator *dg_vert_gen = <iterator *> sig_malloc(sizeof(iterator))
|
1495
|
+
cdef canonical_generator_data *cgd = allocate_cgd(depth, degree)
|
1496
|
+
cdef canonical_generator_data *cgd2
|
1497
|
+
if dg_vert_gen is NULL or cgd is NULL:
|
1498
|
+
sig_free(dg_vert_gen)
|
1499
|
+
deallocate_cgd(cgd)
|
1500
|
+
return NULL
|
1501
|
+
cdef int i, j
|
1502
|
+
for i from 0 <= i < depth:
|
1503
|
+
cgd.object_stack[i] = allocate_dg_vert(i+degree,depth+degree-1)
|
1504
|
+
cgd.parent_stack[i] = allocate_dg_vert(i+degree,depth+degree-1)
|
1505
|
+
if cgd.object_stack[i] is NULL or \
|
1506
|
+
cgd.parent_stack[i] is NULL:
|
1507
|
+
for j from 0 <= j <= i:
|
1508
|
+
free_dg_vert(cgd.object_stack[j])
|
1509
|
+
free_dg_vert(cgd.parent_stack[j])
|
1510
|
+
sig_free(dg_vert_gen)
|
1511
|
+
deallocate_cgd(cgd)
|
1512
|
+
return NULL
|
1513
|
+
for i from 0 <= i < depth-1:
|
1514
|
+
# TODO: in fact, should this not happen in
|
1515
|
+
# dg_vert_gen_children!? otherwise iterator[i].data will be NULL
|
1516
|
+
# and no problems.....
|
1517
|
+
if allocate_subset_gen_2(i+degree, i+degree, cgd.iterator_stack+i):
|
1518
|
+
for j from 0 <= j < depth:
|
1519
|
+
free_dg_vert(cgd.object_stack[j])
|
1520
|
+
free_dg_vert(cgd.parent_stack[j])
|
1521
|
+
for j from 0 <= j < i:
|
1522
|
+
cgd2 = <canonical_generator_data *> cgd.iterator_stack[j].data
|
1523
|
+
deallocate_cgd(cgd2)
|
1524
|
+
sig_free(dg_vert_gen)
|
1525
|
+
deallocate_cgd(cgd)
|
1526
|
+
return NULL
|
1527
|
+
dg_vert_gen.data = <void *> cgd
|
1528
|
+
dg_vert_gen.next = canonical_generator_next
|
1529
|
+
return dg_vert_gen
|
1530
|
+
|
1531
|
+
cdef void free_dg_vert_gen(iterator *dg_vert_gen) noexcept:
|
1532
|
+
r"""
|
1533
|
+
Deallocate the iterator for generating graphs.
|
1534
|
+
"""
|
1535
|
+
cdef canonical_generator_data *cgd = <canonical_generator_data *> dg_vert_gen.data
|
1536
|
+
deallocate_cgd(cgd)
|
1537
|
+
sig_free(dg_vert_gen)
|
1538
|
+
|
1539
|
+
cdef void free_cgd_2(void *data) noexcept:
|
1540
|
+
r"""
|
1541
|
+
A simpler alternative to ``free_cgd``.
|
1542
|
+
"""
|
1543
|
+
cdef canonical_generator_data *cgd = <canonical_generator_data *> data
|
1544
|
+
deallocate_cgd(cgd)
|
1545
|
+
|
1546
|
+
|
1547
|
+
def generate_dense_graphs_vert_addition(int n, base_G=None,
|
1548
|
+
bint construct=False,
|
1549
|
+
bint indicate_mem_err=True):
|
1550
|
+
r"""
|
1551
|
+
|
1552
|
+
EXAMPLES::
|
1553
|
+
|
1554
|
+
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_vert_addition
|
1555
|
+
|
1556
|
+
::
|
1557
|
+
|
1558
|
+
sage: for n in [0..7]:
|
1559
|
+
....: generate_dense_graphs_vert_addition(n)
|
1560
|
+
1
|
1561
|
+
2
|
1562
|
+
4
|
1563
|
+
8
|
1564
|
+
19
|
1565
|
+
53
|
1566
|
+
209
|
1567
|
+
1253
|
1568
|
+
sage: generate_dense_graphs_vert_addition(8) # long time
|
1569
|
+
13599
|
1570
|
+
|
1571
|
+
TESTS::
|
1572
|
+
|
1573
|
+
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_vert_addition
|
1574
|
+
sage: generate_dense_graphs_vert_addition(10, base_G=Graph('HEhf^rs'))
|
1575
|
+
11
|
1576
|
+
"""
|
1577
|
+
from sage.graphs.graph import Graph
|
1578
|
+
cdef iterator *graph_iterator
|
1579
|
+
cdef DenseGraph DG, ODG
|
1580
|
+
cdef GraphStruct GS
|
1581
|
+
if n < 2:
|
1582
|
+
if construct:
|
1583
|
+
L = []
|
1584
|
+
if n < 0:
|
1585
|
+
return L
|
1586
|
+
L.append(Graph(0, implementation='c_graph', sparse=False))
|
1587
|
+
if n == 0:
|
1588
|
+
return L
|
1589
|
+
L.append(Graph(0, implementation='c_graph', sparse=False))
|
1590
|
+
L.reverse()
|
1591
|
+
return L
|
1592
|
+
else:
|
1593
|
+
if n < 0:
|
1594
|
+
return Integer(0)
|
1595
|
+
if n == 0:
|
1596
|
+
return Integer(1)
|
1597
|
+
return Integer(2)
|
1598
|
+
|
1599
|
+
cdef int start_deg = 1 if base_G is None else base_G.num_verts()
|
1600
|
+
graph_iterator = allocate_dg_vert_gen(start_deg, n+1-start_deg)
|
1601
|
+
if graph_iterator is NULL:
|
1602
|
+
raise MemoryError
|
1603
|
+
|
1604
|
+
GS = (<GraphStruct> (<canonical_generator_data *> graph_iterator.data).object_stack[0])
|
1605
|
+
DG = GS.G
|
1606
|
+
if base_G is not None:
|
1607
|
+
for v in base_G.vertices(sort=False):
|
1608
|
+
DG.add_vertex(v)
|
1609
|
+
for u,v in base_G.edges(sort=False, labels=False):
|
1610
|
+
DG.add_arc(u,v)
|
1611
|
+
DG.add_arc(v,u)
|
1612
|
+
|
1613
|
+
graph_iterator = setup_canonical_generator(start_deg,
|
1614
|
+
all_children_are_equivalent,
|
1615
|
+
refine_by_degree,
|
1616
|
+
compare_graphs,
|
1617
|
+
gen_children_dg_vert,
|
1618
|
+
apply_dg_vert_aug,
|
1619
|
+
free_dg_vert,
|
1620
|
+
free_cgd_2,
|
1621
|
+
free_subset,
|
1622
|
+
canonical_dg_vert_parent,
|
1623
|
+
n+1-start_deg, 0, graph_iterator)
|
1624
|
+
|
1625
|
+
start_canonical_generator(NULL, <void *> GS, DG.num_verts, graph_iterator)
|
1626
|
+
|
1627
|
+
cdef list out_list
|
1628
|
+
cdef void *thing
|
1629
|
+
cdef Integer number
|
1630
|
+
cdef bint mem_err = 0
|
1631
|
+
if construct:
|
1632
|
+
out_list = []
|
1633
|
+
else:
|
1634
|
+
number = Integer(0)
|
1635
|
+
if construct:
|
1636
|
+
while True:
|
1637
|
+
thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)
|
1638
|
+
if thing is NULL:
|
1639
|
+
break
|
1640
|
+
ODG = (<GraphStruct>thing).G
|
1641
|
+
G = Graph(0, implementation='c_graph', sparse=False)
|
1642
|
+
DG = DenseGraph(ODG.active_vertices.size, extra_vertices=0)
|
1643
|
+
copy_dense_graph(DG, ODG)
|
1644
|
+
G._backend._cg = DG
|
1645
|
+
out_list.append(G)
|
1646
|
+
else:
|
1647
|
+
while True:
|
1648
|
+
thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)
|
1649
|
+
if thing is NULL:
|
1650
|
+
break
|
1651
|
+
number += 1
|
1652
|
+
|
1653
|
+
free_dg_vert_gen(graph_iterator)
|
1654
|
+
if mem_err:
|
1655
|
+
if indicate_mem_err:
|
1656
|
+
raise MemoryError
|
1657
|
+
else:
|
1658
|
+
out_list.append(MemoryError())
|
1659
|
+
if construct:
|
1660
|
+
if base_G is None:
|
1661
|
+
out_list = [Graph(0, implementation='c_graph', sparse=False)] + out_list
|
1662
|
+
return out_list
|
1663
|
+
else:
|
1664
|
+
if base_G is None:
|
1665
|
+
number += 1
|
1666
|
+
return number
|