Release 0.2.4 #7

Merged
warrence merged 19 commits from release-0.2.4 into main 2026-07-31 09:38:27 +02:00
Showing only changes of commit cf1dafceb7 - Show all commits
+330 -12
View File
@@ -14,7 +14,7 @@
//! [Breadth-first search] traverses a graph topology, exploring all neighboring vertices first //! [Breadth-first search] traverses a graph topology, exploring all neighboring vertices first
//! before descending further into their neighborhoods. //! before descending further into their neighborhoods.
//! //!
//! Variants: [`bfs`], [`bfs_distances`], [`bfs_find`], [`bfs_find_where`] //! Variants: [`bfs`], [`bfs_distances`], [`bfs_find`], [`bfs_find_where`].
//! //!
//! # Depth-first search //! # Depth-first search
//! //!
@@ -22,7 +22,7 @@
//! first before backtracking and exploring other branches. //! first before backtracking and exploring other branches.
//! //!
//! Variants: [`dfs`], [`dfs_visited`], [`dfs_find`], [`dfs_find_where`], [`dfs_find_path`], //! Variants: [`dfs`], [`dfs_visited`], [`dfs_find`], [`dfs_find_where`], [`dfs_find_path`],
//! [`dfs_find_path_where`] //! [`dfs_find_path_where`].
//! //!
//! [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search //! [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
//! [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search //! [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
@@ -52,17 +52,21 @@ impl<V: Eq> Ord for DistanceOrderedVertex<V> {
} }
} }
/// Return data type for [`dijkstra`] and [`dijkstra unweighted`]. /// Return data type for [`dijkstra`] and [`dijkstra_unweighted`].
pub struct DijkstraResult<V: Copy> { pub struct DijkstraResult<V: Copy> {
/// Vertex map of minimum distances from a given `source` vertex. /// Vertex map of minimum distances from a given `source` vertex.
pub distances: VertexMap<V, Option<u32>>, pub distances: VertexMap<V, Option<u32>>,
/// Vertex map of predecessors on some shortest path from a given `source` vertex. /// Vertex map of predecessors on some shortest path from a given `source` vertex.
pub predecessors: VertexMap<V, Option<V>>, pub predecessors: VertexMap<V, Option<V>>,
} }
// TODO: Generalize the return type of the weight function. // TODO: Generalize the return type of the weight function.
/// Dijkstra's algorithm with custom edge weights, returns minimum distances and predecessors. /// [Dijkstra's algorithm] with custom edge weights, returns minimum distances and predecessors.
///
/// Calculates the shortest paths from `source` to all vertices in `graph` with edge weights given
/// by `weight` function. Returns the distances from `source` to each vertex, and the predecessors
/// of each vertex on some shortest path from `source` to that vertex. Returns `None` for any vertex
/// not connected to `source`.
/// ///
/// # Panics /// # Panics
/// ///
@@ -85,6 +89,8 @@ pub struct DijkstraResult<V: Copy> {
/// assert_eq!(result.distances[target], Some(5)); /// assert_eq!(result.distances[target], Some(5));
/// assert_eq!(result.predecessors[target], Some(source)); /// assert_eq!(result.predecessors[target], Some(source));
/// ``` /// ```
///
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
pub fn dijkstra<G, W>(graph: &G, source: G::Vertex, weight: W) -> DijkstraResult<G::Vertex> pub fn dijkstra<G, W>(graph: &G, source: G::Vertex, weight: W) -> DijkstraResult<G::Vertex>
where where
G: GraphTopology, G: GraphTopology,
@@ -100,7 +106,11 @@ where
} }
} }
/// Dijkstra's algorithm with custom edge weights, returns minimum distances. /// [Dijkstra's algorithm] with custom edge weights, returns minimum distances.
///
/// Calculates the shortest paths from `source` to all vertices in `graph` with edge weights given
/// by `weight` function. Returns the distances from `source` to each vertex. Returns `None` for any
/// vertex not connected to `source`.
/// ///
/// # Panics /// # Panics
/// ///
@@ -110,7 +120,7 @@ where
/// ///
/// ``` /// ```
/// # use grapherity::prelude::*; /// # use grapherity::prelude::*;
/// # use grapherity::algorithms::{dijkstra, dijkstra_distances}; /// # use grapherity::algorithms::dijkstra_distances;
/// # use grapherity::models::Graph; /// # use grapherity::models::Graph;
/// let mut graph = Graph::new(); /// let mut graph = Graph::new();
/// let source = graph.add_vertex(); /// let source = graph.add_vertex();
@@ -122,6 +132,8 @@ where
/// let distances = dijkstra_distances(&graph, source, |e| weights[e]); /// let distances = dijkstra_distances(&graph, source, |e| weights[e]);
/// assert_eq!(distances[target], Some(5)); /// assert_eq!(distances[target], Some(5));
/// ``` /// ```
///
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
pub fn dijkstra_distances<G, W>( pub fn dijkstra_distances<G, W>(
graph: &G, graph: &G,
source: G::Vertex, source: G::Vertex,
@@ -134,8 +146,13 @@ where
dijkstra_impl(graph, source, weight, |_, _| {}) dijkstra_impl(graph, source, weight, |_, _| {})
} }
/// Dijkstra's algorithm with constant weights of *1* for all edges, returns minimum distances and /// [Dijkstra's algorithm] with unit edge weights, returns minimum distances and predecessors.
/// predecessors. ///
/// Calculates the shortest paths from `source` to all vertices in an unweighted `graph`. Returns
/// the distances from `source` to each vertex, and the predecessors of each vertex on some shortest
/// path from `source` to that vertex. Returns `None` for any vertex not connected to `source`.
///
/// Prefer [`bfs`] for unweighted graphs, it has better time and space complexity.
/// ///
/// # Panics /// # Panics
/// ///
@@ -145,7 +162,7 @@ where
/// ///
/// ``` /// ```
/// # use grapherity::prelude::*; /// # use grapherity::prelude::*;
/// # use grapherity::algorithms::{dijkstra, dijkstra_unweighted}; /// # use grapherity::algorithms::dijkstra_unweighted;
/// # use grapherity::models::Graph; /// # use grapherity::models::Graph;
/// let mut graph = Graph::new(); /// let mut graph = Graph::new();
/// let source = graph.add_vertex(); /// let source = graph.add_vertex();
@@ -156,6 +173,8 @@ where
/// assert_eq!(result.distances[target], Some(1)); /// assert_eq!(result.distances[target], Some(1));
/// assert_eq!(result.predecessors[target], Some(source)); /// assert_eq!(result.predecessors[target], Some(source));
/// ``` /// ```
///
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
pub fn dijkstra_unweighted<G>(graph: &G, source: G::Vertex) -> DijkstraResult<G::Vertex> pub fn dijkstra_unweighted<G>(graph: &G, source: G::Vertex) -> DijkstraResult<G::Vertex>
where where
G: GraphTopology, G: GraphTopology,
@@ -163,7 +182,13 @@ where
dijkstra(graph, source, |_| 1) dijkstra(graph, source, |_| 1)
} }
/// Dijkstra's algorithm with constant weights of *1* for all edges, returns minimum distances. /// [Dijkstra's algorithm] with unit edge weights, returns minimum distances.
///
/// Calculates the shortest paths from `source` to all vertices in an unweighted `graph`. Returns
/// the distances from `source` to each vertex. Returns `None` for any vertex not connected to
/// `source`.
///
/// Prefer [`bfs_distances`] for unweighted graphs, it has better time and space complexity.
/// ///
/// # Panics /// # Panics
/// ///
@@ -173,7 +198,7 @@ where
/// ///
/// ``` /// ```
/// # use grapherity::prelude::*; /// # use grapherity::prelude::*;
/// # use grapherity::algorithms::{dijkstra, dijkstra_distances_unweighted}; /// # use grapherity::algorithms::dijkstra_distances_unweighted;
/// # use grapherity::models::Graph; /// # use grapherity::models::Graph;
/// let mut graph = Graph::new(); /// let mut graph = Graph::new();
/// let source = graph.add_vertex(); /// let source = graph.add_vertex();
@@ -183,6 +208,8 @@ where
/// let distances = dijkstra_distances_unweighted(&graph, source); /// let distances = dijkstra_distances_unweighted(&graph, source);
/// assert_eq!(distances[target], Some(1)); /// assert_eq!(distances[target], Some(1));
/// ``` /// ```
///
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
pub fn dijkstra_distances_unweighted<G>( pub fn dijkstra_distances_unweighted<G>(
graph: &G, graph: &G,
source: G::Vertex, source: G::Vertex,
@@ -233,11 +260,44 @@ where
distances distances
} }
/// Return data type for [`bfs`].
pub struct BfsResult<V: Copy> { pub struct BfsResult<V: Copy> {
/// Vertex map of minimum distances from a given `source` vertex.
pub distances: VertexMap<V, Option<u32>>, pub distances: VertexMap<V, Option<u32>>,
/// Vertex map of predecessors on some shortest path from a given `source` vertex.
pub predecessors: VertexMap<V, Option<V>>, pub predecessors: VertexMap<V, Option<V>>,
} }
/// [Breadth-first search] traversal from `source`, returns distances and predecessors.
///
/// Traverses vertices in `graph` starting from `source`, exploring all neighbors before descending
/// further. Returns the distances from `source` to each vertex, and the predecessors of each vertex
/// on some shortest path from `source` to that vertex. Returns `None` for any vertex not connected
/// to `source`.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::bfs;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// graph.add_edge(source, target);
///
/// let result = bfs(&graph, source);
/// assert_eq!(result.distances[target], Some(1));
/// assert_eq!(result.predecessors[target], Some(source));
/// ```
///
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
pub fn bfs<G>(graph: &G, source: G::Vertex) -> BfsResult<G::Vertex> pub fn bfs<G>(graph: &G, source: G::Vertex) -> BfsResult<G::Vertex>
where where
G: GraphTopology, G: GraphTopology,
@@ -253,6 +313,34 @@ where
} }
} }
/// [Breadth-first search] traversal from `source`, returns distances.
///
/// Traverses vertices in `graph` starting from `source`, exploring all neighbors before descending
/// further. Returns the distances from `source` to each vertex. Returns `None` for any vertex not
/// connected to `source`.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::bfs_distances;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// graph.add_edge(source, target);
///
/// let distances = bfs_distances(&graph, source);
/// assert_eq!(distances[target], Some(1));
/// ```
///
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
pub fn bfs_distances<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, Option<u32>> pub fn bfs_distances<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, Option<u32>>
where where
G: GraphTopology, G: GraphTopology,
@@ -260,6 +348,33 @@ where
bfs_impl(graph, source, |_, _| true).distances bfs_impl(graph, source, |_, _| true).distances
} }
/// [Breadth-first search] from `source` for `target`, returns the distance.
///
/// Traverses vertices in `graph` starting from `source` until `target` is found, exploring all
/// neighbors before descending further. Returns the minimum distance from `source` to `target` if
/// `target` is a valid vertex of `graph` connected to `source`, or `None` otherwise.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::bfs_find;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// graph.add_edge(source, target);
///
/// assert_eq!(bfs_find(&graph, source, target), Some(1));
/// ```
///
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
pub fn bfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<u32> pub fn bfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<u32>
where where
G: GraphTopology, G: GraphTopology,
@@ -267,6 +382,35 @@ where
bfs_find_where(graph, source, |v| v == target).map(|(_, distance)| distance) bfs_find_where(graph, source, |v| v == target).map(|(_, distance)| distance)
} }
/// [Breadth-first search] from `source` for a vertex matching `predicate`, returns the vertex and
/// its distance.
///
/// Traverses vertices in `graph` starting from `source` until a vertex satisfying `predicate` is
/// found, exploring all neighbors before descending further. Returns the first vertex satisfying
/// `predicate` and its minimum distance from `source`, or `None` if no such vertex is connected to
/// `source`.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::bfs_find_where;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// graph.add_edge(source, target);
///
/// assert_eq!(bfs_find_where(&graph, source, |v| v == target), Some((target, 1)));
/// ```
///
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search
pub fn bfs_find_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<(G::Vertex, u32)> pub fn bfs_find_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<(G::Vertex, u32)>
where where
G: GraphTopology, G: GraphTopology,
@@ -315,11 +459,44 @@ where
} }
} }
/// Return data type for [`dfs`].
pub struct DfsResult<V: Copy> { pub struct DfsResult<V: Copy> {
/// Vertex map indicating which vertices were visited during the search.
pub visited: VertexMap<V, bool>, pub visited: VertexMap<V, bool>,
/// Vertex map of predecessors on the DFS tree path from a given `source` vertex.
pub predecessors: VertexMap<V, Option<V>>, pub predecessors: VertexMap<V, Option<V>>,
} }
/// [Depth-first search] traversal from `source`, returns visited vertices and predecessors.
///
/// Traverses vertices in `graph` starting from `source`, exploring each branch as far as possible
/// before backtracking. Returns for each vertex whether it was visited, and the predecessors of
/// each vertex on the DFS tree path from `source` to that vertex. Any vertex not connected to
/// `source` is marked unvisited and has no predecessor.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::dfs;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// graph.add_edge(source, target);
///
/// let result = dfs(&graph, source);
/// assert!(result.visited[target]);
/// assert_eq!(result.predecessors[target], Some(source));
/// ```
///
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
pub fn dfs<G>(graph: &G, source: G::Vertex) -> DfsResult<G::Vertex> pub fn dfs<G>(graph: &G, source: G::Vertex) -> DfsResult<G::Vertex>
where where
G: GraphTopology, G: GraphTopology,
@@ -335,6 +512,34 @@ where
} }
} }
/// [Depth-first search] traversal from `source`, returns visited vertices.
///
/// Traverses vertices in `graph` starting from `source`, exploring each branch as far as possible
/// before backtracking. Returns a map indicating which vertices were visited, i.e. which vertices
/// are connected to `source`.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::dfs_visited;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// graph.add_edge(source, target);
///
/// let visited = dfs_visited(&graph, source);
/// assert!(visited[target]);
/// ```
///
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
pub fn dfs_visited<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, bool> pub fn dfs_visited<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, bool>
where where
G: GraphTopology, G: GraphTopology,
@@ -342,6 +547,33 @@ where
dfs_impl(graph, source, |_, _| true).visited dfs_impl(graph, source, |_, _| true).visited
} }
/// [Depth-first search] from `source` for `target`, returns whether it was found.
///
/// Traverses vertices in `graph` starting from `source` until `target` is found, exploring each
/// branch as far as possible before backtracking. Returns `true` if `target` is a valid vertex of
/// `graph` connected to `source`, or `false` otherwise.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::dfs_find;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// graph.add_edge(source, target);
///
/// assert!(dfs_find(&graph, source, target));
/// ```
///
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
pub fn dfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> bool pub fn dfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> bool
where where
G: GraphTopology, G: GraphTopology,
@@ -349,6 +581,33 @@ where
dfs_find_where(graph, source, |v| v == target).is_some() dfs_find_where(graph, source, |v| v == target).is_some()
} }
/// [Depth-first search] from `source` for a vertex matching `predicate`, returns the vertex.
///
/// Traverses vertices in `graph` starting from `source` until a vertex satisfying `predicate` is
/// found, exploring each branch as far as possible before backtracking. Returns the first vertex
/// satisfying `predicate`, or `None` if no such vertex is connected to `source`.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::dfs_find_where;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// graph.add_edge(source, target);
///
/// assert_eq!(dfs_find_where(&graph, source, |v| v == target), Some(target));
/// ```
///
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
pub fn dfs_find_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<G::Vertex> pub fn dfs_find_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<G::Vertex>
where where
G: GraphTopology, G: GraphTopology,
@@ -396,6 +655,35 @@ where
} }
} }
/// [Depth-first search] from `source` for `target`, returns the path as a sequence of edges.
///
/// Traverses vertices in `graph` starting from `source` until `target` is found, exploring each
/// branch as far as possible before backtracking. Returns some path from `source` to `target` as a
/// sequence of edges in traversal order if `target` is a valid vertex of `graph` connected to
/// `source`, or `None` otherwise. The returned path is empty if and only if `source` equals
/// `target`.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::dfs_find_path;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// let e = graph.add_edge(source, target);
///
/// assert_eq!(dfs_find_path(&graph, source, target), Some(vec![e]));
/// ```
///
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
pub fn dfs_find_path<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<Vec<G::Edge>> pub fn dfs_find_path<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<Vec<G::Edge>>
where where
G: GraphTopology, G: GraphTopology,
@@ -403,6 +691,36 @@ where
dfs_find_path_where(graph, source, |v| v == target) dfs_find_path_where(graph, source, |v| v == target)
} }
/// [Depth-first search] from `source` for a vertex matching `predicate`, returns the path as a
/// sequence of edges.
///
/// Traverses vertices in `graph` starting from `source` until a vertex satisfying `predicate` is
/// found, exploring each branch as far as possible before backtracking. Returns some path from
/// `source` to the first vertex satisfying `predicate` as a sequence of edges in traversal order,
/// or `None` if no such vertex is connected to `source`. The returned path is empty if and only if
/// `source` satisfies `predicate`.
///
/// Time complexity is *O(|V| + |E|)*, space complexity is *O(|V|)*.
///
/// # Panics
///
/// Panics if `source` is not a valid vertex of `graph`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::algorithms::dfs_find_path_where;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let source = graph.add_vertex();
/// let target = graph.add_vertex();
/// let e = graph.add_edge(source, target);
///
/// assert_eq!(dfs_find_path_where(&graph, source, |v| v == target), Some(vec![e]));
/// ```
///
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
pub fn dfs_find_path_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<Vec<G::Edge>> pub fn dfs_find_path_where<G, P>(graph: &G, source: G::Vertex, predicate: P) -> Option<Vec<G::Edge>>
where where
G: GraphTopology, G: GraphTopology,