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,2040 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # cython: binding=True
3
+ # distutils: language = c++
4
+ r"""
5
+ Path enumeration
6
+
7
+ This module is meant for all functions related to path enumeration in graphs.
8
+
9
+ .. csv-table::
10
+ :class: contentstable
11
+ :widths: 30, 70
12
+ :delim: |
13
+
14
+ :func:`all_paths` | Return the list of all paths between a pair of vertices.
15
+ :func:`yen_k_shortest_simple_paths` | Return an iterator over the simple paths between a pair of vertices in increasing order of weights.
16
+ :func:`feng_k_shortest_simple_paths` | Return an iterator over the simple paths between a pair of vertices in increasing order of weights.
17
+ :func:`all_paths_iterator` | Return an iterator over the paths of ``self``.
18
+ :func:`all_simple_paths` | Return a list of all the simple paths of ``self`` starting with one of the given vertices.
19
+ :func:`shortest_simple_paths` | Return an iterator over the simple paths between a pair of vertices.
20
+
21
+ Functions
22
+ ---------
23
+ """
24
+ # ****************************************************************************
25
+ # Copyright (C) 2019 Rajat Mittal <rajat.mttl@gmail.com>
26
+ # David Coudert <david.coudert@inria.fr>
27
+ #
28
+ # This program is free software: you can redistribute it and/or modify
29
+ # it under the terms of the GNU General Public License as published by
30
+ # the Free Software Foundation, either version 2 of the License, or
31
+ # (at your option) any later version.
32
+ # https://www.gnu.org/licenses/
33
+ # ****************************************************************************
34
+ from itertools import product
35
+
36
+ from sage.misc.misc_c import prod
37
+ from libcpp.queue cimport priority_queue
38
+ from libcpp.pair cimport pair
39
+ from sage.rings.integer_ring import ZZ
40
+ import copy
41
+
42
+
43
+ def all_paths(G, start, end, use_multiedges=False, report_edges=False, labels=False):
44
+ """
45
+ Return the list of all paths between a pair of vertices.
46
+
47
+ If ``start`` is the same vertex as ``end``, then ``[[start]]`` is returned
48
+ -- a list containing the 1-vertex, 0-edge path "``start``".
49
+
50
+ If ``G`` has multiple edges, a path will be returned as many times as the
51
+ product of the multiplicity of the edges along that path depending on the
52
+ value of the flag ``use_multiedges``.
53
+
54
+ INPUT:
55
+
56
+ - ``start`` -- a vertex of a graph, where to start
57
+
58
+ - ``end`` -- a vertex of a graph, where to end
59
+
60
+ - ``use_multiedges`` -- boolean (default: ``False``); this parameter is
61
+ used only if the graph has multiple edges
62
+
63
+ - If ``False``, the graph is considered as simple and an edge label
64
+ is arbitrarily selected for each edge as in
65
+ :meth:`sage.graphs.generic_graph.GenericGraph.to_simple` if
66
+ ``report_edges`` is ``True``
67
+
68
+ - If ``True``, a path will be reported as many times as the edges
69
+ multiplicities along that path (when ``report_edges = False`` or
70
+ ``labels = False``), or with all possible combinations of edge
71
+ labels (when ``report_edges = True`` and ``labels = True``)
72
+
73
+ - ``report_edges`` -- boolean (default: ``False``); whether to report
74
+ paths as list of vertices (default) or list of edges, if ``False``
75
+ then ``labels`` parameter is ignored
76
+
77
+ - ``labels`` -- boolean (default: ``False``); if ``False``, each edge
78
+ is simply a pair ``(u, v)`` of vertices. Otherwise a list of edges
79
+ along with its edge labels are used to represent the path.
80
+
81
+ EXAMPLES::
82
+
83
+ sage: eg1 = Graph({0:[1, 2], 1:[4], 2:[3, 4], 4:[5], 5:[6]})
84
+ sage: eg1.all_paths(0, 6)
85
+ [[0, 1, 4, 5, 6], [0, 2, 4, 5, 6]]
86
+ sage: eg2 = graphs.PetersenGraph()
87
+ sage: sorted(eg2.all_paths(1, 4))
88
+ [[1, 0, 4],
89
+ [1, 0, 5, 7, 2, 3, 4],
90
+ [1, 0, 5, 7, 2, 3, 8, 6, 9, 4],
91
+ [1, 0, 5, 7, 9, 4],
92
+ [1, 0, 5, 7, 9, 6, 8, 3, 4],
93
+ [1, 0, 5, 8, 3, 2, 7, 9, 4],
94
+ [1, 0, 5, 8, 3, 4],
95
+ [1, 0, 5, 8, 6, 9, 4],
96
+ [1, 0, 5, 8, 6, 9, 7, 2, 3, 4],
97
+ [1, 2, 3, 4],
98
+ [1, 2, 3, 8, 5, 0, 4],
99
+ [1, 2, 3, 8, 5, 7, 9, 4],
100
+ [1, 2, 3, 8, 6, 9, 4],
101
+ [1, 2, 3, 8, 6, 9, 7, 5, 0, 4],
102
+ [1, 2, 7, 5, 0, 4],
103
+ [1, 2, 7, 5, 8, 3, 4],
104
+ [1, 2, 7, 5, 8, 6, 9, 4],
105
+ [1, 2, 7, 9, 4],
106
+ [1, 2, 7, 9, 6, 8, 3, 4],
107
+ [1, 2, 7, 9, 6, 8, 5, 0, 4],
108
+ [1, 6, 8, 3, 2, 7, 5, 0, 4],
109
+ [1, 6, 8, 3, 2, 7, 9, 4],
110
+ [1, 6, 8, 3, 4],
111
+ [1, 6, 8, 5, 0, 4],
112
+ [1, 6, 8, 5, 7, 2, 3, 4],
113
+ [1, 6, 8, 5, 7, 9, 4],
114
+ [1, 6, 9, 4],
115
+ [1, 6, 9, 7, 2, 3, 4],
116
+ [1, 6, 9, 7, 2, 3, 8, 5, 0, 4],
117
+ [1, 6, 9, 7, 5, 0, 4],
118
+ [1, 6, 9, 7, 5, 8, 3, 4]]
119
+ sage: dg = DiGraph({0:[1, 3], 1:[3], 2:[0, 3]})
120
+ sage: sorted(dg.all_paths(0, 3))
121
+ [[0, 1, 3], [0, 3]]
122
+ sage: ug = dg.to_undirected()
123
+ sage: sorted(ug.all_paths(0, 3))
124
+ [[0, 1, 3], [0, 2, 3], [0, 3]]
125
+
126
+ sage: g = Graph([(0, 1), (0, 1), (1, 2), (1, 2)], multiedges=True)
127
+ sage: g.all_paths(0, 2, use_multiedges=True)
128
+ [[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]]
129
+
130
+ sage: dg = DiGraph({0:[1, 2, 1], 3:[0, 0]}, multiedges=True)
131
+ sage: dg.all_paths(3, 1, use_multiedges=True)
132
+ [[3, 0, 1], [3, 0, 1], [3, 0, 1], [3, 0, 1]]
133
+
134
+ sage: g = Graph([(0, 1, 'a'), (0, 1, 'b'), (1, 2, 'c'), (1, 2, 'd')], multiedges=True)
135
+ sage: g.all_paths(0, 2, use_multiedges=False)
136
+ [[0, 1, 2]]
137
+ sage: g.all_paths(0, 2, use_multiedges=True)
138
+ [[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]]
139
+ sage: g.all_paths(0, 2, use_multiedges=True, report_edges=True)
140
+ [[(0, 1), (1, 2)], [(0, 1), (1, 2)], [(0, 1), (1, 2)], [(0, 1), (1, 2)]]
141
+ sage: g.all_paths(0, 2, use_multiedges=True, report_edges=True, labels=True)
142
+ [((0, 1, 'b'), (1, 2, 'd')),
143
+ ((0, 1, 'b'), (1, 2, 'c')),
144
+ ((0, 1, 'a'), (1, 2, 'd')),
145
+ ((0, 1, 'a'), (1, 2, 'c'))]
146
+ sage: g.all_paths(0, 2, use_multiedges=False, report_edges=True, labels=True)
147
+ [((0, 1, 'b'), (1, 2, 'd'))]
148
+ sage: g.all_paths(0, 2, use_multiedges=False, report_edges=False, labels=True)
149
+ [[0, 1, 2]]
150
+ sage: g.all_paths(0, 2, use_multiedges=True, report_edges=False, labels=True)
151
+ [[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]]
152
+
153
+ TESTS:
154
+
155
+ Starting and ending at the same vertex (see :issue:`13006`)::
156
+
157
+ sage: graphs.CompleteGraph(4).all_paths(2, 2)
158
+ [[2]]
159
+
160
+ Non-existing vertex as end vertex (see :issue:`24495`)::
161
+
162
+ sage: g = graphs.PathGraph(5)
163
+ sage: g.all_paths(1, 'junk')
164
+ Traceback (most recent call last):
165
+ ...
166
+ LookupError: end vertex (junk) is not a vertex of the graph
167
+
168
+ Distinguishing between multiedged paths (see :issue:`27501`)::
169
+
170
+ sage: g = Graph(multiedges=True)
171
+ sage: g.add_edge(0, 3, 1)
172
+ sage: g.add_edge(0, 2, 3)
173
+ sage: g.add_edge(0, 1, 3)
174
+ sage: g.add_edge(2, 3, 5)
175
+ sage: g.add_edge(2, 3, 15)
176
+ sage: g.add_edge(2, 4, 12)
177
+ sage: g.add_edge(3, 5, 7)
178
+ sage: g.all_paths(0, 5, use_multiedges=True)
179
+ [[0, 2, 3, 5], [0, 2, 3, 5], [0, 3, 5]]
180
+
181
+ sage: g = Graph(multiedges=True)
182
+ sage: g.add_edge(0, 1, 1)
183
+ sage: g.add_edge(0, 2, 3)
184
+ sage: g.add_edge(1, 4, 3)
185
+ sage: g.add_edge(2, 3, 5)
186
+ sage: g.add_edge(2, 4, 15)
187
+ sage: g.add_edge(2, 4, 12)
188
+ sage: g.add_edge(4, 5, 7)
189
+ sage: g.add_edge(4, 5, 8)
190
+ sage: g.add_edge(5, 6, 2)
191
+ sage: g.all_paths(0, 6, use_multiedges=True)
192
+ [[0, 1, 4, 5, 6],
193
+ [0, 1, 4, 5, 6],
194
+ [0, 2, 4, 5, 6],
195
+ [0, 2, 4, 5, 6],
196
+ [0, 2, 4, 5, 6],
197
+ [0, 2, 4, 5, 6]]
198
+
199
+ Added reporting of edges (see :issue:`27501`)::
200
+
201
+ sage: G = DiGraph(multiedges=True)
202
+ sage: G.add_edges([(0, 2), (0, 3), (0, 4), (1, 2), (1, 2), (1, 5), (3, 5), (3, 5)])
203
+ sage: G.all_paths(0, 5, report_edges=True)
204
+ [[(0, 3), (3, 5)]]
205
+ sage: G.all_paths(0, 5, report_edges=True, use_multiedges=True)
206
+ [[(0, 3), (3, 5)], [(0, 3), (3, 5)]]
207
+ """
208
+ if start not in G:
209
+ raise LookupError("start vertex ({0}) is not a vertex of the graph".format(start))
210
+ if end not in G:
211
+ raise LookupError("end vertex ({0}) is not a vertex of the graph".format(end))
212
+
213
+ if G.is_directed():
214
+ iterator = G.neighbor_out_iterator
215
+ else:
216
+ iterator = G.neighbor_iterator
217
+
218
+ if report_edges and labels:
219
+ edge_labels = {}
220
+ if use_multiedges:
221
+ for e in G.edge_iterator():
222
+ if (e[0], e[1]) in edge_labels:
223
+ edge_labels[(e[0], e[1])].append(e)
224
+ else:
225
+ edge_labels[(e[0], e[1])] = [e]
226
+ else:
227
+ for e in G.edge_iterator():
228
+ if (e[0], e[1]) not in edge_labels:
229
+ edge_labels[(e[0], e[1])] = [e]
230
+ if not G.is_directed():
231
+ for u, v in list(edge_labels):
232
+ edge_labels[v, u] = edge_labels[u, v]
233
+ elif use_multiedges and G.has_multiple_edges():
234
+ from collections import Counter
235
+ edge_multiplicity = Counter(G.edge_iterator(labels=False))
236
+
237
+ if start == end:
238
+ return [[start]]
239
+
240
+ all_paths = [] # list of
241
+ act_path = [] # the current path
242
+ act_path_iter = [] # the neighbor/successor-iterators of the current path
243
+ done = False
244
+ s = start
245
+ while not done:
246
+ if s == end: # if path completes, add to list
247
+ all_paths.append(act_path + [s])
248
+ else:
249
+ if s not in act_path: # we want vertices just once in a path
250
+ act_path.append(s) # extend current path
251
+ act_path_iter.append(iterator(s)) # save the state of the neighbor/successor-iterator of the current vertex
252
+ s = None
253
+ while (s is None) and not done:
254
+ try:
255
+ s = next(act_path_iter[-1]) # try to get the next neighbor/successor, ...
256
+ except (StopIteration): # ... if there is none ...
257
+ act_path.pop() # ... go one step back
258
+ act_path_iter.pop()
259
+ if not act_path: # there is no other vertex ...
260
+ done = True # ... so we are done
261
+
262
+ if report_edges and labels:
263
+ path_with_labels = []
264
+ for p in all_paths:
265
+ path_with_labels.extend(product(*[edge_labels[e] for e in zip(p[:-1], p[1:])]))
266
+ return path_with_labels
267
+ elif use_multiedges and G.has_multiple_edges():
268
+ multiple_all_paths = []
269
+ for p in all_paths:
270
+ m = prod(edge_multiplicity[e] for e in zip(p[:-1], p[1:]))
271
+ if report_edges:
272
+ ep = list(zip(p[:-1], p[1:]))
273
+ for _ in range(m):
274
+ if report_edges:
275
+ multiple_all_paths.append(ep)
276
+ else:
277
+ multiple_all_paths.append(p)
278
+ return multiple_all_paths
279
+ elif report_edges:
280
+ return [list(zip(p[:-1], p[1:])) for p in all_paths]
281
+ return all_paths
282
+
283
+
284
+ def shortest_simple_paths(self, source, target, weight_function=None,
285
+ by_weight=False, check_weight=True,
286
+ algorithm=None, report_edges=False,
287
+ labels=False, report_weight=False):
288
+ r"""
289
+ Return an iterator over the simple paths between a pair of vertices.
290
+
291
+ This method returns an iterator over the simple paths (i.e., without
292
+ repetition) from ``source`` to ``target``. By default (``by_weight`` is
293
+ ``False``), the paths are reported by increasing number of edges. When
294
+ ``by_weight`` is ``True``, the paths are reported by increasing weights.
295
+
296
+ In case of weighted graphs negative weights are not allowed.
297
+
298
+ If ``source`` is the same vertex as ``target``, then ``[[source]]`` is
299
+ returned -- a list containing the 1-vertex, 0-edge path ``source``.
300
+
301
+ By default ``Yen's`` algorithm [Yen1970]_ is used for undirected graphs and
302
+ ``Feng's`` algorithm is used for directed graphs [Feng2014]_.
303
+
304
+ The loops and the multiedges if present in the given graph are ignored and
305
+ only minimum of the edge labels is kept in case of multiedges.
306
+
307
+ INPUT:
308
+
309
+ - ``source`` -- a vertex of the graph, where to start
310
+
311
+ - ``target`` -- a vertex of the graph, where to end
312
+
313
+ - ``weight_function`` -- function (default: ``None``); a function that
314
+ takes as input an edge ``(u, v, l)`` and outputs its weight. If not
315
+ ``None``, ``by_weight`` is automatically set to ``True``. If ``None``
316
+ and ``by_weight`` is ``True``, we use the edge label ``l`` as a
317
+ weight.
318
+
319
+ - ``by_weight`` -- boolean (default: ``False``); if ``True``, the edges
320
+ in the graph are weighted, otherwise all edges have weight 1
321
+
322
+ - ``check_weight`` -- boolean (default: ``True``); whether to check that the
323
+ ``weight_function`` outputs a number for each edge
324
+
325
+ - ``algorithm`` -- string (default: ``None``); the algorithm to use in
326
+ computing ``k`` shortest paths of ``self``. The following algorithms are
327
+ supported:
328
+
329
+ - ``'Yen'`` -- Yen's algorithm [Yen1970]_
330
+
331
+ - ``'Feng'`` -- an improved version of Yen's algorithm but that works only
332
+ for directed graphs [Feng2014]_
333
+
334
+ - ``report_edges`` -- boolean (default: ``False``); whether to report paths
335
+ as list of vertices (default) or list of edges. When set to ``False``, the
336
+ ``labels`` parameter is ignored.
337
+
338
+ - ``labels`` -- boolean (default: ``False``); if ``False``, each edge is
339
+ simply a pair ``(u, v)`` of vertices. Otherwise a list of edges along
340
+ with its edge labels are used to represent the path.
341
+
342
+ - ``report_weight`` -- boolean (default: ``False``); if ``False``, just the
343
+ path between ``source`` and ``target`` is returned. Otherwise a tuple of
344
+ path length and path is returned.
345
+
346
+ EXAMPLES::
347
+
348
+ sage: g = DiGraph([(1, 2, 20), (1, 3, 10), (1, 4, 30),
349
+ ....: (2, 5, 20), (3, 5, 10), (4, 5, 30)])
350
+ sage: list(g.shortest_simple_paths(1, 5, by_weight=True, algorithm='Yen'))
351
+ [[1, 3, 5], [1, 2, 5], [1, 4, 5]]
352
+ sage: list(g.shortest_simple_paths(1, 5, algorithm='Yen'))
353
+ [[1, 2, 5], [1, 3, 5], [1, 4, 5]]
354
+ sage: list(g.shortest_simple_paths(1, 1))
355
+ [[1]]
356
+ sage: list(g.shortest_simple_paths(1, 5, by_weight=True,
357
+ ....: report_edges=True, report_weight=True, labels=True))
358
+ [(20, [(1, 3, 10), (3, 5, 10)]),
359
+ (40, [(1, 2, 20), (2, 5, 20)]),
360
+ (60, [(1, 4, 30), (4, 5, 30)])]
361
+ sage: list(g.shortest_simple_paths(1, 5, by_weight=True, algorithm='Feng',
362
+ ....: report_edges=True, report_weight=True))
363
+ [(20, [(1, 3), (3, 5)]), (40, [(1, 2), (2, 5)]), (60, [(1, 4), (4, 5)])]
364
+ sage: list(g.shortest_simple_paths(1, 5, report_edges=True, report_weight=True))
365
+ [(2, [(1, 2), (2, 5)]), (2, [(1, 3), (3, 5)]), (2, [(1, 4), (4, 5)])]
366
+ sage: list(g.shortest_simple_paths(1, 5, by_weight=True, report_edges=True))
367
+ [[(1, 3), (3, 5)], [(1, 2), (2, 5)], [(1, 4), (4, 5)]]
368
+ sage: list(g.shortest_simple_paths(1, 5, by_weight=True, algorithm='Feng',
369
+ ....: report_edges=True, labels=True))
370
+ [[(1, 3, 10), (3, 5, 10)], [(1, 2, 20), (2, 5, 20)], [(1, 4, 30), (4, 5, 30)]]
371
+ sage: g = Graph([(1, 2, 20), (1, 3, 10), (1, 4, 30), (2, 5, 20),
372
+ ....: (3, 5, 10), (4, 5, 30), (1, 6, 100), (5, 6, 5)])
373
+ sage: list(g.shortest_simple_paths(1, 6, by_weight = True))
374
+ [[1, 3, 5, 6], [1, 2, 5, 6], [1, 4, 5, 6], [1, 6]]
375
+ sage: list(g.shortest_simple_paths(1, 6, algorithm='Yen'))
376
+ [[1, 6], [1, 2, 5, 6], [1, 3, 5, 6], [1, 4, 5, 6]]
377
+ sage: list(g.shortest_simple_paths(1, 6,
378
+ ....: report_edges=True, report_weight=True, labels=True))
379
+ [(1, [(1, 6, 100)]),
380
+ (3, [(1, 2, 20), (2, 5, 20), (5, 6, 5)]),
381
+ (3, [(1, 3, 10), (3, 5, 10), (5, 6, 5)]),
382
+ (3, [(1, 4, 30), (4, 5, 30), (5, 6, 5)])]
383
+ sage: list(g.shortest_simple_paths(1, 6, by_weight=True,
384
+ ....: report_edges=True, report_weight=True, labels=True))
385
+ [(25, [(1, 3, 10), (3, 5, 10), (5, 6, 5)]),
386
+ (45, [(1, 2, 20), (2, 5, 20), (5, 6, 5)]),
387
+ (65, [(1, 4, 30), (4, 5, 30), (5, 6, 5)]),
388
+ (100, [(1, 6, 100)])]
389
+ sage: list(g.shortest_simple_paths(1, 6, by_weight=True,
390
+ ....: report_edges=True, labels=True))
391
+ [[(1, 3, 10), (3, 5, 10), (5, 6, 5)],
392
+ [(1, 2, 20), (2, 5, 20), (5, 6, 5)],
393
+ [(1, 4, 30), (4, 5, 30), (5, 6, 5)],
394
+ [(1, 6, 100)]]
395
+ sage: list(g.shortest_simple_paths(1, 6, report_edges=True, labels=True))
396
+ [[(1, 6, 100)],
397
+ [(1, 2, 20), (2, 5, 20), (5, 6, 5)],
398
+ [(1, 3, 10), (3, 5, 10), (5, 6, 5)],
399
+ [(1, 4, 30), (4, 5, 30), (5, 6, 5)]]
400
+
401
+ TESTS::
402
+
403
+ sage: g = Graph([(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 2),
404
+ ....: (5, 6, 100), (4, 7, 3), (7, 6, 4), (3, 8, 5),
405
+ ....: (8, 9, 2), (9, 6, 2), (9, 10, 7), (9, 11, 10),
406
+ ....: (11, 6, 8), (10, 6, 2)])
407
+ sage: list(g.shortest_simple_paths(1, 6, algorithm='Yen', by_weight=True))
408
+ [[1, 2, 3, 4, 7, 6],
409
+ [1, 2, 3, 8, 9, 6],
410
+ [1, 2, 3, 8, 9, 10, 6],
411
+ [1, 2, 3, 8, 9, 11, 6],
412
+ [1, 2, 3, 4, 5, 6]]
413
+ sage: list(g.shortest_simple_paths(1, 6, report_edges=True, labels=True, by_weight=True))
414
+ [[(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 7, 3), (6, 7, 4)],
415
+ [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (6, 9, 2)],
416
+ [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 10, 7), (6, 10, 2)],
417
+ [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 11, 10), (6, 11, 8)],
418
+ [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 2), (5, 6, 100)]]
419
+ sage: list(g.shortest_simple_paths(1, 6, report_edges=True, labels=True, by_weight=True, report_weight=True))
420
+ [(10, [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 7, 3), (6, 7, 4)]),
421
+ (11, [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (6, 9, 2)]),
422
+ (18, [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 10, 7), (6, 10, 2)]),
423
+ (27, [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 11, 10), (6, 11, 8)]),
424
+ (105, [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 2), (5, 6, 100)])]
425
+ sage: list(g.shortest_simple_paths(1, 6, algorithm='Yen'))
426
+ [[1, 2, 3, 4, 5, 6],
427
+ [1, 2, 3, 4, 7, 6],
428
+ [1, 2, 3, 8, 9, 6],
429
+ [1, 2, 3, 8, 9, 10, 6],
430
+ [1, 2, 3, 8, 9, 11, 6]]
431
+ sage: g = DiGraph([(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 1),
432
+ ....: (1, 7, 1), (7, 8, 1), (8, 5, 1), (1, 6, 1),
433
+ ....: (6, 9, 1), (9, 5, 1), (4, 2, 1), (9, 3, 1),
434
+ ....: (9, 10, 1), (10, 5, 1), (9, 11, 1), (11, 10, 1)])
435
+ sage: list(g.shortest_simple_paths(1, 5, algorithm='Feng'))
436
+ [[1, 6, 9, 5],
437
+ [1, 7, 8, 5],
438
+ [1, 2, 3, 4, 5],
439
+ [1, 6, 9, 10, 5],
440
+ [1, 6, 9, 11, 10, 5],
441
+ [1, 6, 9, 3, 4, 5]]
442
+
443
+ sage: # needs sage.combinat
444
+ sage: G = digraphs.DeBruijn(2, 3)
445
+ sage: for u,v in G.edges(sort=True, labels=False):
446
+ ....: G.set_edge_label(u, v, 1)
447
+ sage: G.allow_multiple_edges(True)
448
+ sage: for u,v in G.edges(sort=True, labels=False):
449
+ ....: G.add_edge(u, v, 2)
450
+ sage: list(G.shortest_simple_paths('000', '111'))
451
+ [['000', '001', '011', '111'], ['000', '001', '010', '101', '011', '111']]
452
+ sage: list(G.shortest_simple_paths('000', '111', by_weight=True))
453
+ [['000', '001', '011', '111'], ['000', '001', '010', '101', '011', '111']]
454
+ sage: list(G.shortest_simple_paths('000', '111', by_weight=True, report_weight=True))
455
+ [(3, ['000', '001', '011', '111']),
456
+ (5, ['000', '001', '010', '101', '011', '111'])]
457
+ sage: list(G.shortest_simple_paths('000', '111', by_weight=True, report_weight=True, report_edges=True, labels=True))
458
+ [(3, [('000', '001', 1), ('001', '011', 1), ('011', '111', 1)]),
459
+ (5,
460
+ [('000', '001', 1),
461
+ ('001', '010', 1),
462
+ ('010', '101', 1),
463
+ ('101', '011', 1),
464
+ ('011', '111', 1)])]
465
+
466
+ Feng's algorithm cannot be used on undirected graphs::
467
+
468
+ sage: list(graphs.PathGraph(2).shortest_simple_paths(0, 1, algorithm='Feng'))
469
+ Traceback (most recent call last):
470
+ ...
471
+ ValueError: Feng's algorithm works only for directed graphs
472
+
473
+ If the algorithm is not implemented::
474
+
475
+ sage: list(g.shortest_simple_paths(1, 5, algorithm='tip top'))
476
+ Traceback (most recent call last):
477
+ ...
478
+ ValueError: unknown algorithm "tip top"
479
+
480
+ Check for consistency of results of Yen's and Feng's::
481
+
482
+ sage: # needs sage.combinat
483
+ sage: G = digraphs.DeBruijn(2, 4)
484
+ sage: s = set()
485
+ sage: for p in G.shortest_simple_paths('0000', '1111', by_weight=False, algorithm='Yen'):
486
+ ....: s.add(tuple(p))
487
+ sage: k = set()
488
+ sage: for p in G.shortest_simple_paths('0000', '1111', by_weight=False, algorithm='Feng'):
489
+ ....: k.add(tuple(p))
490
+ sage: k == s
491
+ True
492
+
493
+ sage: G = DiGraph(graphs.Grid2dGraph(3, 3))
494
+ sage: s = set()
495
+ sage: for i, p in enumerate(G.shortest_simple_paths((0, 0), (0, 1), by_weight=False, algorithm='Feng')):
496
+ ....: s.add(tuple(p))
497
+ sage: k = set()
498
+ sage: for i, p in enumerate(G.shortest_simple_paths((0, 0), (0, 1), by_weight=False, algorithm='Yen')):
499
+ ....: k.add(tuple(p))
500
+ sage: s == k
501
+ True
502
+
503
+ sage: G = DiGraph('SL{Sa??B[??iSOBIgA_K?a?@H??aGCsc??_oGCC__AA?H????c@_GA?C@?A_?_C???a?')
504
+ sage: s = set()
505
+ sage: for i, p in enumerate(G.shortest_simple_paths(0, 1, by_weight=False, algorithm='Yen')):
506
+ ....: s.add(tuple(p))
507
+ sage: t = set()
508
+ sage: for i, p in enumerate(G.shortest_simple_paths(0, 1, by_weight=False, algorithm='Feng')):
509
+ ....: t.add(tuple(p))
510
+ sage: s == t
511
+ True
512
+
513
+ sage: G = digraphs.Circulant(10, [2, 3])
514
+ sage: s = set()
515
+ sage: for i, p in enumerate(G.shortest_simple_paths(1, 7, by_weight=False, algorithm='Yen')):
516
+ ....: s.add(tuple(p))
517
+ sage: t = set()
518
+ sage: for i, p in enumerate(G.shortest_simple_paths(1, 7, by_weight=False, algorithm='Feng')):
519
+ ....: t.add(tuple(p))
520
+ sage: s == t
521
+ True
522
+
523
+ Check that "Yen" and "Feng" provide same results on random digraphs::
524
+
525
+ sage: G = digraphs.RandomDirectedGNP(30, .05)
526
+ sage: while not G.is_strongly_connected():
527
+ ....: G = digraphs.RandomDirectedGNP(30, .1)
528
+ sage: for u, v in list(G.edges(labels=False, sort=False)):
529
+ ....: G.set_edge_label(u, v, randint(1, 10))
530
+ sage: V = G.vertices(sort=False)
531
+ sage: shuffle(V)
532
+ sage: u, v = V[:2]
533
+ sage: it_Y = G.shortest_simple_paths(u, v, by_weight=True, report_weight=True, algorithm='Yen')
534
+ sage: it_F = G.shortest_simple_paths(u, v, by_weight=True, report_weight=True, algorithm='Feng')
535
+ sage: for i, (y, f) in enumerate(zip(it_Y, it_F)):
536
+ ....: if y[0] != f[0]:
537
+ ....: raise ValueError("something goes wrong !")
538
+ ....: if i == 100:
539
+ ....: break
540
+ """
541
+ if source not in self:
542
+ raise ValueError("vertex '{}' is not in the graph".format(source))
543
+
544
+ if target not in self:
545
+ raise ValueError("vertex '{}' is not in the graph".format(target))
546
+
547
+ if source == target:
548
+ if report_edges:
549
+ yield []
550
+ elif report_weight:
551
+ yield (0, [source])
552
+ else:
553
+ yield [source]
554
+ return
555
+
556
+ if self.has_loops() or self.allows_multiple_edges():
557
+ self = self.to_simple(to_undirected=False, keep_label='min', immutable=False)
558
+
559
+ if algorithm is None:
560
+ algorithm = "Feng" if self.is_directed() else "Yen"
561
+
562
+ if algorithm == "Feng":
563
+ if not self.is_directed():
564
+ raise ValueError("Feng's algorithm works only for directed graphs")
565
+
566
+ yield from feng_k_shortest_simple_paths(self, source=source, target=target,
567
+ weight_function=weight_function,
568
+ by_weight=by_weight, check_weight=check_weight,
569
+ report_edges=report_edges,
570
+ labels=labels, report_weight=report_weight)
571
+
572
+ elif algorithm == "Yen":
573
+ yield from yen_k_shortest_simple_paths(self, source=source, target=target,
574
+ weight_function=weight_function,
575
+ by_weight=by_weight, check_weight=check_weight,
576
+ report_edges=report_edges,
577
+ labels=labels, report_weight=report_weight)
578
+
579
+ else:
580
+ raise ValueError('unknown algorithm "{}"'.format(algorithm))
581
+
582
+
583
+ def yen_k_shortest_simple_paths(self, source, target, weight_function=None,
584
+ by_weight=False, check_weight=True,
585
+ report_edges=False,
586
+ labels=False, report_weight=False):
587
+ r"""
588
+ Return an iterator over the simple paths between a pair of vertices in
589
+ increasing order of weights.
590
+
591
+ For unweighted graphs paths are returned in order of increasing number
592
+ of edges.
593
+
594
+ In case of weighted graphs negative weights are not allowed.
595
+
596
+ If ``source`` is the same vertex as ``target``, then ``[[source]]`` is
597
+ returned -- a list containing the 1-vertex, 0-edge path ``source``.
598
+
599
+ The loops and the multiedges if present in the given graph are ignored and
600
+ only minimum of the edge labels is kept in case of multiedges.
601
+
602
+ INPUT:
603
+
604
+ - ``source`` -- a vertex of the graph, where to start
605
+
606
+ - ``target`` -- a vertex of the graph, where to end
607
+
608
+ - ``weight_function`` -- function (default: ``None``); a function that
609
+ takes as input an edge ``(u, v, l)`` and outputs its weight. If not
610
+ ``None``, ``by_weight`` is automatically set to ``True``. If ``None``
611
+ and ``by_weight`` is ``True``, we use the edge label ``l`` as a
612
+ weight.
613
+
614
+ - ``by_weight`` -- boolean (default: ``False``); if ``True``, the edges
615
+ in the graph are weighted, otherwise all edges have weight 1
616
+
617
+ - ``check_weight`` -- boolean (default: ``True``); whether to check that
618
+ the ``weight_function`` outputs a number for each edge
619
+
620
+ - ``report_edges`` -- boolean (default: ``False``); whether to report
621
+ paths as list of vertices (default) or list of edges, if ``False``
622
+ then ``labels`` parameter is ignored
623
+
624
+ - ``labels`` -- boolean (default: ``False``); if ``False``, each edge
625
+ is simply a pair ``(u, v)`` of vertices. Otherwise a list of edges
626
+ along with its edge labels are used to represent the path.
627
+
628
+ - ``report_weight`` -- boolean (default: ``False``); if ``False``, just
629
+ the path between ``source`` and ``target`` is returned. Otherwise a
630
+ tuple of path length and path is returned.
631
+
632
+ ALGORITHM:
633
+
634
+ This algorithm can be divided into two parts. Firstly, it determines a
635
+ shortest path from ``source`` to ``target``. Then, it determines all the
636
+ other `k`-shortest paths. This algorithm finds the deviations of previous
637
+ shortest paths to determine the next shortest paths.
638
+
639
+ Time complexity is `O(kn(m+n\log{n}))` where `n` is the number of vertices
640
+ and `m` is the number of edges and `k` is the number of shortest paths
641
+ needed to find.
642
+
643
+ See [Yen1970]_ and the :wikipedia:`Yen%27s_algorithm` for more details on the
644
+ algorithm.
645
+
646
+ EXAMPLES::
647
+
648
+ sage: from sage.graphs.path_enumeration import yen_k_shortest_simple_paths
649
+ sage: g = DiGraph([(1, 2, 20), (1, 3, 10), (1, 4, 30), (2, 5, 20), (3, 5, 10), (4, 5, 30)])
650
+ sage: list(yen_k_shortest_simple_paths(g, 1, 5, by_weight=True))
651
+ [[1, 3, 5], [1, 2, 5], [1, 4, 5]]
652
+ sage: list(yen_k_shortest_simple_paths(g, 1, 5))
653
+ [[1, 2, 5], [1, 3, 5], [1, 4, 5]]
654
+ sage: list(yen_k_shortest_simple_paths(g, 1, 1))
655
+ [[1]]
656
+ sage: list(yen_k_shortest_simple_paths(g, 1, 5, by_weight=True, report_edges=True, report_weight=True, labels=True))
657
+ [(20, [(1, 3, 10), (3, 5, 10)]),
658
+ (40, [(1, 2, 20), (2, 5, 20)]),
659
+ (60, [(1, 4, 30), (4, 5, 30)])]
660
+ sage: list(yen_k_shortest_simple_paths(g, 1, 5, by_weight=True, report_edges=True, report_weight=True))
661
+ [(20, [(1, 3), (3, 5)]), (40, [(1, 2), (2, 5)]), (60, [(1, 4), (4, 5)])]
662
+ sage: list(yen_k_shortest_simple_paths(g, 1, 5, report_edges=True, report_weight=True))
663
+ [(2, [(1, 2), (2, 5)]), (2, [(1, 3), (3, 5)]), (2, [(1, 4), (4, 5)])]
664
+ sage: list(yen_k_shortest_simple_paths(g, 1, 5, by_weight=True, report_edges=True))
665
+ [[(1, 3), (3, 5)], [(1, 2), (2, 5)], [(1, 4), (4, 5)]]
666
+ sage: list(yen_k_shortest_simple_paths(g, 1, 5, by_weight=True, report_edges=True, labels=True))
667
+ [[(1, 3, 10), (3, 5, 10)], [(1, 2, 20), (2, 5, 20)], [(1, 4, 30), (4, 5, 30)]]
668
+ sage: from sage.graphs.path_enumeration import yen_k_shortest_simple_paths
669
+ sage: g = Graph([(1, 2, 20), (1, 3, 10), (1, 4, 30), (2, 5, 20), (3, 5, 10), (4, 5, 30), (1, 6, 100), (5, 6, 5)])
670
+ sage: list(yen_k_shortest_simple_paths(g, 1, 6, by_weight = True))
671
+ [[1, 3, 5, 6], [1, 2, 5, 6], [1, 4, 5, 6], [1, 6]]
672
+ sage: list(yen_k_shortest_simple_paths(g, 1, 6))
673
+ [[1, 6], [1, 2, 5, 6], [1, 3, 5, 6], [1, 4, 5, 6]]
674
+ sage: list(yen_k_shortest_simple_paths(g, 1, 6, report_edges=True, report_weight=True, labels=True))
675
+ [(1, [(1, 6, 100)]),
676
+ (3, [(1, 2, 20), (2, 5, 20), (5, 6, 5)]),
677
+ (3, [(1, 3, 10), (3, 5, 10), (5, 6, 5)]),
678
+ (3, [(1, 4, 30), (4, 5, 30), (5, 6, 5)])]
679
+ sage: list(yen_k_shortest_simple_paths(g, 1, 6, report_edges=True, report_weight=True, labels=True, by_weight=True))
680
+ [(25, [(1, 3, 10), (3, 5, 10), (5, 6, 5)]),
681
+ (45, [(1, 2, 20), (2, 5, 20), (5, 6, 5)]),
682
+ (65, [(1, 4, 30), (4, 5, 30), (5, 6, 5)]),
683
+ (100, [(1, 6, 100)])]
684
+ sage: list(yen_k_shortest_simple_paths(g, 1, 6, report_edges=True, labels=True, by_weight=True))
685
+ [[(1, 3, 10), (3, 5, 10), (5, 6, 5)],
686
+ [(1, 2, 20), (2, 5, 20), (5, 6, 5)],
687
+ [(1, 4, 30), (4, 5, 30), (5, 6, 5)],
688
+ [(1, 6, 100)]]
689
+ sage: list(yen_k_shortest_simple_paths(g, 1, 6, report_edges=True, labels=True))
690
+ [[(1, 6, 100)],
691
+ [(1, 2, 20), (2, 5, 20), (5, 6, 5)],
692
+ [(1, 3, 10), (3, 5, 10), (5, 6, 5)],
693
+ [(1, 4, 30), (4, 5, 30), (5, 6, 5)]]
694
+
695
+ TESTS::
696
+
697
+ sage: from sage.graphs.path_enumeration import yen_k_shortest_simple_paths
698
+ sage: g = Graph([(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 2),
699
+ ....: (5, 6, 100), (4, 7, 3), (7, 6, 4), (3, 8, 5),
700
+ ....: (8, 9, 2), (9, 6, 2), (9, 10, 7), (9, 11, 10),
701
+ ....: (11, 6, 8), (10, 6, 2)])
702
+ sage: list(yen_k_shortest_simple_paths(g, 1, 6, by_weight=True))
703
+ [[1, 2, 3, 4, 7, 6],
704
+ [1, 2, 3, 8, 9, 6],
705
+ [1, 2, 3, 8, 9, 10, 6],
706
+ [1, 2, 3, 8, 9, 11, 6],
707
+ [1, 2, 3, 4, 5, 6]]
708
+ sage: list(yen_k_shortest_simple_paths(g, 1, 6, report_edges=True, labels=True, by_weight=True))
709
+ [[(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 7, 3), (6, 7, 4)],
710
+ [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (6, 9, 2)],
711
+ [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 10, 7), (6, 10, 2)],
712
+ [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 11, 10), (6, 11, 8)],
713
+ [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 2), (5, 6, 100)]]
714
+ sage: list(yen_k_shortest_simple_paths(g, 1, 6, report_edges=True, labels=True, by_weight=True, report_weight=True))
715
+ [(10, [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 7, 3), (6, 7, 4)]),
716
+ (11, [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (6, 9, 2)]),
717
+ (18, [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 10, 7), (6, 10, 2)]),
718
+ (27, [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 11, 10), (6, 11, 8)]),
719
+ (105, [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 2), (5, 6, 100)])]
720
+ sage: list(yen_k_shortest_simple_paths(g, 1, 6))
721
+ [[1, 2, 3, 4, 5, 6],
722
+ [1, 2, 3, 4, 7, 6],
723
+ [1, 2, 3, 8, 9, 6],
724
+ [1, 2, 3, 8, 9, 10, 6],
725
+ [1, 2, 3, 8, 9, 11, 6]]
726
+ sage: from sage.graphs.path_enumeration import yen_k_shortest_simple_paths
727
+ sage: g = DiGraph([(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 1),
728
+ ....: (1, 7, 1), (7, 8, 1), (8, 5, 1), (1, 6, 1),
729
+ ....: (6, 9, 1), (9, 5, 1), (4, 2, 1), (9, 3, 1),
730
+ ....: (9, 10, 1), (10, 5, 1), (9, 11, 1), (11, 10, 1)])
731
+ sage: list(yen_k_shortest_simple_paths(g, 1, 5))
732
+ [[1, 6, 9, 5],
733
+ [1, 7, 8, 5],
734
+ [1, 2, 3, 4, 5],
735
+ [1, 6, 9, 10, 5],
736
+ [1, 6, 9, 3, 4, 5],
737
+ [1, 6, 9, 11, 10, 5]]
738
+ """
739
+ if source not in self:
740
+ raise ValueError("vertex '{}' is not in the graph".format(source))
741
+ if target not in self:
742
+ raise ValueError("vertex '{}' is not in the graph".format(target))
743
+
744
+ if source == target:
745
+ if report_edges:
746
+ yield []
747
+ elif report_weight:
748
+ yield (0, [source])
749
+ else:
750
+ yield [source]
751
+ return
752
+
753
+ if self.has_loops() or self.allows_multiple_edges():
754
+ G = self.to_simple(to_undirected=False, keep_label='min', immutable=False)
755
+ else:
756
+ G = self
757
+
758
+ by_weight, weight_function = self._get_weight_function(by_weight=by_weight,
759
+ weight_function=weight_function,
760
+ check_weight=check_weight)
761
+
762
+ cdef dict edge_wt
763
+ if by_weight:
764
+ # dictionary to get weight of the edges
765
+ edge_wt = {(e[0], e[1]): weight_function(e) for e in G.edge_iterator()}
766
+ if not G.is_directed():
767
+ for u, v in G.edge_iterator(labels=False):
768
+ edge_wt[v, u] = edge_wt[u, v]
769
+
770
+ def length_func(path):
771
+ return sum(edge_wt[e] for e in zip(path[:-1], path[1:]))
772
+ # shortest path function for weighted graph
773
+ shortest_path_func = G._backend.bidirectional_dijkstra_special
774
+ else:
775
+ def length_func(path):
776
+ return len(path) - 1
777
+ # shortest path function for unweighted graph
778
+ shortest_path_func = G._backend.shortest_path_special
779
+
780
+ # compute the shortest path between the source and the target
781
+ cdef list path
782
+ if by_weight:
783
+ path = shortest_path_func(source, target, weight_function=weight_function)
784
+ else:
785
+ path = shortest_path_func(source, target)
786
+ # corner case
787
+ if not path:
788
+ if report_weight:
789
+ yield (0, [])
790
+ else:
791
+ yield []
792
+ return
793
+
794
+ cdef dict edge_labels
795
+ if report_edges and labels:
796
+ edge_labels = {(e[0], e[1]): e for e in G.edge_iterator()}
797
+ if not G.is_directed():
798
+ for u, v in G.edge_iterator(labels=False):
799
+ edge_labels[v, u] = edge_labels[u, v]
800
+
801
+ # heap data structure containing the candidate paths
802
+ cdef priority_queue[pair[double, pair[int, int]]] heap_sorted_paths
803
+ cdef int idx = 0
804
+ heap_sorted_paths.push((-length_func(path), (idx, 0)))
805
+ cdef dict idx_to_path = {idx: path}
806
+ idx = idx + 1
807
+ # list of all paths already yielded
808
+ cdef list listA = list()
809
+
810
+ cdef set exclude_vertices
811
+ cdef set exclude_edges
812
+ cdef list prev_path, new_path, root
813
+ cdef int path_idx, dev_idx
814
+
815
+ while idx_to_path:
816
+ # extracting the next best path from the heap
817
+ cost, (path_idx, dev_idx) = heap_sorted_paths.top()
818
+ heap_sorted_paths.pop()
819
+ prev_path = idx_to_path[path_idx]
820
+ del idx_to_path[path_idx]
821
+ if report_weight:
822
+ cost = -cost
823
+ if cost in ZZ:
824
+ cost = int(cost)
825
+ if report_edges and labels:
826
+ yield (cost, [edge_labels[e] for e in zip(prev_path[:-1], prev_path[1:])])
827
+ elif report_edges:
828
+ yield (cost, list(zip(prev_path[:-1], prev_path[1:])))
829
+ else:
830
+ yield (cost, prev_path)
831
+ else:
832
+ if report_edges and labels:
833
+ yield [edge_labels[e] for e in zip(prev_path[:-1], prev_path[1:])]
834
+ elif report_edges:
835
+ yield list(zip(prev_path[:-1], prev_path[1:]))
836
+ else:
837
+ yield prev_path
838
+
839
+ listA.append(prev_path)
840
+ exclude_vertices = set(prev_path[:dev_idx])
841
+ exclude_edges = set()
842
+ root = prev_path[:dev_idx]
843
+
844
+ # deviating from the previous path to find the candidate paths
845
+ for i in range(dev_idx + 1, len(prev_path)):
846
+ # root part of the previous path
847
+ root.append(prev_path[i - 1])
848
+ for path in listA:
849
+ if path[:i] == root:
850
+ exclude_edges.add((path[i - 1], path[i]))
851
+ if not G.is_directed():
852
+ exclude_edges.add((path[i], path[i - 1]))
853
+ try:
854
+ # finding the spur part of the path after excluding certain
855
+ # vertices and edges
856
+ if by_weight:
857
+ spur = shortest_path_func(root[-1], target,
858
+ exclude_vertices=exclude_vertices,
859
+ exclude_edges=exclude_edges,
860
+ weight_function=weight_function)
861
+ else:
862
+ spur = shortest_path_func(root[-1], target,
863
+ exclude_vertices=exclude_vertices,
864
+ exclude_edges=exclude_edges)
865
+ if not spur:
866
+ continue
867
+ # concatenating the root and the spur paths
868
+ new_path = root[:-1] + spur
869
+ # push operation
870
+ idx_to_path[idx] = new_path
871
+ heap_sorted_paths.push((-length_func(new_path), (idx, i - 1)))
872
+ idx = idx + 1
873
+ except Exception:
874
+ pass
875
+ exclude_vertices.add(root[-1])
876
+
877
+
878
+ def feng_k_shortest_simple_paths(self, source, target, weight_function=None,
879
+ by_weight=False, check_weight=True,
880
+ report_edges=False,
881
+ labels=False, report_weight=False):
882
+ r"""
883
+ Return an iterator over the simple paths between a pair of vertices in
884
+ increasing order of weights.
885
+
886
+ Works only for directed graphs.
887
+
888
+ For unweighted graphs, paths are returned in order of increasing number
889
+ of edges.
890
+
891
+ In case of weighted graphs, negative weights are not allowed.
892
+
893
+ If ``source`` is the same vertex as ``target``, then ``[[source]]`` is
894
+ returned -- a list containing the 1-vertex, 0-edge path ``source``.
895
+
896
+ The loops and the multiedges if present in the given graph are ignored and
897
+ only minimum of the edge labels is kept in case of multiedges.
898
+
899
+ INPUT:
900
+
901
+ - ``source`` -- a vertex of the graph, where to start
902
+
903
+ - ``target`` -- a vertex of the graph, where to end
904
+
905
+ - ``weight_function`` -- function (default: ``None``); a function that
906
+ takes as input an edge ``(u, v, l)`` and outputs its weight. If not
907
+ ``None``, ``by_weight`` is automatically set to ``True``. If ``None``
908
+ and ``by_weight`` is ``True``, we use the edge label ``l`` as a
909
+ weight.
910
+
911
+ - ``by_weight`` -- boolean (default: ``False``); if ``True``, the edges
912
+ in the graph are weighted, otherwise all edges have weight 1
913
+
914
+ - ``check_weight`` -- boolean (default: ``True``); whether to check that
915
+ the ``weight_function`` outputs a number for each edge
916
+
917
+ - ``report_edges`` -- boolean (default: ``False``); whether to report
918
+ paths as list of vertices (default) or list of edges, if ``False``
919
+ then ``labels`` parameter is ignored
920
+
921
+ - ``labels`` -- boolean (default: ``False``); if ``False``, each edge
922
+ is simply a pair ``(u, v)`` of vertices. Otherwise a list of edges
923
+ along with its edge labels are used to represent the path.
924
+
925
+ - ``report_weight`` -- boolean (default: ``False``); if ``False``, just
926
+ the path between ``source`` and ``target`` is returned. Otherwise a
927
+ tuple of path length and path is returned.
928
+
929
+ ALGORITHM:
930
+
931
+ This algorithm can be divided into two parts. Firstly, it determines the
932
+ shortest path from ``source`` to ``target``. Then, it determines all the
933
+ other `k`-shortest paths. This algorithm finds the deviations of previous
934
+ shortest paths to determine the next shortest paths. This algorithm finds
935
+ the candidate paths more efficiently using a node classification
936
+ technique. At first the candidate path is separated by its deviation node
937
+ as prefix and suffix. Then the algorithm classify the nodes as red, yellow
938
+ and green. A node on the prefix is assigned a red color, a node that can
939
+ reach t (the destination node) through a shortest path without visiting a
940
+ red node is assigned a green color, and all other nodes are assigned a
941
+ yellow color. When searching for the suffix of a candidate path, all green
942
+ nodes are bypassed, and ``Dijkstra’s algorithm`` is applied to find an
943
+ all-yellow-node subpath. Since on average the number of yellow nodes is
944
+ much smaller than n, this algorithm has a much lower average-case running
945
+ time.
946
+
947
+ Time complexity is `O(kn(m+n\log{n}))` where `n` is the number of vertices
948
+ and `m` is the number of edges and `k` is the number of shortest paths
949
+ needed to find. Its average running time is much smaller as compared to
950
+ `Yen's` algorithm.
951
+
952
+ See [Feng2014]_ for more details on this algorithm.
953
+
954
+ EXAMPLES::
955
+
956
+ sage: from sage.graphs.path_enumeration import feng_k_shortest_simple_paths
957
+ sage: g = DiGraph([(1, 2, 20), (1, 3, 10), (1, 4, 30), (2, 5, 20), (3, 5, 10), (4, 5, 30)])
958
+ sage: list(feng_k_shortest_simple_paths(g, 1, 5, by_weight=True))
959
+ [[1, 3, 5], [1, 2, 5], [1, 4, 5]]
960
+ sage: list(feng_k_shortest_simple_paths(g, 1, 5))
961
+ [[1, 2, 5], [1, 3, 5], [1, 4, 5]]
962
+ sage: list(feng_k_shortest_simple_paths(g, 1, 1))
963
+ [[1]]
964
+ sage: list(feng_k_shortest_simple_paths(g, 1, 5, report_edges=True, labels=True))
965
+ [[(1, 2, 20), (2, 5, 20)], [(1, 3, 10), (3, 5, 10)], [(1, 4, 30), (4, 5, 30)]]
966
+ sage: list(feng_k_shortest_simple_paths(g, 1, 5, report_edges=True, labels=True, by_weight=True))
967
+ [[(1, 3, 10), (3, 5, 10)], [(1, 2, 20), (2, 5, 20)], [(1, 4, 30), (4, 5, 30)]]
968
+ sage: list(feng_k_shortest_simple_paths(g, 1, 5, report_edges=True, labels=True, by_weight=True, report_weight=True))
969
+ [(20, [(1, 3, 10), (3, 5, 10)]),
970
+ (40, [(1, 2, 20), (2, 5, 20)]),
971
+ (60, [(1, 4, 30), (4, 5, 30)])]
972
+
973
+ sage: from sage.graphs.path_enumeration import feng_k_shortest_simple_paths
974
+ sage: g = DiGraph([(1, 2, 20), (1, 3, 10), (1, 4, 30), (2, 5, 20), (3, 5, 10), (4, 5, 30), (1, 6, 100), (5, 6, 5)])
975
+ sage: list(feng_k_shortest_simple_paths(g, 1, 6, by_weight = True))
976
+ [[1, 3, 5, 6], [1, 2, 5, 6], [1, 4, 5, 6], [1, 6]]
977
+ sage: list(feng_k_shortest_simple_paths(g, 1, 6))
978
+ [[1, 6], [1, 2, 5, 6], [1, 3, 5, 6], [1, 4, 5, 6]]
979
+ sage: list(feng_k_shortest_simple_paths(g, 1, 6, report_edges=True, labels=True, by_weight=True, report_weight=True))
980
+ [(25, [(1, 3, 10), (3, 5, 10), (5, 6, 5)]),
981
+ (45, [(1, 2, 20), (2, 5, 20), (5, 6, 5)]),
982
+ (65, [(1, 4, 30), (4, 5, 30), (5, 6, 5)]),
983
+ (100, [(1, 6, 100)])]
984
+ sage: list(feng_k_shortest_simple_paths(g, 1, 6, report_edges=True, labels=True, report_weight=True))
985
+ [(1, [(1, 6, 100)]),
986
+ (3, [(1, 2, 20), (2, 5, 20), (5, 6, 5)]),
987
+ (3, [(1, 3, 10), (3, 5, 10), (5, 6, 5)]),
988
+ (3, [(1, 4, 30), (4, 5, 30), (5, 6, 5)])]
989
+ sage: from sage.graphs.path_enumeration import feng_k_shortest_simple_paths
990
+ sage: g = DiGraph([(1, 2, 5), (2, 3, 0), (1, 4, 2), (4, 5, 1), (5, 3, 0)])
991
+ sage: list(feng_k_shortest_simple_paths(g, 1, 3, by_weight=True))
992
+ [[1, 4, 5, 3], [1, 2, 3]]
993
+ sage: list(feng_k_shortest_simple_paths(g, 1, 3))
994
+ [[1, 2, 3], [1, 4, 5, 3]]
995
+ sage: list(feng_k_shortest_simple_paths(g, 1, 3, report_weight=True))
996
+ [(2, [1, 2, 3]), (3, [1, 4, 5, 3])]
997
+ sage: list(feng_k_shortest_simple_paths(g, 1, 3, report_weight=True, report_edges=True))
998
+ [(2, [(1, 2), (2, 3)]), (3, [(1, 4), (4, 5), (5, 3)])]
999
+ sage: list(feng_k_shortest_simple_paths(g, 1, 3, report_weight=True, report_edges=True, by_weight=True))
1000
+ [(3, [(1, 4), (4, 5), (5, 3)]), (5, [(1, 2), (2, 3)])]
1001
+ sage: list(feng_k_shortest_simple_paths(g, 1, 3, report_weight=True, report_edges=True, by_weight=True, labels=True))
1002
+ [(3, [(1, 4, 2), (4, 5, 1), (5, 3, 0)]), (5, [(1, 2, 5), (2, 3, 0)])]
1003
+
1004
+ TESTS::
1005
+
1006
+ sage: from sage.graphs.path_enumeration import feng_k_shortest_simple_paths
1007
+ sage: g = DiGraph([(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 2), (5, 6, 100),
1008
+ ....: (4, 7, 3), (7, 6, 4), (3, 8, 5), (8, 9, 2), (9, 6, 2),
1009
+ ....: (9, 10, 7), (9, 11, 10), (11, 6, 8), (10, 6, 2)])
1010
+ sage: list(feng_k_shortest_simple_paths(g, 1, 6, by_weight=True))
1011
+ [[1, 2, 3, 4, 7, 6],
1012
+ [1, 2, 3, 8, 9, 6],
1013
+ [1, 2, 3, 8, 9, 10, 6],
1014
+ [1, 2, 3, 8, 9, 11, 6],
1015
+ [1, 2, 3, 4, 5, 6]]
1016
+ sage: list(feng_k_shortest_simple_paths(g, 1, 6, by_weight=True, report_edges=True))
1017
+ [[(1, 2), (2, 3), (3, 4), (4, 7), (7, 6)],
1018
+ [(1, 2), (2, 3), (3, 8), (8, 9), (9, 6)],
1019
+ [(1, 2), (2, 3), (3, 8), (8, 9), (9, 10), (10, 6)],
1020
+ [(1, 2), (2, 3), (3, 8), (8, 9), (9, 11), (11, 6)],
1021
+ [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]]
1022
+ sage: list(feng_k_shortest_simple_paths(g, 1, 6, by_weight=True, report_edges=True, report_weight=True))
1023
+ [(10, [(1, 2), (2, 3), (3, 4), (4, 7), (7, 6)]),
1024
+ (11, [(1, 2), (2, 3), (3, 8), (8, 9), (9, 6)]),
1025
+ (18, [(1, 2), (2, 3), (3, 8), (8, 9), (9, 10), (10, 6)]),
1026
+ (27, [(1, 2), (2, 3), (3, 8), (8, 9), (9, 11), (11, 6)]),
1027
+ (105, [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)])]
1028
+ sage: list(feng_k_shortest_simple_paths(g, 1, 6, by_weight=True, report_edges=True, report_weight=True, labels=True))
1029
+ [(10, [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 7, 3), (7, 6, 4)]),
1030
+ (11, [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 6, 2)]),
1031
+ (18, [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 10, 7), (10, 6, 2)]),
1032
+ (27, [(1, 2, 1), (2, 3, 1), (3, 8, 5), (8, 9, 2), (9, 11, 10), (11, 6, 8)]),
1033
+ (105, [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 2), (5, 6, 100)])]
1034
+ sage: list(feng_k_shortest_simple_paths(g, 1, 6))
1035
+ [[1, 2, 3, 4, 5, 6],
1036
+ [1, 2, 3, 4, 7, 6],
1037
+ [1, 2, 3, 8, 9, 6],
1038
+ [1, 2, 3, 8, 9, 11, 6],
1039
+ [1, 2, 3, 8, 9, 10, 6]]
1040
+ sage: from sage.graphs.path_enumeration import feng_k_shortest_simple_paths
1041
+ sage: g = DiGraph([(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 1),
1042
+ ....: (1, 7, 1), (7, 8, 1), (8, 5, 1), (1, 6, 1),
1043
+ ....: (6, 9, 1), (9, 5, 1), (4, 2, 1), (9, 3, 1),
1044
+ ....: (9, 10, 1), (10, 5, 1), (9, 11, 1), (11, 10, 1)])
1045
+ sage: list(feng_k_shortest_simple_paths(g, 1, 5))
1046
+ [[1, 6, 9, 5],
1047
+ [1, 7, 8, 5],
1048
+ [1, 2, 3, 4, 5],
1049
+ [1, 6, 9, 10, 5],
1050
+ [1, 6, 9, 11, 10, 5],
1051
+ [1, 6, 9, 3, 4, 5]]
1052
+ sage: list(feng_k_shortest_simple_paths(g, 1, 5, by_weight=True))
1053
+ [[1, 6, 9, 5],
1054
+ [1, 7, 8, 5],
1055
+ [1, 2, 3, 4, 5],
1056
+ [1, 6, 9, 10, 5],
1057
+ [1, 6, 9, 11, 10, 5],
1058
+ [1, 6, 9, 3, 4, 5]]
1059
+ sage: from sage.graphs.path_enumeration import feng_k_shortest_simple_paths
1060
+ sage: g = DiGraph([(1, 2, 5), (6, 3, 0), (2, 6, 6), (1, 4, 15),
1061
+ ....: (4, 5, 1), (4, 3, 0), (7, 1, 2), (8, 7, 1)])
1062
+ sage: list(feng_k_shortest_simple_paths(g, 1, 3))
1063
+ [[1, 4, 3], [1, 2, 6, 3]]
1064
+ sage: list(feng_k_shortest_simple_paths(g, 1, 3, by_weight=True, report_edges=True, report_weight=True, labels=True))
1065
+ [(11, [(1, 2, 5), (2, 6, 6), (6, 3, 0)]), (15, [(1, 4, 15), (4, 3, 0)])]
1066
+ sage: list(feng_k_shortest_simple_paths(g, 1, 3, by_weight=True))
1067
+ [[1, 2, 6, 3], [1, 4, 3]]
1068
+ sage: from sage.graphs.path_enumeration import feng_k_shortest_simple_paths
1069
+ sage: G = DiGraph([(0, 1, 9), (0, 3, 1), (0, 4, 2), (1, 6, 4),
1070
+ ....: (1, 7, 1), (2, 0, 5), (2, 1, 4), (2, 7, 1),
1071
+ ....: (3, 1, 7), (3, 2, 4), (3, 4, 2), (4, 0, 8),
1072
+ ....: (4, 1, 10), (4, 3, 3), (4, 7, 10), (5, 2, 5),
1073
+ ....: (5, 4, 9), (6, 2, 9)], weighted=True)
1074
+ sage: list(feng_k_shortest_simple_paths(G, 2, 1, by_weight=True, report_weight=True, report_edges=True, labels=True))
1075
+ [(4, [(2, 1, 4)]),
1076
+ (13, [(2, 0, 5), (0, 3, 1), (3, 1, 7)]),
1077
+ (14, [(2, 0, 5), (0, 1, 9)]),
1078
+ (17, [(2, 0, 5), (0, 4, 2), (4, 1, 10)]),
1079
+ (17, [(2, 0, 5), (0, 4, 2), (4, 3, 3), (3, 1, 7)]),
1080
+ (18, [(2, 0, 5), (0, 3, 1), (3, 4, 2), (4, 1, 10)])]
1081
+ """
1082
+ if not self.is_directed():
1083
+ raise ValueError("this algorithm works only for directed graphs")
1084
+
1085
+ if source not in self:
1086
+ raise ValueError("vertex '{}' is not in the graph".format(source))
1087
+
1088
+ if target not in self:
1089
+ raise ValueError("vertex '{}' is not in the graph".format(target))
1090
+
1091
+ if source == target:
1092
+ if report_edges:
1093
+ yield []
1094
+ elif report_weight:
1095
+ yield (0, [source])
1096
+ else:
1097
+ yield [source]
1098
+ return
1099
+
1100
+ if self.has_loops() or self.allows_multiple_edges():
1101
+ G = self.to_simple(to_undirected=False, keep_label='min', immutable=False)
1102
+ else:
1103
+ G = self.copy()
1104
+
1105
+ # removing the incoming edges to source and outgoing edges from target as
1106
+ # they do not contribute towards the k shortest simple paths
1107
+ G.delete_edges(G.incoming_edges(source, labels=False))
1108
+ G.delete_edges(G.outgoing_edges(target, labels=False))
1109
+
1110
+ if weight_function is not None:
1111
+ by_weight = True
1112
+
1113
+ if weight_function is None and by_weight:
1114
+ def weight_function(e):
1115
+ return e[2]
1116
+
1117
+ by_weight, weight_function = self._get_weight_function(by_weight=by_weight,
1118
+ weight_function=weight_function,
1119
+ check_weight=check_weight)
1120
+ if by_weight:
1121
+ def reverse_weight_function(e):
1122
+ return weight_function((e[1], e[0], e[2]))
1123
+ else:
1124
+ def reverse_weight_function(e):
1125
+ return 1
1126
+
1127
+ cdef dict edge_labels
1128
+ if report_edges and labels:
1129
+ edge_labels = {(e[0], e[1]): e for e in G.edge_iterator()}
1130
+ if not G.is_directed():
1131
+ for u, v in G.edge_iterator(labels=False):
1132
+ edge_labels[v, u] = edge_labels[u, v]
1133
+
1134
+ from sage.graphs.base.boost_graph import shortest_paths
1135
+ # dictionary of parent node in the shortest path tree of the target vertex
1136
+ cdef dict parent = {}
1137
+ # assign color to each vertex as green, red or yellow
1138
+ cdef dict color = {}
1139
+ # express edges are the edges with head node as green and tail node as
1140
+ # yellow or tail node is a deviation node
1141
+ cdef dict expressEdges = {}
1142
+ # a dictionary of the new edges added to the graph used for restoring the
1143
+ # graph after the iteration
1144
+ cdef dict dic = {}
1145
+ # used to keep track of temporary edges added to the graph
1146
+ cdef dict temp_dict = {}
1147
+ # father of the path
1148
+ cdef dict father = {}
1149
+
1150
+ def getUpStreamNodes(v):
1151
+ """
1152
+ If there exist a path in shortest path subtree of target node from u to
1153
+ v then u is said to be an upstream node of v
1154
+ """
1155
+ cdef list ver = list()
1156
+ S = [v]
1157
+ while S:
1158
+ u = S.pop(0)
1159
+ if u in parent:
1160
+ for u_node in parent[u]:
1161
+ # if node color is green
1162
+ if color[u_node] == 0:
1163
+ S.append(u_node)
1164
+ ver.append(u_node)
1165
+ return ver
1166
+
1167
+ def findExpressEdges(Y):
1168
+ """
1169
+ Find the express edges whose tail nodes belong to a set Y and update the
1170
+ head node of each express edge
1171
+ """
1172
+ for v in Y:
1173
+ for w in G.neighbors_out(v):
1174
+ # if node color is green
1175
+ if color[w] == 0:
1176
+ if w not in expressEdges:
1177
+ expressEdges[w] = []
1178
+ expressEdges[w].append((v, w, G.edge_label(v, w)))
1179
+ if w != target and not G.has_edge(w, target):
1180
+ G.add_edge(w, target, 0)
1181
+ dic[w, target] = 1
1182
+ reduced_cost[w, target] = 0
1183
+ elif w != target and reduced_cost[w, target]:
1184
+ temp_dict[w, target] = reduced_cost[w, target]
1185
+ reduced_cost[w, target] = 0
1186
+ include_vertices.add(w)
1187
+
1188
+ reverse_graph = G.reverse()
1189
+ cdef dict dist
1190
+ cdef dict successor
1191
+ dist, successor = shortest_paths(reverse_graph, target, weight_function=reverse_weight_function,
1192
+ algorithm='Dijkstra_Boost')
1193
+
1194
+ # successor is a child node in the shortest path subtree
1195
+ cdef dict reduced_cost = {(e[0], e[1]): weight_function(e) + dist[e[1]] - dist[e[0]]
1196
+ for e in G.edge_iterator()
1197
+ if e[0] in dist and e[1] in dist}
1198
+
1199
+ cdef set exclude_vert_set = set(G) - set(dist)
1200
+ # finding the parent information from successor
1201
+ for key in successor:
1202
+ if successor[key] and successor[key] not in parent:
1203
+ parent[successor[key]] = [key]
1204
+ elif successor[key]:
1205
+ parent[successor[key]].append(key)
1206
+
1207
+ def length_func(path):
1208
+ return sum(reduced_cost[e] for e in zip(path[:-1], path[1:]))
1209
+
1210
+ # shortest path function for weighted/unweighted graph using reduced weights
1211
+ shortest_path_func = G._backend.bidirectional_dijkstra_special
1212
+
1213
+ try:
1214
+ # compute the shortest path between the source and the target
1215
+ path = shortest_path_func(source, target, exclude_vertices=exclude_vert_set,
1216
+ weight_function=weight_function, reduced_weight=reduced_cost)
1217
+ # corner case
1218
+ if not path:
1219
+ if report_weight:
1220
+ yield (0, [])
1221
+ else:
1222
+ yield []
1223
+ return
1224
+ except Exception:
1225
+ if report_weight:
1226
+ yield (0, [])
1227
+ else:
1228
+ yield []
1229
+ return
1230
+
1231
+ hash_path = tuple(path)
1232
+ father[hash_path] = None
1233
+
1234
+ # heap data structure containing the candidate paths
1235
+ cdef priority_queue[pair[double, pair[int, int]]] heap_sorted_paths
1236
+ cdef int idx = 0
1237
+ cdef dict idx_to_path = {idx: path}
1238
+ heap_sorted_paths.push((-length_func(path), (idx, 0)))
1239
+ idx = idx + 1
1240
+ shortest_path_len = dist[source]
1241
+
1242
+ cdef set exclude_edges
1243
+ cdef set exclude_vertices
1244
+ cdef set include_vertices
1245
+ cdef list allY
1246
+ cdef list prev_path, new_path, root
1247
+ cdef int path_idx, dev_idx
1248
+
1249
+ while idx_to_path:
1250
+ # extracting the next best path from the heap
1251
+ cost, (path_idx, dev_idx) = heap_sorted_paths.top()
1252
+ heap_sorted_paths.pop()
1253
+ prev_path = idx_to_path[path_idx]
1254
+ # removing the path from dictionary
1255
+ del idx_to_path[path_idx]
1256
+ if len(set(prev_path)) == len(prev_path):
1257
+ if report_weight:
1258
+ cost = -cost
1259
+ if cost in ZZ:
1260
+ cost = int(cost)
1261
+ if report_edges and labels:
1262
+ yield (cost + shortest_path_len, [edge_labels[e] for e in zip(prev_path[:-1], prev_path[1:])])
1263
+ elif report_edges:
1264
+ yield (cost + shortest_path_len, list(zip(prev_path[:-1], prev_path[1:])))
1265
+ else:
1266
+ yield (cost + shortest_path_len, prev_path)
1267
+ else:
1268
+ if report_edges and labels:
1269
+ yield [edge_labels[e] for e in zip(prev_path[:-1], prev_path[1:])]
1270
+ elif report_edges:
1271
+ yield list(zip(prev_path[:-1], prev_path[1:]))
1272
+ else:
1273
+ yield prev_path
1274
+
1275
+ # deep copy of the exclude vertices set
1276
+ exclude_vertices = copy.deepcopy(exclude_vert_set)
1277
+ exclude_edges = set()
1278
+ include_vertices = set()
1279
+ expressEdges = {}
1280
+ dic = {}
1281
+ temp_dict = {}
1282
+ root = prev_path[:dev_idx]
1283
+ # coloring all the nodes as green initially
1284
+ color = {v: 0 for v in G}
1285
+ # list of yellow nodes
1286
+ allY = list()
1287
+ for j in range(dev_idx):
1288
+ exclude_vertices.add(prev_path[j])
1289
+ for j in range(dev_idx + 1):
1290
+ # coloring red
1291
+ color[prev_path[j]] = 1
1292
+ Yv = getUpStreamNodes(prev_path[j])
1293
+ allY += Yv
1294
+ for y in Yv:
1295
+ # color yellow for upstream nodes
1296
+ color[y] = 2
1297
+ include_vertices.add(y)
1298
+ # adding the deviation node to find the express edges
1299
+ allY.append(prev_path[dev_idx])
1300
+ findExpressEdges(allY)
1301
+ color[target] = 2
1302
+ include_vertices.add(target)
1303
+ # deviating from the previous path to find the candidate paths
1304
+ for i in range(dev_idx + 1, len(prev_path)):
1305
+ # root part of the previous path
1306
+ root.append(prev_path[i - 1])
1307
+ # if it is the deviation node
1308
+ if i == dev_idx + 1:
1309
+ p = father[tuple(prev_path)]
1310
+ # comparing the deviation nodes
1311
+ while p and len(p) > dev_idx + 1 and p[dev_idx] == root[dev_idx]:
1312
+ # using fatherly approach to filter the edges to be removed
1313
+ exclude_edges.add((p[i - 1], p[i]))
1314
+ p = father[tuple(p)]
1315
+ else:
1316
+ # coloring it red
1317
+ color[root[-1]] = 1
1318
+ Yu = getUpStreamNodes(root[-1])
1319
+ for y in Yu:
1320
+ # coloring upstream nodes as yellow
1321
+ color[y] = 2
1322
+ include_vertices.add(y)
1323
+ Yu.append(root[-1])
1324
+ for n in Yu:
1325
+ if n in expressEdges and expressEdges[n]:
1326
+ # recovering the express edges incident to a node n
1327
+ for e in expressEdges[n]:
1328
+ if (e[1], target) in dic:
1329
+ # restoration of edges in the original graph
1330
+ G.delete_edge(e[1], target)
1331
+ del dic[(e[1], target)]
1332
+ del reduced_cost[(e[1], target)]
1333
+ if (e[1], target) in temp_dict:
1334
+ # restoration of cost function
1335
+ reduced_cost[e[1], target] = temp_dict[e[1], target]
1336
+ del temp_dict[e[1], target]
1337
+ # resetting the expressEdges for node n
1338
+ expressEdges[n] = []
1339
+ findExpressEdges(Yu)
1340
+ # removing the edge in the previous shortest path to find a new
1341
+ # candidate path
1342
+ exclude_edges.add((prev_path[i - 1], prev_path[i]))
1343
+ try:
1344
+ # finding the spur part of the path after excluding certain
1345
+ # vertices and edges, this spur path is an all yellow subpath
1346
+ # so the shortest path algorithm is applied only on the all
1347
+ # yellow node subtree
1348
+ spur = shortest_path_func(root[-1], target,
1349
+ exclude_vertices=exclude_vertices,
1350
+ exclude_edges=exclude_edges,
1351
+ include_vertices=include_vertices,
1352
+ weight_function=weight_function,
1353
+ reduced_weight=reduced_cost)
1354
+ # finding the spur path in the original graph
1355
+ if spur and ((spur[-2], target) in dic or (spur[-2], target) in temp_dict):
1356
+ spur.pop()
1357
+ st = spur[-1]
1358
+ while st != target:
1359
+ st = successor[st]
1360
+ spur.append(st)
1361
+ if not spur:
1362
+ exclude_vertices.add(root[-1])
1363
+ continue
1364
+ # concatenating the root and the spur path
1365
+ new_path = root[:-1] + spur
1366
+ # push operation
1367
+ hash_path = tuple(new_path)
1368
+ father[hash_path] = prev_path
1369
+ idx_to_path[idx] = new_path
1370
+ heap_sorted_paths.push((-length_func(new_path), (idx, i - 1)))
1371
+ idx = idx + 1
1372
+ except Exception:
1373
+ pass
1374
+ exclude_vertices.add(root[-1])
1375
+ # restoring the original graph here
1376
+ for e in dic:
1377
+ G.delete_edge(e)
1378
+ del reduced_cost[(e[0], e[1])]
1379
+ for e in temp_dict:
1380
+ reduced_cost[e[0], e[1]] = temp_dict[e[0], e[1]]
1381
+
1382
+
1383
+ def _all_paths_iterator(self, vertex, ending_vertices=None,
1384
+ simple=False, max_length=None, trivial=False,
1385
+ use_multiedges=False, report_edges=False,
1386
+ labels=False, data=None):
1387
+ r"""
1388
+ Return an iterator over the paths of ``self`` starting with the
1389
+ given vertex.
1390
+
1391
+ INPUT:
1392
+
1393
+ - ``vertex`` -- the starting vertex of the paths
1394
+
1395
+ - ``ending_vertices`` -- iterable (default: ``None``); allowed ending
1396
+ vertices of the paths. If ``None``, then all vertices are allowed.
1397
+
1398
+ - ``simple`` -- boolean (default: ``False``); if set to ``True``, then
1399
+ only simple paths are considered. Simple paths are paths in which no
1400
+ two arcs share a head or share a tail, i.e. every vertex in the path
1401
+ is entered at most once and exited at most once.
1402
+
1403
+ - ``max_length`` -- nonnegative integer (default: ``None``); the
1404
+ maximum length of the enumerated paths. If set to ``None``, then all
1405
+ lengths are allowed.
1406
+
1407
+ - ``trivial`` -- boolean (default: ``False``); if set to ``True``, then
1408
+ the empty paths are also enumerated
1409
+
1410
+ - ``use_multiedges`` -- boolean (default: ``False``); this parameter is
1411
+ used only if the graph has multiple edges
1412
+
1413
+ - If ``False``, the graph is considered as simple and an edge label
1414
+ is arbitrarily selected for each edge as in
1415
+ :meth:`sage.graphs.generic_graph.GenericGraph.to_simple` if
1416
+ ``report_edges`` is ``True``
1417
+
1418
+ - If ``True``, a path will be reported as many times as the edges
1419
+ multiplicities along that path (when ``report_edges = False`` or
1420
+ ``labels = False``), or with all possible combinations of edge
1421
+ labels (when ``report_edges = True`` and ``labels = True``)
1422
+
1423
+ - ``report_edges`` -- boolean (default: ``False``); whether to report
1424
+ paths as list of vertices (default) or list of edges, if ``False``
1425
+ then ``labels`` parameter is ignored
1426
+
1427
+ - ``labels`` -- boolean (default: ``False``); if ``False``, each edge
1428
+ is simply a pair ``(u, v)`` of vertices. Otherwise a list of edges
1429
+ along with its edge labels are used to represent the path.
1430
+
1431
+ - ``data`` -- dictionary (default: ``None``); optional parameter to
1432
+ pass information about edge multiplicities of the graph, if ``None``
1433
+ edge multiplicity values are computed inside the method.
1434
+
1435
+ OUTPUT: iterator
1436
+
1437
+ EXAMPLES::
1438
+
1439
+ sage: G = graphs.CompleteGraph(4)
1440
+ sage: list(G._all_paths_iterator(1, ending_vertices=[3], simple=True))
1441
+ [[1, 3], [1, 0, 3], [1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3]]
1442
+ sage: list(G.shortest_simple_paths(1, 3))
1443
+ [[1, 3], [1, 0, 3], [1, 2, 3], [1, 2, 0, 3], [1, 0, 2, 3]]
1444
+ sage: pi = G._all_paths_iterator(1, ending_vertices=[3])
1445
+ sage: for _ in range(6):
1446
+ ....: print(next(pi))
1447
+ [1, 3]
1448
+ [1, 0, 3]
1449
+ [1, 2, 3]
1450
+ [1, 0, 1, 3]
1451
+ [1, 0, 2, 3]
1452
+ [1, 2, 0, 3]
1453
+
1454
+ sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
1455
+ sage: pi = g._all_paths_iterator('a', ending_vertices=['d'], report_edges=True, simple=True)
1456
+ sage: list(pi)
1457
+ [[('a', 'b'), ('b', 'c'), ('c', 'd')]]
1458
+
1459
+ sage: g = DiGraph([(0, 1, 'a'), (0, 1, 'b'), (1, 2,'c'), (1, 2,'d')], multiedges=True)
1460
+ sage: pi = g._all_paths_iterator(0, use_multiedges=True)
1461
+ sage: for _ in range(6):
1462
+ ....: print(next(pi))
1463
+ [0, 1]
1464
+ [0, 1]
1465
+ [0, 1, 2]
1466
+ [0, 1, 2]
1467
+ [0, 1, 2]
1468
+ [0, 1, 2]
1469
+ sage: pi = g._all_paths_iterator(0, use_multiedges=True, report_edges=True, labels=True)
1470
+ sage: for _ in range(6):
1471
+ ....: print(next(pi))
1472
+ [(0, 1, 'b')]
1473
+ [(0, 1, 'a')]
1474
+ [(0, 1, 'b'), (1, 2, 'd')]
1475
+ [(0, 1, 'b'), (1, 2, 'c')]
1476
+ [(0, 1, 'a'), (1, 2, 'd')]
1477
+ [(0, 1, 'a'), (1, 2, 'c')]
1478
+ sage: list(g._all_paths_iterator(1, ending_vertices=[2], use_multiedges=False, report_edges=True, labels=True, simple=True))
1479
+ [[(1, 2, 'd')]]
1480
+ sage: list(g._all_paths_iterator(0, ending_vertices=[2], use_multiedges=False, report_edges=False, labels=True))
1481
+ [[0, 1, 2]]
1482
+ sage: list(g._all_paths_iterator(0, use_multiedges=True, report_edges=False, labels=True, max_length=1))
1483
+ [[0, 1], [0, 1]]
1484
+ sage: list(g._all_paths_iterator(0, use_multiedges=True, report_edges=True, labels=True, max_length=1))
1485
+ [[(0, 1, 'b')], [(0, 1, 'a')]]
1486
+
1487
+ sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
1488
+ sage: pi = g._all_paths_iterator('a')
1489
+ sage: [len(next(pi)) - 1 for _ in range(5)]
1490
+ [1, 1, 2, 2, 2]
1491
+
1492
+ ::
1493
+
1494
+ sage: pi = g._all_paths_iterator('b')
1495
+ sage: for _ in range(5):
1496
+ ....: print(next(pi))
1497
+ ['b', 'c']
1498
+ ['b', 'c', 'd']
1499
+ ['b', 'c', 'd', 'c']
1500
+ ['b', 'c', 'd', 'c', 'd']
1501
+ ['b', 'c', 'd', 'c', 'd', 'c']
1502
+
1503
+ One may wish to enumerate simple paths, which are paths in which no two
1504
+ arcs share a head or share a tail, i.e. every vertex in the path is
1505
+ entered at most once and exited at most once. The result is always
1506
+ finite but may take a long time to compute::
1507
+
1508
+ sage: pi = g._all_paths_iterator('a', simple=True)
1509
+ sage: sorted(pi)
1510
+ [['a', 'a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'd']]
1511
+ sage: pi = g._all_paths_iterator('d', simple=True)
1512
+ sage: sorted(pi)
1513
+ [['d', 'c'], ['d', 'c', 'd']]
1514
+
1515
+ It is possible to specify the allowed ending vertices::
1516
+
1517
+ sage: pi = g._all_paths_iterator('a', ending_vertices=['c'])
1518
+ sage: [len(next(pi)) - 1 for _ in range(5)]
1519
+ [2, 3, 4, 4, 5]
1520
+ sage: pi = g._all_paths_iterator('a', ending_vertices=['a', 'b'])
1521
+ sage: [len(next(pi)) - 1 for _ in range(5)]
1522
+ [1, 1, 2, 2, 3]
1523
+
1524
+ One can bound the length of the paths::
1525
+
1526
+ sage: pi = g._all_paths_iterator('d', max_length=3)
1527
+ sage: sorted(pi)
1528
+ [['d', 'c'], ['d', 'c', 'd'], ['d', 'c', 'd', 'c']]
1529
+
1530
+ Or include the trivial empty path::
1531
+
1532
+ sage: pi = g._all_paths_iterator('a', max_length=3, trivial=True)
1533
+ sage: sorted(list(pi), key=lambda x:(len(x), x))
1534
+ [['a'], ['a', 'a'], ['a', 'b'], ['a', 'a', 'a'], ['a', 'a', 'b'],
1535
+ ['a', 'b', 'c'], ['a', 'a', 'a', 'a'], ['a', 'a', 'a', 'b'],
1536
+ ['a', 'a', 'b', 'c'], ['a', 'b', 'c', 'd']]
1537
+ sage: pi = g._all_paths_iterator('a', max_length=3, trivial=True)
1538
+ sage: [len(p) - 1 for p in pi]
1539
+ [0, 1, 1, 2, 2, 2, 3, 3, 3, 3]
1540
+ """
1541
+ if ending_vertices is None:
1542
+ ending_vertices = self
1543
+ else:
1544
+ ending_vertices = frozenset(ending_vertices)
1545
+ if max_length is None:
1546
+ from sage.rings.infinity import Infinity
1547
+ max_length = Infinity
1548
+ if max_length < 1:
1549
+ return
1550
+
1551
+ if self.is_directed():
1552
+ neighbor_iterator = self.neighbor_out_iterator
1553
+ else:
1554
+ neighbor_iterator = self.neighbor_iterator
1555
+
1556
+ cdef dict my_dict = {}
1557
+ cdef dict edge_multiplicity
1558
+ if not data:
1559
+ if report_edges and labels:
1560
+ if use_multiedges:
1561
+ for e in self.edge_iterator():
1562
+ if (e[0], e[1]) in my_dict:
1563
+ my_dict[(e[0], e[1])].append(e)
1564
+ else:
1565
+ my_dict[(e[0], e[1])] = [e]
1566
+ else:
1567
+ for e in self.edge_iterator():
1568
+ if (e[0], e[1]) not in my_dict:
1569
+ my_dict[(e[0], e[1])] = [e]
1570
+ elif use_multiedges and self.has_multiple_edges():
1571
+ from collections import Counter
1572
+ edge_multiplicity = dict(Counter(self.edge_iterator(labels=False)))
1573
+ else:
1574
+ if report_edges and labels:
1575
+ my_dict = data
1576
+ elif use_multiedges and self.has_multiple_edges():
1577
+ edge_multiplicity = data
1578
+ # Start with the empty path; we will try all extensions of it
1579
+ cdef list queue = []
1580
+ cdef list path = [vertex]
1581
+ cdef list newpath
1582
+ cdef int m
1583
+ if trivial and not report_edges and vertex in ending_vertices:
1584
+ yield path
1585
+ while True:
1586
+ # Build next generation of paths, one arc longer; max_length refers
1587
+ # to edges and not vertices, hence <= and not <
1588
+ if len(path) <= max_length:
1589
+ # We try all possible extensions
1590
+ if simple:
1591
+ # We only keep simple extensions. An extension is simple iff
1592
+ # the new vertex being entered has not previously occurred
1593
+ # in the path, or has occurred but only been exited (i.e. is
1594
+ # the first vertex in the path). In this latter case we must
1595
+ # not exit the new vertex again, so we do not consider it
1596
+ # for further extension, but just yield it immediately. See
1597
+ # Issue #12385.
1598
+ frozen_path = frozenset(path)
1599
+ for neighbor in neighbor_iterator(path[-1]):
1600
+ if neighbor not in frozen_path:
1601
+ queue.append(path + [neighbor])
1602
+ elif (neighbor == path[0] and
1603
+ neighbor in ending_vertices):
1604
+ newpath = path + [neighbor]
1605
+ if report_edges and labels:
1606
+ for p in product(*[my_dict[e] for e in zip(newpath[:-1], newpath[1:])]):
1607
+ yield list(p)
1608
+ elif use_multiedges and self.has_multiple_edges():
1609
+ m = prod(edge_multiplicity[e] for e in zip(newpath[:-1], newpath[1:]))
1610
+ if report_edges:
1611
+ newpath = list(zip(newpath[:-1], newpath[1:]))
1612
+ for _ in range(m):
1613
+ yield newpath
1614
+ elif report_edges:
1615
+ yield list(zip(newpath[:-1], newpath[1:]))
1616
+ else:
1617
+ yield newpath
1618
+ else:
1619
+ # Non-simple paths requested: we add all of them
1620
+ for neighbor in neighbor_iterator(path[-1]):
1621
+ queue.append(path + [neighbor])
1622
+
1623
+ if not queue:
1624
+ break
1625
+ path = queue.pop(0) # get the next path
1626
+
1627
+ if path[-1] in ending_vertices:
1628
+ # yield good path
1629
+ if report_edges and labels:
1630
+ for p in product(*[my_dict[e] for e in zip(path[:-1], path[1:])]):
1631
+ yield list(p)
1632
+ elif use_multiedges and self.has_multiple_edges():
1633
+ m = prod(edge_multiplicity[e] for e in zip(path[:-1], path[1:]))
1634
+ if report_edges:
1635
+ newpath = list(zip(path[:-1], path[1:]))
1636
+ else:
1637
+ newpath = path
1638
+ for _ in range(m):
1639
+ yield newpath
1640
+ elif report_edges:
1641
+ yield list(zip(path[:-1], path[1:]))
1642
+ else:
1643
+ yield path
1644
+
1645
+
1646
+ def all_paths_iterator(self, starting_vertices=None, ending_vertices=None,
1647
+ simple=False, max_length=None, trivial=False,
1648
+ use_multiedges=False, report_edges=False, labels=False):
1649
+ r"""
1650
+ Return an iterator over the paths of ``self``.
1651
+
1652
+ The paths are enumerated in increasing length order.
1653
+
1654
+ INPUT:
1655
+
1656
+ - ``starting_vertices`` -- iterable (default: ``None``); vertices from
1657
+ which the paths must start. If ``None``, then all vertices of the
1658
+ graph can be starting points.
1659
+
1660
+ - ``ending_vertices`` -- iterable (default: ``None``); allowed ending
1661
+ vertices of the paths. If ``None``, then all vertices are allowed.
1662
+
1663
+ - ``simple`` -- boolean (default: ``False``); if set to ``True``, then
1664
+ only simple paths are considered. Simple paths are paths in which no
1665
+ two arcs share a head or share a tail, i.e. every vertex in the path
1666
+ is entered at most once and exited at most once.
1667
+
1668
+ - ``max_length`` -- nonnegative integer (default: ``None``); the
1669
+ maximum length of the enumerated paths. If set to ``None``, then all
1670
+ lengths are allowed.
1671
+
1672
+ - ``trivial`` -- boolean (default: ``False``); if set to ``True``, then
1673
+ the empty paths are also enumerated
1674
+
1675
+ - ``use_multiedges`` -- boolean (default: ``False``); this parameter is
1676
+ used only if the graph has multiple edges
1677
+
1678
+ - If ``False``, the graph is considered as simple and an edge label
1679
+ is arbitrarily selected for each edge as in
1680
+ :meth:`sage.graphs.generic_graph.GenericGraph.to_simple` if
1681
+ ``report_edges`` is ``True``
1682
+
1683
+ - If ``True``, a path will be reported as many times as the edges
1684
+ multiplicities along that path (when ``report_edges = False`` or
1685
+ ``labels = False``), or with all possible combinations of edge
1686
+ labels (when ``report_edges = True`` and ``labels = True``)
1687
+
1688
+ - ``report_edges`` -- boolean (default: ``False``); whether to report
1689
+ paths as list of vertices (default) or list of edges, if ``False``
1690
+ then ``labels`` parameter is ignored
1691
+
1692
+ - ``labels`` -- boolean (default: ``False``); if ``False``, each edge
1693
+ is simply a pair ``(u, v)`` of vertices. Otherwise a list of edges
1694
+ along with its edge labels are used to represent the path.
1695
+
1696
+ OUTPUT: iterator
1697
+
1698
+ AUTHOR:
1699
+
1700
+ Alexandre Blondin Masse
1701
+
1702
+ EXAMPLES::
1703
+
1704
+ sage: G = graphs.CompleteGraph(4)
1705
+ sage: list(G.all_paths_iterator(starting_vertices=[1], ending_vertices=[3], simple=True))
1706
+ [[1, 3], [1, 0, 3], [1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3]]
1707
+ sage: list(G.shortest_simple_paths(1, 3))
1708
+ [[1, 3], [1, 0, 3], [1, 2, 3], [1, 2, 0, 3], [1, 0, 2, 3]]
1709
+ sage: pi = G.all_paths_iterator(starting_vertices=[1], ending_vertices=[3])
1710
+ sage: for _ in range(6):
1711
+ ....: print(next(pi))
1712
+ [1, 3]
1713
+ [1, 0, 3]
1714
+ [1, 2, 3]
1715
+ [1, 0, 1, 3]
1716
+ [1, 0, 2, 3]
1717
+ [1, 2, 0, 3]
1718
+
1719
+ sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
1720
+ sage: pi = g.all_paths_iterator(starting_vertices=['a'], ending_vertices=['d'], report_edges=True, simple=True)
1721
+ sage: list(pi)
1722
+ [[('a', 'b'), ('b', 'c'), ('c', 'd')]]
1723
+
1724
+ sage: g = DiGraph([(0, 1, 'a'), (0, 1, 'b'), (1, 2,'c'), (1, 2,'d')], multiedges=True)
1725
+ sage: pi = g.all_paths_iterator(starting_vertices=[0], use_multiedges=True)
1726
+ sage: for _ in range(6):
1727
+ ....: print(next(pi))
1728
+ [0, 1]
1729
+ [0, 1]
1730
+ [0, 1, 2]
1731
+ [0, 1, 2]
1732
+ [0, 1, 2]
1733
+ [0, 1, 2]
1734
+ sage: pi = g.all_paths_iterator(starting_vertices=[0], use_multiedges=True, report_edges=True, labels=True)
1735
+ sage: for _ in range(6):
1736
+ ....: print(next(pi))
1737
+ [(0, 1, 'b')]
1738
+ [(0, 1, 'a')]
1739
+ [(0, 1, 'b'), (1, 2, 'd')]
1740
+ [(0, 1, 'b'), (1, 2, 'c')]
1741
+ [(0, 1, 'a'), (1, 2, 'd')]
1742
+ [(0, 1, 'a'), (1, 2, 'c')]
1743
+ sage: list(g.all_paths_iterator(starting_vertices=[0, 1], ending_vertices=[2], use_multiedges=False, report_edges=True, labels=True, simple=True))
1744
+ [[(1, 2, 'd')], [(0, 1, 'b'), (1, 2, 'd')]]
1745
+ sage: list(g.all_paths_iterator(starting_vertices=[0, 1], ending_vertices=[2], use_multiedges=False, report_edges=False, labels=True))
1746
+ [[1, 2], [0, 1, 2]]
1747
+ sage: list(g.all_paths_iterator(use_multiedges=True, report_edges=False, labels=True, max_length=1))
1748
+ [[1, 2], [1, 2], [0, 1], [0, 1]]
1749
+ sage: list(g.all_paths_iterator(use_multiedges=True, report_edges=True, labels=True, max_length=1))
1750
+ [[(1, 2, 'd')], [(1, 2, 'c')], [(0, 1, 'b')], [(0, 1, 'a')]]
1751
+
1752
+ sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
1753
+ sage: pi = g.all_paths_iterator()
1754
+ sage: [len(next(pi)) - 1 for _ in range(7)]
1755
+ [1, 1, 1, 1, 1, 2, 2]
1756
+
1757
+ It is possible to precise the allowed starting and/or ending vertices::
1758
+
1759
+ sage: pi = g.all_paths_iterator(starting_vertices=['a'])
1760
+ sage: [len(next(pi)) - 1 for _ in range(5)]
1761
+ [1, 1, 2, 2, 2]
1762
+ sage: pi = g.all_paths_iterator(starting_vertices=['a'], ending_vertices=['b'])
1763
+ sage: for _ in range(5):
1764
+ ....: print(next(pi))
1765
+ ['a', 'b']
1766
+ ['a', 'a', 'b']
1767
+ ['a', 'a', 'a', 'b']
1768
+ ['a', 'a', 'a', 'a', 'b']
1769
+ ['a', 'a', 'a', 'a', 'a', 'b']
1770
+
1771
+ One may prefer to enumerate only simple paths (see
1772
+ :meth:`all_simple_paths`)::
1773
+
1774
+ sage: pi = g.all_paths_iterator(simple=True)
1775
+ sage: sorted(list(pi), key=lambda x:(len(x), x))
1776
+ [['a', 'a'], ['a', 'b'], ['b', 'c'], ['c', 'd'], ['d', 'c'],
1777
+ ['a', 'b', 'c'], ['b', 'c', 'd'], ['c', 'd', 'c'], ['d', 'c', 'd'],
1778
+ ['a', 'b', 'c', 'd']]
1779
+ sage: pi = g.all_paths_iterator(simple=True)
1780
+ sage: [len(p) - 1 for p in pi]
1781
+ [1, 1, 1, 1, 1, 2, 2, 2, 2, 3]
1782
+
1783
+ Or simply bound the length of the enumerated paths::
1784
+
1785
+ sage: pi = g.all_paths_iterator(starting_vertices=['a'], ending_vertices=['b', 'c'], max_length=6)
1786
+ sage: sorted(list(pi), key=lambda x:(len(x), x))
1787
+ [['a', 'b'], ['a', 'a', 'b'], ['a', 'b', 'c'], ['a', 'a', 'a', 'b'],
1788
+ ['a', 'a', 'b', 'c'], ['a', 'a', 'a', 'a', 'b'],
1789
+ ['a', 'a', 'a', 'b', 'c'], ['a', 'b', 'c', 'd', 'c'],
1790
+ ['a', 'a', 'a', 'a', 'a', 'b'], ['a', 'a', 'a', 'a', 'b', 'c'],
1791
+ ['a', 'a', 'b', 'c', 'd', 'c'],
1792
+ ['a', 'a', 'a', 'a', 'a', 'a', 'b'],
1793
+ ['a', 'a', 'a', 'a', 'a', 'b', 'c'],
1794
+ ['a', 'a', 'a', 'b', 'c', 'd', 'c'],
1795
+ ['a', 'b', 'c', 'd', 'c', 'd', 'c']]
1796
+ sage: pi = g.all_paths_iterator(starting_vertices=['a'], ending_vertices=['b', 'c'], max_length=6)
1797
+ sage: [len(p) - 1 for p in pi]
1798
+ [1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6]
1799
+
1800
+ By default, empty paths are not enumerated, but it may be parametrized::
1801
+
1802
+ sage: pi = g.all_paths_iterator(simple=True, trivial=True)
1803
+ sage: sorted(list(pi), key=lambda x:(len(x), x))
1804
+ [['a'], ['b'], ['c'], ['d'], ['a', 'a'], ['a', 'b'], ['b', 'c'],
1805
+ ['c', 'd'], ['d', 'c'], ['a', 'b', 'c'], ['b', 'c', 'd'],
1806
+ ['c', 'd', 'c'], ['d', 'c', 'd'], ['a', 'b', 'c', 'd']]
1807
+ sage: pi = g.all_paths_iterator(simple=True, trivial=True)
1808
+ sage: [len(p) - 1 for p in pi]
1809
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3]
1810
+ sage: pi = g.all_paths_iterator(simple=True, trivial=False)
1811
+ sage: sorted(list(pi), key=lambda x:(len(x), x))
1812
+ [['a', 'a'], ['a', 'b'], ['b', 'c'], ['c', 'd'], ['d', 'c'],
1813
+ ['a', 'b', 'c'], ['b', 'c', 'd'], ['c', 'd', 'c'], ['d', 'c', 'd'],
1814
+ ['a', 'b', 'c', 'd']]
1815
+ sage: pi = g.all_paths_iterator(simple=True, trivial=False)
1816
+ sage: [len(p) - 1 for p in pi]
1817
+ [1, 1, 1, 1, 1, 2, 2, 2, 2, 3]
1818
+ """
1819
+ if starting_vertices is None:
1820
+ starting_vertices = self
1821
+ cdef dict data = {}
1822
+ cdef dict vertex_iterators
1823
+ cdef list path
1824
+
1825
+ if report_edges and labels:
1826
+ if use_multiedges:
1827
+ for e in self.edge_iterator():
1828
+ if (e[0], e[1]) in data:
1829
+ data[(e[0], e[1])].append(e)
1830
+ else:
1831
+ data[(e[0], e[1])] = [e]
1832
+ else:
1833
+ for e in self.edge_iterator():
1834
+ if (e[0], e[1]) not in data:
1835
+ data[(e[0], e[1])] = [e]
1836
+ elif use_multiedges and self.has_multiple_edges():
1837
+ from collections import Counter
1838
+ edge_multiplicity = Counter(self.edge_iterator(labels=False))
1839
+ data = dict(edge_multiplicity)
1840
+
1841
+ # We create one paths iterator per vertex
1842
+ # This is necessary if we want to iterate over paths
1843
+ # with increasing length
1844
+ vertex_iterators = {v: self._all_paths_iterator(v, ending_vertices=ending_vertices,
1845
+ simple=simple, max_length=max_length,
1846
+ trivial=trivial, use_multiedges=use_multiedges,
1847
+ report_edges=report_edges, labels=labels, data=data)
1848
+ for v in starting_vertices}
1849
+
1850
+ cdef priority_queue[pair[int, int]] pq
1851
+ cdef int idx = 0
1852
+ cdef dict idx_to_path = {}
1853
+ for vi in vertex_iterators.values():
1854
+ try:
1855
+ path = next(vi)
1856
+ idx_to_path[idx] = path
1857
+ pq.push((-len(path), idx))
1858
+ idx = idx + 1
1859
+ except StopIteration:
1860
+ pass
1861
+ # Since we always extract a shortest path, using a heap
1862
+ # can speed up the algorithm
1863
+ while not pq.empty():
1864
+ # We choose the shortest available path
1865
+ _, shortest_path_idx = pq.top()
1866
+ pq.pop()
1867
+ prev_path = idx_to_path[shortest_path_idx]
1868
+ yield prev_path
1869
+ del idx_to_path[shortest_path_idx]
1870
+ # We update the path iterator to its next available path if it exists
1871
+ try:
1872
+ if report_edges:
1873
+ path = next(vertex_iterators[prev_path[0][0]])
1874
+ else:
1875
+ path = next(vertex_iterators[prev_path[0]])
1876
+ idx_to_path[idx] = path
1877
+ pq.push((-len(path), idx))
1878
+ idx = idx + 1
1879
+ except StopIteration:
1880
+ pass
1881
+
1882
+
1883
+ def all_simple_paths(self, starting_vertices=None, ending_vertices=None,
1884
+ max_length=None, trivial=False, use_multiedges=False,
1885
+ report_edges=False, labels=False):
1886
+ r"""
1887
+ Return a list of all the simple paths of ``self`` starting with one of
1888
+ the given vertices.
1889
+
1890
+ Simple paths are paths in which no two arcs share a head or share a
1891
+ tail, i.e. every vertex in the path is entered at most once and exited
1892
+ at most once.
1893
+
1894
+ INPUT:
1895
+
1896
+ - ``starting_vertices`` -- list (default: ``None``); vertices from which
1897
+ the paths must start. If ``None``, then all vertices of the graph can
1898
+ be starting points.
1899
+
1900
+ - ``ending_vertices`` -- iterable (default: ``None``); allowed ending
1901
+ vertices of the paths. If ``None``, then all vertices are allowed.
1902
+
1903
+ - ``max_length`` -- nonnegative integer (default: ``None``); the
1904
+ maximum length of the enumerated paths. If set to ``None``, then all
1905
+ lengths are allowed.
1906
+
1907
+ - ``trivial`` -- boolean (default: ``False``); if set to ``True``, then
1908
+ the empty paths are also enumerated
1909
+
1910
+ - ``use_multiedges`` -- boolean (default: ``False``); this parameter is
1911
+ used only if the graph has multiple edges
1912
+
1913
+ - If ``False``, the graph is considered as simple and an edge label
1914
+ is arbitrarily selected for each edge as in
1915
+ :meth:`sage.graphs.generic_graph.GenericGraph.to_simple` if
1916
+ ``report_edges`` is ``True``
1917
+
1918
+ - If ``True``, a path will be reported as many times as the edges
1919
+ multiplicities along that path (when ``report_edges = False`` or
1920
+ ``labels = False``), or with all possible combinations of edge
1921
+ labels (when ``report_edges = True`` and ``labels = True``)
1922
+
1923
+ - ``report_edges`` -- boolean (default: ``False``); whether to report
1924
+ paths as list of vertices (default) or list of edges, if ``False``
1925
+ then ``labels`` parameter is ignored
1926
+
1927
+ - ``labels`` -- boolean (default: ``False``); if ``False``, each edge
1928
+ is simply a pair ``(u, v)`` of vertices. Otherwise a list of edges
1929
+ along with its edge labels are used to represent the path.
1930
+
1931
+ OUTPUT: list
1932
+
1933
+ .. NOTE::
1934
+
1935
+ Although the number of simple paths of a finite graph is always
1936
+ finite, computing all its paths may take a very long time.
1937
+
1938
+ EXAMPLES::
1939
+
1940
+ sage: G = graphs.CompleteGraph(4)
1941
+ sage: G.all_simple_paths([1], [3])
1942
+ [[1, 3], [1, 0, 3], [1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3]]
1943
+ sage: list(G.shortest_simple_paths(1, 3))
1944
+ [[1, 3], [1, 0, 3], [1, 2, 3], [1, 2, 0, 3], [1, 0, 2, 3]]
1945
+ sage: G.all_simple_paths([0, 1], [2, 3])
1946
+ [[1, 2],
1947
+ [1, 3],
1948
+ [0, 2],
1949
+ [0, 3],
1950
+ [0, 1, 2],
1951
+ [0, 1, 3],
1952
+ [0, 2, 3],
1953
+ [0, 3, 2],
1954
+ [1, 0, 2],
1955
+ [1, 0, 3],
1956
+ [1, 2, 3],
1957
+ [1, 3, 2],
1958
+ [1, 0, 2, 3],
1959
+ [1, 0, 3, 2],
1960
+ [1, 2, 0, 3],
1961
+ [1, 3, 0, 2],
1962
+ [0, 1, 2, 3],
1963
+ [0, 1, 3, 2],
1964
+ [0, 2, 1, 3],
1965
+ [0, 3, 1, 2]]
1966
+
1967
+ sage: g = DiGraph({0: [0, 1], 1: [2], 2: [3], 3: [2]}, loops=True)
1968
+ sage: g.all_simple_paths()
1969
+ [[3, 2],
1970
+ [2, 3],
1971
+ [1, 2],
1972
+ [0, 0],
1973
+ [0, 1],
1974
+ [0, 1, 2],
1975
+ [1, 2, 3],
1976
+ [2, 3, 2],
1977
+ [3, 2, 3],
1978
+ [0, 1, 2, 3]]
1979
+
1980
+ sage: g = DiGraph([(0, 1, 'a'), (0, 1, 'b'), (1, 2,'c'), (1, 2,'d')], multiedges=True)
1981
+ sage: g.all_simple_paths(starting_vertices=[0], ending_vertices=[2], use_multiedges=False)
1982
+ [[0, 1, 2]]
1983
+ sage: g.all_simple_paths(starting_vertices=[0], ending_vertices=[2], use_multiedges=True)
1984
+ [[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]]
1985
+ sage: g.all_simple_paths(starting_vertices=[0], ending_vertices=[2], use_multiedges=True, report_edges=True)
1986
+ [[(0, 1), (1, 2)], [(0, 1), (1, 2)], [(0, 1), (1, 2)], [(0, 1), (1, 2)]]
1987
+ sage: g.all_simple_paths(starting_vertices=[0], ending_vertices=[2], use_multiedges=True, report_edges=True, labels=True)
1988
+ [[(0, 1, 'b'), (1, 2, 'd')],
1989
+ [(0, 1, 'b'), (1, 2, 'c')],
1990
+ [(0, 1, 'a'), (1, 2, 'd')],
1991
+ [(0, 1, 'a'), (1, 2, 'c')]]
1992
+ sage: g.all_simple_paths(starting_vertices=[0, 1], ending_vertices=[2], use_multiedges=False, report_edges=True, labels=True)
1993
+ [[(1, 2, 'd')], [(0, 1, 'b'), (1, 2, 'd')]]
1994
+ sage: g.all_simple_paths(starting_vertices=[0, 1], ending_vertices=[2], use_multiedges=False, report_edges=False, labels=True)
1995
+ [[1, 2], [0, 1, 2]]
1996
+ sage: g.all_simple_paths(use_multiedges=True, report_edges=False, labels=True)
1997
+ [[1, 2], [1, 2], [0, 1], [0, 1], [0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]]
1998
+ sage: g.all_simple_paths(starting_vertices=[0, 1], ending_vertices=[2], use_multiedges=False, report_edges=True, labels=True, trivial=True)
1999
+ [[(1, 2, 'd')], [(0, 1, 'b'), (1, 2, 'd')]]
2000
+
2001
+ One may compute all paths having specific starting and/or ending
2002
+ vertices::
2003
+
2004
+ sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
2005
+ sage: g.all_simple_paths(starting_vertices=['a'])
2006
+ [['a', 'a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'd']]
2007
+ sage: g.all_simple_paths(starting_vertices=['a'], ending_vertices=['c'])
2008
+ [['a', 'b', 'c']]
2009
+ sage: g.all_simple_paths(starting_vertices=['a'], ending_vertices=['b', 'c'])
2010
+ [['a', 'b'], ['a', 'b', 'c']]
2011
+
2012
+ It is also possible to bound the length of the paths::
2013
+
2014
+ sage: g = DiGraph({0: [0, 1], 1: [2], 2: [3], 3: [2]}, loops=True)
2015
+ sage: g.all_simple_paths(max_length=2)
2016
+ [[3, 2],
2017
+ [2, 3],
2018
+ [1, 2],
2019
+ [0, 0],
2020
+ [0, 1],
2021
+ [0, 1, 2],
2022
+ [1, 2, 3],
2023
+ [2, 3, 2],
2024
+ [3, 2, 3]]
2025
+
2026
+ By default, empty paths are not enumerated, but this can be
2027
+ parametrized::
2028
+
2029
+ sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True)
2030
+ sage: g.all_simple_paths(starting_vertices=['a'], trivial=True)
2031
+ [['a'], ['a', 'a'], ['a', 'b'], ['a', 'b', 'c'],
2032
+ ['a', 'b', 'c', 'd']]
2033
+ sage: g.all_simple_paths(starting_vertices=['a'], trivial=False)
2034
+ [['a', 'a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'd']]
2035
+ """
2036
+ return list(self.all_paths_iterator(starting_vertices=starting_vertices,
2037
+ ending_vertices=ending_vertices,
2038
+ simple=True, max_length=max_length,
2039
+ trivial=trivial, use_multiedges=use_multiedges,
2040
+ report_edges=report_edges, labels=labels))