petgraph/algo/
bellman_ford.rs

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