From 63e1e4ddd8dcc9d2647b6389fb1a099014536425 Mon Sep 17 00:00:00 2001 From: Bas Dirks Date: Mon, 1 Apr 2024 18:10:30 +0200 Subject: [PATCH] release: 0.3.2 - Add `algo::dijkstra::unweighted::shortest_paths. - Test `algo::dijkstra::unweighted::min_distances` with multiple source vertices. - Test `algo::dijkstra::weighted::min_distances` without sources. --- CHANGELOG.md | 8 + Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 5 +- src/algo/dijkstra/unweighted.rs | 239 ++++++++++++++++++++++----- src/algo/dijkstra/weighted.rs | 275 +++++++++++++++++--------------- 6 files changed, 357 insertions(+), 174 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c4c117..dafe8bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.3.2] - Unreleased + +### Added + +- Add `algo::dijkstra::unweighted::shortest_paths. +- Test `algo::dijkstra::unweighted::min_distances` with multiple source vertices. +- Test `algo::dijkstra::weighted::min_distances` without sources. + ## [0.3.1] - 2024-04-01 ### Changed diff --git a/Cargo.lock b/Cargo.lock index 00a5116..b7793fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,7 +89,7 @@ dependencies = [ [[package]] name = "graaf" -version = "0.3.1" +version = "0.3.2" dependencies = [ "divan", ] diff --git a/Cargo.toml b/Cargo.toml index 061b5d8..cd43398 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graaf" -version = "0.3.1" +version = "0.3.2" edition = "2021" license = "Apache-2.0" description = "Functions and types for working with graphs" diff --git a/README.md b/README.md index b4bfb14..e2a04fc 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,11 @@ This crate builds on `nightly`. This will change in version `1.0.0`. ## Algorithms -- `algo::dijkstra::unweighted::min_distances` calculates the minimum distances from multiple source vertices to all other vertices in an unweighted directed graph. -- `algo::dijkstra::weighted::min_distances` calculates the minimum distances from multiple source vertices to all other vertices in a weighted directed graph. - `algo::dijkstra::unweighted::min_distances_single_source` calculates the minimum distances from the source vertex to all other vertices in an unweighted graph. +- `algo::dijkstra::unweighted::min_distances` calculates the minimum distances from multiple source vertices to all other vertices in an unweighted directed graph. +- `algo::dijkstra::unweighted::shortest_paths` calculates the shortest paths from multiple source vertices to all other vertices in an unweighted graph. - `algo::dijkstra::weighted::min_distances_single_source` calculates the minimum distances from the source vertex to all other vertices in a weighted graph. +- `algo::dijkstra::weighted::min_distances` calculates the minimum distances from multiple source vertices to all other vertices in a weighted directed graph. ## Graph operation traits diff --git a/src/algo/dijkstra/unweighted.rs b/src/algo/dijkstra/unweighted.rs index df55eee..5064804 100644 --- a/src/algo/dijkstra/unweighted.rs +++ b/src/algo/dijkstra/unweighted.rs @@ -117,59 +117,216 @@ where dist } +/// Calculate the shortest paths from the source vertices to all other +/// vertices. +/// +/// # Arguments +/// +/// * `graph`: The graph. +/// * `step`: A function that calculates the accumulated weight. +/// * `pred`: The predecessors on the shortest paths from the source vertices. +/// * `dist`: The distances from the source vertices. +/// * `heap`: The vertices to visit. +/// +/// # Example +/// +/// ``` +/// extern crate alloc; +/// +/// use { +/// alloc::collections::BinaryHeap, +/// core::cmp::Reverse, +/// graaf::algo::dijkstra::unweighted::shortest_paths, +/// }; +/// +/// // ╭───╮ ╭───╮ +/// // │ 0 │ → │ 1 │ +/// // ╰───╯ ╰───╯ +/// // ↑ ↓ +/// // ╭───╮ ╭───╮ +/// // │ 3 │ │ 2 │ +/// // ╰───╯ ╰───╯ +/// +/// let graph: [Vec; 4] = [vec![1], vec![2], Vec::new(), vec![0]]; +/// let mut pred = [None; 4]; +/// let mut dist = [0, usize::MAX, usize::MAX, usize::MAX]; +/// let mut heap = BinaryHeap::from([(Reverse(0), 0)]); +/// +/// shortest_paths(&graph, |w| w + 1, &mut pred, &mut dist, &mut heap); +/// +/// assert_eq!(pred, [None, Some(0), Some(1), None]); +/// assert_eq!(dist, [0, 1, 2, usize::MAX]); +/// ``` +pub fn shortest_paths( + graph: &G, + step: fn(W) -> W, + pred: &mut [Option], + dist: &mut [W], + heap: &mut BinaryHeap<(Reverse, usize)>, +) where + G: IterEdges, + W: Copy + Ord, +{ + while let Some((Reverse(w), s)) = heap.pop() { + let w = step(w); + + for t in graph.iter_edges(s) { + if w >= dist[t] { + continue; + } + + dist[t] = w; + pred[t] = Some(s); + heap.push((Reverse(w), t)); + } + } +} + #[cfg(test)] mod test { use super::*; - #[test] - fn empty_graph() { - let graph: [Vec; 0] = []; - let mut dist = Vec::new(); - let mut heap = BinaryHeap::new(); - let () = min_distances(&graph, |w: usize| w + 1, &mut dist, &mut heap); + mod min_distances { + use super::*; - assert!(dist.is_empty()); - } + #[test] + fn no_source() { + let graph: [Vec; 0] = []; + let mut dist = Vec::new(); + let mut heap = BinaryHeap::new(); + let () = min_distances(&graph, |w: usize| w + 1, &mut dist, &mut heap); - #[test] - fn small_graph1() { - let graph: [Vec; 8] = [ - vec![1, 3], - vec![0, 2], - vec![1], - vec![0, 4, 7], - vec![3, 5, 6, 7], - vec![4, 6], - vec![4, 5, 7], - vec![3, 4, 6], - ]; - - for (i, &d) in [ - [0, 1, 2, 1, 2, 3, 3, 2], - [1, 0, 1, 2, 3, 4, 4, 3], - [2, 1, 0, 3, 4, 5, 5, 4], - [1, 2, 3, 0, 1, 2, 2, 1], - [2, 3, 4, 1, 0, 1, 1, 1], - [3, 4, 5, 2, 1, 0, 1, 2], - [3, 4, 5, 2, 1, 1, 0, 1], - [2, 3, 4, 1, 1, 2, 1, 0], - ] - .iter() - .enumerate() - { - assert_eq!(min_distances_single_source(&graph, i), d); + assert!(dist.is_empty()); + } + + #[test] + fn single_source() { + let graph: [Vec; 4] = [vec![1], vec![2], Vec::new(), vec![0]]; + let mut dist = [0, usize::MAX, usize::MAX, usize::MAX]; + let mut heap = BinaryHeap::from([(Reverse(0), 0)]); + let () = min_distances(&graph, |w| w + 1, &mut dist, &mut heap); + + assert_eq!(dist, [0, 1, 2, usize::MAX]); } } - #[test] - fn small_graph2() { - let graph: [Vec; 4] = [vec![1, 2], vec![0, 2, 3], vec![0, 1, 3], vec![1, 2]]; + mod min_distances_single_source { + use super::*; + + #[test] + fn graph_1() { + let graph: [Vec; 8] = [ + vec![1, 3], + vec![0, 2], + vec![1], + vec![0, 4, 7], + vec![3, 5, 6, 7], + vec![4, 6], + vec![4, 5, 7], + vec![3, 4, 6], + ]; - for (i, &d) in [[0, 1, 1, 2], [1, 0, 1, 1], [1, 1, 0, 1], [2, 1, 1, 0]] + for (i, &d) in [ + [0, 1, 2, 1, 2, 3, 3, 2], + [1, 0, 1, 2, 3, 4, 4, 3], + [2, 1, 0, 3, 4, 5, 5, 4], + [1, 2, 3, 0, 1, 2, 2, 1], + [2, 3, 4, 1, 0, 1, 1, 1], + [3, 4, 5, 2, 1, 0, 1, 2], + [3, 4, 5, 2, 1, 1, 0, 1], + [2, 3, 4, 1, 1, 2, 1, 0], + ] .iter() .enumerate() - { - assert_eq!(min_distances_single_source(&graph, i), d); + { + assert_eq!(min_distances_single_source(&graph, i), d); + } + } + + #[test] + fn graph_2() { + let graph: [Vec; 4] = [vec![1, 2], vec![0, 2, 3], vec![0, 1, 3], vec![1, 2]]; + + for (i, &d) in [[0, 1, 1, 2], [1, 0, 1, 1], [1, 1, 0, 1], [2, 1, 1, 0]] + .iter() + .enumerate() + { + assert_eq!(min_distances_single_source(&graph, i), d); + } + } + } + + mod shortest_paths { + use super::*; + + #[test] + fn no_source() { + let graph: [Vec; 0] = []; + let mut pred = Vec::new(); + let mut dist = Vec::new(); + let mut heap = BinaryHeap::new(); + let () = shortest_paths(&graph, |w: usize| w + 1, &mut pred, &mut dist, &mut heap); + + assert!(pred.is_empty()); + assert!(dist.is_empty()); + } + + #[test] + fn conquestcampaign() { + let graph: [Vec; 12] = [ + vec![1, 4], + vec![0, 2, 5], + vec![1, 3, 6], + vec![2, 7], + vec![0, 5, 8], + vec![1, 4, 6, 9], + vec![2, 5, 7, 10], + vec![3, 6, 11], + vec![4, 9], + vec![5, 8, 10], + vec![6, 9, 11], + vec![7, 10], + ]; + + let mut pred = [None; 12]; + + let mut dist = [ + usize::MAX, + usize::MAX, + usize::MAX, + usize::MAX, + usize::MAX, + 0, + usize::MAX, + usize::MAX, + usize::MAX, + usize::MAX, + usize::MAX, + 0, + ]; + + let mut heap = BinaryHeap::from([(Reverse(0), 5), (Reverse(0), 11)]); + let () = shortest_paths(&graph, |w| w + 1, &mut pred, &mut dist, &mut heap); + + assert_eq!(dist, [2, 1, 2, 2, 1, 0, 1, 1, 2, 1, 1, 0]); + + assert_eq!( + pred, + [ + Some(4), + Some(5), + Some(6), + Some(7), + Some(5), + None, + Some(5), + Some(11), + Some(9), + Some(5), + Some(11), + None + ] + ); } } } diff --git a/src/algo/dijkstra/weighted.rs b/src/algo/dijkstra/weighted.rs index bda9453..5ae69c3 100644 --- a/src/algo/dijkstra/weighted.rs +++ b/src/algo/dijkstra/weighted.rs @@ -40,7 +40,6 @@ use { /// // ╰───╯ ╰───╯ /// /// let graph: [Vec<(usize, usize)>; 4] = [vec![(1, 2)], vec![(2, 2)], Vec::new(), vec![(0, 2)]]; -/// /// let mut dist = [0, usize::MAX, usize::MAX, usize::MAX]; /// let mut heap = BinaryHeap::from([(Reverse(0), 0)]); /// @@ -121,157 +120,175 @@ mod test { crate::ops::AddWeightedEdge, }; - #[test] - fn shortestpath1() { - let graph: [Vec<(usize, usize)>; 4] = - [vec![(1, 2)], vec![(2, 2)], Vec::new(), vec![(0, 2)]]; + mod min_distances { + use super::*; + + #[test] + fn no_source() { + let graph: [Vec<(usize, usize)>; 0] = []; + let mut dist = Vec::new(); + let mut heap = BinaryHeap::new(); + let () = min_distances(&graph, |acc, w| acc + w, &mut dist, &mut heap); - for (i, &d) in [ - [0, 2, 4, usize::MAX], - [usize::MAX, 0, 2, usize::MAX], - [usize::MAX, usize::MAX, 0, usize::MAX], - [2, 4, 6, 0], - ] - .iter() - .enumerate() - { - assert_eq!(min_distances_single_source(&graph, i), d); + assert!(dist.is_empty()); } } - #[test] - fn crosscountry() { - let graph: [Vec<(usize, usize)>; 4] = [ - vec![(1, 1), (2, 3), (3, 14)], - vec![(0, 2), (2, 4), (3, 22)], - vec![(0, 3), (1, 10), (3, 7)], - vec![(0, 13), (1, 8), (2, 2)], - ]; + mod min_distances_single_source { + use super::*; - for (i, &d) in [[0, 1, 3, 10], [2, 0, 4, 11], [3, 4, 0, 7], [5, 6, 2, 0]] + #[test] + fn shortestpath1() { + let graph: [Vec<(usize, usize)>; 4] = + [vec![(1, 2)], vec![(2, 2)], Vec::new(), vec![(0, 2)]]; + + for (i, &d) in [ + [0, 2, 4, usize::MAX], + [usize::MAX, 0, 2, usize::MAX], + [usize::MAX, usize::MAX, 0, usize::MAX], + [2, 4, 6, 0], + ] .iter() .enumerate() - { - assert_eq!(min_distances_single_source(&graph, i), d); + { + assert_eq!(min_distances_single_source(&graph, i), d); + } } - } - #[test] - fn small_graph1() { - let graph: [Vec<(usize, usize)>; 9] = [ - vec![(1, 4), (7, 8)], - vec![(0, 4), (2, 8), (7, 11)], - vec![(1, 8), (3, 7), (5, 4), (8, 2)], - vec![(2, 7), (4, 9), (5, 14)], - vec![(3, 9), (5, 10)], - vec![(2, 4), (3, 14), (4, 10), (6, 2)], - vec![(5, 2), (7, 1), (8, 6)], - vec![(0, 8), (1, 11), (6, 1), (8, 7)], - vec![(2, 2), (6, 6), (7, 7)], - ]; + #[test] + fn crosscountry() { + let graph: [Vec<(usize, usize)>; 4] = [ + vec![(1, 1), (2, 3), (3, 14)], + vec![(0, 2), (2, 4), (3, 22)], + vec![(0, 3), (1, 10), (3, 7)], + vec![(0, 13), (1, 8), (2, 2)], + ]; - for (i, &d) in [ - [0, 4, 12, 19, 21, 11, 9, 8, 14], - [4, 0, 8, 15, 22, 12, 12, 11, 10], - [12, 8, 0, 7, 14, 4, 6, 7, 2], - [19, 15, 7, 0, 9, 11, 13, 14, 9], - [21, 22, 14, 9, 0, 10, 12, 13, 16], - [11, 12, 4, 11, 10, 0, 2, 3, 6], - [9, 12, 6, 13, 12, 2, 0, 1, 6], - [8, 11, 7, 14, 13, 3, 1, 0, 7], - [14, 10, 2, 9, 16, 6, 6, 7, 0], - ] - .iter() - .enumerate() - { - assert_eq!(min_distances_single_source(&graph, i), d); + for (i, &d) in [[0, 1, 3, 10], [2, 0, 4, 11], [3, 4, 0, 7], [5, 6, 2, 0]] + .iter() + .enumerate() + { + assert_eq!(min_distances_single_source(&graph, i), d); + } } - } - #[test] - fn bryr1() { - let mut graph = vec![Vec::new(); 3]; + #[test] + fn small_graph() { + let graph: [Vec<(usize, usize)>; 9] = [ + vec![(1, 4), (7, 8)], + vec![(0, 4), (2, 8), (7, 11)], + vec![(1, 8), (3, 7), (5, 4), (8, 2)], + vec![(2, 7), (4, 9), (5, 14)], + vec![(3, 9), (5, 10)], + vec![(2, 4), (3, 14), (4, 10), (6, 2)], + vec![(5, 2), (7, 1), (8, 6)], + vec![(0, 8), (1, 11), (6, 1), (8, 7)], + vec![(2, 2), (6, 6), (7, 7)], + ]; - for (s, t, w) in [(2, 0, 1), (0, 1, 1), (1, 2, 1)] { - graph.add_weighted_edge(s, t, w); - graph.add_weighted_edge(t, s, w); + for (i, &d) in [ + [0, 4, 12, 19, 21, 11, 9, 8, 14], + [4, 0, 8, 15, 22, 12, 12, 11, 10], + [12, 8, 0, 7, 14, 4, 6, 7, 2], + [19, 15, 7, 0, 9, 11, 13, 14, 9], + [21, 22, 14, 9, 0, 10, 12, 13, 16], + [11, 12, 4, 11, 10, 0, 2, 3, 6], + [9, 12, 6, 13, 12, 2, 0, 1, 6], + [8, 11, 7, 14, 13, 3, 1, 0, 7], + [14, 10, 2, 9, 16, 6, 6, 7, 0], + ] + .iter() + .enumerate() + { + assert_eq!(min_distances_single_source(&graph, i), d); + } } - for (s, dist) in [[0, 1, 1], [1, 0, 1], [1, 1, 0]].iter().enumerate() { - assert_eq!(min_distances_single_source(&graph, s), dist); - } - } + #[test] + fn bryr_1() { + let mut graph = vec![Vec::new(); 3]; - #[test] - fn bryr2() { - let mut graph = vec![Vec::new(); 6]; + for (s, t, w) in [(2, 0, 1), (0, 1, 1), (1, 2, 1)] { + graph.add_weighted_edge(s, t, w); + graph.add_weighted_edge(t, s, w); + } - for (s, t, w) in [ - (4, 5, 1), - (4, 3, 1), - (1, 0, 1), - (1, 2, 1), - (3, 2, 1), - (0, 3, 1), - ] { - graph.add_weighted_edge(s, t, w); - graph.add_weighted_edge(t, s, w); + for (s, dist) in [[0, 1, 1], [1, 0, 1], [1, 1, 0]].iter().enumerate() { + assert_eq!(min_distances_single_source(&graph, s), dist); + } } - for (s, dist) in [ - [0, 1, 2, 1, 2, 3], - [1, 0, 1, 2, 3, 4], - [2, 1, 0, 1, 2, 3], - [1, 2, 1, 0, 1, 2], - [2, 3, 2, 1, 0, 1], - [3, 4, 3, 2, 1, 0], - ] - .iter() - .enumerate() - { - assert_eq!(min_distances_single_source(&graph, s), dist); - } - } + #[test] + fn bryr_2() { + let mut graph = vec![Vec::new(); 6]; - #[test] - fn bryr3() { - let mut graph = vec![Vec::new(); 10]; + for (s, t, w) in [ + (4, 5, 1), + (4, 3, 1), + (1, 0, 1), + (1, 2, 1), + (3, 2, 1), + (0, 3, 1), + ] { + graph.add_weighted_edge(s, t, w); + graph.add_weighted_edge(t, s, w); + } - for (s, t, w) in [ - (6, 2, 0), - (6, 9, 1), - (7, 1, 0), - (9, 1, 1), - (3, 5, 0), - (3, 0, 0), - (8, 4, 1), - (5, 8, 0), - (6, 5, 1), - (2, 9, 0), - (3, 4, 0), - (4, 6, 1), - (3, 7, 0), - ] { - graph.add_weighted_edge(s, t, w); - graph.add_weighted_edge(t, s, w); + for (s, dist) in [ + [0, 1, 2, 1, 2, 3], + [1, 0, 1, 2, 3, 4], + [2, 1, 0, 1, 2, 3], + [1, 2, 1, 0, 1, 2], + [2, 3, 2, 1, 0, 1], + [3, 4, 3, 2, 1, 0], + ] + .iter() + .enumerate() + { + assert_eq!(min_distances_single_source(&graph, s), dist); + } } - for (s, dist) in [ - [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], - [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], - [1, 1, 0, 1, 1, 1, 0, 1, 1, 0], - [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], - [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], - [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], - [1, 1, 0, 1, 1, 1, 0, 1, 1, 0], - [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], - [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], - [1, 1, 0, 1, 1, 1, 0, 1, 1, 0], - ] - .iter() - .enumerate() - { - assert_eq!(min_distances_single_source(&graph, s), dist); + #[test] + fn bryr_3() { + let mut graph = vec![Vec::new(); 10]; + + for (s, t, w) in [ + (6, 2, 0), + (6, 9, 1), + (7, 1, 0), + (9, 1, 1), + (3, 5, 0), + (3, 0, 0), + (8, 4, 1), + (5, 8, 0), + (6, 5, 1), + (2, 9, 0), + (3, 4, 0), + (4, 6, 1), + (3, 7, 0), + ] { + graph.add_weighted_edge(s, t, w); + graph.add_weighted_edge(t, s, w); + } + + for (s, dist) in [ + [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], + [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], + [1, 1, 0, 1, 1, 1, 0, 1, 1, 0], + [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], + [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], + [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], + [1, 1, 0, 1, 1, 1, 0, 1, 1, 0], + [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], + [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], + [1, 1, 0, 1, 1, 1, 0, 1, 1, 0], + ] + .iter() + .enumerate() + { + assert_eq!(min_distances_single_source(&graph, s), dist); + } } } }