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