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,753 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # cython: binding=True
3
+ r"""
4
+ Cutwidth
5
+
6
+ This module implements several algorithms to compute the cutwidth of a graph and
7
+ the corresponding ordering of the vertices. It also implements tests functions
8
+ for evaluation the width of a linear ordering (or layout).
9
+
10
+ Given an ordering
11
+ `v_1,\cdots, v_n` of the vertices of `V(G)`, its *cost* is defined as:
12
+
13
+ .. MATH::
14
+
15
+ c(v_1, ..., v_n) = \max_{1\leq i \leq n-1} c'(\{v_1, ..., v_i\})
16
+
17
+ Where
18
+
19
+ .. MATH::
20
+
21
+ c'(S) = |\{(u,w)\in E(G)\mid u\in S\text{ and }w\in V(G)\backslash S\}|
22
+
23
+ The *cutwidth* of a graph `G` is equal to the minimum cost of an ordering of its
24
+ vertices.
25
+
26
+
27
+ **This module contains the following methods**
28
+
29
+ .. csv-table::
30
+ :class: contentstable
31
+ :widths: 30, 70
32
+ :delim: |
33
+
34
+ :meth:`cutwidth` | Return the cutwidth of the graph and the corresponding vertex ordering.
35
+ :meth:`cutwidth_dyn` | Compute the cutwidth of `G` using an exponential time and space algorithm based on dynamic programming
36
+ :meth:`cutwidth_MILP` | Compute the cutwidth of `G` and the optimal ordering of its vertices using an MILP formulation
37
+ :meth:`width_of_cut_decomposition` | Return the width of the cut decomposition induced by the linear ordering `L` of the vertices of `G`
38
+
39
+
40
+ Exponential algorithm for cutwidth
41
+ ----------------------------------
42
+
43
+ In order to find an optimal ordering of the vertices for the vertex separation,
44
+ this algorithm tries to save time by computing the function `c'(S)` **at most
45
+ once** once for each of the sets `S\subseteq V(G)`. These values are stored in
46
+ an array of size `2^n` where reading the value of `c'(S)` or updating it can be
47
+ done in constant time.
48
+
49
+ Assuming that we can compute the cost of a set `S` and remember it, finding an
50
+ optimal ordering is an easy task. Indeed, we can think of the sequence `v_1,
51
+ ..., v_n` of vertices as a sequence of *sets* `\{v_1\}, \{v_1,v_2\}, ...,
52
+ \{v_1,...,v_n\}`, whose cost is precisely `\max c'(\{v_1\}), c'(\{v_1,v_2\}),
53
+ ... , c'(\{v_1,...,v_n\})`. Hence, when considering the digraph on the `2^n`
54
+ sets `S\subseteq V(G)` where there is an arc from `S` to `S'` if `S'=S\cap
55
+ \{v\}` for some `v` (that is, if the sets `S` and `S'` can be consecutive in a
56
+ sequence), an ordering of the vertices of `G` corresponds to a *path* from
57
+ `\emptyset` to `\{v_1,...,v_n\}`. In this setting, checking whether there exists
58
+ a ordering of cost less than `k` can be achieved by checking whether there
59
+ exists a directed path `\emptyset` to `\{v_1,...,v_n\}` using only sets of cost
60
+ less than `k`. This is just a depth-first-search, for each `k`.
61
+
62
+ **Lazy evaluation of** `c'`
63
+
64
+ In the previous algorithm, most of the time is actually spent on the computation
65
+ of `c'(S)` for each set `S\subseteq V(G)` -- i.e. `2^n` computations of
66
+ neighborhoods. This can be seen as a huge waste of time when noticing that it is
67
+ useless to know that the value `c'(S)` for a set `S` is less than `k` if all the
68
+ paths leading to `S` have a cost greater than `k`. For this reason, the value of
69
+ `c'(S)` is computed lazily during the depth-first search. Explanation :
70
+
71
+ When the depth-first search discovers a set of size less than `k`, the costs of
72
+ its out-neighbors (the potential sets that could follow it in the optimal
73
+ ordering) are evaluated. When an out-neighbor is found that has a cost smaller
74
+ than `k`, the depth-first search continues with this set, which is explored with
75
+ the hope that it could lead to a path toward `\{v_1,...,v_n\}`. On the other
76
+ hand, if an out-neighbour has a cost larger than `k` it is useless to attempt to
77
+ build a cheap sequence going though this set, and the exploration stops
78
+ there. This way, a large number of sets will never be evaluated and *a lot* of
79
+ computational time is saved this way.
80
+
81
+ Besides, some improvement is also made by "improving" the values found by
82
+ `c'`. Indeed, `c'(S)` is a lower bound on the cost of a sequence containing the
83
+ set `S`, but if all out-neighbors of `S` have a cost of `c'(S) + 5` then one
84
+ knows that having `S` in a sequence means a total cost of at least `c'(S) +
85
+ 5`. For this reason, for each set `S` we store the value of `c'(S)`, and replace
86
+ it by `\max (c'(S), \min_{\text{next}})` (where `\min_{\text{next}}` is the
87
+ minimum of the costs of the out-neighbors of `S`) once the costs of these
88
+ out-neighbors have been evaluated by the algorithm.
89
+
90
+ This algorithm and its implementation are very similar to
91
+ :meth:`sage.graphs.graph_decompositions.vertex_separation.vertex_separation_exp`.
92
+ The main difference is in the computation of `c'(S)`. See the :mod:`vertex
93
+ separation module's documentation
94
+ <sage.graphs.graph_decompositions.vertex_separation>` for more details on this
95
+ algorithm.
96
+
97
+ .. NOTE::
98
+
99
+ Because of its current implementation, this algorithm only works on graphs
100
+ on strictly less than 32 vertices. This can be changed to 64 if necessary,
101
+ but 32 vertices already require 4GB of memory.
102
+
103
+
104
+ MILP formulation for the cutwidth
105
+ ---------------------------------
106
+
107
+ We describe a mixed integer linear program (MILP) for determining an
108
+ optimal layout for the cutwidth of `G`.
109
+
110
+ **Variables:**
111
+
112
+ - `x_v^k` -- variable set to 1 if vertex `v` is placed in the ordering at
113
+ position `i` with `i\leq k`, and 0 otherwise
114
+
115
+ - `y_{u,v}^{k}` -- variable set to 1 if one of `u` or `v` is at a position
116
+ `i\leq k` and the other is at a position `j>k`, and so we have to count edge
117
+ `uv` at position `k`. Otherwise, `y_{u,v}^{k}=0`. The value of `y_{u,v}^{k}`
118
+ is a xor of the values of `x_u^k` and `x_v^k`.
119
+
120
+ - `z` -- objective value to minimize. It is equal to the maximum over all
121
+ position `k` of the number of edges with one extremity at position at most `k`
122
+ and the other at position strictly more than `k`, that is `\sum_{uv\in
123
+ E}y_{u,v}^{k}`.
124
+
125
+
126
+ **MILP formulation:**
127
+
128
+ .. MATH::
129
+ :nowrap:
130
+
131
+ \begin{alignat*}{2}
132
+ \intertext{Minimize:}
133
+ &z&\\
134
+ \intertext{Subject to:}
135
+ \sum_{i=0}^{k-1}x_v^i &\leq k*x_v^{k} & \forall v\in V,\ k\in[1,n-1] \quad(1)\\
136
+ x_v^n & =1 & \quad \forall v\in V \quad(2)\\
137
+ \sum_{v\in V}x_v^k & = k+1 &\quad \forall k\in[0,n-1] \quad(3)\\
138
+ x_u^k - x_v^k & \leq y_{u,v}^k &\quad \forall uv\in E,\ \forall k\in[0,n-1] \quad(4)\\
139
+ x_v^k - x_u^k & \leq y_{u,v}^k &\quad \forall uv\in E,\ \forall k\in[0,n-1] \quad(5)\\
140
+ \sum_{uv\in E}y_{u,v}^k &\leq z &\quad \forall k\in[0,n-1] \quad(6)\\
141
+ 0 \leq z &\leq |E|
142
+ \end{alignat*}
143
+
144
+ Constraints (1)-(3) ensure that all vertices have a distinct position.
145
+ Constraints (4)-(5) force variable `y_{u,v}^k` to 1 if the edge is in the cut.
146
+ Constraint (6) count the number of edges starting at position at most `k` and
147
+ ending at a position strictly larger than `k`.
148
+
149
+ This formulation corresponds to method :meth:`cutwidth_MILP`.
150
+
151
+
152
+ Authors
153
+ -------
154
+
155
+ - David Coudert (2015-06): Initial version
156
+
157
+
158
+ Methods
159
+ -------
160
+ """
161
+
162
+ # ****************************************************************************
163
+ # Copyright (C) 2015 David Coudert <david.coudert@inria.fr>
164
+ #
165
+ # This program is free software: you can redistribute it and/or modify
166
+ # it under the terms of the GNU General Public License as published by
167
+ # the Free Software Foundation, either version 2 of the License, or
168
+ # (at your option) any later version.
169
+ # http://www.gnu.org/licenses/
170
+ # ****************************************************************************
171
+
172
+ from libc.stdint cimport uint8_t
173
+ from libc.string cimport memset
174
+ from cysignals.memory cimport check_allocarray, sig_free
175
+ from cysignals.signals cimport sig_check
176
+
177
+ from sage.graphs.graph_decompositions.fast_digraph cimport FastDigraph, popcount32
178
+ from sage.graphs.graph_decompositions.vertex_separation cimport find_order
179
+ from sage.graphs.graph_decompositions.vertex_separation import is_valid_ordering
180
+ from sage.rings.integer_ring import ZZ
181
+
182
+
183
+ ################################################################################
184
+ # Measurement function of the width of some layout
185
+ ################################################################################
186
+
187
+ def width_of_cut_decomposition(G, L):
188
+ r"""
189
+ Return the width of the cut decomposition induced by the linear ordering
190
+ `L` of the vertices of `G`.
191
+
192
+ If `G` is an instance of :mod:`Graph <sage.graphs.graph>`, this function
193
+ returns the width `cw_L(G)` of the cut decomposition induced by the linear
194
+ ordering `L` of the vertices of `G`.
195
+
196
+ .. MATH::
197
+
198
+ cw_L(G) = \max_{0\leq i< |V|-1} |\{(u,w)\in E(G)\mid u\in L[:i]\text{ and }w\in V(G)\setminus L[:i]\}|
199
+
200
+ INPUT:
201
+
202
+ - ``G`` -- a Graph
203
+
204
+ - ``L`` -- a linear ordering of the vertices of ``G``
205
+
206
+ EXAMPLES:
207
+
208
+ Cut decomposition of a Cycle graph::
209
+
210
+ sage: from sage.graphs.graph_decompositions import cutwidth
211
+ sage: G = graphs.CycleGraph(6)
212
+ sage: L = G.vertices(sort=False)
213
+ sage: cutwidth.width_of_cut_decomposition(G, L)
214
+ 2
215
+
216
+ Cut decomposition of a Path graph::
217
+
218
+ sage: from sage.graphs.graph_decompositions import cutwidth
219
+ sage: P = graphs.PathGraph(6)
220
+ sage: cutwidth.width_of_cut_decomposition(P, [0, 1, 2, 3, 4, 5])
221
+ 1
222
+ sage: cutwidth.width_of_cut_decomposition(P, [5, 0, 1, 2, 3, 4])
223
+ 2
224
+ sage: cutwidth.width_of_cut_decomposition(P, [0, 2, 4, 1, 3, 5])
225
+ 5
226
+
227
+ TESTS:
228
+
229
+ Giving a wrong linear ordering::
230
+
231
+ sage: from sage.graphs.graph_decompositions import cutwidth
232
+ sage: cutwidth.width_of_cut_decomposition(Graph(), ['a','b'])
233
+ Traceback (most recent call last):
234
+ ...
235
+ ValueError: the input linear vertex ordering L is not valid for G
236
+ """
237
+ if not is_valid_ordering(G, L):
238
+ raise ValueError("the input linear vertex ordering L is not valid for G")
239
+ elif G.order() <= 1:
240
+ return 0
241
+
242
+ cdef int i, x, y
243
+ cdef dict position = {u: i for i, u in enumerate(L)}
244
+
245
+ # We count for each position `i` the number of edges going from vertices at
246
+ # positions in `0..i` to vertices at positions in `i+1..n-1`, for each
247
+ # `x\leq i<n-1`.
248
+ cdef list cpt = [0] * G.order()
249
+ for u, v in G.edge_iterator(labels=None):
250
+ x, y = position[u], position[v]
251
+ if x > y:
252
+ x, y = y, x
253
+ # Edge (u,v) contributes 1 to the number of edges going from vertices at
254
+ # positions `0..i` to vertices at positions `i+1..n-1` for each `x\leq
255
+ # i < n-1`.
256
+ for i in range(x, y):
257
+ cpt[i] += 1
258
+
259
+ # The width of L is the maximum computed value.
260
+ return max(cpt)
261
+
262
+
263
+ ################################################################################
264
+ # Front end method for cutwidth
265
+ ################################################################################
266
+
267
+ def cutwidth(G, algorithm='exponential', cut_off=0, solver=None, verbose=False,
268
+ *, integrality_tolerance=1e-3):
269
+ r"""
270
+ Return the cutwidth of the graph and the corresponding vertex ordering.
271
+
272
+ INPUT:
273
+
274
+ - ``G`` -- a Graph or a DiGraph
275
+
276
+ - ``algorithm`` -- string (default: ``'exponential'``); algorithm to use
277
+ among:
278
+
279
+ - ``exponential`` -- use an exponential time and space algorithm based on
280
+ dynamic programming. This algorithm only works on graphs with strictly
281
+ less than 32 vertices.
282
+
283
+ - ``MILP`` -- use a mixed integer linear programming formulation. This
284
+ algorithm has no size restriction but could take a very long time
285
+
286
+ - ``cut_off`` -- integer (default: 0); used to stop the search as soon as a
287
+ solution with width at most ``cut_off`` is found, if any. If this bound
288
+ cannot be reached, the best solution found is returned.
289
+
290
+ - ``solver`` -- string (default: ``None``); specifies a Mixed Integer Linear
291
+ Programming (MILP) solver to be used. If set to ``None``, the default one
292
+ is used. For more information on MILP solvers and which default solver is
293
+ used, see the method :meth:`solve
294
+ <sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class
295
+ :class:`MixedIntegerLinearProgram
296
+ <sage.numerical.mip.MixedIntegerLinearProgram>`.
297
+
298
+ - ``verbose`` -- boolean (default: ``False``); whether to display
299
+ information on the computations
300
+
301
+ - ``integrality_tolerance`` -- float; parameter for use with MILP solvers
302
+ over an inexact base ring; see
303
+ :meth:`MixedIntegerLinearProgram.get_values`.
304
+
305
+ OUTPUT:
306
+
307
+ A pair ``(cost, ordering)`` representing the optimal ordering of the
308
+ vertices and its cost.
309
+
310
+ EXAMPLES:
311
+
312
+ Cutwidth of a Complete Graph::
313
+
314
+ sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth
315
+ sage: G = graphs.CompleteGraph(5)
316
+ sage: cw,L = cutwidth(G); cw
317
+ 6
318
+ sage: K = graphs.CompleteGraph(6)
319
+ sage: cw,L = cutwidth(K); cw
320
+ 9
321
+ sage: cw,L = cutwidth(K+K); cw
322
+ 9
323
+
324
+ The cutwidth of a `p\times q` Grid Graph with `p\leq q` is `p+1`::
325
+
326
+ sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth
327
+ sage: G = graphs.Grid2dGraph(3,3)
328
+ sage: cw,L = cutwidth(G); cw
329
+ 4
330
+ sage: G = graphs.Grid2dGraph(3,5)
331
+ sage: cw,L = cutwidth(G); cw
332
+ 4
333
+
334
+ TESTS:
335
+
336
+ Comparison of algorithms::
337
+
338
+ sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth
339
+ sage: for i in range(2): # long time # needs sage.numerical.mip
340
+ ....: G = graphs.RandomGNP(7, 0.3)
341
+ ....: ve, le = cutwidth(G, algorithm='exponential')
342
+ ....: vm, lm = cutwidth(G, algorithm='MILP', solver='GLPK')
343
+ ....: if ve != vm:
344
+ ....: raise ValueError("Something goes wrong!")
345
+
346
+ Given a wrong algorithm::
347
+
348
+ sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth
349
+ sage: cutwidth(graphs.PathGraph(2), algorithm='SuperFast')
350
+ Traceback (most recent call last):
351
+ ...
352
+ ValueError: algorithm "SuperFast" has not been implemented yet, please contribute
353
+
354
+ Given anything else than a Graph::
355
+
356
+ sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth
357
+ sage: cutwidth(range(4))
358
+ Traceback (most recent call last):
359
+ ...
360
+ ValueError: the first parameter must be a Graph
361
+
362
+ Giving a wrong type cut off::
363
+
364
+ sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth
365
+ sage: cutwidth(Graph(), cut_off='toto')
366
+ Traceback (most recent call last):
367
+ ...
368
+ ValueError: the specified cut off parameter must be an integer
369
+
370
+ Cutwidth of a graph with one edge (:issue:`32131`)::
371
+
372
+ sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth
373
+ sage: G = Graph([(0, 1)])
374
+ sage: cutwidth(G, algorithm='exponential')
375
+ (1, [0, 1])
376
+ sage: cutwidth(G, algorithm='MILP', solver='GLPK') # needs sage.numerical.mip
377
+ (1, [0, 1])
378
+
379
+ Cutwidth of a disconnected graph::
380
+
381
+ sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth
382
+ sage: G = Graph(5)
383
+ sage: G.add_edge(2, 3)
384
+ sage: cutwidth(G, algorithm='exponential')
385
+ (1, [2, 3, 0, 1, 4])
386
+ sage: cutwidth(G, algorithm='MILP', solver='GLPK') # needs sage.numerical.mip
387
+ (1, [2, 3, 0, 1, 4])
388
+ """
389
+ from sage.graphs.graph import Graph
390
+
391
+ if not isinstance(G, Graph):
392
+ raise ValueError('the first parameter must be a Graph')
393
+
394
+ if cut_off not in ZZ:
395
+ raise ValueError("the specified cut off parameter must be an integer")
396
+ elif G.size() <= cut_off:
397
+ # We have a trivial solution
398
+ return width_of_cut_decomposition(G, list(G)), list(G)
399
+
400
+ cdef list CC
401
+ if not G.is_connected():
402
+ CC = G.connected_components_subgraphs()
403
+ else:
404
+ CC = [G]
405
+
406
+ # If the graph has several connected components. We solve the problem on
407
+ # each of them and concatenate the partial orderings. The cutwidth is the
408
+ # maximum over all these subgraphs.
409
+ cdef int cw = 0
410
+ cdef list L = []
411
+ cdef int cwH
412
+ cdef list LH
413
+ cdef int this_cut_off = cut_off
414
+
415
+ for H in CC:
416
+
417
+ if H.size() <= this_cut_off:
418
+ # We can directly add the vertices to the solution
419
+ L.extend(H)
420
+
421
+ else:
422
+ # We have a connected graph and we call the desired algorithm
423
+ if algorithm == "exponential":
424
+ cwH, LH = cutwidth_dyn(H, lower_bound=this_cut_off)
425
+
426
+ elif algorithm == "MILP":
427
+ cwH, LH = cutwidth_MILP(H, lower_bound=this_cut_off, solver=solver,
428
+ verbose=verbose, integrality_tolerance=integrality_tolerance)
429
+
430
+ else:
431
+ raise ValueError('algorithm "{}" has not been implemented yet, please contribute'.format(algorithm))
432
+
433
+ # We update the cutwidth and ordering
434
+ cw = max(cw, cwH)
435
+ L.extend(LH)
436
+ this_cut_off = max(cw, this_cut_off)
437
+
438
+ return cw, L
439
+
440
+
441
+ ################################################################################
442
+ # Dynamic Programming algorithm for cutwidth
443
+ ################################################################################
444
+
445
+ def cutwidth_dyn(G, lower_bound=0):
446
+ r"""
447
+ Dynamic programming algorithm for the cutwidth of a Graph.
448
+
449
+ This function uses dynamic programming algorithm for determining an optimal
450
+ layout for the cutwidth of `G`. See the :mod:`module's documentation
451
+ <sage.graphs.graph_decompositions.cutwidth>` for more details on this
452
+ method.
453
+
454
+ INPUT:
455
+
456
+ - ``G`` -- a Graph
457
+
458
+ - ``lower_bound`` -- integer (default: 0); the algorithm returns immediately
459
+ if it finds a solution lower or equal to ``lower_bound`` (in which case it
460
+ may not be optimal).
461
+
462
+ OUTPUT:
463
+
464
+ A pair ``(cost, ordering)`` representing the optimal ordering of the
465
+ vertices and its cost.
466
+
467
+ .. NOTE::
468
+
469
+ Because of its current implementation, this algorithm only works on
470
+ graphs on strictly less than 32 vertices. This can be changed to 63 if
471
+ necessary, but 32 vertices already require 4GB of memory.
472
+
473
+ TESTS:
474
+
475
+ Giving anything else than a Graph::
476
+
477
+ sage: from sage.graphs.graph_decompositions import cutwidth
478
+ sage: cutwidth.cutwidth_dyn([])
479
+ Traceback (most recent call last):
480
+ ...
481
+ ValueError: the parameter must be a Graph
482
+
483
+ Giving a too large Graph::
484
+
485
+ sage: from sage.graphs.graph_decompositions import cutwidth
486
+ sage: cutwidth.cutwidth_dyn(graphs.PathGraph(40))
487
+ Traceback (most recent call last):
488
+ ...
489
+ ValueError: the graph should have at most 31 vertices
490
+
491
+ Giving a wrong type lower bound::
492
+
493
+ sage: from sage.graphs.graph_decompositions import cutwidth
494
+ sage: cutwidth.cutwidth_dyn(Graph(), lower_bound='toto')
495
+ Traceback (most recent call last):
496
+ ...
497
+ ValueError: the specified lower bound must be an integer
498
+ """
499
+ from sage.graphs.graph import Graph
500
+ if not isinstance(G, Graph):
501
+ raise ValueError("the parameter must be a Graph")
502
+
503
+ if G.order() >= 32:
504
+ raise ValueError("the graph should have at most 31 vertices")
505
+
506
+ if lower_bound not in ZZ:
507
+ raise ValueError("the specified lower bound must be an integer")
508
+
509
+ cdef FastDigraph g = FastDigraph(G)
510
+
511
+ cdef unsigned int mem = 1 << g.n
512
+ cdef uint8_t* neighborhoods = <uint8_t*> check_allocarray(mem, sizeof(uint8_t))
513
+
514
+ memset(neighborhoods, <uint8_t> -1, mem)
515
+
516
+ cdef int i, k
517
+ cdef list order
518
+
519
+ try:
520
+ for k in range(lower_bound, G.size() + 1):
521
+ for i in range(g.n):
522
+ sig_check()
523
+ if exists(g, neighborhoods, 0, 0, i, k) <= k:
524
+ order = find_order(g, neighborhoods, k)
525
+ return k, [g.int_to_vertices[i] for i in order]
526
+
527
+ order = find_order(g, neighborhoods, k)
528
+ return k, [g.int_to_vertices[i] for i in order]
529
+ finally:
530
+ sig_free(neighborhoods)
531
+
532
+
533
+ cdef inline int exists(FastDigraph g, uint8_t* neighborhoods, int S, int cost_S, int v, int k) noexcept:
534
+ r"""
535
+ Check whether an ordering with the given cost `k` exists, and updates data
536
+ in the neighborhoods array at the same time. See the module's documentation.
537
+
538
+ INPUT:
539
+
540
+ - ``g`` -- a FastDiGraph
541
+
542
+ - ``neighborhoods`` -- an array of size `2^(g.n)`; stores for each subset
543
+ `X\subseteq V` of vertices of the graph the number of edges from `X` to
544
+ `V\setminus X`
545
+
546
+ - ``S`` -- integer; encodes the predecessor subset of vertices (from which
547
+ is issued the current call)
548
+
549
+ - ``cost_S`` -- integer; the number of edges from `S` to `V\setminus S`
550
+
551
+ - ``v`` -- integer; a vertex such that the current subset of vertices is
552
+ `current==S\cup\{v\}`
553
+
554
+ - ``k`` -- integer; the maximum admissible cost for a solution
555
+ """
556
+ cdef int current = S | 1 << v
557
+ # If this is true, it means the set has not been evaluated yet
558
+ if neighborhoods[current] == <uint8_t> -1:
559
+ # The number of edges from `current` to `V\setminus current` is the
560
+ # number of edges from `S` to `V\setminus S`, minus the number of edges
561
+ # from `S` to vertex `v`, plus the number of edges from `v` to
562
+ # `V\setminus (S\cup \{v\})`. This can be computed adding the degree of
563
+ # `c` to `cost_S`, and then removing twice the number of edges from `S`
564
+ # to `v`.
565
+ neighborhoods[current] = cost_S + g.degree[v] - 2 * popcount32(S & g.graph[v])
566
+
567
+ # If the cost of this set is too high, there is no point in going further.
568
+ # Same thing if the current set is the whole vertex set.
569
+ if neighborhoods[current] > k or (current == (1 << g.n) - 1):
570
+ return neighborhoods[current]
571
+
572
+ # Minimum of the costs of the outneighbors, initialized with large constant.
573
+ cdef int mini = (<uint8_t> -1)
574
+
575
+ cdef int i
576
+
577
+ # For each possible extension of the current set with a vertex, check whether
578
+ # there exists a cheap path toward {1..n}, and update the cost.
579
+ for i in range(g.n):
580
+ if (current >> i) & 1: # if i in S
581
+ continue
582
+
583
+ mini = min(mini, exists(g, neighborhoods, current, neighborhoods[current], i, k))
584
+
585
+ # We have found a path !
586
+ if mini <= k:
587
+ return mini
588
+
589
+ # Updating the cost of the current set with the minimum of the cost of its
590
+ # outneighbors.
591
+ neighborhoods[current] = mini
592
+
593
+ return neighborhoods[current]
594
+
595
+
596
+ ################################################################################
597
+ # MILP formulations for cutwidth
598
+ ################################################################################
599
+
600
+ def cutwidth_MILP(G, lower_bound=0, solver=None, verbose=0,
601
+ *, integrality_tolerance=1e-3):
602
+ r"""
603
+ MILP formulation for the cutwidth of a Graph.
604
+
605
+ This method uses a mixed integer linear program (MILP) for determining an
606
+ optimal layout for the cutwidth of `G`. See the :mod:`module's documentation
607
+ <sage.graphs.graph_decompositions.cutwidth>` for more details on this MILP
608
+ formulation.
609
+
610
+ INPUT:
611
+
612
+ - ``G`` -- a Graph
613
+
614
+ - ``lower_bound`` -- integer (default: 0); the algorithm searches for a
615
+ solution with cost larger or equal to ``lower_bound``. If the given bound
616
+ is larger than the optimal solution the returned solution might not be
617
+ optimal. If the given bound is too high, the algorithm might not be able
618
+ to find a feasible solution.
619
+
620
+ - ``solver`` -- string (default: ``None``); specifies a Mixed Integer Linear
621
+ Programming (MILP) solver to be used. If set to ``None``, the default one
622
+ is used. For more information on MILP solvers and which default solver is
623
+ used, see the method :meth:`solve
624
+ <sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class
625
+ :class:`MixedIntegerLinearProgram
626
+ <sage.numerical.mip.MixedIntegerLinearProgram>`.
627
+
628
+ - ``verbose`` -- integer (default: 0); sets the level of verbosity. Set
629
+ to 0 by default, which means quiet.
630
+
631
+ - ``integrality_tolerance`` -- float; parameter for use with MILP solvers
632
+ over an inexact base ring; see
633
+ :meth:`MixedIntegerLinearProgram.get_values`.
634
+
635
+ OUTPUT:
636
+
637
+ A pair ``(cost, ordering)`` representing the optimal ordering of the
638
+ vertices and its cost.
639
+
640
+ EXAMPLES:
641
+
642
+ Cutwidth of a Cycle graph::
643
+
644
+ sage: from sage.graphs.graph_decompositions import cutwidth
645
+ sage: G = graphs.CycleGraph(5)
646
+ sage: cw, L = cutwidth.cutwidth_MILP(G); cw # needs sage.numerical.mip
647
+ 2
648
+ sage: cw == cutwidth.width_of_cut_decomposition(G, L) # needs sage.numerical.mip
649
+ True
650
+ sage: cwe, Le = cutwidth.cutwidth_dyn(G); cwe
651
+ 2
652
+
653
+ Cutwidth of a Complete graph::
654
+
655
+ sage: from sage.graphs.graph_decompositions import cutwidth
656
+ sage: G = graphs.CompleteGraph(4)
657
+ sage: cw, L = cutwidth.cutwidth_MILP(G); cw # needs sage.numerical.mip
658
+ 4
659
+ sage: cw == cutwidth.width_of_cut_decomposition(G, L) # needs sage.numerical.mip
660
+ True
661
+
662
+ Cutwidth of a Path graph::
663
+
664
+ sage: from sage.graphs.graph_decompositions import cutwidth
665
+ sage: G = graphs.PathGraph(3)
666
+ sage: cw, L = cutwidth.cutwidth_MILP(G); cw # needs sage.numerical.mip
667
+ 1
668
+ sage: cw == cutwidth.width_of_cut_decomposition(G, L) # needs sage.numerical.mip
669
+ True
670
+
671
+ TESTS:
672
+
673
+ Comparison with exponential algorithm::
674
+
675
+ sage: from sage.graphs.graph_decompositions import cutwidth
676
+ sage: for i in range(2): # long time # needs sage.numerical.mip
677
+ ....: G = graphs.RandomGNP(7, 0.3)
678
+ ....: ve, le = cutwidth.cutwidth_dyn(G)
679
+ ....: vm, lm = cutwidth.cutwidth_MILP(G, solver='GLPK')
680
+ ....: if ve != vm:
681
+ ....: print("The solution is not optimal!")
682
+
683
+ Giving a too large lower bound::
684
+
685
+ sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth_MILP
686
+ sage: G = graphs.CycleGraph(3)
687
+ sage: cutwidth_MILP(G, lower_bound=G.size()+1) # needs sage.numerical.mip
688
+ Traceback (most recent call last):
689
+ ...
690
+ MIPSolverException: ...
691
+
692
+ Giving anything else than a Graph::
693
+
694
+ sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth_MILP
695
+ sage: cutwidth_MILP([])
696
+ Traceback (most recent call last):
697
+ ...
698
+ ValueError: the first input parameter must be a Graph
699
+ """
700
+ from sage.graphs.graph import Graph
701
+ if not isinstance(G, Graph):
702
+ raise ValueError("the first input parameter must be a Graph")
703
+
704
+ from sage.numerical.mip import MixedIntegerLinearProgram
705
+ p = MixedIntegerLinearProgram(maximization=False, solver=solver)
706
+
707
+ # Declaration of variables.
708
+ x = p.new_variable(binary=True, nonnegative=True)
709
+ y = p.new_variable(binary=True, nonnegative=True)
710
+ z = p.new_variable(integer=True, nonnegative=True)
711
+
712
+ N = G.order()
713
+
714
+ # All vertices at different positions
715
+ for v in G:
716
+ for k in range(N - 1):
717
+ p.add_constraint(p.sum(x[v, i] for i in range(k)) <= k * x[v, k])
718
+ p.add_constraint(x[v, N - 1] == 1)
719
+ for k in range(N):
720
+ p.add_constraint(p.sum(x[v, k] for v in G) == k + 1)
721
+
722
+ # Edge uv counts at position i if one of u or v is placed at a position in
723
+ # [0,i] and the other is placed at a position in [i+1,n].
724
+ for u, v in G.edge_iterator(labels=None):
725
+ for i in range(N):
726
+ p.add_constraint(x[u, i] - x[v, i] <= y[u, v, i])
727
+ p.add_constraint(x[v, i] - x[u, i] <= y[u, v, i])
728
+
729
+ # Lower bound on the solution
730
+ p.add_constraint(lower_bound <= z['z'])
731
+
732
+ # Objective
733
+ p.add_constraint(z['z'] <= G.size())
734
+ for i in range(N):
735
+ p.add_constraint(p.sum(y[u, v, i] for u, v in G.edge_iterator(labels=None)) <= z['z'])
736
+
737
+ p.set_objective(z['z'])
738
+
739
+ _ = p.solve(log=verbose)
740
+
741
+ # We now extract the ordering and the cost of the solution
742
+ val_x = p.get_values(x, convert=bool, tolerance=integrality_tolerance)
743
+ cdef int cw = p.get_values(z['z'], convert=True, tolerance=integrality_tolerance)
744
+ cdef list seq = []
745
+ cdef set to_see = set(G)
746
+ for k in range(N):
747
+ for u in to_see:
748
+ if val_x[u, k]:
749
+ seq.append(u)
750
+ to_see.discard(u)
751
+ break
752
+
753
+ return cw, seq