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,1032 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
# cython: binding=True
|
3
|
+
r"""
|
4
|
+
Static dense graphs
|
5
|
+
|
6
|
+
This module gathers everything which is related to static dense graphs, i.e. :
|
7
|
+
|
8
|
+
- The vertices are integer from `0` to `n-1`
|
9
|
+
- No labels on vertices/edges
|
10
|
+
- No multiple edges
|
11
|
+
- No addition/removal of vertices
|
12
|
+
|
13
|
+
This being said, it is technically possible to add/remove edges. The data
|
14
|
+
structure does not mind at all.
|
15
|
+
|
16
|
+
It is all based on the binary matrix data structure described in
|
17
|
+
``data_structures/binary_matrix.pxd``, which is almost a copy of the bitset data
|
18
|
+
structure. The only difference is that it differentiates the rows (the vertices)
|
19
|
+
instead of storing the whole data in a long bitset, and we can use that.
|
20
|
+
|
21
|
+
For an overview of graph data structures in sage, see
|
22
|
+
:mod:`~sage.graphs.base.overview`.
|
23
|
+
|
24
|
+
Index
|
25
|
+
-----
|
26
|
+
|
27
|
+
**Cython functions**
|
28
|
+
|
29
|
+
.. csv-table::
|
30
|
+
:class: contentstable
|
31
|
+
:widths: 30, 70
|
32
|
+
:delim: |
|
33
|
+
|
34
|
+
``dense_graph_init`` | Fill a binary matrix with the information from a Sage (di)graph.
|
35
|
+
|
36
|
+
**Python functions**
|
37
|
+
|
38
|
+
.. csv-table::
|
39
|
+
:class: contentstable
|
40
|
+
:widths: 30, 70
|
41
|
+
:delim: |
|
42
|
+
|
43
|
+
:meth:`is_strongly_regular` | Check whether the graph is strongly regular
|
44
|
+
:meth:`is_triangle_free` | Check whether `G` is triangle free
|
45
|
+
:meth:`triangles_count` | Return the number of triangles containing `v`, for every `v`
|
46
|
+
:meth:`connected_subgraph_iterator` | Iterator over the induced connected subgraphs of order at most `k`
|
47
|
+
|
48
|
+
Functions
|
49
|
+
---------
|
50
|
+
"""
|
51
|
+
from sage.data_structures.binary_matrix cimport *
|
52
|
+
from cysignals.signals cimport sig_on, sig_off, sig_check
|
53
|
+
from memory_allocator cimport MemoryAllocator
|
54
|
+
from itertools import product
|
55
|
+
from sage.misc.flatten import flatten
|
56
|
+
|
57
|
+
|
58
|
+
cdef dict dense_graph_init(binary_matrix_t m, g, translation=None, force_undirected=False):
|
59
|
+
r"""
|
60
|
+
Fill a binary matrix with the information from a Sage (di)graph.
|
61
|
+
|
62
|
+
INPUT:
|
63
|
+
|
64
|
+
- ``binary_matrix_t m`` -- the binary matrix to be filled
|
65
|
+
|
66
|
+
- ``g`` -- a graph or digraph
|
67
|
+
|
68
|
+
- ``translation`` -- (default: ``None``) several options for this parameter
|
69
|
+
used to specify the mapping from vertices to integers:
|
70
|
+
|
71
|
+
- ``True``, ``False``, ``None`` -- the `i`-th vertex in the binary matrix
|
72
|
+
corresponds to vertex ``g.vertices(sort=True)[i]``.
|
73
|
+
When set to ``True``, a dictionary encoding the mapping from the
|
74
|
+
vertices of `g` to integers in `(0, \dots, n-1)` is returned.
|
75
|
+
|
76
|
+
- a ``list`` -- defines a mapping from integers in `(0, \dots, n-1)` to
|
77
|
+
the vertices in the graph `g`. So the `i`-th vertex in the binary matrix
|
78
|
+
corresponds to vertex ``translation[i]``.
|
79
|
+
**Beware that no checks are made that this input is correct**.
|
80
|
+
|
81
|
+
- a ``dict`` -- defines a mapping from the vertices of the graph to
|
82
|
+
integers in `(0, \dots, n-1)`. So the `i`-th vertex in the binary matrix
|
83
|
+
corresponds to ``translation[v]`` for some vertex `v \in g`.
|
84
|
+
**Beware that no checks are made that this input is correct**.
|
85
|
+
|
86
|
+
- ``force_undirected`` -- boolean (default: ``False``); whether to consider
|
87
|
+
the graph as undirected or not
|
88
|
+
"""
|
89
|
+
cdef dict d_translation = {}
|
90
|
+
from sage.graphs.graph import Graph
|
91
|
+
cdef bint is_undirected = isinstance(g, Graph) or force_undirected
|
92
|
+
cdef int n = g.order()
|
93
|
+
cdef int i, j
|
94
|
+
|
95
|
+
binary_matrix_init(m, n, n)
|
96
|
+
|
97
|
+
if translation and translation is not True:
|
98
|
+
if isinstance(translation, list):
|
99
|
+
# We are given a mapping from integers to vertices
|
100
|
+
d_translation = {v: i for i, v in enumerate(translation)}
|
101
|
+
elif isinstance(translation, dict):
|
102
|
+
# We are given a mapping vertices to integers
|
103
|
+
d_translation = translation
|
104
|
+
|
105
|
+
# If the vertices are 0...n-1, let's avoid an unnecessary dictionary
|
106
|
+
if not d_translation and set(g.vertex_iterator()) == set(range(n)):
|
107
|
+
if translation is True:
|
108
|
+
d_translation = {i: i for i in range(n)}
|
109
|
+
|
110
|
+
for i, j in g.edge_iterator(labels=False):
|
111
|
+
binary_matrix_set1(m, i, j)
|
112
|
+
if is_undirected:
|
113
|
+
binary_matrix_set1(m, j, i)
|
114
|
+
else:
|
115
|
+
if not d_translation:
|
116
|
+
d_translation = {v: i for i, v in enumerate(g.vertices(sort=True))}
|
117
|
+
|
118
|
+
for u, v in g.edge_iterator(labels=False):
|
119
|
+
binary_matrix_set1(m, d_translation[u], d_translation[v])
|
120
|
+
if is_undirected:
|
121
|
+
binary_matrix_set1(m, d_translation[v], d_translation[u])
|
122
|
+
|
123
|
+
if translation is True:
|
124
|
+
return d_translation
|
125
|
+
|
126
|
+
|
127
|
+
def is_strongly_regular(g, parameters=False):
|
128
|
+
r"""
|
129
|
+
Check whether the graph is strongly regular.
|
130
|
+
|
131
|
+
A simple graph `G` is said to be strongly regular with parameters
|
132
|
+
`(n, k, \lambda, \mu)` if and only if:
|
133
|
+
|
134
|
+
* `G` has `n` vertices
|
135
|
+
|
136
|
+
* `G` is `k`-regular
|
137
|
+
|
138
|
+
* Any two adjacent vertices of `G` have `\lambda` common neighbors
|
139
|
+
|
140
|
+
* Any two non-adjacent vertices of `G` have `\mu` common neighbors
|
141
|
+
|
142
|
+
By convention, the complete graphs, the graphs with no edges and the empty
|
143
|
+
graph are not strongly regular.
|
144
|
+
|
145
|
+
See the :wikipedia:`Strongly regular graph`.
|
146
|
+
|
147
|
+
INPUT:
|
148
|
+
|
149
|
+
- ``parameters`` -- boolean (default: ``False``); whether to return the
|
150
|
+
quadruple `(n, k, \lambda, \mu)`. If ``parameters = False`` (default),
|
151
|
+
this method only returns ``True`` and ``False`` answers.
|
152
|
+
If ``parameters = True``, the ``True`` answers are replaced by quadruples
|
153
|
+
`(n, k, \lambda, \mu)`. See definition above.
|
154
|
+
|
155
|
+
EXAMPLES:
|
156
|
+
|
157
|
+
Petersen's graph is strongly regular::
|
158
|
+
|
159
|
+
sage: g = graphs.PetersenGraph()
|
160
|
+
sage: g.is_strongly_regular()
|
161
|
+
True
|
162
|
+
sage: g.is_strongly_regular(parameters=True)
|
163
|
+
(10, 3, 0, 1)
|
164
|
+
|
165
|
+
And Clebsch's graph is too::
|
166
|
+
|
167
|
+
sage: g = graphs.ClebschGraph()
|
168
|
+
sage: g.is_strongly_regular()
|
169
|
+
True
|
170
|
+
sage: g.is_strongly_regular(parameters=True)
|
171
|
+
(16, 5, 0, 2)
|
172
|
+
|
173
|
+
But Chvatal's graph is not::
|
174
|
+
|
175
|
+
sage: g = graphs.ChvatalGraph()
|
176
|
+
sage: g.is_strongly_regular()
|
177
|
+
False
|
178
|
+
|
179
|
+
Complete graphs are not strongly regular. (:issue:`14297`) ::
|
180
|
+
|
181
|
+
sage: g = graphs.CompleteGraph(5)
|
182
|
+
sage: g.is_strongly_regular()
|
183
|
+
False
|
184
|
+
|
185
|
+
Completements of complete graphs are not strongly regular::
|
186
|
+
|
187
|
+
sage: g = graphs.CompleteGraph(5).complement()
|
188
|
+
sage: g.is_strongly_regular()
|
189
|
+
False
|
190
|
+
|
191
|
+
The empty graph is not strongly regular::
|
192
|
+
|
193
|
+
sage: g = graphs.EmptyGraph()
|
194
|
+
sage: g.is_strongly_regular()
|
195
|
+
False
|
196
|
+
|
197
|
+
If the input graph has loops or multiedges an exception is raised::
|
198
|
+
|
199
|
+
sage: Graph([(1,1),(2,2)],loops=True).is_strongly_regular()
|
200
|
+
Traceback (most recent call last):
|
201
|
+
...
|
202
|
+
ValueError: This method is not known to work on graphs with
|
203
|
+
loops. Perhaps this method can be updated to handle them, but in the
|
204
|
+
meantime if you want to use it please disallow loops using
|
205
|
+
allow_loops().
|
206
|
+
|
207
|
+
sage: Graph([(1,2),(1,2)],multiedges=True).is_strongly_regular()
|
208
|
+
Traceback (most recent call last):
|
209
|
+
...
|
210
|
+
ValueError: This method is not known to work on graphs with
|
211
|
+
multiedges. Perhaps this method can be updated to handle them, but in
|
212
|
+
the meantime if you want to use it please disallow multiedges using
|
213
|
+
allow_multiple_edges().
|
214
|
+
"""
|
215
|
+
g._scream_if_not_simple()
|
216
|
+
cdef binary_matrix_t m
|
217
|
+
cdef bitset_t b_tmp
|
218
|
+
cdef int n = g.order()
|
219
|
+
cdef int inter
|
220
|
+
cdef int i, j, k
|
221
|
+
|
222
|
+
if not g.order() or not g.size(): # no vertices or no edges
|
223
|
+
return False
|
224
|
+
|
225
|
+
if g.is_clique():
|
226
|
+
return False
|
227
|
+
|
228
|
+
cdef list degree = g.degree()
|
229
|
+
k = degree[0]
|
230
|
+
if any(d != k for d in degree):
|
231
|
+
return False
|
232
|
+
|
233
|
+
bitset_init(b_tmp, n)
|
234
|
+
|
235
|
+
# m is now our copy of the graph
|
236
|
+
dense_graph_init(m, g, translation={v: i for i, v in enumerate(g)})
|
237
|
+
|
238
|
+
cdef int llambda = -1
|
239
|
+
cdef int mu = -1
|
240
|
+
|
241
|
+
for i in range(n):
|
242
|
+
for j in range(i + 1, n):
|
243
|
+
|
244
|
+
# The intersection of the common neighbors of i and j is a AND of
|
245
|
+
# their respective rows. A popcount then returns its cardinality.
|
246
|
+
bitset_and(b_tmp, m.rows[i], m.rows[j])
|
247
|
+
inter = bitset_len(b_tmp)
|
248
|
+
|
249
|
+
# Check that this cardinality is correct according to the values of lambda and mu
|
250
|
+
if binary_matrix_get(m, i, j):
|
251
|
+
if llambda == -1:
|
252
|
+
llambda = inter
|
253
|
+
elif llambda != inter:
|
254
|
+
binary_matrix_free(m)
|
255
|
+
bitset_free(b_tmp)
|
256
|
+
return False
|
257
|
+
else:
|
258
|
+
if mu == -1:
|
259
|
+
mu = inter
|
260
|
+
elif mu != inter:
|
261
|
+
binary_matrix_free(m)
|
262
|
+
bitset_free(b_tmp)
|
263
|
+
return False
|
264
|
+
|
265
|
+
binary_matrix_free(m)
|
266
|
+
bitset_free(b_tmp)
|
267
|
+
|
268
|
+
if parameters:
|
269
|
+
return (n, k, llambda, mu)
|
270
|
+
else:
|
271
|
+
return True
|
272
|
+
|
273
|
+
|
274
|
+
def is_triangle_free(G, certificate=False):
|
275
|
+
r"""
|
276
|
+
Check whether `G` is triangle free.
|
277
|
+
|
278
|
+
INPUT:
|
279
|
+
|
280
|
+
- ``G`` -- a Sage graph
|
281
|
+
|
282
|
+
- ``certificate`` -- boolean (default: ``False``); whether to return a
|
283
|
+
triangle if one is found
|
284
|
+
|
285
|
+
EXAMPLES::
|
286
|
+
|
287
|
+
sage: from sage.graphs.base.static_dense_graph import is_triangle_free
|
288
|
+
sage: is_triangle_free(graphs.PetersenGraph())
|
289
|
+
True
|
290
|
+
sage: K4 = graphs.CompleteGraph(4)
|
291
|
+
sage: is_triangle_free(K4)
|
292
|
+
False
|
293
|
+
sage: b, certif = is_triangle_free(K4, certificate=True)
|
294
|
+
sage: K4.subgraph(certif).is_clique()
|
295
|
+
True
|
296
|
+
|
297
|
+
TESTS::
|
298
|
+
|
299
|
+
sage: from sage.graphs.base.static_dense_graph import is_triangle_free
|
300
|
+
sage: is_triangle_free(Graph())
|
301
|
+
True
|
302
|
+
sage: is_triangle_free(Graph(), certificate=True)
|
303
|
+
(True, [])
|
304
|
+
"""
|
305
|
+
G._scream_if_not_simple()
|
306
|
+
cdef int n = G.order()
|
307
|
+
if n < 3:
|
308
|
+
return (True, []) if certificate else True
|
309
|
+
|
310
|
+
cdef binary_matrix_t g
|
311
|
+
cdef list int_to_vertex = list(G)
|
312
|
+
dense_graph_init(g, G, translation=int_to_vertex)
|
313
|
+
|
314
|
+
cdef mp_size_t i, j, k
|
315
|
+
for i in range(n):
|
316
|
+
j = bitset_next(g.rows[i], i + 1)
|
317
|
+
while j != -1:
|
318
|
+
if bitset_are_disjoint(g.rows[i], g.rows[j]):
|
319
|
+
j = bitset_next(g.rows[i], j + 1)
|
320
|
+
else:
|
321
|
+
if certificate:
|
322
|
+
# Search for a common neighbor
|
323
|
+
k = bitset_first(g.rows[i])
|
324
|
+
while bitset_not_in(g.rows[j], k):
|
325
|
+
k = bitset_next(g.rows[j], k + 1)
|
326
|
+
certif = [int_to_vertex[i], int_to_vertex[j], int_to_vertex[k]]
|
327
|
+
|
328
|
+
binary_matrix_free(g)
|
329
|
+
return (False, certif) if certificate else False
|
330
|
+
|
331
|
+
binary_matrix_free(g)
|
332
|
+
return (True, []) if certificate else True
|
333
|
+
|
334
|
+
|
335
|
+
def triangles_count(G):
|
336
|
+
r"""
|
337
|
+
Return the number of triangles containing `v`, for every `v`.
|
338
|
+
|
339
|
+
INPUT:
|
340
|
+
|
341
|
+
- ``G`` -- a simple Sage graph
|
342
|
+
|
343
|
+
EXAMPLES::
|
344
|
+
|
345
|
+
sage: from sage.graphs.base.static_dense_graph import triangles_count
|
346
|
+
sage: triangles_count(graphs.PetersenGraph())
|
347
|
+
{0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0}
|
348
|
+
sage: sum(triangles_count(graphs.CompleteGraph(15)).values()) == 3 * binomial(15, 3) # needs sage.symbolic
|
349
|
+
True
|
350
|
+
"""
|
351
|
+
from sage.rings.integer import Integer
|
352
|
+
G._scream_if_not_simple()
|
353
|
+
cdef int n = G.order()
|
354
|
+
|
355
|
+
cdef uint64_t * count = <uint64_t *> check_calloc(n, sizeof(uint64_t))
|
356
|
+
|
357
|
+
cdef binary_matrix_t g
|
358
|
+
cdef list int_to_vertex = list(G)
|
359
|
+
dense_graph_init(g, G, translation=int_to_vertex)
|
360
|
+
|
361
|
+
cdef bitset_t b_tmp
|
362
|
+
bitset_init(b_tmp, n)
|
363
|
+
|
364
|
+
cdef int i, j
|
365
|
+
cdef uint64_t tmp_count = 0
|
366
|
+
|
367
|
+
for i in range(n):
|
368
|
+
for j in range(i + 1, n):
|
369
|
+
if not bitset_in(g.rows[i], j):
|
370
|
+
continue
|
371
|
+
bitset_and(b_tmp, g.rows[i], g.rows[j])
|
372
|
+
tmp_count = bitset_len(b_tmp)
|
373
|
+
count[i] += tmp_count
|
374
|
+
count[j] += tmp_count
|
375
|
+
|
376
|
+
ans = {v: Integer(count[i] // 2) for i, v in enumerate(int_to_vertex)}
|
377
|
+
|
378
|
+
bitset_free(b_tmp)
|
379
|
+
binary_matrix_free(g)
|
380
|
+
sig_free(count)
|
381
|
+
|
382
|
+
return ans
|
383
|
+
|
384
|
+
|
385
|
+
def _format_result(G, edges, edges_only, labels):
|
386
|
+
r"""
|
387
|
+
Helper method for ``connected_full_subgraphs`` to return a result.
|
388
|
+
|
389
|
+
INPUT:
|
390
|
+
|
391
|
+
- ``G`` -- a :class:`DiGraph`
|
392
|
+
|
393
|
+
- ``edges`` -- list of edges ignoring the orientation
|
394
|
+
|
395
|
+
- ``edges_only`` -- boolean; whether to return DiGraph or list of vertices
|
396
|
+
|
397
|
+
- ``labels`` -- boolean; whether to return labeled edges or not. This
|
398
|
+
parameter is used only when ``edges_only`` is ``True``.
|
399
|
+
|
400
|
+
EXAMPLES:
|
401
|
+
|
402
|
+
The complete graph of order 3 has 4 connected subgraphs::
|
403
|
+
|
404
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
405
|
+
sage: G = graphs.CompleteGraph(3)
|
406
|
+
sage: len(list(connected_full_subgraphs(G)))
|
407
|
+
4
|
408
|
+
"""
|
409
|
+
if edges_only:
|
410
|
+
if labels:
|
411
|
+
return [(u, v, G.edge_label(u, v)) for u, v in edges]
|
412
|
+
else:
|
413
|
+
return edges
|
414
|
+
else:
|
415
|
+
return G.subgraph(vertices=G, edges=edges)
|
416
|
+
|
417
|
+
|
418
|
+
def _yield_results_for_digraph(G, edges, edges_only, labels, min_edges, max_edges):
|
419
|
+
r"""
|
420
|
+
Helper method for ``connected_full_subgraphs`` to yield all subdigraphs.
|
421
|
+
|
422
|
+
INPUT:
|
423
|
+
|
424
|
+
- ``G`` -- a :class:`DiGraph`
|
425
|
+
|
426
|
+
- ``edges`` -- list of edges ignoring the orientation
|
427
|
+
|
428
|
+
- ``edges_only`` -- boolean; whether to return DiGraph or list of vertices
|
429
|
+
|
430
|
+
- ``labels`` -- boolean; whether to return labeled edges or not. This
|
431
|
+
parameter is used only when ``edges_only`` is ``True``.
|
432
|
+
|
433
|
+
- ``min_edges`` -- integer; minimum number of edges of reported subgraphs
|
434
|
+
|
435
|
+
- ``max_edges`` -- integer; maximum number of edges of reported subgraphs
|
436
|
+
|
437
|
+
EXAMPLES::
|
438
|
+
|
439
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
440
|
+
sage: G = digraphs.Complete(3)
|
441
|
+
sage: len(list(connected_full_subgraphs(G)))
|
442
|
+
54
|
443
|
+
"""
|
444
|
+
if not edges:
|
445
|
+
return
|
446
|
+
L = []
|
447
|
+
for u, v in edges:
|
448
|
+
tmp = []
|
449
|
+
if G.has_edge(u, v):
|
450
|
+
tmp.append([(u, v)])
|
451
|
+
if G.has_edge(v, u):
|
452
|
+
tmp.append([(v, u)])
|
453
|
+
if G.has_edge(u, v):
|
454
|
+
tmp.append([(u, v), (v, u)])
|
455
|
+
L.append(tmp)
|
456
|
+
|
457
|
+
if len(L) == 1:
|
458
|
+
for F in L[0]:
|
459
|
+
if min_edges <= len(F) and len(F) <= max_edges:
|
460
|
+
yield _format_result(G, F, edges_only, labels)
|
461
|
+
else:
|
462
|
+
for E in product(*L):
|
463
|
+
F = [e for le in E for e in le]
|
464
|
+
if min_edges <= len(F) and len(F) <= max_edges:
|
465
|
+
yield _format_result(G, F, edges_only, labels)
|
466
|
+
return
|
467
|
+
|
468
|
+
|
469
|
+
def connected_full_subgraphs(G, edges_only=False, labels=False,
|
470
|
+
min_edges=None, max_edges=None):
|
471
|
+
r"""
|
472
|
+
Return an iterator over the connected subgraphs of `G` with same vertex set.
|
473
|
+
|
474
|
+
This method implements a iterator over the connected subgraphs of the input
|
475
|
+
(di)graph with the same ground set of vertices. That is, it iterates over
|
476
|
+
every subgraph `H = (V_H, E_H)` of `G = (V, E)` such that `V_H = V`,
|
477
|
+
`E_H \subseteq E` and `H` is connected. Hence, this method may yield a huge
|
478
|
+
number of graphs.
|
479
|
+
|
480
|
+
When the input (di)graph `G` is not connected, this method returns nothing.
|
481
|
+
|
482
|
+
As for method :meth:`sage.graphs.generic_graph.connected_components`, edge
|
483
|
+
orientation is ignored. Hence, the directed graph with a single arc `0 \to
|
484
|
+
1` is considered connected.
|
485
|
+
|
486
|
+
INPUT:
|
487
|
+
|
488
|
+
- ``G`` -- a :class:`Graph` or a :class:`DiGraph`; loops and multiple edges
|
489
|
+
are *not* allowed
|
490
|
+
|
491
|
+
- ``edges_only`` -- boolean (default: ``False``); whether to return
|
492
|
+
(Di)Graph or list of vertices
|
493
|
+
|
494
|
+
- ``labels`` -- boolean (default: ``False``); whether to return labeled
|
495
|
+
edges or not. This parameter is used only when ``edges_only`` is ``True``.
|
496
|
+
|
497
|
+
- ``min_edges`` -- integer (default: ``None``); minimum number of edges of
|
498
|
+
reported subgraphs. By default (``None``), this lower bound will be set to
|
499
|
+
`n - 1`.
|
500
|
+
|
501
|
+
- ``max_edges`` -- integer (default: ``None``); maximum number of edges of
|
502
|
+
reported subgraphs. By default (``None``), this lower bound will be set to
|
503
|
+
the number of edges of the input (di)graph.
|
504
|
+
|
505
|
+
.. NOTE::
|
506
|
+
|
507
|
+
Roughly, this method explores all possible subsets of neighbors of each
|
508
|
+
vertex, which represents a huge number of subsets. We have thus chosen
|
509
|
+
to limit the degree of the vertices of the graphs that can be
|
510
|
+
considered, even if the graph has a single connected subgraph (e.g., a
|
511
|
+
tree). It is therefore recommended to call this method on biconnected
|
512
|
+
components, as done in :meth:`connected_subgraph_iterator`.
|
513
|
+
|
514
|
+
EXAMPLES:
|
515
|
+
|
516
|
+
The complete graph of order 3 has 4 connected subgraphs::
|
517
|
+
|
518
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
519
|
+
sage: G = graphs.CompleteGraph(3)
|
520
|
+
sage: len(list(connected_full_subgraphs(G)))
|
521
|
+
4
|
522
|
+
|
523
|
+
A cycle of order 5 has 6 connected subgraphs::
|
524
|
+
|
525
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
526
|
+
sage: G = graphs.CycleGraph(5)
|
527
|
+
sage: len(list(connected_full_subgraphs(G)))
|
528
|
+
6
|
529
|
+
|
530
|
+
The House graph has 18 connected subgraphs of order 5::
|
531
|
+
|
532
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
533
|
+
sage: G = graphs.HouseGraph()
|
534
|
+
sage: L = list(connected_full_subgraphs(G))
|
535
|
+
sage: len(L)
|
536
|
+
18
|
537
|
+
sage: all(g.order() == 5 for g in L)
|
538
|
+
True
|
539
|
+
sage: all(g.is_connected() for g in L)
|
540
|
+
True
|
541
|
+
sage: F = frozenset(frozenset(g.edges(sort=False, labels=False)) for g in L)
|
542
|
+
sage: len(F)
|
543
|
+
18
|
544
|
+
|
545
|
+
Specifying bounds on the number of edges::
|
546
|
+
|
547
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
548
|
+
sage: G = graphs.HouseGraph()
|
549
|
+
sage: [g.size() for g in connected_full_subgraphs(G)]
|
550
|
+
[6, 5, 5, 5, 4, 4, 5, 4, 4, 4, 5, 4, 4, 4, 5, 4, 4, 4]
|
551
|
+
sage: [g.size() for g in connected_full_subgraphs(G, max_edges=4)]
|
552
|
+
[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
|
553
|
+
sage: [g.size() for g in connected_full_subgraphs(G, min_edges=6)]
|
554
|
+
[6]
|
555
|
+
sage: [g.size() for g in connected_full_subgraphs(G, min_edges=5, max_edges=5)]
|
556
|
+
[5, 5, 5, 5, 5, 5]
|
557
|
+
|
558
|
+
Asking for edges only::
|
559
|
+
|
560
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
561
|
+
sage: G = Graph([(0, 1, "01"), (0, 2, "02"), (1, 2, "12")])
|
562
|
+
sage: it = connected_full_subgraphs(G, edges_only=True)
|
563
|
+
sage: next(it)
|
564
|
+
[(0, 1), (0, 2), (1, 2)]
|
565
|
+
sage: next(it)
|
566
|
+
[(0, 1), (0, 2)]
|
567
|
+
sage: it = connected_full_subgraphs(G, edges_only=True, labels=True)
|
568
|
+
sage: next(it)
|
569
|
+
[(0, 1, '01'), (0, 2, '02'), (1, 2, '12')]
|
570
|
+
sage: next(it)
|
571
|
+
[(0, 1, '01'), (0, 2, '02')]
|
572
|
+
|
573
|
+
Subgraphs of a digraph::
|
574
|
+
|
575
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
576
|
+
sage: G = digraphs.Complete(2)
|
577
|
+
sage: list(connected_full_subgraphs(G, edges_only=True))
|
578
|
+
[[(0, 1)], [(1, 0)], [(0, 1), (1, 0)]]
|
579
|
+
|
580
|
+
TESTS:
|
581
|
+
|
582
|
+
Non connected input graph::
|
583
|
+
|
584
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
585
|
+
sage: list(connected_full_subgraphs(Graph(2)))
|
586
|
+
Traceback (most recent call last):
|
587
|
+
...
|
588
|
+
ValueError: the input (di)graph is not connected
|
589
|
+
|
590
|
+
Too large degree::
|
591
|
+
|
592
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
593
|
+
sage: G = graphs.StarGraph(100)
|
594
|
+
sage: list(connected_full_subgraphs(G))
|
595
|
+
Traceback (most recent call last):
|
596
|
+
...
|
597
|
+
ValueError: the degree of the graph is too large for this method
|
598
|
+
|
599
|
+
Wrong bounds on the number of edges::
|
600
|
+
|
601
|
+
sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
|
602
|
+
sage: G = graphs.HouseGraph()
|
603
|
+
sage: [g.size() for g in connected_full_subgraphs(G, min_edges=G.size() + 1)]
|
604
|
+
Traceback (most recent call last):
|
605
|
+
...
|
606
|
+
ValueError: we must have 4 <= min_edges <= max_edges <= 6
|
607
|
+
sage: [g.size() for g in connected_full_subgraphs(G, max_edges=G.order() - 2)]
|
608
|
+
Traceback (most recent call last):
|
609
|
+
...
|
610
|
+
ValueError: we must have 4 <= min_edges <= max_edges <= 6
|
611
|
+
"""
|
612
|
+
G._scream_if_not_simple()
|
613
|
+
if not G.is_connected():
|
614
|
+
raise ValueError("the input (di)graph is not connected")
|
615
|
+
|
616
|
+
for d in G.degree():
|
617
|
+
if d >= 8 * sizeof(unsigned long) - 1:
|
618
|
+
raise ValueError("the degree of the graph is too large for this method")
|
619
|
+
|
620
|
+
cdef Py_ssize_t n = G.order()
|
621
|
+
cdef Py_ssize_t m = G.size()
|
622
|
+
if min_edges is None or min_edges < n - 1:
|
623
|
+
min_edges = n - 1
|
624
|
+
if max_edges is None or max_edges > m:
|
625
|
+
max_edges = m
|
626
|
+
if min_edges > max_edges:
|
627
|
+
raise ValueError("we must have {} <= min_edges <= max_edges <= {}".format(n -1, m))
|
628
|
+
|
629
|
+
if n <= 1 or (not G.is_directed() and n == 2) or min_edges == m:
|
630
|
+
if edges_only:
|
631
|
+
yield list(G.edges(sort=False, labels=labels))
|
632
|
+
else:
|
633
|
+
yield G.copy()
|
634
|
+
return
|
635
|
+
|
636
|
+
# We map vertices to integers. We sort them by degree as a heuristic to
|
637
|
+
# reduce the number of operations.
|
638
|
+
cdef list int_to_vertex = sorted(G, key=G.degree)
|
639
|
+
cdef binary_matrix_t DG
|
640
|
+
sig_on()
|
641
|
+
dense_graph_init(DG, G, translation=int_to_vertex, force_undirected=True)
|
642
|
+
|
643
|
+
cdef MemoryAllocator mem = MemoryAllocator()
|
644
|
+
cdef int * order = <int *> mem.calloc(n, sizeof(int))
|
645
|
+
cdef mp_bitcnt_t * n_cpt = <mp_bitcnt_t *> mem.calloc(n, sizeof(mp_bitcnt_t))
|
646
|
+
|
647
|
+
# We use several bitsets to store the current boundary and active neighbors.
|
648
|
+
# We also need another bitset that we create at the same time
|
649
|
+
cdef binary_matrix_t boundaries
|
650
|
+
binary_matrix_init(boundaries, n + 1, n)
|
651
|
+
cdef binary_matrix_t neighborhoods
|
652
|
+
binary_matrix_init(neighborhoods, n, n)
|
653
|
+
sig_off()
|
654
|
+
|
655
|
+
cdef bitset_t active = boundaries.rows[n] # remaining vertices to consider
|
656
|
+
cdef bitset_t boundary # neighbors of the current subset
|
657
|
+
|
658
|
+
# Initialize the process
|
659
|
+
cdef Py_ssize_t i = 0
|
660
|
+
order[0] = 0
|
661
|
+
bitset_complement(active, active)
|
662
|
+
bitset_discard(active, 0)
|
663
|
+
bitset_copy(neighborhoods.rows[0], DG.rows[0])
|
664
|
+
n_cpt[0] = 1 << bitset_len(DG.rows[0])
|
665
|
+
|
666
|
+
cdef long u, v, j
|
667
|
+
cdef mp_bitcnt_t c
|
668
|
+
cdef Py_ssize_t num_edges = 0
|
669
|
+
cdef list E = []
|
670
|
+
cdef list edges
|
671
|
+
|
672
|
+
while i >= 0:
|
673
|
+
sig_check()
|
674
|
+
if i == n - 1 and num_edges >= min_edges:
|
675
|
+
# yield the current solution
|
676
|
+
edges = [(int_to_vertex[u], int_to_vertex[v]) for le in E for u, v in le]
|
677
|
+
if G.is_directed():
|
678
|
+
yield from _yield_results_for_digraph(G, edges, edges_only, labels, min_edges, max_edges)
|
679
|
+
else:
|
680
|
+
yield _format_result(G, edges, edges_only, labels)
|
681
|
+
|
682
|
+
if n_cpt[i] > 1:
|
683
|
+
# Consider the next neighborhood of the current vertex.
|
684
|
+
# If vertex u has k active neighbors, we have 2^k possibilities. We
|
685
|
+
# use the binary representation of n_cpt[i] to indicate which of the
|
686
|
+
# k neighbors are selected. We omit the empty neighborhood which is
|
687
|
+
# considered elsewhere.
|
688
|
+
n_cpt[i] -= 1
|
689
|
+
c = n_cpt[i]
|
690
|
+
if num_edges + _bitset_len(&c, 1) > max_edges:
|
691
|
+
# Too many edges
|
692
|
+
continue
|
693
|
+
|
694
|
+
boundary = boundaries.rows[i + 1]
|
695
|
+
bitset_copy(boundary, boundaries.rows[i])
|
696
|
+
edges = []
|
697
|
+
j = bitset_first(neighborhoods.rows[i])
|
698
|
+
while c and j != -1:
|
699
|
+
if c & 1:
|
700
|
+
bitset_add(boundary, j)
|
701
|
+
edges.append((order[i], j))
|
702
|
+
c >>= 1
|
703
|
+
j = bitset_next(neighborhoods.rows[i], j + 1)
|
704
|
+
|
705
|
+
if not bitset_len(boundary):
|
706
|
+
# We cannot extend
|
707
|
+
continue
|
708
|
+
|
709
|
+
E.append(edges)
|
710
|
+
num_edges += len(edges)
|
711
|
+
# otherwise, we select a vertex from the boundary and extend the order
|
712
|
+
|
713
|
+
elif bitset_len(boundaries.rows[i]):
|
714
|
+
# We prepare the boundary for the selection of the next vertex.
|
715
|
+
# This is equivalent to consider an empty neighborhood.
|
716
|
+
bitset_copy(boundaries.rows[i + 1], boundaries.rows[i])
|
717
|
+
bitset_clear(boundaries.rows[i]) # to prevent doing twice this operation
|
718
|
+
E.append([])
|
719
|
+
|
720
|
+
else:
|
721
|
+
# We have considered all possible extensions
|
722
|
+
bitset_add(active, order[i])
|
723
|
+
i -= 1
|
724
|
+
if E:
|
725
|
+
num_edges -= len(E[-1])
|
726
|
+
E.pop()
|
727
|
+
continue
|
728
|
+
|
729
|
+
# We select a vertex from the boundary to add to the order
|
730
|
+
i += 1
|
731
|
+
boundary = boundaries.rows[i]
|
732
|
+
u = bitset_first(boundary)
|
733
|
+
order[i] = u
|
734
|
+
bitset_discard(boundary, u)
|
735
|
+
bitset_discard(active, u)
|
736
|
+
# prepare neighborhood of u
|
737
|
+
bitset_and(neighborhoods.rows[i], active, DG.rows[u])
|
738
|
+
j = bitset_len(neighborhoods.rows[i])
|
739
|
+
n_cpt[i] = bool(j) << j # 0 if not j else 2^j
|
740
|
+
|
741
|
+
sig_on()
|
742
|
+
binary_matrix_free(boundaries)
|
743
|
+
binary_matrix_free(neighborhoods)
|
744
|
+
binary_matrix_free(DG)
|
745
|
+
sig_off()
|
746
|
+
|
747
|
+
|
748
|
+
def connected_subgraph_iterator(G, k=None, bint vertices_only=False,
|
749
|
+
edges_only=False, labels=False, induced=True,
|
750
|
+
exactly_k=False):
|
751
|
+
r"""
|
752
|
+
Return an terator over the induced connected subgraphs of order at most `k`.
|
753
|
+
|
754
|
+
This method implements a iterator over the induced connected subgraphs of
|
755
|
+
the input (di)graph. An induced subgraph of a graph is another graph, formed
|
756
|
+
from a subset of the vertices of the graph and all of the edges connecting
|
757
|
+
pairs of vertices in that subset (:wikipedia:`Induced_subgraph`).
|
758
|
+
|
759
|
+
As for method :meth:`sage.graphs.generic_graph.connected_components`, edge
|
760
|
+
orientation is ignored. Hence, the directed graph with a single arc `0 \to
|
761
|
+
1` is considered connected.
|
762
|
+
|
763
|
+
INPUT:
|
764
|
+
|
765
|
+
- ``G`` -- a :class:`Graph` or a :class:`DiGraph`; loops and multiple edges
|
766
|
+
are allowed
|
767
|
+
|
768
|
+
- ``k`` -- (optional) integer; maximum order of the connected subgraphs to
|
769
|
+
report; by default, the method iterates over all connected subgraphs
|
770
|
+
(equivalent to ``k == n``)
|
771
|
+
|
772
|
+
- ``vertices_only`` -- boolean (default: ``False``); whether to return
|
773
|
+
(Di)Graph or list of vertices. This parameter is ignored when ``induced``
|
774
|
+
is ``True``.
|
775
|
+
|
776
|
+
- ``edges_only`` -- boolean (default: ``False``); whether to
|
777
|
+
return (Di)Graph or list of edges. When ``vertices_only`` is
|
778
|
+
``True``, this parameter is ignored.
|
779
|
+
|
780
|
+
- ``labels`` -- boolean (default: ``False``); whether to return labeled
|
781
|
+
edges or not. This parameter is used only when ``vertices_only`` is
|
782
|
+
``False`` and ``edges_only`` is ``True``.
|
783
|
+
|
784
|
+
- ``induced`` -- boolean (default: ``True``); whether to return induced
|
785
|
+
connected sub(di)graph only or also non-induced sub(di)graphs.
|
786
|
+
This parameter can be set to ``False`` for simple (di)graphs only.
|
787
|
+
|
788
|
+
- ``exactly_k`` -- boolean (default: ``False``); ``True`` if we only
|
789
|
+
return graphs of order `k`, ``False`` if we return graphs of order
|
790
|
+
at most `k`.
|
791
|
+
|
792
|
+
EXAMPLES::
|
793
|
+
|
794
|
+
sage: G = DiGraph([(1, 2), (2, 3), (3, 4), (4, 2)])
|
795
|
+
sage: list(G.connected_subgraph_iterator())
|
796
|
+
[Subgraph of (): Digraph on 1 vertex,
|
797
|
+
Subgraph of (): Digraph on 2 vertices,
|
798
|
+
Subgraph of (): Digraph on 3 vertices,
|
799
|
+
Subgraph of (): Digraph on 4 vertices,
|
800
|
+
Subgraph of (): Digraph on 3 vertices,
|
801
|
+
Subgraph of (): Digraph on 1 vertex,
|
802
|
+
Subgraph of (): Digraph on 2 vertices,
|
803
|
+
Subgraph of (): Digraph on 3 vertices,
|
804
|
+
Subgraph of (): Digraph on 2 vertices,
|
805
|
+
Subgraph of (): Digraph on 1 vertex,
|
806
|
+
Subgraph of (): Digraph on 2 vertices,
|
807
|
+
Subgraph of (): Digraph on 1 vertex]
|
808
|
+
sage: list(G.connected_subgraph_iterator(vertices_only=True))
|
809
|
+
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 4],
|
810
|
+
[2], [2, 3], [2, 3, 4], [2, 4], [3], [3, 4], [4]]
|
811
|
+
sage: list(G.connected_subgraph_iterator(k=2))
|
812
|
+
[Subgraph of (): Digraph on 1 vertex,
|
813
|
+
Subgraph of (): Digraph on 2 vertices,
|
814
|
+
Subgraph of (): Digraph on 1 vertex,
|
815
|
+
Subgraph of (): Digraph on 2 vertices,
|
816
|
+
Subgraph of (): Digraph on 2 vertices,
|
817
|
+
Subgraph of (): Digraph on 1 vertex,
|
818
|
+
Subgraph of (): Digraph on 2 vertices,
|
819
|
+
Subgraph of (): Digraph on 1 vertex]
|
820
|
+
sage: list(G.connected_subgraph_iterator(k=3, vertices_only=True, exactly_k=True))
|
821
|
+
[[1, 2, 3], [1, 2, 4], [2, 3, 4]]
|
822
|
+
sage: list(G.connected_subgraph_iterator(k=2, vertices_only=True))
|
823
|
+
[[1], [1, 2], [2], [2, 3], [2, 4], [3], [3, 4], [4]]
|
824
|
+
|
825
|
+
sage: G = DiGraph([(1, 2), (2, 1)])
|
826
|
+
sage: list(G.connected_subgraph_iterator())
|
827
|
+
[Subgraph of (): Digraph on 1 vertex,
|
828
|
+
Subgraph of (): Digraph on 2 vertices,
|
829
|
+
Subgraph of (): Digraph on 1 vertex]
|
830
|
+
sage: list(G.connected_subgraph_iterator(vertices_only=True))
|
831
|
+
[[1], [1, 2], [2]]
|
832
|
+
|
833
|
+
sage: G = graphs.CompleteGraph(3)
|
834
|
+
sage: len(list(G.connected_subgraph_iterator()))
|
835
|
+
7
|
836
|
+
sage: len(list(G.connected_subgraph_iterator(vertices_only=True)))
|
837
|
+
7
|
838
|
+
sage: len(list(G.connected_subgraph_iterator(edges_only=True)))
|
839
|
+
7
|
840
|
+
sage: len(list(G.connected_subgraph_iterator(induced=False)))
|
841
|
+
10
|
842
|
+
|
843
|
+
sage: G = DiGraph([(0, 1), (1, 0), (1, 2), (2, 1)])
|
844
|
+
sage: len(list(G.connected_subgraph_iterator()))
|
845
|
+
6
|
846
|
+
sage: len(list(G.connected_subgraph_iterator(vertices_only=True)))
|
847
|
+
6
|
848
|
+
sage: len(list(G.connected_subgraph_iterator(edges_only=True)))
|
849
|
+
6
|
850
|
+
sage: len(list(G.connected_subgraph_iterator(induced=False)))
|
851
|
+
18
|
852
|
+
|
853
|
+
TESTS:
|
854
|
+
|
855
|
+
The Path Graph of order `n` has `n (n + 1) / 2` connected subgraphs::
|
856
|
+
|
857
|
+
sage: G = graphs.PathGraph(10)
|
858
|
+
sage: len(list(G.connected_subgraph_iterator(vertices_only=True)))
|
859
|
+
55
|
860
|
+
sage: len(list(G.connected_subgraph_iterator(vertices_only=False)))
|
861
|
+
55
|
862
|
+
|
863
|
+
The Complete Graph of order `n` has `2^n - 1` connected subgraphs::
|
864
|
+
|
865
|
+
sage: G = graphs.CompleteGraph(5)
|
866
|
+
sage: len(list(G.connected_subgraph_iterator(vertices_only=False))) == 2**G.order() - 1
|
867
|
+
True
|
868
|
+
sage: G = graphs.CompleteGraph(6)
|
869
|
+
sage: len(list(G.connected_subgraph_iterator(vertices_only=True))) == 2**G.order() - 1
|
870
|
+
True
|
871
|
+
|
872
|
+
Checks that it works with general graphs and corner cases::
|
873
|
+
|
874
|
+
sage: G = DiGraph([(1, 2), (1, 2)], multiedges=True)
|
875
|
+
sage: len(list(G.connected_subgraph_iterator()))
|
876
|
+
3
|
877
|
+
sage: len(list(G.connected_subgraph_iterator(k=0)))
|
878
|
+
0
|
879
|
+
sage: len(list(G.connected_subgraph_iterator(vertices_only=True)))
|
880
|
+
3
|
881
|
+
|
882
|
+
sage: G = Graph([(1, 2), (1, 1)], loops=True)
|
883
|
+
sage: len(list(G.connected_subgraph_iterator(vertices_only=False)))
|
884
|
+
3
|
885
|
+
sage: G = Graph([(1, 2), (1, 2), (1, 1)], loops=True, multiedges=True)
|
886
|
+
sage: len(list(G.connected_subgraph_iterator()))
|
887
|
+
3
|
888
|
+
sage: len(list(G.connected_subgraph_iterator(k=1)))
|
889
|
+
2
|
890
|
+
sage: len(list(G.connected_subgraph_iterator(vertices_only=True)))
|
891
|
+
3
|
892
|
+
"""
|
893
|
+
if not induced:
|
894
|
+
G._scream_if_not_simple()
|
895
|
+
vertices_only = False
|
896
|
+
|
897
|
+
cdef Py_ssize_t mk = G.order() if k is None else k
|
898
|
+
cdef Py_ssize_t n = G.order()
|
899
|
+
if not n or mk < 1:
|
900
|
+
return
|
901
|
+
|
902
|
+
cdef list int_to_vertex = list(G)
|
903
|
+
cdef binary_matrix_t DG
|
904
|
+
sig_on()
|
905
|
+
dense_graph_init(DG, G, translation=int_to_vertex, force_undirected=True)
|
906
|
+
|
907
|
+
# We use a stack of bitsets. We need 3 bitsets per level with at most n + 1
|
908
|
+
# levels, so 3 * n + 3 bitsets. We also need 1 bitset that we create at the
|
909
|
+
# same time, so we need overall 3 * n + 4 bitsets
|
910
|
+
cdef binary_matrix_t stack
|
911
|
+
binary_matrix_init(stack, 3 * n + 4, n)
|
912
|
+
sig_off()
|
913
|
+
|
914
|
+
cdef bitset_t current # current subset of vertices
|
915
|
+
cdef bitset_t left # remaining vertices to consider
|
916
|
+
cdef bitset_t boundary # neighbors of the current subset
|
917
|
+
# candidate vertices for extending the current subset, i.e., vertices that
|
918
|
+
# are both in left and in boundary
|
919
|
+
cdef bitset_t candidates = stack.rows[3 * n + 3]
|
920
|
+
|
921
|
+
cdef Py_ssize_t level
|
922
|
+
cdef Py_ssize_t u, v, a
|
923
|
+
cdef list vertices
|
924
|
+
|
925
|
+
# We first generate subsets containing vertex 0, then the subsets containing
|
926
|
+
# vertex 1 but not vertex 0 since we have already generated all subsets
|
927
|
+
# containing 0, then subsets containing 2 but not 0 or 1, etc.
|
928
|
+
for u in range(n):
|
929
|
+
sig_check()
|
930
|
+
|
931
|
+
vertices = [int_to_vertex[u]]
|
932
|
+
if not exactly_k or mk == 1:
|
933
|
+
if vertices_only:
|
934
|
+
yield vertices
|
935
|
+
else:
|
936
|
+
H = G.subgraph(vertices)
|
937
|
+
if edges_only:
|
938
|
+
yield H.edges(sort=False, labels=labels)
|
939
|
+
else:
|
940
|
+
yield H
|
941
|
+
|
942
|
+
# We initialize the loop with vertices u in current, {u+1, ..., n-1}
|
943
|
+
# in left, and N(u) in boundary
|
944
|
+
bitset_clear(stack.rows[0])
|
945
|
+
bitset_add(stack.rows[0], u)
|
946
|
+
bitset_set_first_n(stack.rows[1], u + 1)
|
947
|
+
bitset_complement(stack.rows[1], stack.rows[1])
|
948
|
+
bitset_copy(stack.rows[2], DG.rows[u])
|
949
|
+
level = 0
|
950
|
+
|
951
|
+
while level >= 0:
|
952
|
+
sig_check()
|
953
|
+
|
954
|
+
# We take the values at the top of the stack
|
955
|
+
current = stack.rows[level]
|
956
|
+
left = stack.rows[level + 1]
|
957
|
+
boundary = stack.rows[level + 2]
|
958
|
+
|
959
|
+
bitset_and(candidates, left, boundary)
|
960
|
+
|
961
|
+
# Search for a candidate vertex v
|
962
|
+
v = bitset_next(candidates, u + 1)
|
963
|
+
if v >= 0 and bitset_len(current) < mk:
|
964
|
+
# We select vertex v
|
965
|
+
bitset_discard(left, v)
|
966
|
+
|
967
|
+
# Since we have not modified 'level', the bitsets for iterating
|
968
|
+
# without v are already at the top of the stack, with correct
|
969
|
+
# values
|
970
|
+
|
971
|
+
# We also build the subset with v and so we add values at the
|
972
|
+
# top of the stack
|
973
|
+
level += 3
|
974
|
+
bitset_copy(stack.rows[level], current)
|
975
|
+
bitset_add(stack.rows[level], v)
|
976
|
+
bitset_copy(stack.rows[level + 1], left)
|
977
|
+
bitset_union(stack.rows[level + 2], boundary, DG.rows[v])
|
978
|
+
|
979
|
+
# We yield that new subset
|
980
|
+
vertices = [int_to_vertex[a] for a in range(u, n)
|
981
|
+
if bitset_in(stack.rows[level], a)]
|
982
|
+
if not exactly_k or bitset_len(current) == mk - 1:
|
983
|
+
if vertices_only:
|
984
|
+
yield vertices
|
985
|
+
else:
|
986
|
+
H = G.subgraph(vertices)
|
987
|
+
if induced:
|
988
|
+
if edges_only:
|
989
|
+
yield H.edges(sort=False, labels=labels)
|
990
|
+
else:
|
991
|
+
yield H
|
992
|
+
else:
|
993
|
+
# We use a decomposition into biconnected components to
|
994
|
+
# work on smaller graphs.
|
995
|
+
if H.is_directed():
|
996
|
+
blocks = H.to_undirected().blocks_and_cut_vertices()[0]
|
997
|
+
else:
|
998
|
+
blocks = H.blocks_and_cut_vertices()[0]
|
999
|
+
if len(blocks) == 1:
|
1000
|
+
# H is strongly connected or biconnected
|
1001
|
+
yield from connected_full_subgraphs(H, edges_only=edges_only,
|
1002
|
+
labels=labels)
|
1003
|
+
else:
|
1004
|
+
L = []
|
1005
|
+
for bloc in blocks:
|
1006
|
+
if len(bloc) == 2:
|
1007
|
+
bb = [[e] for e in H.edge_boundary(bloc, bloc, labels=labels)]
|
1008
|
+
if len(bb) == 2:
|
1009
|
+
# H is directed with edges (u, v) and (v, u)
|
1010
|
+
bb.append(H.edge_boundary(bloc, bloc, labels=labels))
|
1011
|
+
L.append(bb)
|
1012
|
+
else:
|
1013
|
+
L.append(connected_full_subgraphs(H.subgraph(vertices=bloc),
|
1014
|
+
edges_only=True, labels=labels))
|
1015
|
+
|
1016
|
+
for edges in product(*L):
|
1017
|
+
good_edges = flatten(edges, ltypes=list)
|
1018
|
+
if edges_only:
|
1019
|
+
yield list(good_edges)
|
1020
|
+
else:
|
1021
|
+
yield H.subgraph(vertices=H, edges=good_edges)
|
1022
|
+
|
1023
|
+
else:
|
1024
|
+
# We cannot extend the current subset, either due to a lack of
|
1025
|
+
# candidate (v == -1), or because the current subset has maximum
|
1026
|
+
# size. We pop
|
1027
|
+
level -= 3
|
1028
|
+
|
1029
|
+
sig_on()
|
1030
|
+
binary_matrix_free(stack)
|
1031
|
+
binary_matrix_free(DG)
|
1032
|
+
sig_off()
|