petgraph/algo/
bellman_ford.rs

1//! Bellman-Ford algorithms.
2
3use crate::prelude::*;
4
5use crate::visit::{IntoEdges, IntoNodeIdentifiers, NodeCount, NodeIndexable, VisitMap, Visitable};
6
7use super::{FloatMeasure, NegativeCycle};
8
9#[derive(Debug, Clone)]
10pub struct Paths<NodeId, EdgeWeight> {
11    pub distances: Vec<EdgeWeight>,
12    pub predecessors: Vec<Option<NodeId>>,
13}
14
15/// \[Generic\] Compute shortest paths from node `source` to all other.
16///
17/// Using the [Bellman–Ford algorithm][bf]; negative edge costs are
18/// permitted, but the graph must not have a cycle of negative weights
19/// (in that case it will return an error).
20///
21/// On success, return one vec with path costs, and another one which points
22/// out the predecessor of a node along a shortest path. The vectors
23/// are indexed by the graph's node indices.
24///
25/// [bf]: https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
26///
27/// # Example
28/// ```rust
29/// use petgraph::Graph;
30/// use petgraph::algo::bellman_ford;
31/// use petgraph::prelude::*;
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, 2.0),
42///     (0, 3, 4.0),
43///     (1, 2, 1.0),
44///     (1, 5, 7.0),
45///     (2, 4, 5.0),
46///     (4, 5, 1.0),
47///     (3, 4, 1.0),
48/// ]);
49///
50/// // Graph represented with the weight of each edge
51/// //
52/// //     2       1
53/// // a ----- b ----- c
54/// // | 4     | 7     |
55/// // d       f       | 5
56/// // | 1     | 1     |
57/// // \------ e ------/
58///
59/// let path = bellman_ford(&g, a);
60/// assert!(path.is_ok());
61/// let path = path.unwrap();
62/// assert_eq!(path.distances, vec![    0.0,     2.0,    3.0,    4.0,     5.0,     6.0]);
63/// assert_eq!(path.predecessors, vec![None, Some(a),Some(b),Some(a), Some(d), Some(e)]);
64///
65/// // Node f (indice 5) can be reach from a with a path costing 6.
66/// // Predecessor of f is Some(e) which predecessor is Some(d) which predecessor is Some(a).
67/// // Thus the path from a to f is a <-> d <-> e <-> f
68///
69/// let graph_with_neg_cycle = Graph::<(), f32, Undirected>::from_edges(&[
70///         (0, 1, -2.0),
71///         (0, 3, -4.0),
72///         (1, 2, -1.0),
73///         (1, 5, -25.0),
74///         (2, 4, -5.0),
75///         (4, 5, -25.0),
76///         (3, 4, -1.0),
77/// ]);
78///
79/// assert!(bellman_ford(&graph_with_neg_cycle, NodeIndex::new(0)).is_err());
80/// ```
81pub fn bellman_ford<G>(
82    g: G,
83    source: G::NodeId,
84) -> Result<Paths<G::NodeId, G::EdgeWeight>, NegativeCycle>
85where
86    G: NodeCount + IntoNodeIdentifiers + IntoEdges + NodeIndexable,
87    G::EdgeWeight: FloatMeasure,
88{
89    let ix = |i| g.to_index(i);
90
91    // Step 1 and Step 2: initialize and relax
92    let (distances, predecessors) = bellman_ford_initialize_relax(g, source);
93
94    // Step 3: check for negative weight cycle
95    for i in g.node_identifiers() {
96        for edge in g.edges(i) {
97            let j = edge.target();
98            let w = *edge.weight();
99            if distances[ix(i)] + w < distances[ix(j)] {
100                return Err(NegativeCycle(()));
101            }
102        }
103    }
104
105    Ok(Paths {
106        distances,
107        predecessors,
108    })
109}
110
111/// \[Generic\] Find the path of a negative cycle reachable from node `source`.
112///
113/// Using the [find_negative_cycle][nc]; will search the Graph for negative cycles using
114/// [Bellman–Ford algorithm][bf]. If no negative cycle is found the function will return `None`.
115///
116/// If a negative cycle is found from source, return one vec with a path of `NodeId`s.
117///
118/// The time complexity of this algorithm should be the same as the Bellman-Ford (O(|V|·|E|)).
119///
120/// [nc]: https://blogs.asarkar.com/assets/docs/algorithms-curated/Negative-Weight%20Cycle%20Algorithms%20-%20Huang.pdf
121/// [bf]: https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
122///
123/// # Example
124/// ```rust
125/// use petgraph::Graph;
126/// use petgraph::algo::find_negative_cycle;
127/// use petgraph::prelude::*;
128///
129/// let graph_with_neg_cycle = Graph::<(), f32, Directed>::from_edges(&[
130///         (0, 1, 1.),
131///         (0, 2, 1.),
132///         (0, 3, 1.),
133///         (1, 3, 1.),
134///         (2, 1, 1.),
135///         (3, 2, -3.),
136/// ]);
137///
138/// let path = find_negative_cycle(&graph_with_neg_cycle, NodeIndex::new(0));
139/// assert_eq!(
140///     path,
141///     Some([NodeIndex::new(1), NodeIndex::new(3), NodeIndex::new(2)].to_vec())
142/// );
143/// ```
144pub fn find_negative_cycle<G>(g: G, source: G::NodeId) -> Option<Vec<G::NodeId>>
145where
146    G: NodeCount + IntoNodeIdentifiers + IntoEdges + NodeIndexable + Visitable,
147    G::EdgeWeight: FloatMeasure,
148{
149    let ix = |i| g.to_index(i);
150    let mut path = Vec::<G::NodeId>::new();
151
152    // Step 1: initialize and relax
153    let (distance, predecessor) = bellman_ford_initialize_relax(g, source);
154
155    // Step 2: Check for negative weight cycle
156    'outer: for i in g.node_identifiers() {
157        for edge in g.edges(i) {
158            let j = edge.target();
159            let w = *edge.weight();
160            if distance[ix(i)] + w < distance[ix(j)] {
161                // Step 3: negative cycle found
162                let start = j;
163                let mut node = start;
164                let mut visited = g.visit_map();
165                // Go backward in the predecessor chain
166                loop {
167                    let ancestor = match predecessor[ix(node)] {
168                        Some(predecessor_node) => predecessor_node,
169                        None => node, // no predecessor, self cycle
170                    };
171                    // We have only 2 ways to find the cycle and break the loop:
172                    // 1. start is reached
173                    if ancestor == start {
174                        path.push(ancestor);
175                        break;
176                    }
177                    // 2. some node was reached twice
178                    else if visited.is_visited(&ancestor) {
179                        // Drop any node in path that is before the first ancestor
180                        let pos = path
181                            .iter()
182                            .position(|&p| p == ancestor)
183                            .expect("we should always have a position");
184                        path = path[pos..path.len()].to_vec();
185
186                        break;
187                    }
188
189                    // None of the above, some middle path node
190                    path.push(ancestor);
191                    visited.visit(ancestor);
192                    node = ancestor;
193                }
194                // We are done here
195                break 'outer;
196            }
197        }
198    }
199    if !path.is_empty() {
200        // Users will probably need to follow the path of the negative cycle
201        // so it should be in the reverse order than it was found by the algorithm.
202        path.reverse();
203        Some(path)
204    } else {
205        None
206    }
207}
208
209// Perform Step 1 and Step 2 of the Bellman-Ford algorithm.
210#[inline(always)]
211fn bellman_ford_initialize_relax<G>(
212    g: G,
213    source: G::NodeId,
214) -> (Vec<G::EdgeWeight>, Vec<Option<G::NodeId>>)
215where
216    G: NodeCount + IntoNodeIdentifiers + IntoEdges + NodeIndexable,
217    G::EdgeWeight: FloatMeasure,
218{
219    // Step 1: initialize graph
220    let mut predecessor = vec![None; g.node_bound()];
221    let mut distance = vec![<_>::infinite(); g.node_bound()];
222    let ix = |i| g.to_index(i);
223    distance[ix(source)] = <_>::zero();
224
225    // Step 2: relax edges repeatedly
226    for _ in 1..g.node_count() {
227        let mut did_update = false;
228        for i in g.node_identifiers() {
229            for edge in g.edges(i) {
230                let j = edge.target();
231                let w = *edge.weight();
232                if distance[ix(i)] + w < distance[ix(j)] {
233                    distance[ix(j)] = distance[ix(i)] + w;
234                    predecessor[ix(j)] = Some(i);
235                    did_update = true;
236                }
237            }
238        }
239        if !did_update {
240            break;
241        }
242    }
243    (distance, predecessor)
244}