petgraph/algo/
spfa.rs

1//! Shortest Path Faster Algorithm.
2use super::{bellman_ford::Paths, BoundedMeasure, NegativeCycle};
3use crate::prelude::*;
4use crate::visit::{IntoEdges, IntoNodeIdentifiers, NodeIndexable};
5use alloc::{vec, vec::Vec};
6
7/// \[Generic\] Compute shortest paths from node `source` to all other.
8///
9/// Using the [Shortest Path Faster Algorithm][spfa].
10/// Compute shortest distances from node `source` to all other.
11///
12/// Compute shortest paths lengths in a weighted graph with positive or negative edge weights,
13/// but with no negative cycles.
14///
15/// ## Arguments
16/// * `graph`: weighted graph.
17/// * `source`: the source vertex, for which we calculate the lengths of the shortest paths to all the others.
18/// * `edge_cost`: closure that returns cost of a particular edge
19///
20/// ## Returns
21/// * `Err`: if graph contains negative cycle.
22/// * `Ok`: a pair of a vector of shortest distances and a vector
23///   of predecessors of each vertex along the shortest path.
24///
25/// [spfa]: https://www.geeksforgeeks.org/shortest-path-faster-algorithm/
26///
27/// # Example
28///
29/// ```
30/// use petgraph::Graph;
31/// use petgraph::algo::spfa;
32///
33/// let mut g = Graph::new();
34/// let a = g.add_node(()); // node with no weight
35/// let b = g.add_node(());
36/// let c = g.add_node(());
37/// let d = g.add_node(());
38/// let e = g.add_node(());
39/// let f = g.add_node(());
40/// g.extend_with_edges(&[
41///     (0, 1, 3.0),
42///     (0, 3, 2.0),
43///     (1, 2, 1.0),
44///     (1, 5, 7.0),
45///     (2, 4, -4.0),
46///     (3, 4, -1.0),
47///     (4, 5, 1.0),
48/// ]);
49///
50/// // Graph represented with the weight of each edge.
51/// //
52/// //     3       1
53/// // a ----- b ----- c
54/// // | 2     | 7     |
55/// // d       f       | -4
56/// // | -1    | 1     |
57/// // \------ e ------/
58///
59/// let path = spfa(&g, a, |edge| *edge.weight());
60/// assert!(path.is_ok());
61/// let path = path.unwrap();
62/// assert_eq!(path.distances, vec![0.0 ,     3.0,     4.0,    2.0,     0.0,     1.0]);
63/// assert_eq!(path.predecessors, vec![None, Some(a), Some(b), Some(a), Some(c), Some(e)]);
64///
65///
66/// // Negative cycle.
67/// let graph = Graph::<(), f32>::from_edges(&[
68///     (0, 1, 2.0), (1, 2, 2.0), (2, 0, -10.0)]);
69///
70/// assert!(spfa(&graph, 0.into(), |edge| *edge.weight()).is_err());
71/// ```
72pub fn spfa<G, F, K>(
73    graph: G,
74    source: G::NodeId,
75    mut edge_cost: F,
76) -> Result<Paths<G::NodeId, K>, NegativeCycle>
77where
78    G: IntoEdges + IntoNodeIdentifiers + NodeIndexable,
79    F: FnMut(G::EdgeRef) -> K,
80    K: BoundedMeasure + Copy,
81{
82    let ix = |i| graph.to_index(i);
83
84    let mut predecessors = vec![None; graph.node_bound()];
85    let mut distances = vec![K::max(); graph.node_bound()];
86    distances[ix(source)] = K::default();
87
88    // Queue of vertices capable of relaxation of the found shortest distances.
89    let mut queue: Vec<G::NodeId> = Vec::with_capacity(graph.node_bound());
90    let mut in_queue = vec![false; graph.node_bound()];
91
92    queue.push(source);
93    in_queue[ix(source)] = true;
94
95    // Keep track of how many times each vertex appeared
96    // in the queue to be able to detect a negative cycle.
97    let mut visits = vec![0; graph.node_bound()];
98
99    while let Some(i) = queue.pop() {
100        in_queue[ix(i)] = false;
101
102        // In a graph without a negative cycle, no vertex can improve
103        // the shortest distances by more than |V| times.
104        if visits[ix(i)] >= graph.node_bound() {
105            return Err(NegativeCycle(()));
106        }
107        visits[ix(i)] += 1;
108
109        for edge in graph.edges(i) {
110            let j = edge.target();
111            let w = edge_cost(edge);
112
113            let (dist, overflow) = distances[ix(i)].overflowing_add(w);
114
115            if !overflow && dist < distances[ix(j)] {
116                distances[ix(j)] = dist;
117                predecessors[ix(j)] = Some(i);
118
119                if !in_queue[ix(j)] {
120                    in_queue[ix(j)] = true;
121                    queue.push(j);
122                }
123            }
124        }
125    }
126
127    Ok(Paths {
128        distances,
129        predecessors,
130    })
131}