passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,1872 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # cython: binding=True
3
+ # distutils: language = c++
4
+ # distutils: extra_compile_args = -std=c++11
5
+ r"""
6
+ Graph traversals
7
+
8
+ **This module implements the following graph traversals**
9
+
10
+ .. csv-table::
11
+ :class: contentstable
12
+ :widths: 30, 70
13
+ :delim: |
14
+
15
+ :meth:`~lex_BFS` | Perform a lexicographic breadth first search (LexBFS) on the graph.
16
+ :meth:`~lex_DFS` | Perform a lexicographic depth first search (LexDFS) on the graph.
17
+ :meth:`~lex_UP` | Perform a lexicographic UP search (LexUP) on the graph.
18
+ :meth:`~lex_DOWN` | Perform a lexicographic DOWN search (LexDOWN) on the graph.
19
+ :meth:`~lex_M` | Return an ordering of the vertices according the LexM graph traversal.
20
+ :meth:`~lex_M_slow` | Return an ordering of the vertices according the LexM graph traversal.
21
+ :meth:`~lex_M_fast` | Return an ordering of the vertices according the LexM graph traversal.
22
+ :meth:`~maximum_cardinality_search` | Return an ordering of the vertices according a maximum cardinality search.
23
+ :meth:`~maximum_cardinality_search_M` | Return the ordering and the edges of the triangulation produced by MCS-M.
24
+
25
+
26
+ ALGORITHM:
27
+
28
+ For :meth:`~lex_BFS` with ``algorithm="slow"``, :meth:`~lex_DFS`,
29
+ :meth:`~lex_UP` and :meth:`~lex_DOWN` the same generic implementation is used.
30
+ It corresponds to an implementation the generic algorithm described in
31
+ "Algorithm 1" of [Mil2017]_.
32
+
33
+ This algorithm maintains for each vertex left in the graph a lexicographic label
34
+ corresponding to the vertices already removed. The vertex of maximal
35
+ lexicographic label is then removed, and the lexicographic labels of its
36
+ neighbors are updated. Depending on how the update is done, it corresponds to
37
+ LexBFS, LexUP, LexDFS or LexDOWN: during the `i`-th iteration of the algorithm
38
+ `n-i` (for LexBFS and LexDOWN) or `i` (for LexDFS and LexUP) is appended (for
39
+ LexBFS and LexUP) or prepended (for LexDFS and LexDOWN) to the lexicographic
40
+ labels of all neighbors of the selected vertex that are left in the graph.
41
+
42
+ The time complexity of the algorithm is `O(mn)` for ``SparseGraph`` and
43
+ `O(\max\{mn, n^2\})` for ``DenseGraph``, where `n` is the number of vertices
44
+ and `m` is the number of edges.
45
+
46
+ See [CK2008]_ and [Mil2017]_ for more details on the algorithm and graphs
47
+ searching.
48
+
49
+ Methods
50
+ -------
51
+ """
52
+ # ****************************************************************************
53
+ # Copyright (C) 2019 Georgios Giapitzakis Tzintanos <giorgosgiapis@mail.com>
54
+ # David Coudert <david.coudert@inria.fr>
55
+ # 2024 Cyril Bouvier <cyril.bouvier@lirmm.fr>
56
+ #
57
+ # This program is free software: you can redistribute it and/or modify
58
+ # it under the terms of the GNU General Public License as published by
59
+ # the Free Software Foundation, either version 2 of the License, or
60
+ # (at your option) any later version.
61
+ # https://www.gnu.org/licenses/
62
+ # ****************************************************************************
63
+
64
+ from collections import deque
65
+
66
+ from libc.stdint cimport uint32_t
67
+ from libcpp.vector cimport vector
68
+ from cysignals.signals cimport sig_on, sig_off
69
+ from memory_allocator cimport MemoryAllocator
70
+
71
+ from sage.data_structures.pairing_heap cimport PairingHeap_of_n_integers
72
+ from sage.graphs.base.c_graph cimport CGraph, CGraphBackend
73
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph
74
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend
75
+ from sage.graphs.base.static_sparse_graph cimport init_short_digraph
76
+ from sage.graphs.base.static_sparse_graph cimport free_short_digraph
77
+ from sage.graphs.graph_decompositions.slice_decomposition cimport \
78
+ extended_lex_BFS
79
+
80
+
81
+ def _lex_order_common(G, algo, reverse, tree, initial_vertex):
82
+ r"""
83
+ Perform a lexicographic search (LexBFS, LexUP, LexDFS or LexDOWN) on the
84
+ graph.
85
+
86
+ INPUT:
87
+
88
+ - ``G`` -- a sage graph
89
+
90
+ - ``algo`` -- string; the name of the actual algorithm among:
91
+
92
+ - ``"lex_BFS"``
93
+
94
+ - ``"lex_UP"``
95
+
96
+ - ``"lex_DFS"``
97
+
98
+ - ``"lex_DOWN"``
99
+
100
+ - ``reverse`` -- whether to return the vertices in discovery order, or the
101
+ reverse
102
+
103
+ - ``tree`` -- whether to return the discovery directed tree (each vertex
104
+ being linked to the one that saw it last)
105
+
106
+ - ``initial_vertex`` -- the first vertex to consider, can be None
107
+
108
+ .. NOTE::
109
+
110
+ Loops and multiple edges are ignored and directed graphs are considered
111
+ as undirected graphs.
112
+
113
+ ALGORITHM:
114
+
115
+ See the documentation of the :mod:`~sage.graphs.traversals` module.
116
+
117
+ TESTS:
118
+
119
+ Lex ordering of a graph on one vertex::
120
+
121
+ sage: Graph(1).lex_BFS(tree=True, algorithm="slow")
122
+ ([0], Digraph on 1 vertex)
123
+ sage: Graph(1).lex_UP(tree=True)
124
+ ([0], Digraph on 1 vertex)
125
+ sage: Graph(1).lex_DFS(tree=True)
126
+ ([0], Digraph on 1 vertex)
127
+ sage: Graph(1).lex_DOWN(tree=True)
128
+ ([0], Digraph on 1 vertex)
129
+
130
+ Lex ordering of an empty (di)graph is an empty sequence::
131
+
132
+ sage: g = Graph()
133
+ sage: g.lex_BFS(algorithm="slow")
134
+ []
135
+ sage: g.lex_BFS(algorithm="slow", tree=True)
136
+ ([], Digraph on 0 vertices)
137
+ sage: g.lex_UP()
138
+ []
139
+ sage: g.lex_UP(tree=True)
140
+ ([], Digraph on 0 vertices)
141
+ sage: g.lex_DFS()
142
+ []
143
+ sage: g.lex_DFS(tree=True)
144
+ ([], Digraph on 0 vertices)
145
+ sage: g.lex_DOWN()
146
+ []
147
+ sage: g.lex_DFS(tree=True)
148
+ ([], Digraph on 0 vertices)
149
+
150
+ Lex UP ordering of a symmetric digraph should be the same as the Lex UP
151
+ ordering of the corresponding undirected graph::
152
+
153
+ sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)])
154
+ sage: H = DiGraph(G)
155
+ sage: G.lex_BFS(algorithm="slow") == H.lex_BFS(algorithm="slow")
156
+ True
157
+ sage: G.lex_UP() == H.lex_UP()
158
+ True
159
+ sage: G.lex_DFS() == H.lex_DFS()
160
+ True
161
+ sage: G.lex_DOWN() == H.lex_DOWN()
162
+ True
163
+
164
+ ``initial_vertex`` should be a valid graph vertex::
165
+
166
+ sage: G = graphs.CompleteGraph(6)
167
+ sage: G.lex_BFS(initial_vertex='foo', algorithm="slow")
168
+ Traceback (most recent call last):
169
+ ...
170
+ ValueError: 'foo' is not a graph vertex
171
+ sage: G.lex_UP(initial_vertex='foo')
172
+ Traceback (most recent call last):
173
+ ...
174
+ ValueError: 'foo' is not a graph vertex
175
+ sage: G.lex_DFS(initial_vertex='foo')
176
+ Traceback (most recent call last):
177
+ ...
178
+ ValueError: 'foo' is not a graph vertex
179
+ sage: G.lex_DOWN(initial_vertex='foo')
180
+ Traceback (most recent call last):
181
+ ...
182
+ ValueError: 'foo' is not a graph vertex
183
+ """
184
+ if initial_vertex is not None and initial_vertex not in G:
185
+ raise ValueError(f"'{initial_vertex}' is not a graph vertex")
186
+
187
+ if algo not in ("lex_BFS", "lex_UP", "lex_DFS", "lex_DOWN"):
188
+ raise ValueError(f"unknown algorithm '{algo}'")
189
+
190
+ cdef size_t n = G.order()
191
+ cdef list sigma = []
192
+ cdef dict predecessor = {}
193
+
194
+ cdef bint right = algo in ("lex_BFS", "lex_UP")
195
+ cdef bint decr = algo in ("lex_BFS", "lex_DOWN")
196
+
197
+ cdef size_t cur_label = n if decr else -1
198
+ cdef int label_incr = -1 if decr else 1
199
+
200
+ # Perform the search
201
+ lexicographic_label = {u: deque() for u in G}
202
+ if initial_vertex is not None:
203
+ # append or appendleft does not matter here, as the deque is empty
204
+ lexicographic_label[initial_vertex].append(cur_label)
205
+ while lexicographic_label:
206
+ u = max(lexicographic_label, key=lexicographic_label.get)
207
+ lexicographic_label.pop(u)
208
+ sigma.append(u)
209
+ cur_label += label_incr
210
+ for v in G.neighbor_iterator(u): # graphs are considered undirected
211
+ if v in lexicographic_label:
212
+ if right:
213
+ lexicographic_label[v].append(cur_label)
214
+ else:
215
+ lexicographic_label[v].appendleft(cur_label)
216
+ predecessor[v] = u
217
+
218
+ if reverse:
219
+ sigma.reverse()
220
+
221
+ if tree:
222
+ from sage.graphs.digraph import DiGraph
223
+ edges = predecessor.items()
224
+ g = DiGraph([G, edges], format='vertices_and_edges', sparse=True)
225
+ return sigma, g
226
+ return sigma
227
+
228
+
229
+ def _is_valid_lex_BFS_order(G, L):
230
+ r"""
231
+ Check whether ``L`` is a valid LexBFS ordering of the vertices of ``G``.
232
+
233
+ Given two vertices `a` and `b` of `G = (V, E)`, we write `a < b` if `a` has
234
+ a smaller label than `b`, and so if `a` is after `b` in the ordering `L`.
235
+ It is proved in [DNB1996]_ that any LexBFS ordering satisfies that,
236
+ if `a < b < c` and `ac \in E` and `bc \not\in E`, then there exists `d\in V`
237
+ such that `c < d`, `db \in E` and `da \not\in E`.
238
+
239
+ INPUT:
240
+
241
+ - ``G`` -- a sage Graph
242
+
243
+ - ``L`` -- list; an ordering of the vertices of `G`
244
+
245
+ OUTPUT:
246
+
247
+ - ``True`` if ``L`` is a LexBFS ordering of ``G``; ``False`` otherwise
248
+
249
+ .. NOTE::
250
+
251
+ Loops and multiple edges are ignored for LexBFS ordering and directed
252
+ graphs are considered as undirected graphs.
253
+
254
+ .. SEEALSO::
255
+
256
+ * :wikipedia:`Lexicographic_breadth-first_search`
257
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_BFS` -- perform a
258
+ lexicographic depth first search (LexBFS) on the graph
259
+
260
+ TESTS::
261
+
262
+ sage: from sage.graphs.traversals import _is_valid_lex_BFS_order
263
+ sage: G = graphs.PathGraph(3)
264
+ sage: _is_valid_lex_BFS_order(G, [0, 1, 2])
265
+ True
266
+ sage: _is_valid_lex_BFS_order(G, [0, 2, 1])
267
+ False
268
+
269
+ sage: G = DiGraph("I?O@??A?CCA?A??C??")
270
+ sage: _is_valid_lex_BFS_order(G, [0, 7, 1, 2, 3, 4, 5, 8, 6, 9])
271
+ False
272
+ sage: _is_valid_lex_BFS_order(G, G.lex_BFS())
273
+ True
274
+ sage: H = G.to_undirected()
275
+ sage: _is_valid_lex_BFS_order(H, G.lex_BFS())
276
+ True
277
+ sage: _is_valid_lex_BFS_order(G, H.lex_BFS())
278
+ True
279
+ """
280
+ # Convert G to a simple undirected graph
281
+ if G.has_loops() or G.has_multiple_edges() or G.is_directed():
282
+ G = G.to_simple(immutable=False, to_undirected=True)
283
+
284
+ cdef int n = G.order()
285
+
286
+ if set(L) != set(G):
287
+ return False
288
+
289
+ cdef dict L_inv = {u: i for i, u in enumerate(L)}
290
+ cdef int pos_a, pos_b, pos_c
291
+
292
+ for pos_a in range(n - 1, -1, -1):
293
+ a = L[pos_a]
294
+ for c in G.neighbor_iterator(a):
295
+ pos_c = L_inv[c]
296
+ if pos_c > pos_a:
297
+ continue
298
+ for pos_b in range(pos_c + 1, pos_a):
299
+ b = L[pos_b]
300
+ if G.has_edge(c, b):
301
+ continue
302
+ if any(L_inv[d] < pos_c and not G.has_edge(d, a)
303
+ for d in G.neighbor_iterator(b)):
304
+ # The condition is satisfied for a < b < c
305
+ continue
306
+ return False
307
+ return True
308
+
309
+
310
+ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast"):
311
+ r"""
312
+ Perform a lexicographic breadth first search (LexBFS) on the graph.
313
+
314
+ INPUT:
315
+
316
+ - ``G`` -- a sage graph
317
+
318
+ - ``reverse`` -- boolean (default: ``False``); whether to return the
319
+ vertices in discovery order, or the reverse
320
+
321
+ - ``tree`` -- boolean (default: ``False``); whether to return the
322
+ discovery directed tree (each vertex being linked to the one that saw
323
+ it last)
324
+
325
+ - ``initial_vertex`` -- (default: ``None``) the first vertex to
326
+ consider
327
+
328
+ - ``algorithm`` -- string (default: ``'fast'``); algorithm to use among:
329
+
330
+ - ``'slow'`` -- it use the generic algorithm for all the lexicographic
331
+ searchs. See the documentation of the :mod:`~sage.graphs.traversals`
332
+ module for more details.
333
+
334
+ - ``'fast'`` -- this algorithm uses the notion of *partition refinement*
335
+ to determine the position of the vertices in the ordering. The time
336
+ complexity of this algorithm is in `O(n + m)`, and our implementation
337
+ follows that complexity for ``SparseGraph``. For ``DenseGraph``,
338
+ the complexity is `O(n^2)`. See [HMPV2000]_ and [TCHP2008]_ for more
339
+ details. This algorithm is also used to compute slice decompositions of
340
+ undirected graphs, a more thorough description can be found in the
341
+ documentation of the
342
+ :mod:`~sage.graphs.graph_decompositions.slice_decomposition` module.
343
+
344
+ .. NOTE::
345
+
346
+ Loops and multiple edges are ignored during the computation of
347
+ ``lex_BFS`` and directed graphs are converted to undirected graphs.
348
+
349
+ .. SEEALSO::
350
+
351
+ * :wikipedia:`Lexicographic_breadth-first_search`
352
+ * :mod:`~sage.graphs.graph_decompositions.slice_decomposition` module
353
+ and :meth:`~sage.graphs.graph.Graph.slice_decomposition` -- compute a
354
+ slice decomposition of the graph using an extended lex BFS algorithm
355
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DFS` -- perform a
356
+ lexicographic depth first search (LexDFS) on the graph
357
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_UP` -- perform a
358
+ lexicographic UP search (LexUP) on the graph
359
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DOWN` -- perform a
360
+ lexicographic DOWN search (LexDOWN) on the graph
361
+
362
+ EXAMPLES:
363
+
364
+ A Lex BFS is obviously an ordering of the vertices::
365
+
366
+ sage: g = graphs.CompleteGraph(6)
367
+ sage: set(g.lex_BFS()) == set(g)
368
+ True
369
+
370
+ Lex BFS ordering of the 3-sun graph::
371
+
372
+ sage: g = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)])
373
+ sage: g.lex_BFS()
374
+ [1, 2, 3, 5, 4, 6]
375
+
376
+ The method also works for directed graphs::
377
+
378
+ sage: G = DiGraph([(1, 2), (2, 3), (1, 3)])
379
+ sage: correct_anwsers = [[2, 1, 3], [2, 3, 1]]
380
+ sage: G.lex_BFS(initial_vertex=2, algorithm='slow') in correct_anwsers
381
+ True
382
+ sage: G.lex_BFS(initial_vertex=2, algorithm='fast') in correct_anwsers
383
+ True
384
+
385
+ For a Chordal Graph, a reversed Lex BFS is a Perfect Elimination Order::
386
+
387
+ sage: g = graphs.PathGraph(3).lexicographic_product(graphs.CompleteGraph(2))
388
+ sage: g.lex_BFS(reverse=True)
389
+ [(2, 1), (2, 0), (1, 1), (1, 0), (0, 1), (0, 0)]
390
+
391
+ And the vertices at the end of the tree of discovery are, for chordal
392
+ graphs, simplicial vertices (their neighborhood is a complete graph)::
393
+
394
+ sage: g = graphs.ClawGraph().lexicographic_product(graphs.CompleteGraph(2))
395
+ sage: v = g.lex_BFS()[-1]
396
+ sage: peo, tree = g.lex_BFS(initial_vertex = v, tree=True)
397
+ sage: leaves = [v for v in tree if tree.in_degree(v) ==0]
398
+ sage: all(g.subgraph(g.neighbors(v)).is_clique() for v in leaves)
399
+ True
400
+
401
+ Different orderings for different traversals::
402
+
403
+ sage: # needs sage.combinat
404
+ sage: G = digraphs.DeBruijn(2,3)
405
+ sage: G.lex_BFS(initial_vertex='000', algorithm='fast')
406
+ ['000', '001', '100', '010', '011', '110', '101', '111']
407
+ sage: G.lex_BFS(initial_vertex='000', algorithm='slow')
408
+ ['000', '001', '100', '010', '011', '110', '101', '111']
409
+ sage: G.lex_DFS(initial_vertex='000')
410
+ ['000', '001', '100', '010', '101', '110', '011', '111']
411
+ sage: G.lex_UP(initial_vertex='000')
412
+ ['000', '001', '010', '101', '110', '111', '011', '100']
413
+ sage: G.lex_DOWN(initial_vertex='000')
414
+ ['000', '001', '100', '011', '010', '110', '111', '101']
415
+
416
+ TESTS:
417
+
418
+ Computed orderings are valid::
419
+
420
+ sage: from sage.graphs.traversals import _is_valid_lex_BFS_order
421
+ sage: G = graphs.RandomChordalGraph(15)
422
+ sage: v0 = ZZ.random_element(G.order())
423
+ sage: L = G.lex_BFS(initial_vertex=v0, algorithm='fast')
424
+ sage: _is_valid_lex_BFS_order(G, L)
425
+ True
426
+ sage: L = G.lex_BFS(initial_vertex=v0, algorithm='slow')
427
+ sage: _is_valid_lex_BFS_order(G, L)
428
+ True
429
+ sage: G = digraphs.RandomDirectedGNP(15, .3)
430
+ sage: v0 = ZZ.random_element(G.order())
431
+ sage: L = G.lex_BFS(initial_vertex=v0, algorithm='fast')
432
+ sage: _is_valid_lex_BFS_order(G, L)
433
+ True
434
+ sage: L = G.lex_BFS(initial_vertex=v0, algorithm='slow')
435
+ sage: _is_valid_lex_BFS_order(G, L)
436
+ True
437
+
438
+ Lex BFS ordering of a graph on one vertex::
439
+
440
+ sage: Graph(1).lex_BFS(algorithm="fast", tree=True)
441
+ ([0], Digraph on 1 vertex)
442
+
443
+ Lex BFS ordering of an empty (di)graph is an empty sequence::
444
+
445
+ sage: g = Graph()
446
+ sage: g.lex_BFS(algorithm="fast")
447
+ []
448
+ sage: g.lex_BFS(algorithm="fast", tree=True)
449
+ ([], Digraph on 0 vertices)
450
+
451
+ Lex BFS ordering of a symmetric digraph should be the same as the Lex BFS
452
+ ordering of the corresponding undirected graph::
453
+
454
+ sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)])
455
+ sage: H = DiGraph(G)
456
+ sage: G.lex_BFS(algorithm="fast") == H.lex_BFS(algorithm="fast")
457
+ True
458
+
459
+ ``initial_vertex`` should be a valid graph vertex::
460
+
461
+ sage: G = graphs.CompleteGraph(6)
462
+ sage: G.lex_BFS(algorithm="fast", initial_vertex='foo')
463
+ Traceback (most recent call last):
464
+ ...
465
+ ValueError: 'foo' is not a graph vertex
466
+
467
+ Check that :issue:`39934` is fixed::
468
+
469
+ sage: G = Graph(1, immutable=True)
470
+ sage: G.lex_BFS(algorithm='slow')
471
+ [0]
472
+ sage: G.lex_BFS(algorithm='fast')
473
+ [0]
474
+ """
475
+ if initial_vertex is not None and initial_vertex not in G:
476
+ raise ValueError(f"'{initial_vertex}' is not a graph vertex")
477
+
478
+ if algorithm == "slow":
479
+ return _lex_order_common(G, "lex_BFS", reverse, tree, initial_vertex)
480
+
481
+ if algorithm != "fast":
482
+ raise ValueError(f"unknown algorithm '{algorithm}'")
483
+
484
+ # For algorithm "fast" we need to convert G to an undirected graph
485
+ if G.is_directed():
486
+ G = G.to_undirected()
487
+
488
+ # Initialize variables needed by the fast algorithm
489
+ cdef CGraphBackend Gbackend = <CGraphBackend> G._backend
490
+ cdef CGraph cg = Gbackend.cg()
491
+ cdef vector[int] sigma_int
492
+ cdef vector[int] pred
493
+ # Temporary variables
494
+ cdef int vi, i, initial_v_int
495
+
496
+ # Perform Lex BFS
497
+ if initial_vertex is not None:
498
+ # we already checked that initial_vertex is in G
499
+ initial_v_int = Gbackend.get_vertex(initial_vertex)
500
+ else:
501
+ initial_v_int = -1
502
+ sig_on()
503
+ extended_lex_BFS(cg, sigma_int, NULL, initial_v_int, &pred, NULL, NULL)
504
+ sig_off()
505
+ cdef list sigma = [Gbackend.vertex_label(vi) for vi in sigma_int]
506
+ cdef dict predecessor = {u: sigma[i] for u, i in zip(sigma, pred) if i != -1}
507
+
508
+ if reverse:
509
+ sigma.reverse()
510
+
511
+ if tree:
512
+ from sage.graphs.digraph import DiGraph
513
+ edges = predecessor.items()
514
+ g = DiGraph([G, edges], format='vertices_and_edges', sparse=True)
515
+ return sigma, g
516
+ return sigma
517
+
518
+
519
+ def lex_UP(G, reverse=False, tree=False, initial_vertex=None):
520
+ r"""
521
+ Perform a lexicographic UP search (LexUP) on the graph.
522
+
523
+ INPUT:
524
+
525
+ - ``G`` -- a sage graph
526
+
527
+ - ``reverse`` -- boolean (default: ``False``); whether to return the
528
+ vertices in discovery order, or the reverse
529
+
530
+ - ``tree`` -- boolean (default: ``False``); whether to return the
531
+ discovery directed tree (each vertex being linked to the one that saw
532
+ it last)
533
+
534
+ - ``initial_vertex`` -- (default: ``None``) the first vertex to
535
+ consider
536
+
537
+ .. NOTE::
538
+
539
+ Loops and multiple edges are ignored during the computation of
540
+ ``lex_UP`` and directed graphs are converted to undirected graphs.
541
+
542
+ .. SEEALSO::
543
+
544
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_BFS` -- perform a
545
+ lexicographic breadth first search (LexBFS) on the graph
546
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DFS` -- perform a
547
+ lexicographic depth first search (LexDFS) on the graph
548
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DOWN` -- perform a
549
+ lexicographic DOWN search (LexDOWN) on the graph
550
+
551
+ ALGORITHM:
552
+
553
+ See the documentation of the :mod:`~sage.graphs.traversals` module.
554
+
555
+ EXAMPLES:
556
+
557
+ A Lex UP is obviously an ordering of the vertices::
558
+
559
+ sage: g = graphs.CompleteGraph(6)
560
+ sage: set(g.lex_UP()) == set(g)
561
+ True
562
+
563
+ Lex UP ordering of the 3-sun graph::
564
+
565
+ sage: g = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)])
566
+ sage: g.lex_UP()
567
+ [1, 2, 4, 5, 6, 3]
568
+
569
+ The method also works for directed graphs::
570
+
571
+ sage: G = DiGraph([(1, 2), (2, 3), (1, 3)])
572
+ sage: correct_anwsers = [[2, 1, 3], [2, 3, 1]]
573
+ sage: G.lex_UP(initial_vertex=2) in correct_anwsers
574
+ True
575
+
576
+ Different orderings for different traversals::
577
+
578
+ sage: # needs sage.combinat
579
+ sage: G = digraphs.DeBruijn(2,3)
580
+ sage: G.lex_BFS(initial_vertex='000')
581
+ ['000', '001', '100', '010', '011', '110', '101', '111']
582
+ sage: G.lex_DFS(initial_vertex='000')
583
+ ['000', '001', '100', '010', '101', '110', '011', '111']
584
+ sage: G.lex_UP(initial_vertex='000')
585
+ ['000', '001', '010', '101', '110', '111', '011', '100']
586
+ sage: G.lex_DOWN(initial_vertex='000')
587
+ ['000', '001', '100', '011', '010', '110', '111', '101']
588
+ """
589
+ return _lex_order_common(G, "lex_UP", reverse, tree, initial_vertex)
590
+
591
+
592
+ def lex_DFS(G, reverse=False, tree=False, initial_vertex=None):
593
+ r"""
594
+ Perform a lexicographic depth first search (LexDFS) on the graph.
595
+
596
+ INPUT:
597
+
598
+ - ``G`` -- a sage graph
599
+
600
+ - ``reverse`` -- boolean (default: ``False``); whether to return the
601
+ vertices in discovery order, or the reverse
602
+
603
+ - ``tree`` -- boolean (default: ``False``); whether to return the
604
+ discovery directed tree (each vertex being linked to the one that saw
605
+ it last)
606
+
607
+ - ``initial_vertex`` -- (default: ``None``) the first vertex to
608
+ consider
609
+
610
+ .. NOTE::
611
+
612
+ Loops and multiple edges are ignored during the computation of
613
+ ``lex_DFS`` and directed graphs are converted to undirected graphs.
614
+
615
+ .. SEEALSO::
616
+
617
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_BFS` -- perform a
618
+ lexicographic breadth first search (LexBFS) on the graph
619
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_UP` -- perform a
620
+ lexicographic UP search (LexUP) on the graph
621
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DOWN` -- perform a
622
+ lexicographic DOWN search (LexDOWN) on the graph
623
+
624
+ ALGORITHM:
625
+
626
+ See the documentation of the :mod:`~sage.graphs.traversals` module.
627
+
628
+ EXAMPLES:
629
+
630
+ A Lex DFS is obviously an ordering of the vertices::
631
+
632
+ sage: g = graphs.CompleteGraph(6)
633
+ sage: set(g.lex_DFS()) == set(g)
634
+ True
635
+
636
+ Lex DFS ordering of the 3-sun graph::
637
+
638
+ sage: g = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)])
639
+ sage: g.lex_DFS()
640
+ [1, 2, 3, 5, 6, 4]
641
+
642
+ The method also works for directed graphs::
643
+
644
+ sage: G = DiGraph([(1, 2), (2, 3), (1, 3)])
645
+ sage: correct_anwsers = [[2, 1, 3], [2, 3, 1]]
646
+ sage: G.lex_DFS(initial_vertex=2) in correct_anwsers
647
+ True
648
+
649
+ Different orderings for different traversals::
650
+
651
+ sage: # needs sage.combinat
652
+ sage: G = digraphs.DeBruijn(2,3)
653
+ sage: G.lex_BFS(initial_vertex='000')
654
+ ['000', '001', '100', '010', '011', '110', '101', '111']
655
+ sage: G.lex_DFS(initial_vertex='000')
656
+ ['000', '001', '100', '010', '101', '110', '011', '111']
657
+ sage: G.lex_UP(initial_vertex='000')
658
+ ['000', '001', '010', '101', '110', '111', '011', '100']
659
+ sage: G.lex_DOWN(initial_vertex='000')
660
+ ['000', '001', '100', '011', '010', '110', '111', '101']
661
+ """
662
+ return _lex_order_common(G, "lex_DFS", reverse, tree, initial_vertex)
663
+
664
+
665
+ def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None):
666
+ r"""
667
+ Perform a lexicographic DOWN search (LexDOWN) on the graph.
668
+
669
+ INPUT:
670
+
671
+ - ``G`` -- a sage graph
672
+
673
+ - ``reverse`` -- boolean (default: ``False``); whether to return the
674
+ vertices in discovery order, or the reverse
675
+
676
+ - ``tree`` -- boolean (default: ``False``); whether to return the
677
+ discovery directed tree (each vertex being linked to the one that saw
678
+ it)
679
+
680
+ - ``initial_vertex`` -- (default: ``None``) the first vertex to
681
+ consider
682
+
683
+ .. NOTE::
684
+
685
+ Loops and multiple edges are ignored during the computation of
686
+ ``lex_DOWN`` and directed graphs are converted to undirected graphs.
687
+
688
+ .. SEEALSO::
689
+
690
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_BFS` -- perform a
691
+ lexicographic breadth first search (LexBFS) on the graph
692
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DFS` -- perform a
693
+ lexicographic depth first search (LexDFS) on the graph
694
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_UP` -- perform a
695
+ lexicographic UP search (LexUP) on the graph
696
+
697
+ ALGORITHM:
698
+
699
+ See the documentation of the :mod:`~sage.graphs.traversals` module.
700
+
701
+ EXAMPLES:
702
+
703
+ A Lex DOWN is obviously an ordering of the vertices::
704
+
705
+ sage: g = graphs.CompleteGraph(6)
706
+ sage: set(g.lex_DOWN()) == set(g)
707
+ True
708
+
709
+ Lex DOWN ordering of the 3-sun graph::
710
+
711
+ sage: g = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)])
712
+ sage: g.lex_DOWN()
713
+ [1, 2, 3, 4, 6, 5]
714
+
715
+ The method also works for directed graphs::
716
+
717
+ sage: G = DiGraph([(1, 2), (2, 3), (1, 3)])
718
+ sage: correct_anwsers = [[2, 1, 3], [2, 3, 1]]
719
+ sage: G.lex_DOWN(initial_vertex=2) in correct_anwsers
720
+ True
721
+
722
+ Different orderings for different traversals::
723
+
724
+ sage: # needs sage.combinat
725
+ sage: G = digraphs.DeBruijn(2,3)
726
+ sage: G.lex_BFS(initial_vertex='000')
727
+ ['000', '001', '100', '010', '011', '110', '101', '111']
728
+ sage: G.lex_DFS(initial_vertex='000')
729
+ ['000', '001', '100', '010', '101', '110', '011', '111']
730
+ sage: G.lex_UP(initial_vertex='000')
731
+ ['000', '001', '010', '101', '110', '111', '011', '100']
732
+ sage: G.lex_DOWN(initial_vertex='000')
733
+ ['000', '001', '100', '011', '010', '110', '111', '101']
734
+ """
735
+ return _lex_order_common(G, "lex_DOWN", reverse, tree, initial_vertex)
736
+
737
+
738
+ def lex_M(self, triangulation=False, labels=False, initial_vertex=None, algorithm=None):
739
+ r"""
740
+ Return an ordering of the vertices according the LexM graph traversal.
741
+
742
+ LexM is a lexicographic ordering scheme that is a special type of
743
+ breadth-first-search. LexM can also produce a triangulation of the
744
+ given graph. This functionality is implemented in this method. For
745
+ more details on the algorithms used see Sections 4 (``'lex_M_slow'``)
746
+ and 5.3 (``'lex_M_fast'``) of [RTL76]_.
747
+
748
+ .. NOTE::
749
+
750
+ This method works only for undirected graphs.
751
+
752
+ INPUT:
753
+
754
+ - ``triangulation`` -- boolean (default: ``False``); whether to return a
755
+ list of edges that need to be added in order to triangulate the graph
756
+
757
+ - ``labels`` -- boolean (default: ``False``); whether to return the labels
758
+ assigned to each vertex
759
+
760
+ - ``initial_vertex`` -- (default: ``None``); the first vertex to consider
761
+
762
+ - ``algorithm`` -- string (default: ``None``); one of the following
763
+ algorithms:
764
+
765
+ - ``'lex_M_slow'``: slower implementation of LexM traversal
766
+
767
+ - ``'lex_M_fast'``: faster implementation of LexM traversal (works only
768
+ when ``labels`` is set to ``False``)
769
+
770
+ - ``None``: Sage chooses the best algorithm: ``'lex_M_slow'`` if
771
+ ``labels`` is set to ``True``, ``'lex_M_fast'`` otherwise.
772
+
773
+ OUTPUT:
774
+
775
+ Depending on the values of the parameters ``triangulation`` and ``labels``
776
+ the method will return one or more of the following (in that order):
777
+
778
+ - an ordering of vertices of the graph according to LexM ordering scheme
779
+
780
+ - the labels assigned to each vertex
781
+
782
+ - a list of edges that when added to the graph will triangulate it
783
+
784
+ EXAMPLES:
785
+
786
+ LexM produces an ordering of the vertices::
787
+
788
+ sage: g = graphs.CompleteGraph(6)
789
+ sage: ord = g.lex_M(algorithm='lex_M_fast')
790
+ sage: len(ord) == g.order()
791
+ True
792
+ sage: set(ord) == set(g.vertices(sort=False))
793
+ True
794
+ sage: ord = g.lex_M(algorithm='lex_M_slow')
795
+ sage: len(ord) == g.order()
796
+ True
797
+ sage: set(ord) == set(g.vertices(sort=False))
798
+ True
799
+
800
+ Both algorithms produce a valid LexM ordering `\alpha` (i.e the neighbors of
801
+ `\alpha(i)` in `G[\{\alpha(i), ..., \alpha(n)\}]` induce a clique)::
802
+
803
+ sage: from sage.graphs.traversals import is_valid_lex_M_order
804
+ sage: G = graphs.PetersenGraph()
805
+ sage: ord, F = G.lex_M(triangulation=True, algorithm='lex_M_slow')
806
+ sage: is_valid_lex_M_order(G, ord, F)
807
+ True
808
+ sage: ord, F = G.lex_M(triangulation=True, algorithm='lex_M_fast')
809
+ sage: is_valid_lex_M_order(G, ord, F)
810
+ True
811
+
812
+ LexM produces a triangulation of given graph::
813
+
814
+ sage: G = graphs.PetersenGraph()
815
+ sage: _, F = G.lex_M(triangulation=True)
816
+ sage: H = Graph(F, format='list_of_edges')
817
+ sage: H.is_chordal()
818
+ True
819
+
820
+ LexM ordering of the 3-sun graph::
821
+
822
+ sage: g = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)])
823
+ sage: g.lex_M()
824
+ [6, 4, 5, 3, 2, 1]
825
+
826
+ The ordering depends on the initial vertex::
827
+
828
+ sage: G = graphs.HouseGraph()
829
+ sage: G.lex_M(algorithm='lex_M_slow', initial_vertex=0)
830
+ [4, 3, 2, 1, 0]
831
+ sage: G.lex_M(algorithm='lex_M_slow', initial_vertex=2)
832
+ [1, 4, 3, 0, 2]
833
+ sage: G.lex_M(algorithm='lex_M_fast', initial_vertex=0)
834
+ [4, 3, 2, 1, 0]
835
+ sage: G.lex_M(algorithm='lex_M_fast', initial_vertex=2)
836
+ [1, 4, 3, 0, 2]
837
+
838
+ TESTS:
839
+
840
+ ``'lex_M_fast'`` cannot return labels::
841
+
842
+ sage: Graph().lex_M(labels=True, algorithm='lex_M_fast')
843
+ Traceback (most recent call last):
844
+ ...
845
+ ValueError: 'lex_M_fast' cannot return labels assigned to vertices
846
+
847
+ The method works only for undirected graphs::
848
+
849
+ sage: from sage.graphs.traversals import lex_M
850
+ sage: lex_M(DiGraph())
851
+ Traceback (most recent call last):
852
+ ...
853
+ ValueError: input graph must be undirected
854
+
855
+ LexM ordering of empty graph::
856
+
857
+ sage: G = Graph()
858
+ sage: G.lex_M()
859
+ []
860
+
861
+ Parameter ``algorithm`` must be either ``'lex_M_slow'``,
862
+ ``'lex_M_fast'`` or ``None``::
863
+
864
+ sage: G = graphs.CompleteGraph(6)
865
+ sage: G.lex_M(algorithm='Bob')
866
+ Traceback (most recent call last):
867
+ ...
868
+ ValueError: unknown algorithm 'Bob'
869
+
870
+ ``initial_vertex`` should be a valid graph vertex::
871
+
872
+ sage: Graph().lex_M(initial_vertex='foo')
873
+ Traceback (most recent call last):
874
+ ...
875
+ ValueError: 'foo' is not a graph vertex
876
+ """
877
+ if initial_vertex is not None and initial_vertex not in self:
878
+ raise ValueError("'{}' is not a graph vertex".format(initial_vertex))
879
+
880
+ if self.is_directed():
881
+ raise ValueError("input graph must be undirected")
882
+
883
+ if not algorithm:
884
+ if labels:
885
+ algorithm = "lex_M_slow"
886
+ else:
887
+ algorithm = "lex_M_fast"
888
+
889
+ elif algorithm not in ["lex_M_slow", "lex_M_fast"]:
890
+ raise ValueError("unknown algorithm '{}'".format(algorithm))
891
+
892
+ if algorithm == "lex_M_slow":
893
+ return lex_M_slow(self, triangulation=triangulation, labels=labels, initial_vertex=initial_vertex)
894
+ if labels:
895
+ raise ValueError("'{}' cannot return labels assigned to vertices".format(algorithm))
896
+ return lex_M_fast(self, triangulation=triangulation, initial_vertex=initial_vertex)
897
+
898
+
899
+ def lex_M_slow(G, triangulation=False, labels=False, initial_vertex=None):
900
+ r"""
901
+ Return an ordering of the vertices according the LexM graph traversal.
902
+
903
+ LexM is a lexicographic ordering scheme that is a special type of
904
+ breadth-first-search. This function implements the algorithm described in
905
+ Section 4 of [RTL76]_.
906
+
907
+ During the search, the vertices are numbered from `n` to `1`. Let
908
+ `\alpha(i)` denote the vertex numbered `i` and let `\alpha^{-1}(u)` denote
909
+ the number assigned to `u`. Each vertex `u` has also a label, denoted by
910
+ `label(u)`, consisting of a list of numbers selected from `[1,n]` and
911
+ ordered in decreasing order. Given two labels `L_1=[p_1, p_2,\ldots, p_k]`
912
+ and `L_1=[q_1, q_2,\ldots, q_l]`, we define `L_1<L_2` if, for some `j`,
913
+ `p_i==q_i` for `i=1,\ldots,j-1` and `p_j<q_j`, or if `p_i==q_i` for
914
+ `i=1,\ldots,k` and `k<l`. Observe that this is exactly how Python compares
915
+ two lists.
916
+
917
+ .. NOTE::
918
+
919
+ This method works only for undirected graphs.
920
+
921
+ INPUT:
922
+
923
+ - ``G`` -- a sage graph
924
+
925
+ - ``triangulation`` -- boolean (default: ``False``); whether to return the
926
+ triangulation of the graph produced by the method
927
+
928
+ - ``labels`` -- boolean (default: ``False``); whether to return the labels
929
+ assigned to each vertex
930
+
931
+ - ``initial_vertex`` -- (default: ``None``) the first vertex to
932
+ consider. If not specified, an arbitrary vertex is chosen.
933
+
934
+ OUTPUT:
935
+
936
+ Depending on the values of the parameters ``triangulation`` and ``labels``
937
+ the method will return one or more of the following (in that order):
938
+
939
+ - the ordering of vertices of `G`
940
+
941
+ - the labels assigned to each vertex
942
+
943
+ - a list of edges that when added to `G` will produce a triangulation of `G`
944
+
945
+ EXAMPLES:
946
+
947
+ A LexM ordering is obviously an ordering of the vertices::
948
+
949
+ sage: from sage.graphs.traversals import lex_M_slow
950
+ sage: g = graphs.CompleteGraph(6)
951
+ sage: len(lex_M_slow(g)) == g.order()
952
+ True
953
+
954
+ LexM ordering and label assignments on the vertices of the 3-sun graph::
955
+
956
+ sage: from sage.graphs.traversals import lex_M_slow
957
+ sage: g = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)])
958
+ sage: lex_M_slow(g, labels=True)
959
+ ([6, 4, 5, 3, 2, 1],
960
+ {1: [], 2: [5], 3: [5, 4], 4: [4, 2], 5: [4, 3], 6: [3, 2]})
961
+
962
+ LexM produces a triangulation of given graph::
963
+
964
+ sage: from sage.graphs.traversals import lex_M_slow
965
+ sage: G = graphs.PetersenGraph()
966
+ sage: _, F = lex_M_slow(G, triangulation=True)
967
+ sage: H = G.copy()
968
+ sage: H.add_edges(F)
969
+ sage: H.is_chordal()
970
+ True
971
+
972
+ TESTS:
973
+
974
+ LexM ordering of empty graph::
975
+
976
+ sage: from sage.graphs.traversals import lex_M_slow
977
+ sage: G = Graph()
978
+ sage: lex_M_slow(G)
979
+ []
980
+
981
+ The method works only for undirected graphs::
982
+
983
+ sage: from sage.graphs.traversals import lex_M_slow
984
+ sage: G = digraphs.Circuit(15)
985
+ sage: lex_M_slow(G)
986
+ Traceback (most recent call last):
987
+ ...
988
+ ValueError: input graph must be undirected
989
+
990
+ ``initial_vertex`` should be a valid graph vertex::
991
+
992
+ sage: G = graphs.CompleteGraph(6)
993
+ sage: from sage.graphs.traversals import lex_M_slow
994
+ sage: lex_M_slow(G, initial_vertex='foo')
995
+ Traceback (most recent call last):
996
+ ...
997
+ ValueError: 'foo' is not a graph vertex
998
+ """
999
+ if initial_vertex is not None and initial_vertex not in G:
1000
+ raise ValueError("'{}' is not a graph vertex".format(initial_vertex))
1001
+
1002
+ if G.is_directed():
1003
+ raise ValueError("input graph must be undirected")
1004
+
1005
+ # ==>Initialization
1006
+ # Assign empty label to all vertices of G and empty list to F
1007
+ cdef list unnumbered_vertices = list(G)
1008
+ cdef int n = G.order()
1009
+ cdef list alpha = [0] * n
1010
+ cdef dict label = {v: [] for v in unnumbered_vertices}
1011
+ cdef list F = []
1012
+ cdef int i
1013
+ cdef set active, reach
1014
+
1015
+ if initial_vertex is not None:
1016
+ i = unnumbered_vertices.index(initial_vertex)
1017
+ unnumbered_vertices[0], unnumbered_vertices[i] = unnumbered_vertices[i], unnumbered_vertices[0]
1018
+
1019
+ for i in range(n-1, -1, -1):
1020
+ # Select: pick an unnumbered vertex u with largest label
1021
+ u = unnumbered_vertices[0]
1022
+ for v in unnumbered_vertices[1:]:
1023
+ if label[u] < label[v]:
1024
+ u = v
1025
+
1026
+ unnumbered_vertices.remove(u)
1027
+ alpha[i] = u
1028
+
1029
+ # Update: for each vertex v in unnumbered_vertices such that there is a
1030
+ # chain u = w_1, w_2, ..., w_{p+1} = v with w_j unnumbered and
1031
+ # label(w_j) < label(v) for all j in {2,...,p}. If so, we add i to the
1032
+ # label of v and add edge {u,v} to F.
1033
+ for v in unnumbered_vertices:
1034
+
1035
+ # We check if there is a chain u = w_1, w_2, ..., w_{p+1} = v with
1036
+ # w_j unnumbered and label(w_j) < label(v) for all j in {2, ..., p}
1037
+ active = set([w for w in unnumbered_vertices if label[w] < label[v]])
1038
+ active.add(v)
1039
+ reach = set([u])
1040
+ while active and reach and v not in reach:
1041
+ w = reach.pop()
1042
+ for x in G.neighbor_iterator(w):
1043
+ if x in active:
1044
+ reach.add(x)
1045
+ active.discard(x)
1046
+
1047
+ if v in reach:
1048
+ label[v].append(i)
1049
+ if triangulation:
1050
+ F.append((u, v))
1051
+
1052
+ if triangulation and labels:
1053
+ return alpha, label, F
1054
+ elif triangulation:
1055
+ return alpha, F
1056
+ elif labels:
1057
+ return alpha, label
1058
+ return alpha
1059
+
1060
+
1061
+ def lex_M_fast(G, triangulation=False, initial_vertex=None):
1062
+ r"""
1063
+ Return an ordering of the vertices according the LexM graph traversal.
1064
+
1065
+ LexM is a lexicographic ordering scheme that is a special type of
1066
+ breadth-first-search. This function implements the algorithm described in
1067
+ Section 5.3 of [RTL76]_.
1068
+
1069
+ Note that instead of using labels `1, 2, \ldots, k` and adding `1/2`, we
1070
+ use labels `2, 4, \ldots, k` and add `1`, thus avoiding to use floats or
1071
+ rationals.
1072
+
1073
+ .. NOTE::
1074
+
1075
+ This method works only for undirected graphs.
1076
+
1077
+ INPUT:
1078
+
1079
+ - ``G`` -- a sage graph
1080
+
1081
+ - ``triangulation`` -- boolean (default: ``False``); whether to return the
1082
+ triangulation of given graph produced by the method
1083
+
1084
+ - ``initial_vertex`` -- (default: ``None``) the first vertex to consider
1085
+
1086
+ OUTPUT:
1087
+
1088
+ This method will return an ordering of the vertices of ``G`` according to
1089
+ the LexM ordering scheme. Furthermore, if ``triangulation`` is set to
1090
+ ``True`` the method also returns a list of edges ``F`` such that when added
1091
+ to ``G`` the resulting graph is a triangulation of ``G``.
1092
+
1093
+ EXAMPLES:
1094
+
1095
+ A LexM ordering is obviously an ordering of the vertices::
1096
+
1097
+ sage: from sage.graphs.traversals import lex_M_fast
1098
+ sage: g = graphs.CompleteGraph(6)
1099
+ sage: len(lex_M_fast(g)) == g.order()
1100
+ True
1101
+
1102
+ LexM ordering of the 3-sun graph::
1103
+
1104
+ sage: from sage.graphs.traversals import lex_M_fast
1105
+ sage: g = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)])
1106
+ sage: lex_M_fast(g)
1107
+ [6, 4, 5, 3, 2, 1]
1108
+
1109
+ LexM produces a triangulation of given graph::
1110
+
1111
+ sage: from sage.graphs.traversals import lex_M_fast
1112
+ sage: G = graphs.PetersenGraph()
1113
+ sage: _, F = lex_M_fast(G, triangulation=True)
1114
+ sage: H = G.copy()
1115
+ sage: H.add_edges(F)
1116
+ sage: H.is_chordal()
1117
+ True
1118
+
1119
+ TESTS:
1120
+
1121
+ LexM ordering of empty graph::
1122
+
1123
+ sage: from sage.graphs.traversals import lex_M_fast
1124
+ sage: G = Graph()
1125
+ sage: lex_M_fast(G)
1126
+ []
1127
+
1128
+ The method works only for undirected graphs::
1129
+
1130
+ sage: from sage.graphs.traversals import lex_M_fast
1131
+ sage: G = digraphs.Circuit(15)
1132
+ sage: lex_M_fast(G)
1133
+ Traceback (most recent call last):
1134
+ ...
1135
+ ValueError: input graph must be undirected
1136
+
1137
+ ``initial_vertex`` should be a valid graph vertex::
1138
+
1139
+ sage: G = graphs.CompleteGraph(6)
1140
+ sage: from sage.graphs.traversals import lex_M_fast
1141
+ sage: lex_M_fast(G, initial_vertex='foo')
1142
+ Traceback (most recent call last):
1143
+ ...
1144
+ ValueError: 'foo' is not a graph vertex
1145
+
1146
+ Immutable graphs::
1147
+
1148
+ sage: from sage.graphs.traversals import lex_M_fast
1149
+ sage: G = graphs.RandomGNP(10, .7)
1150
+ sage: G._backend
1151
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
1152
+ sage: H = Graph(G, immutable=True)
1153
+ sage: H._backend
1154
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
1155
+ sage: lex_M_fast(G) == lex_M_fast(H)
1156
+ True
1157
+ """
1158
+ if initial_vertex is not None and initial_vertex not in G:
1159
+ raise ValueError("'{}' is not a graph vertex".format(initial_vertex))
1160
+
1161
+ if G.is_directed():
1162
+ raise ValueError("input graph must be undirected")
1163
+
1164
+ # ==> Initialization
1165
+
1166
+ cdef int i, j, k, v, w, z
1167
+
1168
+ cdef list int_to_v
1169
+ cdef StaticSparseCGraph cg
1170
+ cdef short_digraph sd
1171
+ if isinstance(G, StaticSparseBackend):
1172
+ cg = <StaticSparseCGraph> G._cg
1173
+ sd = <short_digraph> cg.g
1174
+ int_to_v = cg._vertex_to_labels
1175
+ else:
1176
+ int_to_v = list(G)
1177
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v)
1178
+
1179
+ cdef uint32_t* p_tmp
1180
+ cdef uint32_t* p_end
1181
+
1182
+ cdef int n = G.order()
1183
+
1184
+ cdef list unnumbered_vertices = list(range(n))
1185
+
1186
+ if initial_vertex is not None:
1187
+ # We put the initial vertex at the first place
1188
+ i = int_to_v.index(initial_vertex)
1189
+ unnumbered_vertices[0], unnumbered_vertices[i] = unnumbered_vertices[i], unnumbered_vertices[0]
1190
+
1191
+ cdef MemoryAllocator mem = MemoryAllocator()
1192
+ cdef int* label = <int*>mem.allocarray(n, sizeof(int))
1193
+ cdef int* alpha = <int*>mem.allocarray(n, sizeof(int))
1194
+ cdef int* alphainv = <int*>mem.allocarray(n, sizeof(int))
1195
+ cdef bint* reached = <bint*>mem.allocarray(n, sizeof(bint))
1196
+
1197
+ for i in range(n):
1198
+ label[i] = 2
1199
+ alpha[i] = 0
1200
+ alphainv[i] = 0
1201
+ reached[i] = False
1202
+
1203
+ cdef list F = list()
1204
+ cdef dict reach
1205
+
1206
+ k = 2
1207
+ for i in range(n - 1, -1, -1):
1208
+
1209
+ # Select: pick an unnumbered vertex v with label(v)==k and assign it
1210
+ # number i
1211
+ for v in unnumbered_vertices:
1212
+ if label[v] == k:
1213
+ alpha[i] = v
1214
+ alphainv[v] = i
1215
+ reached[v] = True
1216
+ unnumbered_vertices.remove(v)
1217
+ break
1218
+ else:
1219
+ raise ValueError('unable to find an unnumbered vertex v with label[v] == k')
1220
+
1221
+ # Mark all unnumbered vertices unreached
1222
+ for w in unnumbered_vertices:
1223
+ reached[w] = False
1224
+
1225
+ reach = dict()
1226
+ for j in range(2, k + 1, 2):
1227
+ reach[j] = set()
1228
+
1229
+ p_tmp = sd.neighbors[v]
1230
+ p_end = sd.neighbors[v + 1]
1231
+ while p_tmp < p_end:
1232
+ w = p_tmp[0]
1233
+ p_tmp += 1
1234
+ if alphainv[w]:
1235
+ continue
1236
+ reach[label[w]].add(w)
1237
+ reached[w] = True
1238
+ label[w] += 1
1239
+ if triangulation:
1240
+ F.append((int_to_v[v], int_to_v[w]))
1241
+
1242
+ # Search
1243
+ for j in range(2, k + 1, 2):
1244
+ while reach[j]:
1245
+ w = reach[j].pop()
1246
+ p_tmp = sd.neighbors[w]
1247
+ p_end = sd.neighbors[w + 1]
1248
+ while p_tmp < p_end:
1249
+ z = p_tmp[0]
1250
+ p_tmp += 1
1251
+ if reached[z]:
1252
+ continue
1253
+ reached[z] = True
1254
+ if label[z] > j:
1255
+ reach[label[z]].add(z)
1256
+ label[z] += 1
1257
+ if triangulation:
1258
+ F.append((int_to_v[v], int_to_v[z]))
1259
+ else:
1260
+ reach[j].add(z)
1261
+
1262
+ if unnumbered_vertices:
1263
+ # Sort: sort unnumbered vertices by label(w) value
1264
+ order = sorted((label[w], w) for w in unnumbered_vertices)
1265
+
1266
+ # Reassign labels as integers from 2 to k, redefining k appropriately
1267
+ k = 2
1268
+ l, _ = order[0]
1269
+ for ll, w in order:
1270
+ if l != ll:
1271
+ l = ll
1272
+ k += 2
1273
+ label[w] = k
1274
+
1275
+ if not isinstance(G, StaticSparseBackend):
1276
+ free_short_digraph(sd)
1277
+
1278
+ cdef list ordering = [int_to_v[alpha[i]] for i in range(n)]
1279
+
1280
+ if triangulation:
1281
+ return ordering, F
1282
+ return ordering
1283
+
1284
+
1285
+ def is_valid_lex_M_order(G, alpha, F):
1286
+ r"""
1287
+ Check whether the ordering alpha and the triangulation F are valid for G.
1288
+
1289
+ Given the graph `G = (V, E)` with vertex set `V` and edge set `E`, and the
1290
+ set `F` of edges of a triangulation of `G`, let `H = (V, E\cup F)`.
1291
+ By induction one can see that for every `i \in \{1, ..., n - 1\}` the
1292
+ neighbors of `\alpha(i)` in `H[\{\alpha(i), ..., \alpha(n)\}]` induce a
1293
+ clique. The ordering `\alpha` is a perfect elimination ordering of `H`, so
1294
+ `H` is chordal. See [RTL76]_ for more details.
1295
+
1296
+ INPUT:
1297
+
1298
+ - ``G`` -- a Graph
1299
+
1300
+ - ``alpha`` -- list; an ordering of the vertices of `G`
1301
+
1302
+ - ``F`` -- an iterable of edges given either as ``(u, v)`` or ``(u, v,
1303
+ label)``, the edges of the triangulation of `G`
1304
+
1305
+
1306
+ TESTS::
1307
+
1308
+ sage: from sage.graphs.traversals import lex_M_slow, is_valid_lex_M_order
1309
+ sage: G = graphs.PetersenGraph()
1310
+ sage: alpha, F = lex_M_slow(G, triangulation=True)
1311
+ sage: is_valid_lex_M_order(G, alpha, F)
1312
+ True
1313
+ sage: H = Graph(G.edges(sort=False))
1314
+ sage: H.add_edges(F)
1315
+ sage: H.is_chordal()
1316
+ True
1317
+ sage: from sage.graphs.traversals import lex_M_fast
1318
+ sage: alpha, F = lex_M_fast(G, triangulation=True)
1319
+ sage: is_valid_lex_M_order(G, alpha, F)
1320
+ True
1321
+ sage: H = Graph(G.edges(sort=False))
1322
+ sage: H.add_edges(F)
1323
+ sage: H.is_chordal()
1324
+ True
1325
+ """
1326
+ H = G.copy()
1327
+ H.add_edges(F)
1328
+ s_alpha = set(alpha)
1329
+ for u in alpha:
1330
+ K = H.subgraph(H.neighbors(u))
1331
+ s_alpha.discard(u)
1332
+ K.delete_vertices([v for v in K if v not in s_alpha])
1333
+ if not K.is_clique():
1334
+ return False
1335
+ return True
1336
+
1337
+
1338
+ def maximum_cardinality_search(G, reverse=False, tree=False, initial_vertex=None):
1339
+ r"""
1340
+ Return an ordering of the vertices according a maximum cardinality search.
1341
+
1342
+ Maximum cardinality search (MCS) is a graph traversal introduced in
1343
+ [TY1984]_. It starts by assigning an arbitrary vertex (or the specified
1344
+ ``initial_vertex``) of `G` the last position in the ordering `\alpha`. Every
1345
+ vertex keeps a weight equal to the number of its already processed neighbors
1346
+ (i.e., already added to `\alpha`), and a vertex of largest such number is
1347
+ chosen at each step `i` to be placed in position `n - i` in `\alpha`. This
1348
+ ordering can be computed in time `O(n + m)`.
1349
+
1350
+ Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for
1351
+ ``DenseGraph`` where `n` is the number of vertices and `m` is the number of
1352
+ edges.
1353
+
1354
+ When the graph is chordal, the ordering returned by MCS is a *perfect
1355
+ elimination ordering*, like :meth:`~sage.graphs.traversals.lex_BFS`. So
1356
+ this ordering can be used to recognize chordal graphs. See [He2006]_ for
1357
+ more details.
1358
+
1359
+ .. NOTE::
1360
+
1361
+ The current implementation is for connected graphs only.
1362
+
1363
+ INPUT:
1364
+
1365
+ - ``G`` -- a Sage graph
1366
+
1367
+ - ``reverse`` -- boolean (default: ``False``); whether to return the
1368
+ vertices in discovery order, or the reverse
1369
+
1370
+ - ``tree`` -- boolean (default: ``False``); whether to also return the
1371
+ discovery directed tree (each vertex being linked to the one that saw
1372
+ it for the first time)
1373
+
1374
+ - ``initial_vertex`` -- (default: ``None``) the first vertex to consider
1375
+
1376
+ OUTPUT:
1377
+
1378
+ By default, return the ordering `\alpha` as a list. When ``tree`` is
1379
+ ``True``, the method returns a tuple `(\alpha, T)`, where `T` is a directed
1380
+ tree with the same set of vertices as `G` and a directed edge from `u` to `v`
1381
+ if `u` was the first vertex to see `v`.
1382
+
1383
+ EXAMPLES:
1384
+
1385
+ When specified, the ``initial_vertex`` is placed at the end of the ordering,
1386
+ unless parameter ``reverse`` is ``True``, in which case it is placed at the
1387
+ beginning::
1388
+
1389
+ sage: G = graphs.PathGraph(4)
1390
+ sage: G.maximum_cardinality_search(initial_vertex=0)
1391
+ [3, 2, 1, 0]
1392
+ sage: G.maximum_cardinality_search(initial_vertex=1)
1393
+ [3, 2, 0, 1]
1394
+ sage: G.maximum_cardinality_search(initial_vertex=2)
1395
+ [0, 3, 1, 2]
1396
+ sage: G.maximum_cardinality_search(initial_vertex=3)
1397
+ [0, 1, 2, 3]
1398
+ sage: G.maximum_cardinality_search(initial_vertex=3, reverse=True)
1399
+ [3, 2, 1, 0]
1400
+
1401
+ Returning the discovery tree::
1402
+
1403
+ sage: G = graphs.PathGraph(4)
1404
+ sage: _, T = G.maximum_cardinality_search(tree=True, initial_vertex=0)
1405
+ sage: T.order(), T.size()
1406
+ (4, 3)
1407
+ sage: T.edges(labels=False, sort=True)
1408
+ [(1, 0), (2, 1), (3, 2)]
1409
+ sage: _, T = G.maximum_cardinality_search(tree=True, initial_vertex=3)
1410
+ sage: T.edges(labels=False, sort=True)
1411
+ [(0, 1), (1, 2), (2, 3)]
1412
+
1413
+ TESTS::
1414
+
1415
+ sage: Graph().maximum_cardinality_search()
1416
+ []
1417
+ sage: Graph(1).maximum_cardinality_search()
1418
+ [0]
1419
+ sage: Graph(2).maximum_cardinality_search()
1420
+ Traceback (most recent call last):
1421
+ ...
1422
+ ValueError: the input graph is not connected
1423
+ sage: graphs.PathGraph(2).maximum_cardinality_search(initial_vertex=17)
1424
+ Traceback (most recent call last):
1425
+ ...
1426
+ ValueError: vertex (17) is not a vertex of the graph
1427
+
1428
+ Immutable graphs;:
1429
+
1430
+ sage: G = graphs.RandomGNP(10, .7)
1431
+ sage: G._backend
1432
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
1433
+ sage: H = Graph(G, immutable=True)
1434
+ sage: H._backend
1435
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
1436
+ sage: G.maximum_cardinality_search() == H.maximum_cardinality_search()
1437
+ True
1438
+ """
1439
+ if tree:
1440
+ from sage.graphs.digraph import DiGraph
1441
+
1442
+ cdef int N = G.order()
1443
+ if not N:
1444
+ return ([], DiGraph()) if tree else []
1445
+ if N == 1:
1446
+ return (list(G), DiGraph(G)) if tree else list(G)
1447
+
1448
+ cdef list int_to_vertex
1449
+ cdef StaticSparseCGraph cg
1450
+ cdef short_digraph sd
1451
+ if isinstance(G, StaticSparseBackend):
1452
+ cg = <StaticSparseCGraph> G._cg
1453
+ sd = <short_digraph> cg.g
1454
+ int_to_vertex = cg._vertex_to_labels
1455
+ else:
1456
+ int_to_vertex = list(G)
1457
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
1458
+
1459
+ if initial_vertex is None:
1460
+ initial_vertex = 0
1461
+ elif initial_vertex in G:
1462
+ if isinstance(G, StaticSparseBackend):
1463
+ initial_vertex = cg._vertex_to_int[initial_vertex]
1464
+ else:
1465
+ initial_vertex = int_to_vertex.index(initial_vertex)
1466
+ else:
1467
+ raise ValueError("vertex ({0}) is not a vertex of the graph".format(initial_vertex))
1468
+
1469
+ cdef uint32_t** p_vertices = sd.neighbors
1470
+ cdef uint32_t* p_tmp
1471
+ cdef uint32_t* p_end
1472
+
1473
+ cdef MemoryAllocator mem = MemoryAllocator()
1474
+ cdef int* weight = <int*>mem.calloc(N, sizeof(int))
1475
+ cdef bint* seen = <bint*>mem.calloc(N, sizeof(bint))
1476
+ cdef int* pred = <int *>mem.allocarray(N, sizeof(int))
1477
+
1478
+ cdef int i, u, v
1479
+ for i in range(N):
1480
+ pred[i] = i
1481
+
1482
+ # We emulate a max-heap data structure using a min-heap with negative values
1483
+ cdef PairingHeap_of_n_integers P = PairingHeap_of_n_integers(N)
1484
+ P.push(initial_vertex, 0)
1485
+
1486
+ # The ordering alpha is feed in reversed order and revert afterword
1487
+ cdef list alpha = []
1488
+
1489
+ while P:
1490
+ u = P.top_item()
1491
+ P.pop()
1492
+ alpha.append(int_to_vertex[u])
1493
+ seen[u] = True
1494
+
1495
+ p_tmp = p_vertices[u]
1496
+ p_end = p_vertices[u + 1]
1497
+ while p_tmp < p_end:
1498
+ v = p_tmp[0]
1499
+ if not seen[v]:
1500
+ weight[v] += 1
1501
+ P.decrease(v, -weight[v])
1502
+ if pred[v] == v:
1503
+ pred[v] = u
1504
+ p_tmp += 1
1505
+
1506
+ if not isinstance(G, StaticSparseBackend):
1507
+ free_short_digraph(sd)
1508
+
1509
+ if len(alpha) < N:
1510
+ raise ValueError("the input graph is not connected")
1511
+
1512
+ if not reverse:
1513
+ alpha.reverse()
1514
+
1515
+ if tree:
1516
+ D = DiGraph([int_to_vertex, [(int_to_vertex[i], int_to_vertex[pred[i]])
1517
+ for i in range(N) if pred[i] != i]],
1518
+ format='vertices_and_edges')
1519
+ return alpha, D
1520
+
1521
+ return alpha
1522
+
1523
+
1524
+ cdef inline int swap(int* alpha, int* alpha_inv, int u, int new_pos_u) noexcept:
1525
+ """
1526
+ Swap positions of u and v in alpha, where v is be the vertex occupying cell
1527
+ new_pos_u in alpha.
1528
+ """
1529
+ cdef int v = alpha[new_pos_u]
1530
+ alpha[new_pos_u], alpha[alpha_inv[u]] = u, v
1531
+ alpha_inv[u], alpha_inv[v] = alpha_inv[v], alpha_inv[u]
1532
+ return v
1533
+
1534
+
1535
+ cdef maximum_cardinality_search_M_short_digraph(short_digraph sd, int initial_vertex,
1536
+ int* alpha, int* alpha_inv, list F, bint* X):
1537
+ r"""
1538
+ Compute the ordering and the edges of the triangulation produced by MCS-M.
1539
+
1540
+ Maximum cardinality search M (MCS-M) is an extension of MCS
1541
+ (:meth:`~sage.graphs.traversals.maximum_cardinality_search`) in the same way
1542
+ that Lex-M (:meth:`~sage.graphs.traversals.lex_M`) is an extension of
1543
+ Lex-BFS (:meth:`~sage.graphs.traversalslex_BFS`). That is, in MCS-M when `u`
1544
+ receives number `i` at step `n - i + 1`, it increments the weight of all
1545
+ unnumbered vertices `v` for which there exists a path between `u` and `v`
1546
+ consisting only of unnumbered vertices with weight strictly less than
1547
+ `w^-(u)` and `w^-(v)`, where `w^-` is the number of times a vertex has been
1548
+ reached during previous iterations. See [BBHP2004]_ for the details of this
1549
+ `O(nm)` time algorithm.
1550
+
1551
+ If `G` is not connected, the orderings of each of its connected components
1552
+ are added consecutively.
1553
+
1554
+ This method is the core of
1555
+ :meth:`~sage.graphs.traversals.maximum_cardinality_search_M`.
1556
+
1557
+ INPUT:
1558
+
1559
+ - ``sd`` -- a ``short_digraph`` as documented in
1560
+ :mod:`~sage.graphs.base.static_sparse_graph`
1561
+
1562
+ - ``initial_vertex`` -- integer; initial vertex for the search
1563
+
1564
+ - ``alpha`` -- int array of size `N`; the computed ordering of MCS-M
1565
+
1566
+ - ``alpha_inv`` -- int array of size `N`; the position of vertex ``u`` in
1567
+ ``alpha``, that is the inverse function of alpha. So we have
1568
+ ``alpha[alpha_inv[u]] == u`` for all `0 \leq u < N - 1`.
1569
+
1570
+ - ``F`` -- list; to be filled with the edges of the triangulation
1571
+
1572
+ - ``X`` -- boolean array of size `N`; ``X[u]`` is set to ``True`` if the
1573
+ neighborhood of `u` is a separator of the graph
1574
+
1575
+ TESTS::
1576
+
1577
+ sage: Graph().maximum_cardinality_search_M()
1578
+ ([], [], [])
1579
+ sage: Graph(1).maximum_cardinality_search_M()
1580
+ ([0], [], [])
1581
+ sage: graphs.PathGraph(2).maximum_cardinality_search_M(initial_vertex=17)
1582
+ Traceback (most recent call last):
1583
+ ...
1584
+ ValueError: vertex (17) is not a vertex of the graph
1585
+
1586
+ .. TODO::
1587
+
1588
+ Use a fast heap data structure with decrease-key operation.
1589
+ """
1590
+ # Initialization of data structures
1591
+ cdef int N = sd.n
1592
+ cdef MemoryAllocator mem = MemoryAllocator()
1593
+ # number of times a vertex is reached, initially 0
1594
+ cdef int* weight = <int*>mem.calloc(N, sizeof(int))
1595
+ # has a vertex been reached, initially False
1596
+ cdef bint* reached = <bint*>mem.calloc(N, sizeof(bint))
1597
+
1598
+ cdef int i, u, v, xi
1599
+ for i in range(N):
1600
+ weight[i] = 0
1601
+ alpha[i] = i
1602
+ alpha_inv[i] = i
1603
+ X[i] = False
1604
+
1605
+ # If an initial vertex is specified, we put it at position 0 in alpha.
1606
+ # This way, it will be the first vertex to be considered.
1607
+ if initial_vertex:
1608
+ swap(alpha, alpha_inv, initial_vertex, 0)
1609
+
1610
+ # variables for the manipulation of the short digraph
1611
+ cdef uint32_t** p_vertices = sd.neighbors
1612
+ cdef uint32_t* p_tmp
1613
+ cdef uint32_t* p_end
1614
+
1615
+ cdef vector[vector[int]] reach
1616
+ cdef int s = -1
1617
+ cdef int current_pos = N
1618
+
1619
+ while current_pos:
1620
+
1621
+ # Choose an unnumbered vertex of maximum weight.
1622
+ # This could be done faster if we had a heap data structure with
1623
+ # decrease key operation.
1624
+ u = alpha[0]
1625
+ for i in range(current_pos):
1626
+ v = alpha[i]
1627
+ if weight[u] < weight[v]:
1628
+ u = v
1629
+
1630
+ # Swap u and the vertex v occupying position current_pos in alpha
1631
+ current_pos -= 1
1632
+ v = swap(alpha, alpha_inv, u, current_pos)
1633
+ reached[u] = True
1634
+
1635
+ # If the weight decreases, the neighborhood of u is a separator
1636
+ if weight[u] <= s:
1637
+ X[u] = True
1638
+ s = weight[u]
1639
+
1640
+ # Search for new edges of the triangulation.
1641
+ # We add an edge to the triangulation between u and any unnumbered
1642
+ # vertex v such that there is a path (v, x1, x2,... , xk, u) through
1643
+ # unnumbered vertices such that count-[xi] < count-[v], 1 <= i <= k. If
1644
+ # such an edge is found, we increase the count of v for next round.
1645
+
1646
+ # Mark all unnumbered vertices unreached. These vertices occupy
1647
+ # positions 0,..,current_pos-1 in alpha
1648
+ reach.clear()
1649
+ reach.resize(N)
1650
+ for i in range(current_pos):
1651
+ v = alpha[i]
1652
+ reached[v] = False
1653
+
1654
+ # Initialize reach with unnumbered neighbors of u
1655
+ p_tmp = p_vertices[u]
1656
+ p_end = p_vertices[u + 1]
1657
+ while p_tmp < p_end:
1658
+ v = p_tmp[0]
1659
+ p_tmp += 1
1660
+ if not reached[v]:
1661
+ reach[weight[v]].push_back(v)
1662
+ reached[v] = True
1663
+ weight[v] += 1
1664
+
1665
+ # Search
1666
+ for i in range(N):
1667
+ while not reach[i].empty():
1668
+ xi = reach[i].back()
1669
+ reach[i].pop_back()
1670
+ p_tmp = p_vertices[xi]
1671
+ p_end = p_vertices[xi + 1]
1672
+ while p_tmp < p_end:
1673
+ v = p_tmp[0]
1674
+ p_tmp += 1
1675
+ if reached[v]:
1676
+ continue
1677
+ reached[v] = True
1678
+ if i < weight[v]:
1679
+ reach[weight[v]].push_back(v)
1680
+ weight[v] += 1
1681
+ F.append((u, v))
1682
+ else:
1683
+ reach[i].push_back(v)
1684
+
1685
+ reach.clear()
1686
+
1687
+
1688
+ def maximum_cardinality_search_M(G, initial_vertex=None):
1689
+ r"""
1690
+ Return the ordering and the edges of the triangulation produced by MCS-M.
1691
+
1692
+ Maximum cardinality search M (MCS-M) is an extension of MCS
1693
+ (:meth:`~sage.graphs.traversals.maximum_cardinality_search`) in the same way
1694
+ that Lex-M (:meth:`~sage.graphs.traversals.lex_M`) is an extension of
1695
+ Lex-BFS (:meth:`~sage.graphs.traversals.lex_BFS`). That is, in MCS-M when
1696
+ `u` receives number `i` at step `n - i + 1`, it increments the weight of all
1697
+ unnumbered vertices `v` for which there exists a path between `u` and `v`
1698
+ consisting only of unnumbered vertices with weight strictly less than
1699
+ `w^-(u)` and `w^-(v)`, where `w^-` is the number of times a vertex has been
1700
+ reached during previous iterations. See [BBHP2004]_ for the details of this
1701
+ `O(nm)` time algorithm.
1702
+
1703
+ If `G` is not connected, the orderings of each of its connected components
1704
+ are added consecutively. Furthermore, if `G` has `k` connected components
1705
+ `C_i` for `0 \leq i < k`, `X` contains at least one vertex of `C_i` for each
1706
+ `i \geq 1`. Hence, `|X| \geq k - 1`. In particular, some isolated vertices
1707
+ (i.e., of degree 0) can appear in `X` as for such a vertex `x`, we have that
1708
+ `G \setminus N(x) = G` is not connected.
1709
+
1710
+ INPUT:
1711
+
1712
+ - ``G`` -- a Sage graph
1713
+
1714
+ - ``initial_vertex`` -- (default: ``None``) the first vertex to consider
1715
+
1716
+ OUTPUT: a tuple `(\alpha, F, X)`, where
1717
+
1718
+ - `\alpha` is the resulting ordering of the vertices. If an initial vertex
1719
+ is specified, it gets the last position in the ordering `\alpha`.
1720
+
1721
+ - `F` is the list of edges of a minimal triangulation of `G` according
1722
+ `\alpha`
1723
+
1724
+ - `X` is a list of vertices such that for each `x \in X`, the
1725
+ neighborhood of `x` in `G` is a separator (i.e., `G \setminus N(x)` is not
1726
+ connected). Note that we may have `N(x) = \emptyset` if `G` is not
1727
+ connected and `x` has degree 0.
1728
+
1729
+ EXAMPLES:
1730
+
1731
+ Chordal graphs have a perfect elimination ordering, and so the set `F` of
1732
+ edges of the triangulation is empty::
1733
+
1734
+ sage: G = graphs.RandomChordalGraph(20)
1735
+ sage: alpha, F, X = G.maximum_cardinality_search_M(); F
1736
+ []
1737
+
1738
+ The cycle of order 4 is not chordal and so the triangulation has one edge::
1739
+
1740
+ sage: G = graphs.CycleGraph(4)
1741
+ sage: alpha, F, X = G.maximum_cardinality_search_M(); len(F)
1742
+ 1
1743
+
1744
+ The number of edges needed to triangulate of a cycle graph or order `n` is
1745
+ `n - 3`, independently of the initial vertex::
1746
+
1747
+ sage: n = randint(3, 20)
1748
+ sage: C = graphs.CycleGraph(n)
1749
+ sage: _, F, X = C.maximum_cardinality_search_M()
1750
+ sage: len(F) == n - 3
1751
+ True
1752
+ sage: _, F, X = C.maximum_cardinality_search_M(initial_vertex=C.random_vertex())
1753
+ sage: len(F) == n - 3
1754
+ True
1755
+
1756
+ When an initial vertex is specified, it gets the last position in the
1757
+ ordering::
1758
+
1759
+ sage: G = graphs.PathGraph(4)
1760
+ sage: G.maximum_cardinality_search_M(initial_vertex=0)
1761
+ ([3, 2, 1, 0], [], [2, 3])
1762
+ sage: G.maximum_cardinality_search_M(initial_vertex=1)
1763
+ ([3, 2, 0, 1], [], [2, 3])
1764
+ sage: G.maximum_cardinality_search_M(initial_vertex=2)
1765
+ ([0, 1, 3, 2], [], [0, 1])
1766
+ sage: G.maximum_cardinality_search_M(initial_vertex=3)
1767
+ ([0, 1, 2, 3], [], [0, 1])
1768
+
1769
+
1770
+ When `G` is not connected, the orderings of each of its connected components
1771
+ are added consecutively, the vertices of the component containing the
1772
+ initial vertex occupying the last positions::
1773
+
1774
+ sage: G = graphs.CycleGraph(4) * 2
1775
+ sage: G.maximum_cardinality_search_M()[0]
1776
+ [5, 4, 6, 7, 2, 3, 1, 0]
1777
+ sage: G.maximum_cardinality_search_M(initial_vertex=7)[0]
1778
+ [2, 1, 3, 0, 5, 6, 4, 7]
1779
+
1780
+ Furthermore, if `G` has `k` connected components, `X` contains at least one
1781
+ vertex per connected component, except for the first one, and so at least `k
1782
+ - 1` vertices::
1783
+
1784
+ sage: for k in range(1, 5):
1785
+ ....: _, _, X = Graph(k).maximum_cardinality_search_M()
1786
+ ....: if len(X) < k - 1:
1787
+ ....: raise ValueError("something goes wrong")
1788
+ sage: G = graphs.RandomGNP(10, .2)
1789
+ sage: cc = G.connected_components(sort=False)
1790
+ sage: _, _, X = G.maximum_cardinality_search_M()
1791
+ sage: len(X) >= len(cc) - 1
1792
+ True
1793
+
1794
+ In the example of [BPS2010]_, the triangulation has 3 edges::
1795
+
1796
+ sage: G = Graph({'a': ['b', 'k'], 'b': ['c'], 'c': ['d', 'j', 'k'],
1797
+ ....: 'd': ['e', 'f', 'j', 'k'], 'e': ['g'],
1798
+ ....: 'f': ['g', 'j', 'k'], 'g': ['j', 'k'], 'h': ['i', 'j'],
1799
+ ....: 'i': ['k'], 'j': ['k']})
1800
+ sage: _, F, _ = G.maximum_cardinality_search_M(initial_vertex='a')
1801
+ sage: len(F)
1802
+ 3
1803
+
1804
+ TESTS::
1805
+
1806
+ sage: Graph().maximum_cardinality_search_M()
1807
+ ([], [], [])
1808
+ sage: Graph(1).maximum_cardinality_search_M()
1809
+ ([0], [], [])
1810
+ sage: graphs.PathGraph(2).maximum_cardinality_search_M(initial_vertex=17)
1811
+ Traceback (most recent call last):
1812
+ ...
1813
+ ValueError: vertex (17) is not a vertex of the graph
1814
+
1815
+ Immutable graphs::
1816
+
1817
+ sage: G = graphs.RandomGNP(10, .7)
1818
+ sage: G._backend
1819
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
1820
+ sage: H = Graph(G, immutable=True)
1821
+ sage: H._backend
1822
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
1823
+ sage: G.maximum_cardinality_search_M() == H.maximum_cardinality_search_M()
1824
+ True
1825
+ """
1826
+ cdef int N = G.order()
1827
+ if not N:
1828
+ return ([], [], [])
1829
+ if N == 1:
1830
+ return (list(G), [], [])
1831
+
1832
+ # Copying the whole graph to obtain the list of neighbors quicker than by
1833
+ # calling out_neighbors. This data structure is well documented in the
1834
+ # module sage.graphs.base.static_sparse_graph
1835
+ cdef list int_to_vertex
1836
+ cdef StaticSparseCGraph cg
1837
+ cdef short_digraph sd
1838
+ if isinstance(G, StaticSparseBackend):
1839
+ cg = <StaticSparseCGraph> G._cg
1840
+ sd = <short_digraph> cg.g
1841
+ int_to_vertex = cg._vertex_to_labels
1842
+ else:
1843
+ int_to_vertex = list(G)
1844
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
1845
+
1846
+ if initial_vertex is None:
1847
+ initial_vertex = 0
1848
+ elif initial_vertex in G:
1849
+ if isinstance(G, StaticSparseBackend):
1850
+ initial_vertex = cg._vertex_to_int[initial_vertex]
1851
+ else:
1852
+ initial_vertex = int_to_vertex.index(initial_vertex)
1853
+ else:
1854
+ raise ValueError("vertex ({0}) is not a vertex of the graph".format(initial_vertex))
1855
+
1856
+ cdef MemoryAllocator mem = MemoryAllocator()
1857
+ cdef int* alpha = <int*>mem.calloc(N, sizeof(int))
1858
+ cdef int* alpha_inv = <int*>mem.calloc(N, sizeof(int))
1859
+ cdef bint* X = <bint*>mem.calloc(N, sizeof(bint))
1860
+ cdef list F = []
1861
+
1862
+ sig_on()
1863
+ maximum_cardinality_search_M_short_digraph(sd, initial_vertex, alpha, alpha_inv, F, X)
1864
+ sig_off()
1865
+
1866
+ if not isinstance(G, StaticSparseBackend):
1867
+ free_short_digraph(sd)
1868
+
1869
+ cdef int u, v
1870
+ return ([int_to_vertex[alpha[u]] for u in range(N)],
1871
+ [(int_to_vertex[u], int_to_vertex[v]) for u, v in F],
1872
+ [int_to_vertex[u] for u in range(N) if X[u]])