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,1106 @@
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
+ Slice decomposition
7
+
8
+ This module implements an extended lexBFS algorithm for computing the slice
9
+ decomposition of undirected graphs and the class :class:`~SliceDecomposition` to
10
+ represent such decompositions.
11
+
12
+ A formal definition of slice decompositions can be found in Section 3.2 of
13
+ [TCHP2008]_ and a description of the extended lexBFS algorithm is given in
14
+ Section 3.3 of [TCHP2008]_.
15
+
16
+ AUTHORS:
17
+
18
+ - Cyril Bouvier (2024-06-25): initial version
19
+ """
20
+ # ****************************************************************************
21
+ # Copyright (C) 2024 Cyril Bouvier <cyril.bouvier@lirmm.fr>
22
+ #
23
+ # This program is free software: you can redistribute it and/or modify
24
+ # it under the terms of the GNU General Public License as published by
25
+ # the Free Software Foundation, either version 2 of the License, or
26
+ # (at your option) any later version.
27
+ # https://www.gnu.org/licenses/
28
+ # ****************************************************************************
29
+
30
+ from libcpp.algorithm cimport swap
31
+ from cysignals.signals cimport sig_on, sig_off
32
+ from cython.operator cimport dereference as deref
33
+
34
+ from sage.graphs.base.c_graph cimport CGraphBackend
35
+ from sage.data_structures.bitset_base cimport bitset_in
36
+
37
+ cdef void extended_lex_BFS(
38
+ CGraph cg, vector[int] &sigma, vector[int] *sigma_inv,
39
+ int initial_v_int, vector[int] *pred, vector[size_t] *xslice_len,
40
+ vector[vector[int]] *lex_label) except *:
41
+ r"""
42
+ Perform a extended lexicographic breadth first search (LexBFS) on the
43
+ undirected graph `G`.
44
+
45
+ In addition to computing a LexBFS ordering, the extended LexBFS algorithm
46
+ can be used to compute the slice decomposition of the graph.
47
+
48
+ This function implements the `O(n+m)` time algorithm proposed in [HMPV2000]_
49
+ and [TCHP2008]_.
50
+
51
+ INPUT:
52
+
53
+ - ``cg`` -- a ``CGraph``. This function ignores loops and multiple edges and
54
+ assumes that the graph is undirected.
55
+
56
+ - ``sigma`` -- vector of ``int`` to store the ordering of the vertices
57
+ resulting from the LexBFS traversal. At the end, the vector will have size
58
+ `n` (the number of vertices of the graph).
59
+
60
+ - ``sigma_inv`` -- a pointer to a vector to store the inverse of the
61
+ permutation ``sigma``. ``sigma_inv`` can be ``NULL`` if the caller does
62
+ not need it (but, note that, the inverse of ``sigma`` is still needed by
63
+ the algorithm, so it does not save time nor memory to have ``sigma_inv``
64
+ equal to ``NULL``). At the end, if ``sigma_inv`` is not NULL, the vector
65
+ pointer by it will have size `n` (the number of vertices of the graph)
66
+ and will satisfy:
67
+ * sigma[deref(sigma_inv)[v_int]] = v_int
68
+ * deref(sigma_inv)[sigma[i]] = i
69
+
70
+ - ``initial_v_int`` -- the first vertex to consider. It can be `-1`; in this
71
+ case the first active vertex (corresponding to the first bit set in
72
+ ``cg.active_vertices``) will be taken as first vertex.
73
+
74
+ - ``pred`` -- a pointer to a vector of int to store the predecessor of a
75
+ vertex in the LexBFS traversal. ``pred`` can be ``NULL`` if the caller
76
+ does not need it (and the information will not be computed by the
77
+ algorithm). At the end, if ``pred`` is not NULL, the vector pointer by it
78
+ will have size `n` (the number of vertices of the graph) and pred[i] will
79
+ be either -1 (if sigma[i] as no predecessor) or a positive value less than
80
+ n such that the predecessor of sigma[i] is sigma[pred[i]].
81
+
82
+ - ``xslice_len`` -- a pointer to a vector of size_t to store the length of
83
+ the x-slices associated with the lexBFS traversal. ``xslice_len`` can be
84
+ ``NULL`` if the caller does not need it (and the information will not be
85
+ computed by the algorithm). At the end, if ``xslice_len`` is not NULL, the
86
+ vector pointer by it will have size `n` (the number of vertices of the
87
+ graph) and the length of the x-slice starting at sigma[i] will be
88
+ xslice_len[i].
89
+
90
+ - ``lex_label`` -- a pointer to a vector of vector[int] to store the
91
+ lexicographic labels associated with the lexBFS traversal. ``lex_label``
92
+ can be ``NULL`` if the caller does not need it (and the information will
93
+ not be computed by the algorithm). At the end, if ``lex_label`` is not
94
+ NULL, the vector pointer by it will have size `n` (the number of
95
+ vertices of the graph) and the lexicographic label of sigma[i]
96
+ will given by lex_label[i].
97
+
98
+ ALGORITHM:
99
+
100
+ This algorithm uses the notion of *partition refinement* to determine the
101
+ exact position of the vertices in the ordering.
102
+
103
+ Consider an ordering `\sigma` of the vertices. For a vertex `v`, we define
104
+ `N_i(v) = \{u | u \in N(v) \text{ and } \sigma(u) < i\}`, that is the subset
105
+ of neighbors of `v` appearing before the `i`-th vertex in the ordering
106
+ `\sigma`. Now, a part of an ordering `\sigma` is a set of consecutive
107
+ vertices, `S = \{u | i \leq \sigma(u) \leq j\}`, such that for any `u \in
108
+ S`, we have `N_i(u) = N_i(\sigma^{-1}(i))` and for any `v` such that `j <
109
+ \sigma(v)`, `N_i(v) \neq N_i(\sigma^{-1}(i))`. The *head* of a part is the
110
+ first position of its vertices.
111
+
112
+ The algorithm starts with a single part containing all vertices. Then, when
113
+ the position of the `i`-th vertex `v` is fixed, it explores the neighbors of
114
+ `v` that have not yet been ordered. Consider a part `S` such that `N(x)\cap
115
+ S \neq \emptyset`. The algorithm will rearrange the ordering of the vertices
116
+ in `S` so that the first vertices are the neighbors of `v`. The subpart
117
+ containing the neighbors of `v` is assigned a new name, and the head of `S`
118
+ is set to the position of the first vertex of `S \setminus N(v)` in the
119
+ ordering `\sigma`.
120
+
121
+ Observe that each arc of the graph can induce the subdivision of a part.
122
+ Hence, the algorithm can use up to `m + 1` different parts.
123
+
124
+ The time complexity of this algorithm is in `O(n + m)`, and our
125
+ implementation follows that complexity ``SparseGraph``. For ``DenseGraph``,
126
+ the complexity is `O(n^2)`. See [HMPV2000]_ and [TCHP2008]_ for more
127
+ details.
128
+
129
+ This implementation of extended LexBFS offers some guarantee on the order in
130
+ which the vertices appear in the computed ordering: in case of a tie between
131
+ lexicographic labels during the computation, this function will "choose" the
132
+ vertices in the order in which they appear during the enumeration of the
133
+ neighbors of their last common neighbor.
134
+ For example, if `(u_0, ..., u_k)` is the beginning of the ordering being
135
+ computed and the vertices `v` and `w` currently have the same lexicographic
136
+ label (it means that they have the same neighbors in `(u_0, ..., u_k)`).
137
+ Let call `u_j` their last neighbor in the current ordering (*i.e.*, for all
138
+ `i > j`, `u_i` is not a neighbor of `v` and `w`). This implementation
139
+ will choose `v` for the next vertex of the ordering if and only if `v`
140
+ appeared before `w` when the neighbors of `u_j` where enumerated.
141
+
142
+ One possible use of this guarantee is that the caller can reorder the
143
+ adjacency list of vertices (by using, for example, a static sparse graph)
144
+ to force the computed LexBFS order to respect a previous one.
145
+
146
+ EXAMPLES::
147
+
148
+ To see how it can be used, see the code of the lex_BFS method (in
149
+ traversals.pyx) or of the class SliceDecomposition in this module.
150
+
151
+ TESTS:
152
+
153
+ Indirect doctests::
154
+
155
+ sage: G = graphs.HouseGraph()
156
+ sage: G.slice_decomposition()
157
+ [0[1[2]] [3] [4]]
158
+ sage: G.lex_BFS(algorithm="fast")
159
+ [0, 1, 2, 3, 4]
160
+
161
+ Check that :issue:`39934` is fixed::
162
+
163
+ sage: G = Graph(graphs.HouseGraph(), immutable=True)
164
+ sage: G.slice_decomposition()
165
+ [0[1[2]] [3] [4]]
166
+ sage: G.lex_BFS(algorithm="fast")
167
+ [0, 1, 2, 3, 4]
168
+ sage: G = Graph(graphs.HouseGraph(), sparse=False)
169
+ sage: G.slice_decomposition()
170
+ [0[1[2]] [3] [4]]
171
+ sage: G.lex_BFS(algorithm="fast")
172
+ [0, 1, 2, 3, 4]
173
+ """
174
+ cdef int n = <int> cg.num_verts
175
+ # Variables for the partition refinement algorithm
176
+ cdef size_t max_nparts = cg.num_arcs // 2 + 1
177
+ cdef bint need_to_delete_sigma_inv = sigma_inv == NULL
178
+ if sigma_inv == NULL:
179
+ sigma_inv = new vector[int]()
180
+ cdef vector[size_t] part_of = vector[size_t](n, 0)
181
+ cdef vector[size_t] part_len # only used if xslice_len != NULL (see below)
182
+ cdef vector[size_t] part_head = vector[size_t](max_nparts)
183
+ cdef vector[size_t] subpart = vector[size_t](max_nparts)
184
+ cdef size_t p, part_of_i, nparts, old_nparts
185
+ # Temporary variables
186
+ cdef int max_degree = 0
187
+ cdef int i, j, k, l, u_int, v_int, t_int
188
+
189
+ # Resize vectors
190
+ sigma.resize(n)
191
+ deref(sigma_inv).resize(cg.active_vertices.size)
192
+ if pred != NULL:
193
+ deref(pred).clear()
194
+ deref(pred).resize(n, -1) # initialize pred[i] to -1 for 0 <= i < n
195
+ if xslice_len != NULL:
196
+ deref(xslice_len).resize(n)
197
+ part_len.resize(max_nparts)
198
+ if lex_label != NULL:
199
+ deref(lex_label).clear()
200
+ deref(lex_label).resize(n)
201
+
202
+ # Initialize the position of vertices in sigma (and compute max_degree)
203
+ if initial_v_int >= 0:
204
+ sigma[0] = initial_v_int
205
+ deref(sigma_inv)[initial_v_int] = 0
206
+ i = 1
207
+ else:
208
+ i = 0
209
+ for v_int in range(<int> cg.active_vertices.size):
210
+ if bitset_in(cg.active_vertices, v_int):
211
+ if v_int != initial_v_int:
212
+ sigma[i] = v_int
213
+ deref(sigma_inv)[v_int] = i
214
+ i = i + 1
215
+ max_degree = max(max_degree, cg.out_degree(v_int))
216
+
217
+ # Variables needed to iterate over neighbors of a vertex
218
+ cdef int nneighbors
219
+ cdef vector[int] neighbors = vector[int](max_degree)
220
+
221
+ # Initialize partition: one part containing all the vertices
222
+ nparts = 1
223
+ # all element of part_of are already initialized to 0
224
+ part_head[0] = 0
225
+ subpart[0] = 0
226
+ if xslice_len != NULL:
227
+ part_len[0] = n
228
+
229
+ # Main loop
230
+ for i in range(n):
231
+ old_nparts = nparts
232
+
233
+ part_of_i = part_of[i]
234
+
235
+ # put i out of its part (updating part_len if needed)
236
+ part_head[part_of_i] += 1
237
+ if xslice_len != NULL:
238
+ deref(xslice_len)[i] = part_len[part_of_i]
239
+ part_len[part_of_i] -= 1
240
+
241
+ v_int = sigma[i]
242
+
243
+ # Iterate over the neighbors of v
244
+ nneighbors = cg.out_neighbors_unsafe(v_int, neighbors.data(), max_degree)
245
+ for k in range(nneighbors):
246
+ u_int = neighbors[k]
247
+ j = deref(sigma_inv)[u_int] # get the position of u
248
+ if j <= i:
249
+ continue # already taken care of
250
+
251
+ if lex_label != NULL:
252
+ deref(lex_label)[j].push_back(v_int)
253
+
254
+ p = part_of[j] # get the part of u
255
+ l = part_head[p] # get the beginning of the part containing u
256
+
257
+ # if not last and next elem belongs in the same part (ie #part >= 2)
258
+ if l < n - 1 and part_of[l + 1] == p:
259
+ if l != j: # not already first elem of the part
260
+ # Place u at the position of the head of the part
261
+ t_int = sigma[l]
262
+ deref(sigma_inv)[t_int], deref(sigma_inv)[u_int] = j, l
263
+ sigma[j], sigma[l] = t_int, u_int
264
+ if lex_label != NULL:
265
+ swap[vector[int]](deref(lex_label)[j],
266
+ deref(lex_label)[l])
267
+ j = l
268
+ part_head[p] += 1 # move the head of the part to next elem
269
+
270
+ # if part p was not already cut in two during this iteration, we
271
+ # create a new part using subpart
272
+ if subpart[p] < old_nparts:
273
+ subpart[p] = nparts
274
+ part_head[nparts] = j
275
+ if xslice_len != NULL:
276
+ part_len[nparts] = 0
277
+ subpart[nparts] = 0
278
+ nparts += 1
279
+
280
+ # Finally, we update the name of the part for position j and set v
281
+ # as predecessor of u
282
+ part_of[j] = subpart[p]
283
+ if xslice_len != NULL:
284
+ part_len[p] -= 1
285
+ part_len[subpart[p]] += 1
286
+ if pred != NULL:
287
+ deref(pred)[j] = i
288
+
289
+ if need_to_delete_sigma_inv:
290
+ del sigma_inv
291
+
292
+
293
+ def slice_decomposition(G, initial_vertex=None):
294
+ r"""
295
+ Compute a slice decomposition of the simple undirected graph
296
+
297
+ INPUT:
298
+
299
+ - ``G`` -- a Sage graph.
300
+
301
+ - ``initial_vertex`` -- (default: ``None``); the first vertex to consider.
302
+
303
+ OUTPUT:
304
+
305
+ An object of type :class:`~sage.graphs.graph_decompositions.slice_decomposition.SliceDecomposition`
306
+ that represents a slice decomposition of ``G``
307
+
308
+ .. NOTE::
309
+
310
+ Loops and multiple edges are ignored during the computation of the slice
311
+ decomposition.
312
+
313
+ ALGORITHM:
314
+
315
+ The method use the algorithm based on "partition refinement" described in
316
+ [HMPV2000]_ and [TCHP2008]_.
317
+ The time complexity of this algorithm is in `O(n + m)`, and our
318
+ implementation follows that complexity for ``SparseGraph``. For
319
+ ``DenseGraph``, the complexity is `O(n^2)`.
320
+
321
+ EXAMPLES:
322
+
323
+ Slice decomposition of the Petersen Graph::
324
+
325
+ sage: G = graphs.PetersenGraph()
326
+ sage: SD = G.slice_decomposition(); SD
327
+ [0[1[4[5]]] [2[6]] [3] [9] [7] [8]]
328
+
329
+ The graph can have loops or multiple edges but they are ignored::
330
+
331
+ sage: H = Graph(G,loops=True,multiedges=True)
332
+ sage: H.add_edges([(4, 4), (2, 2), (1, 6)])
333
+ sage: SD2 = H.slice_decomposition()
334
+ sage: SD2 == SD
335
+ True
336
+ sage: SD2.underlying_graph() == G.to_simple(immutable=True)
337
+ True
338
+
339
+ The tree corresponding to the slice decomposition can be displayed using
340
+ ``view``::
341
+
342
+ sage: from sage.graphs.graph_latex import check_tkz_graph
343
+ sage: check_tkz_graph() # random - depends on Tex installation
344
+ sage: view(G) # not tested
345
+ sage: latex(G) # to obtain the corresponding LaTeX code # needs sage.plot
346
+ \begin{tikzpicture}
347
+ ...
348
+ \end{tikzpicture}
349
+
350
+ Slice decompositions are only defined for undirected graphs::
351
+
352
+ sage: from sage.graphs.graph_decompositions.slice_decomposition import slice_decomposition
353
+ sage: slice_decomposition(DiGraph())
354
+ Traceback (most recent call last):
355
+ ...
356
+ ValueError: parameter G must be an undirected graph
357
+
358
+ TESTS:
359
+
360
+ Check that :issue:`39934` is fixed::
361
+
362
+ sage: G = Graph(graphs.HouseGraph(), immutable=True)
363
+ sage: G.slice_decomposition()
364
+ [0[1[2]] [3] [4]]
365
+ """
366
+ return SliceDecomposition(G, initial_vertex=initial_vertex)
367
+
368
+
369
+ cdef class SliceDecomposition(SageObject):
370
+
371
+ def __init__(self, G, initial_vertex=None):
372
+ r"""
373
+ Represents a slice decomposition of a simple directed graph.
374
+
375
+ INPUT:
376
+
377
+ - ``G`` -- a Sage graph.
378
+
379
+ - ``initial_vertex`` -- (default: ``None``); the first vertex to
380
+ consider.
381
+
382
+ .. SEEALSO::
383
+
384
+ * :meth:`~slice_decomposition` -- compute a slice decomposition of
385
+ the simple undirected graph
386
+ * Section 3.2 of [TCHP2008]_ for a formal definition.
387
+
388
+ EXAMPLES:
389
+
390
+ The constructor of the :class:`~SliceDecomposition` class is called by
391
+ the :meth:`~slice_decomposition` method of undirected graphs::
392
+
393
+ sage: from sage.graphs.graph_decompositions.slice_decomposition import SliceDecomposition
394
+ sage: G = graphs.PetersenGraph()
395
+ sage: SliceDecomposition(G) == G.slice_decomposition()
396
+ True
397
+
398
+ The vertex appearing first in the slice decomposition can be specified::
399
+
400
+ sage: from sage.graphs.graph_decompositions.slice_decomposition import SliceDecomposition
401
+ sage: SliceDecomposition(graphs.PetersenGraph(), initial_vertex=3)
402
+ [3[2[4[8]]] [1[7]] [0] [9] [6] [5]]
403
+
404
+ Slice decompositions are not defined for directed graphs::
405
+
406
+ sage: from sage.graphs.graph_decompositions.slice_decomposition import SliceDecomposition
407
+ sage: SliceDecomposition(DiGraph())
408
+ Traceback (most recent call last):
409
+ ...
410
+ ValueError: parameter G must be an undirected graph
411
+
412
+ .. automethod:: __getitem__
413
+ """
414
+ if G.is_directed():
415
+ raise ValueError("parameter G must be an undirected graph")
416
+
417
+ if initial_vertex is not None and initial_vertex not in G:
418
+ raise LookupError(f"vertex ({initial_vertex}) is not a vertex of the graph")
419
+
420
+ cdef CGraphBackend Gbackend = <CGraphBackend> G._backend
421
+ cdef CGraph cg = Gbackend.cg()
422
+
423
+ self._graph_class = type(G)
424
+
425
+ cdef int initial_v_int
426
+ if initial_vertex is not None:
427
+ # we already checked that initial_vertex is in G
428
+ initial_v_int = Gbackend.get_vertex(initial_vertex)
429
+ else:
430
+ initial_v_int = -1
431
+
432
+ cdef vector[int] sigma
433
+ cdef vector[vector[int]] lex_label
434
+
435
+ # Compute the slice decomposition using the extended lexBFS algorithm
436
+ sig_on()
437
+ extended_lex_BFS(cg, sigma, NULL, initial_v_int, NULL,
438
+ &(self.xslice_len), &lex_label)
439
+ sig_off()
440
+
441
+ # Translate the results with the actual vertices of the graph
442
+ self.sigma = tuple(Gbackend.vertex_label(v_int) for v_int in sigma)
443
+ self.sigma_inv = {v: i for i, v in enumerate(self.sigma)}
444
+ self.lex_label = {i: tuple(Gbackend.vertex_label(v_int)
445
+ for v_int in lli)
446
+ for i, lli in enumerate(lex_label)}
447
+
448
+ def __eq__(self, other):
449
+ """
450
+ Return whether ``self`` and ``other`` are equal.
451
+
452
+ TESTS::
453
+
454
+ sage: G = graphs.PetersenGraph()
455
+ sage: SD = G.slice_decomposition()
456
+ sage: SD == SD
457
+ True
458
+ sage: SD == G.slice_decomposition()
459
+ True
460
+
461
+ sage: P3 = graphs.PathGraph(3)
462
+ sage: SD1 = P3.slice_decomposition(initial_vertex=0)
463
+ sage: SD2 = P3.slice_decomposition(initial_vertex=2)
464
+ sage: SD1 == SD2
465
+ False
466
+ sage: SD3 = graphs.CompleteGraph(3).slice_decomposition()
467
+ sage: SD1 == SD3 # same lexBFS but different slice for 1
468
+ False
469
+ sage: SD4 = Graph([(0,1), (0,2)]).slice_decomposition()
470
+ sage: SD3 == SD4 # same lexBFS and slices but different active edges
471
+ False
472
+ """
473
+ if not isinstance(other, type(self)):
474
+ return False
475
+
476
+ cdef SliceDecomposition sd = <SliceDecomposition>other
477
+
478
+ return self.sigma_inv == sd.sigma_inv \
479
+ and self.lex_label == sd.lex_label \
480
+ and self.xslice_len == sd.xslice_len
481
+
482
+ def __hash__(self):
483
+ r"""
484
+ Compute a hash of a ``SliceDecomposition`` object.
485
+
486
+ TESTS::
487
+
488
+ sage: P3 = graphs.PathGraph(3)
489
+ sage: SD1 = P3.slice_decomposition(initial_vertex=0)
490
+ sage: SD2 = P3.slice_decomposition(initial_vertex=2)
491
+ sage: len({SD1: 1, SD2: 2}) # indirect doctest
492
+ 2
493
+ """
494
+ return hash((tuple(self.sigma_inv.items()),
495
+ tuple(self.lex_label.items()),
496
+ tuple(self.xslice_len)))
497
+
498
+ def __getitem__(self, v):
499
+ r"""
500
+ Return the data about the x-slice of the vertex `v`.
501
+
502
+ INPUT:
503
+
504
+ - ``v`` -- a vertex of the graph corresponding to the slice
505
+ decomposition.
506
+
507
+ OUTPUT:
508
+
509
+ A dictionary with the keys:
510
+
511
+ * ``"pivot"`` -- the vertex `v` given as parameter
512
+
513
+ * ``"slice"`` -- the slice of `v` (see :meth:`~slice`)
514
+
515
+ * ``"active_edges"`` -- the actives edges of `v` (see
516
+ :meth:`~active_edges`)
517
+
518
+ * ``"lexicographic_label"`` -- the lexicographic label of `v` (see
519
+ :meth:`~lexicographic_label`)
520
+
521
+ * ``"sequence"`` -- the x-slice sequence of `v` (see
522
+ :meth:`~xslice_sequence`)
523
+
524
+ This method can also be called via :meth:`xslice_data`.
525
+
526
+ EXAMPLES:
527
+
528
+ ::
529
+
530
+ sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False)
531
+ sage: SD = G.slice_decomposition(initial_vertex='x')
532
+ sage: SD.xslice_data('a')
533
+ {'active_edges': [('a', 'b'),
534
+ ('a', 'c'),
535
+ ('a', 'd'),
536
+ ('a', 'e'),
537
+ ('a', 'f'),
538
+ ('c', 'g'),
539
+ ('d', 'g'),
540
+ ('f', 'g')],
541
+ 'lexicographic_label': ['x'],
542
+ 'pivot': 'a',
543
+ 'sequence': [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']],
544
+ 'slice': ['a', 'b', 'c', 'd', 'e', 'f', 'g']}
545
+ sage: SD.xslice_data('u')
546
+ {'active_edges': [],
547
+ 'lexicographic_label': ['a', 'b', 'c', 'd', 'e', 'f', 'g'],
548
+ 'pivot': 'u',
549
+ 'sequence': [['u'], ['y', 'z']],
550
+ 'slice': ['u', 'y', 'z']}
551
+
552
+ Some values of the returned dictionary can be obtained via other
553
+ methods (:meth:`~slice`, :meth:`~xslice_sequence`,
554
+ :meth:`~active_edges`, :meth:`~lexicographic_label`)::
555
+
556
+ sage: SD.slice('a')
557
+ ['a', 'b', 'c', 'd', 'e', 'f', 'g']
558
+ sage: SD.xslice_data('a')['slice']
559
+ ['a', 'b', 'c', 'd', 'e', 'f', 'g']
560
+
561
+ sage: SD.xslice_sequence('a')
562
+ [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']]
563
+ sage: SD.xslice_data('a')['sequence']
564
+ [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']]
565
+
566
+ sage: SD.active_edges('b') == SD.xslice_data('b')['active_edges']
567
+ True
568
+
569
+ sage: SD.lexicographic_label('u')
570
+ ['a', 'b', 'c', 'd', 'e', 'f', 'g']
571
+ sage: SD.xslice_data('u')['lexicographic_label']
572
+ ['a', 'b', 'c', 'd', 'e', 'f', 'g']
573
+
574
+ TESTS::
575
+
576
+ sage: G = graphs.RandomGNP(15, 0.3)
577
+ sage: SD = G.slice_decomposition()
578
+ sage: all(SD[v]['slice'] == SD.slice(v) for v in G)
579
+ True
580
+ sage: all(SD[v]['sequence'] == SD.xslice_sequence(v) for v in G)
581
+ True
582
+ sage: all(SD[v]['active_edges'] == SD.active_edges(v) for v in G)
583
+ True
584
+ sage: all(SD[v]['lexicographic_label'] == SD.lexicographic_label(v) for v in G)
585
+ True
586
+
587
+ sage: SD = graphs.PetersenGraph().slice_decomposition()
588
+ sage: SD['John']
589
+ Traceback (most recent call last):
590
+ ...
591
+ LookupError: vertex (John) does not appear in the slice decomposition
592
+ """
593
+ if v not in self.sigma_inv:
594
+ raise LookupError(f"vertex ({v}) does not appear in the slice "
595
+ "decomposition")
596
+ cdef size_t i = self.sigma_inv[v]
597
+ return {'pivot': v,
598
+ 'slice': self._slice(i),
599
+ 'sequence': self._xslice_sequence(i),
600
+ 'lexicographic_label': self._xslice_lex_label(i),
601
+ 'active_edges': self._xslice_active_edges(i),
602
+ }
603
+
604
+ def lexBFS_order(self):
605
+ r"""
606
+ Return the lexBFS order corresponding to the slice decomposition.
607
+
608
+ EXAMPLES::
609
+
610
+ sage: from sage.graphs.traversals import _is_valid_lex_BFS_order
611
+ sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition()
612
+ sage: SD.lexBFS_order()
613
+ [0, 1, 4, 5, 2, 6, 3, 9, 7, 8]
614
+ sage: _is_valid_lex_BFS_order(G, SD.lexBFS_order())
615
+ True
616
+
617
+ TESTS::
618
+
619
+ sage: from sage.graphs.traversals import _is_valid_lex_BFS_order
620
+ sage: for _ in range(5):
621
+ ....: G = graphs.RandomGNP(15, 0.3)
622
+ ....: SD = G.slice_decomposition()
623
+ ....: _is_valid_lex_BFS_order(G, SD.lexBFS_order())
624
+ True
625
+ True
626
+ True
627
+ True
628
+ True
629
+ """
630
+ return list(self.sigma)
631
+
632
+ def xslice_data(self, v):
633
+ r"""
634
+ Return the data about the x-slice of the vertex `v`.
635
+
636
+ This method is a wrapper around :meth:`SliceDecomposition.__getitem__`
637
+
638
+ TESTS::
639
+
640
+ sage: G = graphs.RandomGNP(15, 0.3)
641
+ sage: SD = G.slice_decomposition()
642
+ sage: all(SD[v] == SD.xslice_data(v) for v in G)
643
+ True
644
+ """
645
+ return self[v]
646
+
647
+ def slice(self, v):
648
+ r"""
649
+ Return the slice of the vertex `v`.
650
+
651
+ The slice of `v` is the list of vertices `u` such that the neighbors of
652
+ `u` that are before `v` in the lexBFS order are that same that the
653
+ neighbors of `v` that are before `v` in the lexBFS order (*i.e.*, the
654
+ lexicographic label of `v`). It can be shown that it is a factor of the
655
+ lexBFS order.
656
+
657
+ INPUT:
658
+
659
+ - ``v`` -- a vertex of the graph corresponding to the slice
660
+ decomposition.
661
+
662
+ OUTPUT:
663
+
664
+ A list of vertices
665
+
666
+ EXAMPLES:
667
+
668
+ ::
669
+
670
+ sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False)
671
+ sage: SD = G.slice_decomposition(initial_vertex='x')
672
+ sage: SD.slice('a')
673
+ ['a', 'b', 'c', 'd', 'e', 'f', 'g']
674
+
675
+ The vertices of the slice have the same neighborhood "on the left"::
676
+
677
+ sage: pos = lambda v: SD.lexBFS_order().index(v)
678
+ sage: lla = set(SD.lexicographic_label('a'))
679
+ sage: all(lla == {u for u in G.neighbors(v) if pos(u) < pos('a')} \
680
+ ....: for v in SD.slice('a'))
681
+ True
682
+
683
+ The slice is a factor of the lexBFS order::
684
+
685
+ sage: ''.join(SD.slice('a')) in ''.join(SD.lexBFS_order())
686
+ True
687
+
688
+ The slice of the initial vertex is the whole graph::
689
+
690
+ sage: SD.slice('x') == SD.lexBFS_order()
691
+ True
692
+
693
+ TESTS::
694
+
695
+ sage: SD.slice('Michael')
696
+ Traceback (most recent call last):
697
+ ...
698
+ LookupError: vertex (Michael) does not appear in the slice decomposition
699
+ """
700
+ if v not in self.sigma_inv:
701
+ raise LookupError(f"vertex ({v}) does not appear in the slice "
702
+ "decomposition")
703
+ cdef size_t i = self.sigma_inv[v]
704
+ return self._slice(i)
705
+
706
+ def xslice_sequence(self, v):
707
+ r"""
708
+ Return the x-slice sequence of the vertex `v`.
709
+
710
+ INPUT:
711
+
712
+ - ``v`` -- a vertex of the graph corresponding to the slice
713
+ decomposition.
714
+
715
+ OUTPUT:
716
+
717
+ A list of list corresponding to the x-slice sequence of ``v``.
718
+
719
+ EXAMPLES:
720
+
721
+ ::
722
+
723
+ sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False)
724
+ sage: SD = G.slice_decomposition(initial_vertex='x')
725
+ sage: SD.xslice_sequence('x')
726
+ [['x'], ['a', 'b', 'c', 'd', 'e', 'f', 'g'], ['u', 'y', 'z'], ['v', 'w']]
727
+ sage: SD.xslice_sequence('a')
728
+ [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']]
729
+
730
+ The flatten x-slice sequence of a vertex corresponds to the slice of the
731
+ same vertex::
732
+
733
+ sage: from itertools import chain
734
+ sage: all(list(chain(*SD.xslice_sequence(v))) == SD.slice(v) \
735
+ ....: for v in G)
736
+ True
737
+
738
+ The first list of the sequence is always a singleton containing the
739
+ input vertex::
740
+
741
+ sage: all(SD.xslice_sequence(v)[0] == [v] for v in G)
742
+ True
743
+
744
+ If the length of the slice if more than 1, the second list of the
745
+ sequence is either, all the remaining vertices of the slice of `v`, if
746
+ `v` is isolated in the subgraph induced by the slice of `v`, or the
747
+ neighbors of `v` in the subgraph induced by the slice of `v`::
748
+
749
+ sage: all(SD.xslice_sequence(v)[1] == SD.slice(v)[1:] for v in G \
750
+ ....: if G.subgraph(SD.slice(v)).degree(v) == 0 \
751
+ ....: and len(SD.slice(v)) > 1)
752
+ True
753
+ sage: for v in G:
754
+ ....: if len(SD.slice(v)) > 1:
755
+ ....: xslice_seq = SD.xslice_sequence(v)
756
+ ....: S = G.subgraph(SD.slice(v))
757
+ ....: if S.degree(v) > 0:
758
+ ....: set(xslice_seq[1]) == set(S.neighbor_iterator(v))
759
+ True
760
+ True
761
+ True
762
+ True
763
+
764
+ TESTS::
765
+
766
+ sage: SD = graphs.PetersenGraph().slice_decomposition()
767
+ sage: SD.xslice_sequence('Terry')
768
+ Traceback (most recent call last):
769
+ ...
770
+ LookupError: vertex (Terry) does not appear in the slice decomposition
771
+ """
772
+ if v not in self.sigma_inv:
773
+ raise LookupError(f"vertex ({v}) does not appear in the slice "
774
+ "decomposition")
775
+ cdef size_t i = self.sigma_inv[v]
776
+ return self._xslice_sequence(i)
777
+
778
+ def lexicographic_label(self, v):
779
+ r"""
780
+ Return the lexicographic label of the vertex `v`.
781
+
782
+ The lexicographic label of a vertex `v` is the list of all the
783
+ neighbors of `v` that appear before `v` in the lexBFS ordering
784
+ corresponding to the slice decomposition.
785
+
786
+ INPUT:
787
+
788
+ - ``v`` -- a vertex of the graph corresponding to the slice
789
+ decomposition.
790
+
791
+ OUTPUT:
792
+
793
+ A list of vertices.
794
+
795
+ EXAMPLES::
796
+
797
+ sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False)
798
+ sage: SD = G.slice_decomposition(initial_vertex='x')
799
+ sage: SD.lexicographic_label('f')
800
+ ['x', 'a', 'c', 'd']
801
+ sage: pos = lambda v: SD.lexBFS_order().index(v)
802
+ sage: set(SD.lexicographic_label('f')) \
803
+ ....: == {v for v in G.neighbors('f') if pos(v) < pos('f')}
804
+ True
805
+
806
+ TESTS::
807
+
808
+ sage: SD = graphs.PetersenGraph().slice_decomposition()
809
+ sage: SD.lexicographic_label('Eric')
810
+ Traceback (most recent call last):
811
+ ...
812
+ LookupError: vertex (Eric) does not appear in the slice decomposition
813
+ """
814
+ if v not in self.sigma_inv:
815
+ raise LookupError(f"vertex ({v}) does not appear in the slice "
816
+ "decomposition")
817
+ cdef size_t i = self.sigma_inv[v]
818
+ return self._xslice_lex_label(i)
819
+
820
+ def active_edges(self, v):
821
+ r"""
822
+ Return the active edges of the vertex `v`.
823
+
824
+ An edge `(u, w)` is said to be active for `v` if `u` and `w` belongs
825
+ to two differents slices of the x-slice sequence of `v`. Note that it
826
+ defines a partition of the edges of the underlying graph.
827
+
828
+ INPUT:
829
+
830
+ - ``v`` -- a vertex of the graph corresponding to the slice
831
+ decomposition.
832
+
833
+ OUTPUT:
834
+
835
+ A list of edges
836
+
837
+ EXAMPLES:
838
+
839
+ ::
840
+
841
+ sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False)
842
+ sage: SD = G.slice_decomposition(initial_vertex='x')
843
+ sage: SD.xslice_sequence('a')
844
+ [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']]
845
+ sage: ('c', 'g') in SD.active_edges('a')
846
+ True
847
+ sage: ('a', 'c') in SD.active_edges('a')
848
+ True
849
+ sage: ('c', 'd') in SD.active_edges('a') # c and d in same slice
850
+ False
851
+ sage: ('a', 'u') in SD.active_edges('a') # u not in x-slice of a
852
+ False
853
+
854
+ The set of active edges of every vertex is a partition of the edges::
855
+
856
+ sage: from itertools import chain
857
+ sage: E = list(chain(*(SD.active_edges(v) for v in G)))
858
+ sage: G.size() == len(E) == len(set(E)) \
859
+ ....: and all(G.has_edge(u, w) for v in G for u, w in SD.active_edges(v))
860
+ True
861
+
862
+ TESTS::
863
+
864
+ sage: SD = graphs.PetersenGraph().slice_decomposition()
865
+ sage: SD.active_edges('Graham')
866
+ Traceback (most recent call last):
867
+ ...
868
+ LookupError: vertex (Graham) does not appear in the slice decomposition
869
+ """
870
+ if v not in self.sigma_inv:
871
+ raise LookupError(f"vertex ({v}) does not appear in the slice "
872
+ "decomposition")
873
+ cdef size_t i = self.sigma_inv[v]
874
+ return self._xslice_active_edges(i)
875
+
876
+ def _slice(self, size_t idx):
877
+ r"""
878
+ This method is for internal use only
879
+
880
+ TESTS:
881
+
882
+ Indirect doctests::
883
+
884
+ sage: SD = graphs.HouseGraph().slice_decomposition()
885
+ sage: SD.slice(1)
886
+ [1, 2]
887
+ """
888
+ return list(self.sigma[idx:idx+self.xslice_len[idx]])
889
+
890
+ def _xslice_sequence(self, size_t idx):
891
+ r"""
892
+ This method is for internal use only
893
+
894
+ TESTS:
895
+
896
+ Indirect doctests::
897
+
898
+ sage: SD = graphs.HouseGraph().slice_decomposition()
899
+ sage: SD.xslice_sequence(0)
900
+ [[0], [1, 2], [3], [4]]
901
+ """
902
+ cdef size_t l = self.xslice_len[idx]
903
+ cdef size_t j = idx + 1
904
+ cdef size_t lj
905
+
906
+ S = [ [self.sigma[idx]] ]
907
+ while j < idx + l:
908
+ lj = self.xslice_len[j]
909
+ S.append(list(self.sigma[j:j+lj]))
910
+ j += lj
911
+ assert j == idx + l, "slice decomposition is ill-formed"
912
+ return S
913
+
914
+ def _xslice_lex_label(self, size_t idx):
915
+ r"""
916
+ This method is for internal use only
917
+
918
+ TESTS:
919
+
920
+ Indirect doctests::
921
+
922
+ sage: SD = graphs.HouseGraph().slice_decomposition()
923
+ sage: SD.lexicographic_label(3)
924
+ [1, 2]
925
+ """
926
+ return list(self.lex_label[idx])
927
+
928
+ def _xslice_active_edges(self, size_t idx):
929
+ r"""
930
+ This method is for internal use only
931
+
932
+ TESTS:
933
+
934
+ Indirect doctests::
935
+
936
+ sage: SD = graphs.HouseGraph().slice_decomposition()
937
+ sage: SD.active_edges(0)
938
+ [(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]
939
+ """
940
+ cdef size_t l = self.xslice_len[idx]
941
+ cdef size_t llv_prefix = len(self.lex_label[idx])
942
+ cdef size_t j = idx + 1
943
+ cdef size_t lj
944
+
945
+ A = []
946
+ while j < idx + l:
947
+ lj = self.xslice_len[j]
948
+ llj = self.lex_label[j]
949
+ for u in self.sigma[j:j+lj]:
950
+ for w in llj[llv_prefix:]:
951
+ A.append((w, u))
952
+ j += lj
953
+ assert j == idx + l, "slice decomposition is ill-formed"
954
+ return A
955
+
956
+ def underlying_graph(self):
957
+ r"""
958
+ Return the underlying graph corresponding to the slice decomposition.
959
+
960
+ If `G` was the graph given as parameter to compute the slice
961
+ decomposition, the underlying graph corresponds to ``G.to_simple()``
962
+ where labels are ignored, *i.e.*, it is the input graph without loops,
963
+ multiple edges and labels.
964
+
965
+ .. NOTE::
966
+
967
+ This method is mostly defined to test the computation of
968
+ lexicographic labels and actives edges.
969
+
970
+ EXAMPLES:
971
+
972
+ ::
973
+
974
+ sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False)
975
+ sage: SD = G.slice_decomposition(initial_vertex='x')
976
+ sage: SD.underlying_graph() == G
977
+ True
978
+
979
+ The graph can have loops or multiple edges but they are ignored::
980
+
981
+ sage: G = graphs.CubeConnectedCycle(2) # multiple edges
982
+ sage: SD = G.slice_decomposition()
983
+ sage: SD.underlying_graph() == G.to_simple(immutable=True)
984
+ True
985
+
986
+ sage: G = graphs.CubeConnectedCycle(1) # loops
987
+ sage: SD = G.slice_decomposition()
988
+ sage: SD.underlying_graph() == G.to_simple(immutable=True)
989
+ True
990
+
991
+ TESTS::
992
+
993
+ sage: for _ in range(5):
994
+ ....: G = graphs.RandomGNP(15, 0.3)
995
+ ....: SD = G.slice_decomposition()
996
+ ....: SD.underlying_graph() == G
997
+ True
998
+ True
999
+ True
1000
+ True
1001
+ True
1002
+ """
1003
+ if not hasattr(self, '_underlying_graph'):
1004
+ vertices = self.sigma
1005
+ edges = [(u, v) for i, v in enumerate(self.sigma)
1006
+ for u in self.lex_label[i]]
1007
+ data = [vertices, edges]
1008
+ Gclass = self._graph_class
1009
+ self._underlying_graph = Gclass(data, format='vertices_and_edges',
1010
+ immutable=True)
1011
+ return self._underlying_graph
1012
+
1013
+ def _repr_(self):
1014
+ r"""
1015
+ Return a string representation of a ``SliceDecomposition`` object.
1016
+
1017
+ TESTS::
1018
+
1019
+ sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition()
1020
+ sage: repr(SD)
1021
+ '[0[1[4[5]]] [2[6]] [3] [9] [7] [8]]'
1022
+ sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False)
1023
+ sage: SD = G.slice_decomposition(initial_vertex='x'); repr(SD)
1024
+ '[x[a[b[c[d]] [e[f]]] [g]] [u[y[z]]] [v[w]]]'
1025
+ """
1026
+ def inner_repr(idx):
1027
+ l = self.xslice_len[idx]
1028
+ S = []
1029
+ if l > 1:
1030
+ j = idx + 1
1031
+ while j < idx + l:
1032
+ lj = self.xslice_len[j]
1033
+ S.append(inner_repr(j))
1034
+ j += lj
1035
+ assert j == idx + l, "slice decomposition is ill-formed"
1036
+ return f'{self.sigma[idx]}' + ' '.join(f'[{s}]' for s in S)
1037
+ return f'[{inner_repr(0)}]'
1038
+
1039
+ def _latex_(self):
1040
+ r"""
1041
+ Return a string to render, using `\LaTeX`, the slice decomposition as a
1042
+ tree.
1043
+
1044
+ TESTS::
1045
+
1046
+ sage: from sage.graphs.graph_latex import check_tkz_graph
1047
+ sage: check_tkz_graph() # random - depends on Tex installation
1048
+ sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition()
1049
+ sage: latex(SD)
1050
+ \begin{tikzpicture}
1051
+ ...
1052
+ v0 -- {l0, v1, v4, v6, v7, v8, v9};
1053
+ v1 -- {l1, v2};
1054
+ v2 -- {l2, v3};
1055
+ v3 -- {l3};
1056
+ v4 -- {l4, v5};
1057
+ v5 -- {l5};
1058
+ v6 -- {l6};
1059
+ v7 -- {l7};
1060
+ v8 -- {l8};
1061
+ v9 -- {l9};
1062
+ ...
1063
+ \end{tikzpicture}
1064
+
1065
+ """
1066
+ from sage.misc.latex import latex
1067
+
1068
+ latex.add_package_to_preamble_if_available("tikz")
1069
+ latex.add_to_preamble(r"\usetikzlibrary{arrows,shapes,fit}")
1070
+ latex.add_to_preamble(r"\usetikzlibrary{graphs,graphdrawing}")
1071
+ latex.add_to_preamble(r"\usegdlibrary{trees}")
1072
+
1073
+ # Call latex() on all vertices
1074
+ sigma_latex = [ latex(v) for v in self.sigma ]
1075
+ slices = [[] for _ in self.sigma]
1076
+
1077
+ lines = [ r"\begin{tikzpicture}" ]
1078
+ lines.append(r"\graph [tree layout,level distance=0,level sep=1em,"
1079
+ r"sibling distance=0,sibling sep=0.6em,"
1080
+ r"tail anchor=center,head anchor=north,"
1081
+ r"nodes={draw,rectangle,inner xsep=0.2em},edges={thick}]")
1082
+ lines.append("{")
1083
+ bo, bc = "{", "}" # to write { and } in f-strings
1084
+ # Create the nodes and leaves of the slice decomposition tree
1085
+ for i in range(len(self.sigma)):
1086
+ l = self.xslice_len[i]
1087
+ label = r"\ ".join(sigma_latex[i:i+l])
1088
+ lines.append(f" v{i}[as={bo}${label}${bc}];")
1089
+ lines.append(f" l{i}[draw=none,as={bo}${sigma_latex[i]}${bc}];")
1090
+ j = i + 1
1091
+ slices[i].append(f"l{i}")
1092
+ while j < i + l:
1093
+ slices[i].append(f"v{j}")
1094
+ j += self.xslice_len[j]
1095
+ # Create the edges of the slice decomposition tree
1096
+ for i, S in enumerate(slices):
1097
+ lines.append(f" v{i} -- " + "{" + ", ".join(S) + "};")
1098
+ lines.append("};")
1099
+ # Add dahsed red boxes around xslices
1100
+ for i, S in enumerate(slices):
1101
+ fit=" ".join(f"({s})" for s in S)
1102
+ lines.append(rf"\node (s{i}) [rectangle,inner xsep=0.2em,draw=red,"
1103
+ f"densely dashed,fit={fit}]{bo}{bc};")
1104
+
1105
+ lines.append(r"\end{tikzpicture}")
1106
+ return "\n".join(lines)