Thanks to visit codestin.com
Credit goes to docs.rs

simple_graph_algorithms/
algorithms.rs

1//! # Algorithms implemented
2//! 
3//! ## Pathfinding algorithms
4//! 
5//! - [Bellman-Ford algorithm](fn.bellman_ford.html)
6//! - [Dijkstra's algorithm](fn.dijkstra.html)
7//! 
8//! # Performance
9//! 
10//! All algorithms where measured using [criterion](https://github.com/bheisler/criterion.rs)
11//! on a graph with 10,000 nodes and 39,600 edges. The algorithms where run 100 times on the test graph,
12//! the mean time is listed in the table below. The tests where performed on a `Ryzen 5 7600x` processor.
13//! 
14//! | Algorithm | Mean time per run |
15//! | - | - |
16//! | Bellman-Ford | 2.1883 s |
17//! | Dijkstra | 52.3155 ms |
18use std::{fmt::Display, hash::Hash, collections::{BinaryHeap, HashSet}, rc::Rc, cell::RefCell};
19
20#[cfg(feature = "serde")]
21use serde::{Serialize, Deserialize};
22
23use crate::{Graph, Node, ShortestPathTree};
24
25/// Calculates the shortest distance between one source node and all other nodes on the graph using 
26/// [Dijkstra's algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm).
27/// 
28/// Dijkstra's algorithm does not work properly on graphs with negative edge weights.
29/// Use [bellman_ford](fn.bellman_ford.html) instead if your graph contains those edges with negative weights.
30/// 
31/// This function takes a `graph` on which the algorithm should be run and the id of the start node.
32/// 
33/// Returns `Ok(ShortestPathTree)` when the algorithm was run on the graph or `Err(())` when the start node is missing from the graph.
34/// 
35/// Once the algorithm was run successfully, the resulting [ShortestPathTree](../struct.ShortestPathTree.html) can be used to receive the shortest distance and path to a node.
36/// 
37/// # Example
38/// ```rust
39/// use simple_graph_algorithms::{Graph, ShortestPathTree, algorithms::{dijkstra, RunAlgorithmError}};
40/// 
41/// # fn main() -> Result<(), RunAlgorithmError> {
42/// // Create new graph
43/// let mut graph: Graph<char> = Graph::new();
44/// 
45/// // Add nodes to graph
46/// graph.add_node('a');
47/// graph.add_node('b');
48/// graph.add_node('c');
49/// graph.add_node('d');
50/// 
51/// // Add edges between nodes
52/// graph.add_edge(3, &'a', &'b');
53/// graph.add_edge(4, &'a', &'c');
54/// graph.add_edge(5, &'b', &'a');
55/// graph.add_edge(9, &'c', &'a');
56/// graph.add_edge(4, &'c', &'d');
57/// 
58/// /// Run Dijkstra's algorithm to calculate the shortest path tree, starting at node a.
59/// let spt = dijkstra(&mut graph, &'a')?;
60/// 
61/// // Retrieve shortest distances
62/// assert_eq!(spt.shortest_distance(&'c'), Some(4));
63/// assert_eq!(spt.shortest_distance(&'d'), Some(8));
64/// 
65/// /// When run on a graph, that is missing the start node an Err is returned:
66/// assert_eq!(dijkstra(&mut graph, &'e'), Err(RunAlgorithmError::SourceNodeMissing));
67/// # Ok(())
68/// # }
69/// ```
70pub fn dijkstra<T: Display + Clone + Eq + Hash>(graph: &mut Graph<T>, source_node_id: &T) -> Result<ShortestPathTree<T>, RunAlgorithmError> {
71    graph.reset_nodes();
72    let source_node = match graph.nodes.get(source_node_id) {
73        Some(node) => node,
74        None => return Err(RunAlgorithmError::SourceNodeMissing),
75    };
76    source_node.borrow_mut().distance = 0;
77    let mut open_nodes: BinaryHeap<Rc<RefCell<Node<T>>>> = BinaryHeap::new();
78    let mut open_node_ids: HashSet<T> = HashSet::new();
79    let mut closed_node_ids: HashSet<T> = HashSet::new();
80    //let mut closed_nodes: Vec<Rc<RefCell<Node<T>>>> = Vec::new();
81    open_nodes.push(source_node.clone());
82
83    while !open_nodes.is_empty() {
84        let node = open_nodes.pop().unwrap();
85
86        for edge in &node.borrow().edges {
87            let target = &edge.target;
88            let edge_weight = edge.weight;
89            if !closed_node_ids.contains(&target.borrow().id) {
90                let new_distance = node.borrow().distance + edge_weight;
91                calc_min_distance(target, edge_weight, &node);
92                if new_distance < target.borrow().distance {
93                    target.borrow_mut().distance = new_distance;
94                }
95                if !open_node_ids.contains(&target.borrow().id) {
96                    open_nodes.push(target.clone());
97                    open_node_ids.insert(target.borrow().clone().id);
98                }
99            }
100        }
101        closed_node_ids.insert(node.borrow().clone().id);
102    }
103
104    Ok(ShortestPathTree::from_graph(graph, source_node_id))
105}
106
107fn calc_min_distance<T: Display + Eq + Clone>(node: &Rc<RefCell<Node<T>>>, weight: i32, source: &Rc<RefCell<Node<T>>>) {
108    let source_distance = source.borrow().distance;
109    if source_distance + weight < node.borrow().distance {
110        node.borrow_mut().distance = source_distance + weight;
111        let mut shortest_path = source.borrow().shortest_path.clone();
112        shortest_path.push(source.clone());
113        node.borrow_mut().shortest_path = shortest_path;
114    }
115}
116
117/// Calculates the shortest distance between one source node and all other nodes on the graph using 
118/// [Bellman-Ford algorithm](https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm).
119/// 
120/// This algorithm works on graphs with negative edge weights but is slower than [Dijkstra's algorithm](fn.dijkstra.html).
121/// Note that the graph may contain negative edges **but may not** contain negative circles.
122/// 
123/// This function takes a `graph` on which the algorithm should be run and the id of the start node.
124/// 
125/// Returns `Ok(ShortestPathTree)` when the algorithm was successfully run on the graph, `Err(AlgorithmError::StartNodeMissing)` when the start node is missing from the graph
126/// or `Err(AlgorithmError::NegativeCircleDetected)` when the graph contains a negative circle.
127/// 
128/// Once the algorithm was run successfully, the resulting [ShortestPathTree](../struct.ShortestPathTree.html) can be used to receive the shortest distance and path to a node.
129/// 
130/// # Examples
131/// ```rust
132/// use simple_graph_algorithms::{Graph, ShortestPathTree, algorithms::{bellman_ford, RunAlgorithmError}};
133/// 
134/// # fn main() -> Result<(), RunAlgorithmError> {
135/// // Create new graph
136/// let mut graph: Graph<char> = Graph::new();
137/// 
138/// // Add nodes to graph
139/// graph.add_node('a');
140/// graph.add_node('b');
141/// graph.add_node('c');
142/// graph.add_node('d');
143/// 
144/// // Add edges between nodes
145/// graph.add_edge(3, &'a', &'b');
146/// graph.add_edge(4, &'a', &'c');
147/// graph.add_edge(5, &'b', &'a');
148/// graph.add_edge(9, &'c', &'a');
149/// graph.add_edge(4, &'c', &'d');
150/// 
151/// // Run Bellman-Ford algorithm to calculate the shortest path tree, starting at node a.
152/// let spt = bellman_ford(&mut graph, &'a')?;
153/// 
154/// // Retrieve shortest distances
155/// assert_eq!(spt.shortest_distance(&'c'), Some(4));
156/// assert_eq!(spt.shortest_distance(&'d'), Some(8));
157/// 
158/// // When run on a graph, that is missing the start node an Err is returned:
159/// assert_eq!(bellman_ford(&mut graph, &'e'), Err(RunAlgorithmError::SourceNodeMissing));
160/// # Ok(())
161/// # }
162/// ```
163/// ## Graph contains negative circle
164/// ```
165/// use simple_graph_algorithms::{Graph, ShortestPathTree, algorithms::{bellman_ford, RunAlgorithmError}};
166/// 
167/// # fn main() -> Result<(), RunAlgorithmError> {
168/// // Create new graph
169/// let mut graph: Graph<char> = Graph::new();
170/// 
171/// // Add nodes to graph
172/// graph.add_node('a');
173/// graph.add_node('b');
174/// graph.add_node('c');
175/// graph.add_node('d');
176/// 
177/// // Add edges between nodes
178/// graph.add_edge(-5, &'a', &'b');
179/// graph.add_edge(4, &'a', &'c');
180/// graph.add_edge(5, &'b', &'a');
181/// graph.add_edge(-9, &'c', &'a');
182/// graph.add_edge(4, &'c', &'d');
183/// 
184/// // Run Bellman-Ford algorithm to calculate the shortest path tree, starting at node a.
185/// // Errors because the graph contains a negative circle.
186/// let spt = bellman_ford(&mut graph, &'a');
187/// assert_eq!(spt, Err(RunAlgorithmError::NegativeCircleDetected));
188/// # Ok(())
189/// # }
190/// ```
191pub fn bellman_ford<T: Display + Eq + Clone + Hash>(graph: &mut Graph<T>, source_node_id: &T) -> Result<ShortestPathTree<T>, RunAlgorithmError> {
192    graph.reset_nodes();
193
194    let source_node = match graph.nodes.get(source_node_id) {
195        Some(node) => node,
196        None => return Err(RunAlgorithmError::SourceNodeMissing),
197    };
198    source_node.borrow_mut().distance = 0;
199
200    let nodes_count = graph.size();
201
202    for _ in 0..nodes_count - 1 {
203        for node in graph.nodes.values() {
204            let node_ref = node.borrow();
205
206            if node_ref.distance == std::i32::MAX {
207                continue;
208            }
209
210            for edge in &node_ref.edges {
211                let target_node = Rc::clone(&edge.target);
212                let mut target_node_ref = target_node.borrow_mut();
213                let new_distance = node_ref.distance + edge.weight;
214
215                if new_distance < target_node_ref.distance {
216                    target_node_ref.distance = new_distance;
217                    let mut shortest_path = node_ref.shortest_path.clone();
218                    shortest_path.push(Rc::clone(node));
219                    target_node_ref.shortest_path = shortest_path;
220                }
221            }
222        }
223    }
224
225    // Check for negative cycles
226    for node in graph.nodes.values() {
227        let node_ref = node.borrow();
228
229        for edge in &node_ref.edges {
230            let target_node = Rc::clone(&edge.target);
231            let target_node_ref = target_node.borrow();
232
233            let new_distance = node_ref.distance + edge.weight;
234            if new_distance < target_node_ref.distance {
235                return Err(RunAlgorithmError::NegativeCircleDetected);
236            }
237        }
238    }
239
240    Ok(ShortestPathTree::from_graph(graph, source_node_id))
241}
242
243/// Errors that can occur when algorithms are run.
244#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone, Copy, Hash)]
245#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
246pub enum RunAlgorithmError {
247    /// Indicates that the source node is not contained within the graph.
248    SourceNodeMissing,
249    /// Indicates that the graph contains a negative circle, which causes the algorithm to not work properly.
250    NegativeCircleDetected,
251}
252
253#[cfg(test)]
254mod tests {
255    use crate::{Graph, algorithms::{dijkstra, bellman_ford, RunAlgorithmError}, graph_1, graph_2};
256
257    #[test]
258    fn dijkstra_test_1() {
259        let mut graph = graph_1();
260        let spt = dijkstra(&mut graph, &"New York");
261        assert!(spt.is_ok());
262        assert_eq!(spt.as_ref().unwrap().shortest_distance(&"Oslo"), Some(19));
263        assert_eq!(spt.as_ref().unwrap().shortest_distance(&"London"), None);
264        assert_eq!(spt.as_ref().unwrap().shortest_distance(&"Munich"), None);
265    }
266
267    #[test]
268    fn dijkstra_test_2() {
269        let mut graph = graph_2();
270        let spt = dijkstra(&mut graph, &'b');
271        assert_eq!(dijkstra(&mut graph, &'b').unwrap().shortest_distance(&'c'), Some(9));
272        assert_eq!(dijkstra(&mut graph, &'a').unwrap().shortest_distance(&'e'), None); 
273        assert_eq!(dijkstra(&mut graph, &'a').unwrap().shortest_distance(&'d'), Some(5));
274    }
275
276    #[test]
277    fn bellman_ford_test_1() {
278        let mut graph = graph_1();
279        let spt = bellman_ford(&mut graph, &"New York");
280        assert!(spt.is_ok());
281        assert_eq!(spt.as_ref().unwrap().shortest_distance(&"Oslo"), Some(19));
282        assert_eq!(spt.as_ref().unwrap().shortest_distance(&"London"), None);
283        assert_eq!(spt.as_ref().unwrap().shortest_distance(&"Munich"), None);
284    }
285
286    #[test]
287    fn bellman_ford_test_2() {
288        let mut graph = graph_2();
289        let spt = bellman_ford(&mut graph, &'b');
290        assert_eq!(dijkstra(&mut graph, &'b').unwrap().shortest_distance(&'c'), Some(9));
291        assert_eq!(dijkstra(&mut graph, &'a').unwrap().shortest_distance(&'e'), None); 
292        assert_eq!(dijkstra(&mut graph, &'a').unwrap().shortest_distance(&'d'), Some(5));
293    }
294
295    /// Returns a graph that contains negative edges
296    fn graph_with_negative_edges() -> Graph<char> {
297        let mut graph: Graph<char> = Graph::new();
298        graph.add_node('a');
299        graph.add_node('b');
300        graph.add_node('c');
301        graph.add_node('d');
302        graph.add_double_edge(4, &'a', &'b');
303        graph.add_edge(2, &'a', &'c');
304        graph.add_edge(-1, &'c', &'a');
305        graph.add_edge(5, &'a', &'d');
306        graph.add_edge(-3, &'d', &'b');
307        graph.add_double_edge(2, &'b', &'c');
308        graph.add_edge(7, &'d', &'c');
309        graph
310    }
311
312    #[test]
313    fn bellman_ford_negative_edges_test() {
314        let mut graph = graph_with_negative_edges();
315        assert_eq!(bellman_ford(&mut graph, &'c').unwrap().shortest_distance(&'b'), Some(1));
316        assert_eq!(bellman_ford(&mut graph, &'d').unwrap().shortest_distance(&'b'), Some(-3)); 
317        assert_eq!(bellman_ford(&mut graph, &'d').unwrap().shortest_distance(&'a'), Some(-2));
318    }
319
320    #[test]
321    fn bellman_ford_node_shortest_path_test() {
322        let mut graph = graph_with_negative_edges();
323        let spt = bellman_ford(&mut graph, &'d');
324        assert!(spt.is_ok());
325        assert_eq!(spt.unwrap().shortest_path(&'a').unwrap().to_string(), "d -> b -> c -> a");
326    }
327
328    #[test]
329    fn bellman_ford_negative_circle_error_test() {
330        let mut graph = graph_with_negative_edges();
331        graph.add_double_edge(-10, &'a', &'d');
332        let spt = bellman_ford(&mut graph, &'a');
333        assert_eq!(spt, Err(RunAlgorithmError::NegativeCircleDetected));
334    }
335
336    #[test]
337    fn bellman_ford_negative_circle_error_test_2() {
338        let mut graph = graph_1();
339        graph.add_double_edge(-10, &"Oslo", &"London");
340        graph.add_double_edge(-10, &"New York", &"London");
341        let spt = bellman_ford(&mut graph, &"Berlin");
342        assert_eq!(spt, Err(RunAlgorithmError::NegativeCircleDetected));
343    }
344
345}