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.
- passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
- passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
- passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
- passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
- passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
- passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
- passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- sage/all__sagemath_graphs.py +39 -0
- sage/combinat/abstract_tree.py +2723 -0
- sage/combinat/all__sagemath_graphs.py +34 -0
- sage/combinat/binary_tree.py +5306 -0
- sage/combinat/cluster_algebra_quiver/all.py +22 -0
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
- sage/combinat/cluster_algebra_quiver/interact.py +124 -0
- sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
- sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
- sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
- sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
- sage/combinat/designs/MOLS_handbook_data.py +570 -0
- sage/combinat/designs/all.py +58 -0
- sage/combinat/designs/bibd.py +1655 -0
- sage/combinat/designs/block_design.py +1071 -0
- sage/combinat/designs/covering_array.py +269 -0
- sage/combinat/designs/covering_design.py +530 -0
- sage/combinat/designs/database.py +5615 -0
- sage/combinat/designs/design_catalog.py +122 -0
- sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/designs_pyx.pxd +21 -0
- sage/combinat/designs/designs_pyx.pyx +993 -0
- sage/combinat/designs/difference_family.py +3951 -0
- sage/combinat/designs/difference_matrices.py +279 -0
- sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
- sage/combinat/designs/ext_rep.py +1064 -0
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
- sage/combinat/designs/group_divisible_designs.py +361 -0
- sage/combinat/designs/incidence_structures.py +2357 -0
- sage/combinat/designs/latin_squares.py +581 -0
- sage/combinat/designs/orthogonal_arrays.py +2244 -0
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
- sage/combinat/designs/resolvable_bibd.py +815 -0
- sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
- sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +530 -0
- sage/combinat/designs/twographs.py +306 -0
- sage/combinat/finite_state_machine.py +14874 -0
- sage/combinat/finite_state_machine_generators.py +2006 -0
- sage/combinat/graph_path.py +448 -0
- sage/combinat/interval_posets.py +3908 -0
- sage/combinat/nu_tamari_lattice.py +269 -0
- sage/combinat/ordered_tree.py +1446 -0
- sage/combinat/posets/all.py +46 -0
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/cartesian_product.py +493 -0
- sage/combinat/posets/d_complete.py +182 -0
- sage/combinat/posets/elements.py +273 -0
- sage/combinat/posets/forest.py +30 -0
- sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +174 -0
- sage/combinat/posets/hasse_diagram.py +3672 -0
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +794 -0
- sage/combinat/posets/lattices.py +5117 -0
- sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +292 -0
- sage/combinat/posets/linear_extensions.py +1037 -0
- sage/combinat/posets/mobile.py +275 -0
- sage/combinat/posets/moebius_algebra.py +776 -0
- sage/combinat/posets/poset_examples.py +2178 -0
- sage/combinat/posets/posets.py +9360 -0
- sage/combinat/rooted_tree.py +1070 -0
- sage/combinat/shard_order.py +239 -0
- sage/combinat/tamari_lattices.py +384 -0
- sage/combinat/yang_baxter_graph.py +923 -0
- sage/databases/all__sagemath_graphs.py +1 -0
- sage/databases/knotinfo_db.py +1231 -0
- sage/ext_data/all__sagemath_graphs.py +1 -0
- sage/ext_data/graphs/graph_plot_js.html +330 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/graphs/all.py +42 -0
- sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/asteroidal_triples.pyx +320 -0
- sage/graphs/base/all.py +1 -0
- sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/boost_graph.pxd +106 -0
- sage/graphs/base/boost_graph.pyx +3045 -0
- sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/c_graph.pxd +106 -0
- sage/graphs/base/c_graph.pyx +5096 -0
- sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/dense_graph.pxd +28 -0
- sage/graphs/base/dense_graph.pyx +801 -0
- sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/graph_backends.pxd +5 -0
- sage/graphs/base/graph_backends.pyx +797 -0
- sage/graphs/base/overview.py +85 -0
- sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/sparse_graph.pxd +90 -0
- sage/graphs/base/sparse_graph.pyx +1653 -0
- sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_dense_graph.pxd +5 -0
- sage/graphs/base/static_dense_graph.pyx +1032 -0
- sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_backend.pxd +27 -0
- sage/graphs/base/static_sparse_backend.pyx +1583 -0
- sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_graph.pxd +37 -0
- sage/graphs/base/static_sparse_graph.pyx +1375 -0
- sage/graphs/bipartite_graph.py +2732 -0
- sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/centrality.pyx +1038 -0
- sage/graphs/cographs.py +519 -0
- sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/comparability.pyx +851 -0
- sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/connectivity.pxd +157 -0
- sage/graphs/connectivity.pyx +4813 -0
- sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/convexity_properties.pxd +16 -0
- sage/graphs/convexity_properties.pyx +870 -0
- sage/graphs/digraph.py +4754 -0
- sage/graphs/digraph_generators.py +1993 -0
- sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/distances_all_pairs.pxd +12 -0
- sage/graphs/distances_all_pairs.pyx +2938 -0
- sage/graphs/domination.py +1363 -0
- sage/graphs/dot2tex_utils.py +100 -0
- sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/edge_connectivity.pyx +1215 -0
- sage/graphs/generators/all.py +1 -0
- sage/graphs/generators/basic.py +1769 -0
- sage/graphs/generators/chessboard.py +538 -0
- sage/graphs/generators/classical_geometries.py +1611 -0
- sage/graphs/generators/degree_sequence.py +235 -0
- sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generators/distance_regular.pyx +2846 -0
- sage/graphs/generators/families.py +4759 -0
- sage/graphs/generators/intersection.py +565 -0
- sage/graphs/generators/platonic_solids.py +262 -0
- sage/graphs/generators/random.py +2623 -0
- sage/graphs/generators/smallgraphs.py +5741 -0
- sage/graphs/generators/world_map.py +724 -0
- sage/graphs/generic_graph.py +26867 -0
- sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generic_graph_pyx.pxd +34 -0
- sage/graphs/generic_graph_pyx.pyx +1673 -0
- sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/genus.pyx +622 -0
- sage/graphs/graph.py +9645 -0
- sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_coloring.pyx +2284 -0
- sage/graphs/graph_database.py +1177 -0
- sage/graphs/graph_decompositions/all.py +1 -0
- sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
- sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +508 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
- sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
- sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
- sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
- sage/graphs/graph_editor.py +82 -0
- sage/graphs/graph_generators.py +3314 -0
- sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +95 -0
- sage/graphs/graph_input.py +812 -0
- sage/graphs/graph_latex.py +2064 -0
- sage/graphs/graph_list.py +410 -0
- sage/graphs/graph_plot.py +1756 -0
- sage/graphs/graph_plot_js.py +338 -0
- sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/hyperbolicity.pyx +1704 -0
- sage/graphs/hypergraph_generators.py +364 -0
- sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/independent_sets.pxd +13 -0
- sage/graphs/independent_sets.pyx +402 -0
- sage/graphs/isgci.py +1033 -0
- sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +489 -0
- sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/line_graph.pyx +743 -0
- sage/graphs/lovasz_theta.py +77 -0
- sage/graphs/matching.py +1633 -0
- sage/graphs/matching_covered_graph.py +3590 -0
- sage/graphs/orientations.py +1489 -0
- sage/graphs/partial_cube.py +459 -0
- sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/path_enumeration.pyx +2040 -0
- sage/graphs/pq_trees.py +1129 -0
- sage/graphs/print_graphs.py +201 -0
- sage/graphs/schnyder.py +865 -0
- sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/spanning_tree.pyx +1457 -0
- sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/strongly_regular_db.pyx +3340 -0
- sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/traversals.pxd +9 -0
- sage/graphs/traversals.pyx +1872 -0
- sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/trees.pxd +15 -0
- sage/graphs/trees.pyx +310 -0
- sage/graphs/tutte_polynomial.py +713 -0
- sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/views.pyx +794 -0
- sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/weakly_chordal.pyx +604 -0
- sage/groups/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
- sage/knots/all.py +6 -0
- sage/knots/free_knotinfo_monoid.py +507 -0
- sage/knots/gauss_code.py +291 -0
- sage/knots/knot.py +682 -0
- sage/knots/knot_table.py +284 -0
- sage/knots/knotinfo.py +2900 -0
- sage/knots/link.py +4715 -0
- sage/sandpiles/all.py +13 -0
- sage/sandpiles/examples.py +225 -0
- sage/sandpiles/sandpile.py +6365 -0
- sage/topology/all.py +22 -0
- sage/topology/cell_complex.py +1214 -0
- sage/topology/cubical_complex.py +1976 -0
- sage/topology/delta_complex.py +1806 -0
- sage/topology/filtered_simplicial_complex.py +744 -0
- sage/topology/moment_angle_complex.py +823 -0
- sage/topology/simplicial_complex.py +5160 -0
- sage/topology/simplicial_complex_catalog.py +92 -0
- sage/topology/simplicial_complex_examples.py +1680 -0
- sage/topology/simplicial_complex_homset.py +205 -0
- sage/topology/simplicial_complex_morphism.py +836 -0
- sage/topology/simplicial_set.py +4102 -0
- sage/topology/simplicial_set_catalog.py +55 -0
- sage/topology/simplicial_set_constructions.py +2954 -0
- sage/topology/simplicial_set_examples.py +865 -0
- 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))
|