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}