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,1129 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ r"""
3
+ PQ-Trees
4
+
5
+ This module implements PQ-Trees, a data structure use to represent all
6
+ permutations of the columns of a matrix which satisfy the *consecutive ones*
7
+ *property*:
8
+
9
+ A binary matrix satisfies the *consecutive ones property* if the 1s are
10
+ contiguous in each of its rows (or equivalently, if no row contains the regexp
11
+ pattern `10^+1`).
12
+
13
+ Alternatively, one can say that a sequence of sets `S_1,...,S_n` satisfies the
14
+ *consecutive ones property* if for any `x` the indices of the sets containing
15
+ `x` is an interval of `[1,n]`.
16
+
17
+ This module is used for the recognition of Interval Graphs (see
18
+ :meth:`~sage.graphs.generic_graph.GenericGraph.is_interval`).
19
+
20
+ **P-tree and Q-tree**
21
+
22
+
23
+ - A `P`-tree with children `c_1,...,c_k` (which can be `P`-trees, `Q`-trees, or
24
+ actual sets of points) indicates that all `k!` permutations of the children
25
+ are allowed.
26
+
27
+ Example: `\{1,2\},\{3,4\},\{5,6\}` (disjoint sets can be permuted in any way)
28
+
29
+ - A `Q`-tree with children `c_1,...,c_k` (which can be `P`-trees, `Q`-trees, or
30
+ actual sets of points) indicates that only two permutations of its children
31
+ are allowed: `c_1,...,c_k` or `c_k,...,c_1`.
32
+
33
+ Example: `\{1,2\},\{2,3\},\{3,4\},\{4,5\},\{5,6\}` (only two permutations of
34
+ these sets have the *consecutive ones property*).
35
+
36
+ **Computation of all possible orderings**
37
+
38
+ #. In order to compute all permutations of a sequence of sets `S_1,...,S_k`
39
+ satisfying the *consecutive ones property*, we initialize `T` as a `P`-tree
40
+ whose children are all the `S_1,...,S_k`, thus representing the set of all
41
+ `k!` permutations of them.
42
+
43
+ #. We select some element `x` and update the data structure `T` to restrict the
44
+ permutations it describes to those that keep the occurrences of `x` on an
45
+ interval of `[1,...,k]`. This will result in a new `P`-tree whose children
46
+ are:
47
+
48
+ * all `\bar c_x` sets `S_i` which do *not* contain `x`.
49
+ * a new `P`-tree whose children are the `c_x` sets `S_i` containing `x`.
50
+
51
+ This describes the set of all `c_x!\times \bar c'_x!` permutations of
52
+ `S_1,...,S_k` that keep the sets containing `x` on an interval.
53
+
54
+ #. We take a second element `x'` and update the data structure `T` to restrict
55
+ the permutations it describes to those that keep `x'` on an interval of
56
+ `[1,...,k]`. The sets `S_1,...,S_k` belong to 4 categories:
57
+
58
+ * The family `S_{00}` of sets which do not contain any of
59
+ `x,x'`.
60
+
61
+ * The family `S_{01}` of sets which contain `x'` but do not contain
62
+ `x`.
63
+
64
+ * The family `S_{10}` of sets which contain `x` but do not contain
65
+ `x'`.
66
+
67
+ * The family `S_{11}` of sets which contain `x'` and `x'`.
68
+
69
+ With these notations, the permutations of `S_1,...,S_k` which keep the
70
+ occurrences of `x` and `x'` on an interval are of two forms:
71
+
72
+ * <some sets `S_{00}`>, <sets from `S_{10}`>, <sets from `S_{11}`>, <sets from `S_{01}`>, <other sets from `S_{00}`>
73
+ * <some sets `S_{00}`>, <sets from `S_{01}`>, <sets from `S_{11}`>, <sets from `S_{10}`>, <other sets from `S_{00}`>
74
+
75
+ These permutations can be modeled with the following `PQ`-tree:
76
+
77
+ * A `P`-tree whose children are:
78
+
79
+ * All sets from `S_{00}`
80
+ * A `Q`-tree whose children are:
81
+
82
+ * A `P`-tree with whose children are the sets from `S_{10}`
83
+ * A `P`-tree with whose children are the sets from `S_{11}`
84
+ * A `P`-tree with whose children are the sets from `S_{01}`
85
+
86
+ #. One at a time, we update the data structure with each element until they are
87
+ all exhausted, or until we reach a proof that no permutation satisfying the
88
+ *consecutive ones property* exists.
89
+
90
+ Using these two types of tree, and exploring the different cases of
91
+ intersection, it is possible to represent all the possible permutations of
92
+ our sets satisfying our constraints, or to prove that no such ordering
93
+ exists. This is the whole purpose of this module, and is explained with more
94
+ details in many places, for example in the following document from Hajiaghayi
95
+ [Haj2000]_.
96
+
97
+ Authors:
98
+
99
+ Nathann Cohen (initial implementation)
100
+
101
+
102
+ Methods and functions
103
+ ---------------------
104
+ """
105
+
106
+ # ****************************************************************************
107
+ # Copyright (C) 2012 Nathann Cohen <nathann.cohen@gmail.com>
108
+ #
109
+ # This program is free software: you can redistribute it and/or modify
110
+ # it under the terms of the GNU General Public License as published by
111
+ # the Free Software Foundation, either version 2 of the License, or
112
+ # (at your option) any later version.
113
+ # https://www.gnu.org/licenses/
114
+ # ****************************************************************************
115
+
116
+ # Constants, to make the code more readable
117
+
118
+ FULL = 2
119
+ PARTIAL = 1
120
+ EMPTY = 0
121
+ ALIGNED = True
122
+ UNALIGNED = False
123
+
124
+
125
+ ##########################################################################
126
+ # Some Helper Functions #
127
+ # #
128
+ # As the elements of a PQ-Tree can be either P-Trees, Q-Trees, or the #
129
+ # sets themselves (the leaves), the following functions are #
130
+ # meant to be applied both on PQ-Trees and Sets, and mimic for the #
131
+ # latter the behaviour we expect from the corresponding methods #
132
+ # defined in class PQ #
133
+ ##########################################################################
134
+
135
+ def _set_contiguous(tree, x):
136
+ """
137
+ Helper function for updating ``tree``.
138
+
139
+ The objective is to ensure that the sets containing ``x`` are contiguous for
140
+ any admissible permutation of its subtrees.
141
+
142
+ TESTS::
143
+
144
+ sage: from sage.graphs.pq_trees import _set_contiguous, P
145
+ sage: p1 = P([[0, 1], [1, 2], [2, 3], [3, 0]])
146
+ sage: _set_contiguous(p1, 0)
147
+ (1, True)
148
+ sage: p1
149
+ ('P', [{1, 2}, {2, 3}, ('P', [{0, 1}, {0, 3}])])
150
+ sage: _set_contiguous(p1, 2)
151
+ (1, True)
152
+ sage: p1
153
+ ('P', [('P', [{0, 1}, {0, 3}]), ('P', [{1, 2}, {2, 3}])])
154
+ sage: _set_contiguous(p1, 1)
155
+ (1, False)
156
+ sage: p1
157
+ ('P', [('Q', [{0, 3}, {0, 1}, {1, 2}, {2, 3}])])
158
+ sage: p2 = P([[0, 1], [0, 2], [0, 3]])
159
+ sage: _set_contiguous(p2, 0)
160
+ (2, True)
161
+ sage: p2
162
+ ('P', [{0, 1}, {0, 2}, {0, 3}])
163
+ sage: _set_contiguous(p2, Set([1, 2]))
164
+ (0, True)
165
+ """
166
+ if isinstance(tree, PQ):
167
+ return tree.set_contiguous(x)
168
+ elif x in tree:
169
+ return (FULL, ALIGNED)
170
+ return (EMPTY, ALIGNED)
171
+
172
+
173
+ def _new_P(liste):
174
+ """
175
+ Helper function returning a new P-tree.
176
+
177
+ TESTS::
178
+
179
+ sage: from sage.graphs.pq_trees import _new_P
180
+ sage: _new_P([[1,2], [2,3]])
181
+ ('P', [{1, 2}, {2, 3}])
182
+ sage: _new_P([[1,2]])
183
+ [1, 2]
184
+ """
185
+ if len(liste) > 1:
186
+ return P(liste)
187
+ return liste[0]
188
+
189
+
190
+ def _new_Q(liste):
191
+ """
192
+ Helper function returning a new Q-tree.
193
+
194
+ TESTS::
195
+
196
+ sage: from sage.graphs.pq_trees import _new_Q
197
+ sage: _new_Q([[1,2], [2,3]])
198
+ ('Q', [{1, 2}, {2, 3}])
199
+ sage: _new_Q([[1,2]])
200
+ [1, 2]
201
+ """
202
+ if len(liste) > 1:
203
+ return Q(liste)
204
+ return liste[0]
205
+
206
+
207
+ def _flatten(x):
208
+ """
209
+ Helper function returning a flatten version of ``x``, if ``x`` is a PQ-tree.
210
+
211
+ TESTS::
212
+
213
+ sage: from sage.graphs.pq_trees import P, Q, _flatten
214
+ sage: p = Q([P([[1,2], [2,3]])])
215
+ sage: _flatten(p)
216
+ ('P', [{1, 2}, {2, 3}])
217
+ sage: _flatten([p, p])
218
+ [('Q', [('P', [{1, 2}, {2, 3}])]), ('Q', [('P', [{1, 2}, {2, 3}])])]
219
+ """
220
+ if isinstance(x, PQ):
221
+ return x.flatten()
222
+ return x
223
+
224
+
225
+ impossible_msg = "Impossible"
226
+
227
+
228
+ def reorder_sets(sets):
229
+ r"""
230
+ Reorders a collection of sets such that each element appears on an
231
+ interval.
232
+
233
+ Given a collection of sets `C = S_1,...,S_k` on a ground set `X`,
234
+ this function attempts to reorder them in such a way that `\forall
235
+ x \in X` and `i<j` with `x\in S_i, S_j`, then `x\in S_l` for every
236
+ `i<l<j` if it exists.
237
+
238
+ INPUT:
239
+
240
+ - ``sets`` -- list of instances of ``list, Set`` or ``set``
241
+
242
+ ALGORITHM: PQ-Trees
243
+
244
+ EXAMPLES:
245
+
246
+ There is only one way (up to reversal) to represent contiguously
247
+ the sequence of sets `\{i-1, i, i+1\}`::
248
+
249
+ sage: from sage.graphs.pq_trees import reorder_sets
250
+ sage: seq = [Set([i-1,i,i+1]) for i in range(1,15)]
251
+
252
+ We apply a random permutation::
253
+
254
+ sage: p = Permutations(len(seq)).random_element()
255
+ sage: seq = [ seq[p(i+1)-1] for i in range(len(seq)) ]
256
+ sage: ordered = reorder_sets(seq)
257
+ sage: if not 0 in ordered[0]:
258
+ ....: ordered = ordered.reverse()
259
+ sage: print(ordered)
260
+ [{0, 1, 2}, {1, 2, 3}, {2, 3, 4}, {3, 4, 5}, {4, 5, 6}, {5, 6, 7},
261
+ {8, 6, 7}, {8, 9, 7}, {8, 9, 10}, {9, 10, 11}, {10, 11, 12},
262
+ {11, 12, 13}, {12, 13, 14}, {13, 14, 15}]
263
+ """
264
+ if len(sets) <= 2:
265
+ return sets
266
+
267
+ s = set().union(*sets) # union of the sets
268
+
269
+ tree = P(sets)
270
+
271
+ for i in s:
272
+ tree.set_contiguous(i)
273
+ tree = _flatten(tree)
274
+
275
+ return tree.ordering()
276
+
277
+
278
+ class PQ:
279
+ r"""
280
+ PQ-Trees
281
+
282
+ This class should not be instantiated by itself: it is extended by
283
+ :class:`P` and :class:`Q`. See the documentation of
284
+ :mod:`sage.graphs.pq_trees` for more information.
285
+
286
+ AUTHOR : Nathann Cohen
287
+ """
288
+
289
+ def __init__(self, seq):
290
+ r"""
291
+ Construction of a PQ-Tree.
292
+
293
+ EXAMPLES::
294
+
295
+ sage: from sage.graphs.pq_trees import P, Q
296
+ sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
297
+
298
+ :issue:`17787`::
299
+
300
+ sage: Graph('GvGNp?').is_interval()
301
+ False
302
+ """
303
+ from sage.sets.set import Set
304
+
305
+ self._children = []
306
+ for e in seq:
307
+ if isinstance(e, list):
308
+ e = Set(e)
309
+
310
+ if e not in self._children:
311
+ self._children.append(e)
312
+
313
+ def reverse(self):
314
+ r"""
315
+ Recursively reverse ``self`` and its children.
316
+
317
+ EXAMPLES::
318
+
319
+ sage: from sage.graphs.pq_trees import P, Q
320
+ sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
321
+ sage: p.ordering()
322
+ [{1, 2}, {2, 3}, {2, 4}, {8, 2}, {9, 2}]
323
+ sage: p.reverse()
324
+ sage: p.ordering()
325
+ [{9, 2}, {8, 2}, {2, 4}, {2, 3}, {1, 2}]
326
+ """
327
+ for i in self._children:
328
+ if isinstance(i, PQ):
329
+ i.reverse()
330
+
331
+ self._children.reverse()
332
+
333
+ def __contains__(self, v):
334
+ r"""
335
+ Test whether there exists an element of ``self`` containing
336
+ an element ``v``.
337
+
338
+ INPUT:
339
+
340
+ - ``v`` -- an element of the ground set
341
+
342
+ EXAMPLES::
343
+
344
+ sage: from sage.graphs.pq_trees import P, Q
345
+ sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
346
+ sage: 5 in p
347
+ False
348
+ sage: 9 in p
349
+ True
350
+ """
351
+ return any(v in i for i in self)
352
+
353
+ def __iter__(self):
354
+ r"""
355
+ Iterate over the children of ``self``.
356
+
357
+ EXAMPLES::
358
+
359
+ sage: from sage.graphs.pq_trees import P, Q
360
+ sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
361
+ sage: for i in p:
362
+ ....: print(i)
363
+ {1, 2}
364
+ {2, 3}
365
+ ('P', [{2, 4}, {8, 2}, {9, 2}])
366
+ """
367
+ yield from self._children
368
+
369
+ def number_of_children(self):
370
+ r"""
371
+ Return the number of children of ``self``.
372
+
373
+ EXAMPLES::
374
+
375
+ sage: from sage.graphs.pq_trees import P, Q
376
+ sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
377
+ sage: p.number_of_children()
378
+ 3
379
+ """
380
+ return len(self._children)
381
+
382
+ def ordering(self):
383
+ r"""
384
+ Return the current ordering given by listing the leaves from
385
+ left to right.
386
+
387
+ EXAMPLES::
388
+
389
+ sage: from sage.graphs.pq_trees import P, Q
390
+ sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
391
+ sage: p.ordering()
392
+ [{1, 2}, {2, 3}, {2, 4}, {8, 2}, {9, 2}]
393
+ """
394
+ value = []
395
+ for i in self:
396
+ if isinstance(i, PQ):
397
+ value.extend(i.ordering())
398
+ else:
399
+ value.append(i)
400
+
401
+ return value
402
+
403
+ def __repr__(self) -> str:
404
+ r"""
405
+ Succinctly represent ``self``.
406
+
407
+ EXAMPLES::
408
+
409
+ sage: from sage.graphs.pq_trees import P, Q
410
+ sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
411
+ sage: print(p)
412
+ ('Q', [{1, 2}, {2, 3}, ('P', [{2, 4}, {8, 2}, {9, 2}])])
413
+ """
414
+ return str((("P" if isinstance(self, P) else "Q"), self._children))
415
+
416
+ def simplify(self, v, left=False, right=False):
417
+ r"""
418
+ Return a simplified copy of ``self`` according to the element ``v``.
419
+
420
+ If ``self`` is a partial P-tree for ``v``, we would like to
421
+ restrict the permutations of its children to permutations
422
+ keeping the children containing ``v`` contiguous. This
423
+ function also "locks" all the elements not containing ``v``
424
+ inside a `P`-tree, which is useful when one want to keep the
425
+ elements containing ``v`` on one side (which is the case when
426
+ this method is called).
427
+
428
+ INPUT:
429
+
430
+ - ``left``, ``right`` -- booleans; whether ``v`` is aligned to the
431
+ right or to the left
432
+
433
+ - ``v`` -- an element of the ground set
434
+
435
+ OUTPUT:
436
+
437
+ If ``self`` is a `Q`-Tree, the sequence of its children is
438
+ returned. If ``self`` is a `P`-tree, 2 `P`-tree are returned,
439
+ namely the two `P`-tree defined above and restricting the
440
+ permutations, in the order implied by ``left, right`` (if
441
+ ``right =True``, the second `P`-tree will be the one gathering
442
+ the elements containing ``v``, if ``left=True``, the
443
+ opposite).
444
+
445
+ .. NOTE::
446
+
447
+ This method is assumes that ``self`` is partial for ``v``,
448
+ and aligned to the side indicated by ``left, right``.
449
+
450
+ EXAMPLES:
451
+
452
+ A `P`-Tree ::
453
+
454
+ sage: from sage.graphs.pq_trees import P, Q
455
+ sage: p = P([[2,4], [1,2], [0,8], [0,5]])
456
+ sage: p.simplify(0, right = True)
457
+ [('P', [{2, 4}, {1, 2}]), ('P', [{0, 8}, {0, 5}])]
458
+
459
+ A `Q`-Tree ::
460
+
461
+ sage: q = Q([[2,4], [1,2], [0,8], [0,5]])
462
+ sage: q.simplify(0, right = True)
463
+ [{2, 4}, {1, 2}, {0, 8}, {0, 5}]
464
+ """
465
+ if sum([left, right]) != 1:
466
+ raise ValueError("Exactly one of left or right must be specified")
467
+
468
+ if isinstance(self, Q):
469
+ L = []
470
+ for c in self._children:
471
+ if (isinstance(c, PQ) and # Is c partial?
472
+ v in c and # (does c contain sets with
473
+ any(v not in cc for cc in c)): # and without v ?)
474
+ L.extend(c.simplify(v, right=right, left=left))
475
+ else:
476
+ L.append(c)
477
+ return L
478
+ else:
479
+ empty = []
480
+ full = []
481
+ partial = []
482
+
483
+ for c in self._children:
484
+ if v in c:
485
+ if (isinstance(c, PQ) and # Is c partial? (does c contain
486
+ any(v not in cc for cc in c)): # sets with and without v ?)
487
+ partial = c.simplify(v, right=right, left=left)
488
+ else:
489
+ full.append(c)
490
+ else:
491
+ empty.append(c)
492
+ if empty:
493
+ empty = [_new_P(empty)]
494
+ if full:
495
+ full = [_new_P(full)]
496
+
497
+ if right:
498
+ return empty + partial + full
499
+ else:
500
+ return full + partial + empty
501
+
502
+ def flatten(self):
503
+ r"""
504
+ Return a flattened copy of ``self``.
505
+
506
+ If ``self`` has only one child, we may as well consider its
507
+ child's children, as ``self`` encodes no information. This
508
+ method recursively "flattens" trees having only on PQ-tree
509
+ child, and returns it.
510
+
511
+ EXAMPLES::
512
+
513
+ sage: from sage.graphs.pq_trees import P, Q
514
+ sage: p = Q([P([[2,4], [2,8], [2,9]])])
515
+ sage: p.flatten()
516
+ ('P', [{2, 4}, {8, 2}, {9, 2}])
517
+ """
518
+ if self.number_of_children() == 1:
519
+ return _flatten(self._children[0])
520
+ else:
521
+ self._children = [_flatten(x) for x in self._children]
522
+ return self
523
+
524
+
525
+ class P(PQ):
526
+ r"""
527
+ A P-Tree is a PQ-Tree whose children can be permuted in any way.
528
+
529
+ For more information, see the documentation of :mod:`sage.graphs.pq_trees`.
530
+ """
531
+ def set_contiguous(self, v):
532
+ r"""
533
+ Update ``self`` so that the sets containing ``v`` are
534
+ contiguous for any admissible permutation of its subtrees.
535
+
536
+ INPUT:
537
+
538
+ - ``v`` -- an element of the ground set
539
+
540
+ OUTPUT:
541
+
542
+ According to the cases:
543
+
544
+ * ``(EMPTY, ALIGNED)`` if no set of the tree contains
545
+ an occurrence of ``v``
546
+
547
+ * ``(FULL, ALIGNED)`` if all the sets of the tree contain
548
+ ``v``
549
+
550
+ * ``(PARTIAL, ALIGNED)`` if some (but not all) of the sets
551
+ contain ``v``, all of which are aligned
552
+ to the right of the ordering at the end when the function ends
553
+
554
+ * ``(PARTIAL, UNALIGNED)`` if some (but not all) of the
555
+ sets contain ``v``, though it is impossible to align them
556
+ all to the right
557
+
558
+ In any case, the sets containing ``v`` are contiguous when this
559
+ function ends. If there is no possibility of doing so, the function
560
+ raises a :exc:`ValueError` exception.
561
+
562
+ EXAMPLES:
563
+
564
+ Ensuring the sets containing ``0`` are continuous::
565
+
566
+ sage: from sage.graphs.pq_trees import P, Q
567
+ sage: p = P([[0,3], [1,2], [2,3], [2,4], [4,0],[2,8], [2,9]])
568
+ sage: p.set_contiguous(0)
569
+ (1, True)
570
+ sage: print(p)
571
+ ('P', [{1, 2}, {2, 3}, {2, 4}, {8, 2}, {9, 2}, ('P', [{0, 3}, {0, 4}])])
572
+
573
+ Impossible situation::
574
+
575
+ sage: p = P([[0,1], [1,2], [2,3], [3,0]])
576
+ sage: p.set_contiguous(0)
577
+ (1, True)
578
+ sage: p.set_contiguous(1)
579
+ (1, True)
580
+ sage: p.set_contiguous(2)
581
+ (1, True)
582
+ sage: p.set_contiguous(3)
583
+ Traceback (most recent call last):
584
+ ...
585
+ ValueError: Impossible
586
+ """
587
+
588
+ ###############################################################
589
+ # Defining Variables : #
590
+ # #
591
+ # Collecting the information of which children are FULL of v, #
592
+ # which ones are EMPTY, PARTIAL_ALIGNED and PARTIAL_UNALIGNED #
593
+ # #
594
+ # Defining variables for their cardinals, just to make the #
595
+ # code slightly more readable :-) #
596
+ ###############################################################
597
+
598
+ for x in self:
599
+ _set_contiguous(x, v)
600
+ self.flatten()
601
+ seq = [_set_contiguous(x, v) for x in self]
602
+
603
+ f_seq = dict(zip(self, seq))
604
+
605
+ set_FULL = []
606
+ set_EMPTY = []
607
+ set_PARTIAL_ALIGNED = []
608
+ set_PARTIAL_UNALIGNED = []
609
+
610
+ sorting = {
611
+ (FULL, ALIGNED): set_FULL,
612
+ (EMPTY, ALIGNED): set_EMPTY,
613
+ (PARTIAL, ALIGNED): set_PARTIAL_ALIGNED,
614
+ (PARTIAL, UNALIGNED): set_PARTIAL_UNALIGNED
615
+ }
616
+
617
+ for i in self:
618
+ sorting[f_seq[i]].append(i)
619
+
620
+ n_FULL = len(set_FULL)
621
+ n_EMPTY = len(set_EMPTY)
622
+ n_PARTIAL_ALIGNED = len(set_PARTIAL_ALIGNED)
623
+ n_PARTIAL_UNALIGNED = len(set_PARTIAL_UNALIGNED)
624
+
625
+ # Excludes the situation where there is no solution.
626
+ # read next comment for more explanations
627
+
628
+ if (n_PARTIAL_ALIGNED > 2 or
629
+ (n_PARTIAL_UNALIGNED >= 1 and n_EMPTY != self.number_of_children() - 1)):
630
+ raise ValueError(impossible_msg)
631
+
632
+ # From now on, there are at most two pq-trees which are partially filled
633
+ # If there is one which is not aligned to the right, all the others are empty
634
+
635
+ #########################################################
636
+ # 1/2 #
637
+ # #
638
+ # Several easy cases where we can decide without paying #
639
+ # attention #
640
+ #########################################################
641
+
642
+ # All the children are FULL
643
+ elif n_FULL == self.number_of_children():
644
+ return FULL, True
645
+
646
+ # All the children are empty
647
+ elif n_EMPTY == self.number_of_children():
648
+ return EMPTY, True
649
+
650
+ # There is a PARTIAL UNALIGNED element (and all the others are
651
+ # empty as we checked before
652
+
653
+ elif n_PARTIAL_UNALIGNED == 1:
654
+ return (PARTIAL, UNALIGNED)
655
+
656
+ # If there is just one partial element and all the others are
657
+ # empty, we just reorder the set to put it at the right end
658
+
659
+ elif (n_PARTIAL_ALIGNED == 1 and
660
+ n_EMPTY == self.number_of_children()-1):
661
+
662
+ self._children = set_EMPTY + set_PARTIAL_ALIGNED
663
+ return (PARTIAL, ALIGNED)
664
+
665
+ ################################################################
666
+ # 2/2 #
667
+ # #
668
+ # From now on, there are at most two partial pq-trees and all #
669
+ # of them have v aligned to their right #
670
+ # #
671
+ # We now want to order them in such a way that all the #
672
+ # elements containing v are located on the right #
673
+ ################################################################
674
+
675
+ else:
676
+
677
+ self._children = []
678
+
679
+ # We first move the empty elements to the left, if any
680
+
681
+ if n_EMPTY > 0:
682
+ self._children.extend(set_EMPTY)
683
+
684
+ # If there is one partial element we but have to add it to
685
+ # the sequence, then add all the full elements
686
+
687
+ # We must also make sure these elements will not be
688
+ # reordered in such a way that the elements containing v
689
+ # are not contiguous
690
+
691
+ # ==> We create a Q-tree
692
+
693
+ if n_PARTIAL_ALIGNED < 2:
694
+
695
+ new = []
696
+
697
+ # add the partial element, if any
698
+ if n_PARTIAL_ALIGNED == 1:
699
+
700
+ subtree = set_PARTIAL_ALIGNED[0]
701
+ new.extend(subtree.simplify(v, right=ALIGNED))
702
+
703
+ # Then the full elements, if any, in a P-tree (we can
704
+ # permute any two of them while keeping all the
705
+ # elements containing v on an interval
706
+
707
+ if n_FULL > 0:
708
+
709
+ new.append(_new_P(set_FULL))
710
+
711
+ # We lock all of them in a Q-tree
712
+
713
+ self._children.append(_new_Q(new))
714
+
715
+ return PARTIAL, True
716
+
717
+ # If there are 2 partial elements, we take care of both
718
+ # ends. We also know it will not be possible to align the
719
+ # interval of sets containing v to the right
720
+
721
+ else:
722
+ new = []
723
+
724
+ # The second partial element is aligned to the right
725
+ # while, as we want to put it at the end of the
726
+ # interval, it should be aligned to the left
727
+ set_PARTIAL_ALIGNED[1].reverse()
728
+
729
+ # 1/3
730
+ # Left partial subtree
731
+ subtree = set_PARTIAL_ALIGNED[0]
732
+ new.extend(subtree.simplify(v, right=ALIGNED))
733
+
734
+ # 2/3
735
+ # Center (Full elements, in a P-tree, as they can be
736
+ # permuted)
737
+
738
+ if n_FULL > 0:
739
+ new.append(_new_P(set_FULL))
740
+
741
+ # 3/3
742
+ # Right partial subtree
743
+ subtree = set_PARTIAL_ALIGNED[1]
744
+ new.extend(subtree.simplify(v, left=ALIGNED))
745
+
746
+ # We add all of it, locked in a Q-Tree
747
+ self._children.append(_new_Q(new))
748
+
749
+ return PARTIAL, False
750
+
751
+ def cardinality(self):
752
+ r"""
753
+ Return the number of orderings allowed by the structure.
754
+
755
+ .. SEEALSO::
756
+
757
+ :meth:`orderings` -- iterate over all admissible orderings
758
+
759
+ EXAMPLES::
760
+
761
+ sage: from sage.graphs.pq_trees import P, Q
762
+ sage: p = P([[0,3], [1,2], [2,3], [2,4], [4,0],[2,8], [2,9]])
763
+ sage: p.cardinality()
764
+ 5040
765
+ sage: p.set_contiguous(3)
766
+ (1, True)
767
+ sage: p.cardinality()
768
+ 1440
769
+ """
770
+ from math import factorial
771
+ n = factorial(self.number_of_children())
772
+ for c in self._children:
773
+ if isinstance(c, PQ):
774
+ n = n*c.cardinality()
775
+ return n
776
+
777
+ def orderings(self):
778
+ r"""
779
+ Iterate over all orderings of the sets allowed by the structure.
780
+
781
+ .. SEEALSO::
782
+
783
+ :meth:`cardinality` -- return the number of orderings
784
+
785
+ EXAMPLES::
786
+
787
+ sage: from sage.graphs.pq_trees import P, Q
788
+ sage: p = P([[2,4], [1,2], [0,8], [0,5]])
789
+ sage: for o in p.orderings():
790
+ ....: print(o)
791
+ ({2, 4}, {1, 2}, {0, 8}, {0, 5})
792
+ ({2, 4}, {1, 2}, {0, 5}, {0, 8})
793
+ ({2, 4}, {0, 8}, {1, 2}, {0, 5})
794
+ ({2, 4}, {0, 8}, {0, 5}, {1, 2})
795
+ ...
796
+ """
797
+ from itertools import permutations, product
798
+ for p in permutations(self._children):
799
+ yield from product(*[x.orderings() if isinstance(x, PQ) else [x]
800
+ for x in p])
801
+
802
+
803
+ class Q(PQ):
804
+ r"""
805
+ A Q-Tree is a PQ-Tree whose children are ordered up to reversal.
806
+
807
+ For more information, see the documentation of :mod:`sage.graphs.pq_trees`.
808
+ """
809
+
810
+ def set_contiguous(self, v):
811
+ r"""
812
+ Update ``self`` so that the sets containing ``v`` are
813
+ contiguous for any admissible permutation of its subtrees.
814
+
815
+ INPUT:
816
+
817
+ - ``v`` -- an element of the ground set
818
+
819
+ OUTPUT:
820
+
821
+ According to the cases:
822
+
823
+ * ``(EMPTY, ALIGNED)`` if no set of the tree contains
824
+ an occurrence of ``v``
825
+
826
+ * ``(FULL, ALIGNED)`` if all the sets of the tree contain
827
+ ``v``
828
+
829
+ * ``(PARTIAL, ALIGNED)`` if some (but not all) of the sets
830
+ contain ``v``, all of which are aligned
831
+ to the right of the ordering at the end when the function ends
832
+
833
+ * ``(PARTIAL, UNALIGNED)`` if some (but not all) of the
834
+ sets contain ``v``, though it is impossible to align them
835
+ all to the right
836
+
837
+ In any case, the sets containing ``v`` are contiguous when this
838
+ function ends. If there is no possibility of doing so, the function
839
+ raises a :exc:`ValueError` exception.
840
+
841
+ EXAMPLES:
842
+
843
+ Ensuring the sets containing ``0`` are continuous::
844
+
845
+ sage: from sage.graphs.pq_trees import P, Q
846
+ sage: q = Q([[2,3], Q([[3,0],[3,1]]), Q([[4,0],[4,5]])])
847
+ sage: q.set_contiguous(0)
848
+ (1, False)
849
+ sage: print(q)
850
+ ('Q', [{2, 3}, {1, 3}, {0, 3}, {0, 4}, {4, 5}])
851
+
852
+ Impossible situation::
853
+
854
+ sage: p = Q([[0,1], [1,2], [2,0]])
855
+ sage: p.set_contiguous(0)
856
+ Traceback (most recent call last):
857
+ ...
858
+ ValueError: Impossible
859
+ """
860
+ #################################################################
861
+ # Guidelines : #
862
+ # #
863
+ # As the tree is a Q-Tree, we can but reverse the order in #
864
+ # which the elements appear. It means that we can but check #
865
+ # the elements containing v are already contiguous (even #
866
+ # though we have to take special care of partial elements -- #
867
+ # the endpoints of the interval), and answer accordingly #
868
+ # (partial, full, empty, aligned..). We also want to align the #
869
+ # elements containing v to the right if possible. #
870
+ ################################################################
871
+
872
+ ###############################################################
873
+ # Defining Variables : #
874
+ # #
875
+ # Collecting the information of which children are FULL of v, #
876
+ # which ones are EMPTY, PARTIAL_ALIGNED and PARTIAL_UNALIGNED #
877
+ # #
878
+ # Defining variables for their cardinals, just to make the #
879
+ # code slightly more readable :-) #
880
+ ###############################################################
881
+
882
+ for x in self:
883
+ _set_contiguous(x, v)
884
+ self.flatten()
885
+ seq = [_set_contiguous(x, v) for x in self]
886
+
887
+ f_seq = dict(zip(self, seq))
888
+
889
+ set_FULL = []
890
+ set_EMPTY = []
891
+ set_PARTIAL_ALIGNED = []
892
+ set_PARTIAL_UNALIGNED = []
893
+
894
+ sorting = {
895
+ (FULL, ALIGNED): set_FULL,
896
+ (EMPTY, ALIGNED): set_EMPTY,
897
+ (PARTIAL, ALIGNED): set_PARTIAL_ALIGNED,
898
+ (PARTIAL, UNALIGNED): set_PARTIAL_UNALIGNED
899
+ }
900
+
901
+ for i in self:
902
+ sorting[f_seq[i]].append(i)
903
+
904
+ n_FULL = len(set_FULL)
905
+ n_EMPTY = len(set_EMPTY)
906
+ n_PARTIAL_ALIGNED = len(set_PARTIAL_ALIGNED)
907
+ n_PARTIAL_UNALIGNED = len(set_PARTIAL_UNALIGNED)
908
+
909
+ ###################################################################
910
+ # #
911
+ # Picking the good ordering for the children : #
912
+ # #
913
+ # #
914
+ # There is a possibility of aligning to the right iif #
915
+ # the vector can assume the form (as a regular expression) : #
916
+ # #
917
+ # (EMPTY *) PARTIAL (FULL *) Of course, each of these three #
918
+ # members could be empty #
919
+ # #
920
+ # Hence, in the following case we reverse the vector : #
921
+ # #
922
+ # * if the last element is empty (as we checked the whole #
923
+ # vector is not empty #
924
+ # #
925
+ # * if the last element is partial, aligned, and all the #
926
+ # others are full #
927
+ ###################################################################
928
+
929
+ if (f_seq[self._children[-1]] == (EMPTY, ALIGNED) or
930
+ (f_seq[self._children[-1]] == (PARTIAL, ALIGNED) and
931
+ n_FULL == self.number_of_children() - 1)):
932
+ # We reverse the order of the elements in the SET only.
933
+ # Which means that they are still aligned to the right !
934
+ self._children.reverse()
935
+
936
+ #########################################################
937
+ # 1/2 #
938
+ # #
939
+ # Several easy cases where we can decide without paying #
940
+ # attention #
941
+ #########################################################
942
+
943
+ # Excludes the situation where there is no solution.
944
+ # read next comment for more explanations
945
+
946
+ if (n_PARTIAL_ALIGNED > 2 or
947
+ (n_PARTIAL_UNALIGNED >= 1 and
948
+ n_EMPTY != self.number_of_children() - 1)):
949
+ raise ValueError(impossible_msg)
950
+
951
+ # From now on, there are at most two pq-trees which are partially filled
952
+ # If there is one which is not aligned to the right, all the others are empty
953
+
954
+ # First trivial case, no checking needed
955
+ elif n_FULL == self.number_of_children():
956
+ return FULL, True
957
+
958
+ # Second trivial case, no checking needed
959
+ elif n_EMPTY == self.number_of_children():
960
+ return EMPTY, True
961
+
962
+ # Third trivial case, no checking needed
963
+ elif n_PARTIAL_UNALIGNED == 1:
964
+ return (PARTIAL, UNALIGNED)
965
+
966
+ # If there is just one partial element
967
+ # and all the others are empty, we just reorder
968
+ # the set to put it at the right end
969
+
970
+ elif (n_PARTIAL_ALIGNED == 1 and
971
+ n_EMPTY == self.number_of_children() - 1):
972
+
973
+ if set_PARTIAL_ALIGNED[0] == self._children[-1]:
974
+ return (PARTIAL, ALIGNED)
975
+
976
+ else:
977
+ return (PARTIAL, UNALIGNED)
978
+
979
+ ##############################################################
980
+ # 2/2 #
981
+ # #
982
+ # We iteratively consider all the children, and check #
983
+ # that the elements containing v are indeed #
984
+ # located on an interval. #
985
+ # #
986
+ # We are also interested in knowing whether this interval is #
987
+ # aligned to the right #
988
+ # #
989
+ # Because of the previous tests, we can assume there are at #
990
+ # most two partial pq-trees and all of them are aligned to #
991
+ # their right #
992
+ ##############################################################
993
+
994
+ else:
995
+
996
+ new_children = []
997
+
998
+ # Two variables to remember where we are
999
+ # according to the interval
1000
+
1001
+ seen_nonempty = False
1002
+ seen_right_end = False
1003
+
1004
+ for i in self:
1005
+
1006
+ type, aligned = f_seq[i]
1007
+
1008
+ # We met an empty element
1009
+ if type == EMPTY:
1010
+
1011
+ # 2 possibilities :
1012
+ #
1013
+ # * we have NOT met a non-empty element before
1014
+ # and it just means we are looking at the
1015
+ # leading empty elements
1016
+ #
1017
+ # * we have met a non-empty element before and it
1018
+ # means we will never met another non-empty
1019
+ # element again => we have seen the right end
1020
+ # of the interval
1021
+
1022
+ new_children.append(i)
1023
+
1024
+ if seen_nonempty:
1025
+ seen_right_end = True
1026
+
1027
+ # We met a non-empty element
1028
+ else:
1029
+ if seen_right_end:
1030
+ raise ValueError(impossible_msg)
1031
+
1032
+ if type == PARTIAL:
1033
+
1034
+ # if we see an ALIGNED partial tree after
1035
+ # having seen a nonempty element then the
1036
+ # partial tree must be aligned to the left and
1037
+ # so we have seen the right end
1038
+
1039
+ if seen_nonempty and aligned:
1040
+ i.reverse()
1041
+ seen_right_end = True
1042
+
1043
+ # right partial subtree
1044
+ subtree = i
1045
+ new_children.extend(subtree.simplify(v, left=True))
1046
+
1047
+ # If we see an UNALIGNED partial element after
1048
+ # having met a nonempty element, there is no
1049
+ # solution to the alignment problem
1050
+
1051
+ elif seen_nonempty and not aligned:
1052
+ raise ValueError(impossible_msg)
1053
+
1054
+ # If we see an unaligned element but no non-empty
1055
+ # element since the beginning, we are witnessing both the
1056
+ # left and right end
1057
+
1058
+ elif not seen_nonempty and not aligned:
1059
+ raise ValueError("Bon, ben ca arrive O_o")
1060
+ seen_right_end = True
1061
+
1062
+ elif not seen_nonempty and aligned:
1063
+
1064
+ # left partial subtree
1065
+ subtree = i
1066
+
1067
+ new_children.extend(subtree.simplify(v, right=True))
1068
+
1069
+ else:
1070
+ new_children.append(i)
1071
+
1072
+ seen_nonempty = True
1073
+
1074
+ # Setting the updated sequence of children
1075
+ self._children = new_children
1076
+
1077
+ # Whether we achieved an alignment to the right is the
1078
+ # complement of whether we have seen the right end
1079
+
1080
+ return (PARTIAL, not seen_right_end)
1081
+
1082
+ def cardinality(self):
1083
+ r"""
1084
+ Return the number of orderings allowed by the structure.
1085
+
1086
+ .. SEEALSO::
1087
+
1088
+ :meth:`orderings` -- iterate over all admissible orderings
1089
+
1090
+ EXAMPLES::
1091
+
1092
+ sage: from sage.graphs.pq_trees import P, Q
1093
+ sage: q = Q([[0,3], [1,2], [2,3], [2,4], [4,0],[2,8], [2,9]])
1094
+ sage: q.cardinality()
1095
+ 2
1096
+ """
1097
+ n = 1
1098
+ for c in self._children:
1099
+ if isinstance(c, PQ):
1100
+ n = n*c.cardinality()
1101
+
1102
+ return n if (self.number_of_children() == 1) else 2*n
1103
+
1104
+ def orderings(self):
1105
+ r"""
1106
+ Iterate over all orderings of the sets allowed by the structure.
1107
+
1108
+ .. SEEALSO::
1109
+
1110
+ :meth:`cardinality` -- return the number of orderings
1111
+
1112
+ EXAMPLES::
1113
+
1114
+ sage: from sage.graphs.pq_trees import P, Q
1115
+ sage: q = Q([[2,4], [1,2], [0,8], [0,5]])
1116
+ sage: for o in q.orderings():
1117
+ ....: print(o)
1118
+ ({2, 4}, {1, 2}, {0, 8}, {0, 5})
1119
+ ({0, 5}, {0, 8}, {1, 2}, {2, 4})
1120
+ """
1121
+ if len(self._children) == 1:
1122
+ c = self._children[0]
1123
+ yield from (c.orderings() if isinstance(c, PQ) else [c])
1124
+ else:
1125
+ from itertools import product
1126
+ for o in product(*[x.orderings() if isinstance(x, PQ) else [x]
1127
+ for x in self._children]):
1128
+ yield o
1129
+ yield o[::-1]