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