Compare commits
31 Commits
0.1.0
...
release-0.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
| ad9850800d | |||
| a14202effc | |||
| 5fa415841e | |||
| e8568a084a | |||
| e6b08d9148 | |||
| b62c263b08 | |||
| 7345684f00 | |||
| 2c53d92943 | |||
| 913a9e0baa | |||
| 8c6c98859c | |||
| 24c08438d6 | |||
| bf6a7142f3 | |||
| c131f520f4 | |||
| b6c27ea605 | |||
| 5b30151010 | |||
| 2f549cc506 | |||
| cf31ee2f01 | |||
| e58261ba47 | |||
| fb9dfe8280 | |||
| 818b37ec15 | |||
| dd0399dcdd | |||
| 3c4391ac1d | |||
| bbd9506bbc | |||
| 4b3586b1eb | |||
| c8dda39b98 | |||
| 1446103d34 | |||
| 266dee783b | |||
| e3a24894f6 | |||
| 6d2d1de8ea | |||
| 2d400628f7 | |||
| 6a0c426c77 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "grapherity"
|
name = "grapherity"
|
||||||
version = "0.1.0"
|
version = "0.2.3"
|
||||||
authors = ["Stefan Müller"]
|
authors = ["Stefan Müller"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.85.0"
|
rust-version = "1.85.0"
|
||||||
|
|||||||
@@ -1,14 +1,69 @@
|
|||||||
# grapherity
|
# grapherity
|
||||||
|
|
||||||
Library for graph models and algorithms.
|
Graph models and algorithms.
|
||||||
|
|
||||||
This library is still experimental and its API may therefore change frequently.
|
This crate provides data structures to model graphs and to perform algorithms on these data structures. The functionality presented is designed to be easy to use, flexible, and performant.
|
||||||
|
|
||||||
|
Currently supported are:
|
||||||
|
|
||||||
|
* Undirected graph types `Graph` and `AppendGraph`, built on a flat, index-based adjacency list,
|
||||||
|
* Maps to freely associate custom data with vertices and edges,
|
||||||
|
* Connectivity and pathing algorithms: Dijkstra's algorithm, DFS, and BFS in different variants,
|
||||||
|
* Exposed traits to implement custom graph data structures or algorithms.
|
||||||
|
|
||||||
|
For more information, see the [documentation](https://docs.rs/grapherity/latest/grapherity/).
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```
|
||||||
|
// Brings commonly used traits into scope.
|
||||||
|
use grapherity::prelude::*;
|
||||||
|
use grapherity::models::Graph;
|
||||||
|
use grapherity::algorithms;
|
||||||
|
|
||||||
|
let mut graph = Graph::new();
|
||||||
|
// Creates a map for edge weights with a default weight.
|
||||||
|
let mut weights = graph.edge_map(1);
|
||||||
|
// Adds three vertices and two edges.
|
||||||
|
let v1 = graph.add_vertex();
|
||||||
|
let v2 = graph.add_vertex();
|
||||||
|
let v3 = graph.add_vertex();
|
||||||
|
graph.add_edge(v1, v2);
|
||||||
|
let e = graph.add_edge(v2, v3);
|
||||||
|
// Sets a non-default weight for the second edge.
|
||||||
|
weights[e] = 5;
|
||||||
|
|
||||||
|
// Checks the number of vertices.
|
||||||
|
assert_eq!(graph.vertex_count(), 3);
|
||||||
|
// Checks the number of edges.
|
||||||
|
assert_eq!(graph.edge_count(), 2);
|
||||||
|
// Checks that v1 and v2 are adjacent.
|
||||||
|
assert!(graph.are_adjacent(v1, v2));
|
||||||
|
// Checks that v1 and v3 are not adjacent.
|
||||||
|
assert!(!graph.are_adjacent(v1, v3));
|
||||||
|
// Checks the sum of all vertex degrees.
|
||||||
|
let sum: usize = graph.vertices().map(|v| graph.degree(v)).sum();
|
||||||
|
assert_eq!(sum, 1 + 2 + 1);
|
||||||
|
|
||||||
|
// Calls Dijkstra's algorithm using the edge weights to find the shortest path from v1 to v3.
|
||||||
|
let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
|
||||||
|
assert_eq!(result.distances[v3], Some(1 + 5));
|
||||||
|
assert_eq!(result.predecessors[v3], Some(v2));
|
||||||
|
|
||||||
|
// Deletes the middle vertex and its incident edges.
|
||||||
|
graph.delete_vertex(v2);
|
||||||
|
|
||||||
|
// Calls Dijkstra's algorithm again on the now disconnected graph.
|
||||||
|
let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
|
||||||
|
assert_eq!(result.distances[v3], None);
|
||||||
|
assert_eq!(result.predecessors[v3], None);
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is dual-licensed under the terms of both:
|
This project is dual-licensed under the terms of both:
|
||||||
|
|
||||||
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/mit)
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/mit)
|
||||||
|
|
||||||
You may choose either license for your use of this software.
|
You may choose either license for your use of this software.
|
||||||
|
|||||||
+39
-25
@@ -122,12 +122,12 @@ where
|
|||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
{
|
{
|
||||||
let mut predecessors = graph.vertex_map(None);
|
let mut predecessors = graph.vertex_map(None);
|
||||||
let (distances, _) = bfs_impl(graph, source, |neighbor, predecessor| {
|
let result = bfs_impl(graph, source, |neighbor, predecessor| {
|
||||||
predecessors[neighbor] = Some(predecessor);
|
predecessors[neighbor] = Some(predecessor);
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
BfsResult {
|
BfsResult {
|
||||||
distances,
|
distances: result.distances,
|
||||||
predecessors,
|
predecessors,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ pub fn bfs_distances<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, Op
|
|||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
{
|
{
|
||||||
bfs_impl(graph, source, |_, _| true).0
|
bfs_impl(graph, source, |_, _| true).distances
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
@@ -154,14 +154,15 @@ where
|
|||||||
if predicate(source) {
|
if predicate(source) {
|
||||||
return Some((source, 0));
|
return Some((source, 0));
|
||||||
}
|
}
|
||||||
bfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).1
|
bfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).found
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bfs_impl<G, F>(
|
struct BfsImplResult<V: Copy> {
|
||||||
graph: &G,
|
distances: VertexMap<V, Option<u32>>,
|
||||||
source: G::Vertex,
|
found: Option<(V, u32)>,
|
||||||
mut on_discover: F,
|
}
|
||||||
) -> (VertexMap<G::Vertex, Option<u32>>, Option<(G::Vertex, u32)>)
|
|
||||||
|
fn bfs_impl<G, F>(graph: &G, source: G::Vertex, mut on_discover: F) -> BfsImplResult<G::Vertex>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
F: FnMut(G::Vertex, G::Vertex) -> bool,
|
F: FnMut(G::Vertex, G::Vertex) -> bool,
|
||||||
@@ -178,13 +179,19 @@ where
|
|||||||
let distance = distances[v].unwrap() + 1;
|
let distance = distances[v].unwrap() + 1;
|
||||||
distances[neighbor] = Some(distance);
|
distances[neighbor] = Some(distance);
|
||||||
if !on_discover(neighbor, v) {
|
if !on_discover(neighbor, v) {
|
||||||
return (distances, Some((neighbor, distance)));
|
return BfsImplResult {
|
||||||
|
distances,
|
||||||
|
found: Some((neighbor, distance)),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
queue.push_back(neighbor);
|
queue.push_back(neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(distances, None)
|
BfsImplResult {
|
||||||
|
distances,
|
||||||
|
found: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DfsResult<V: Copy> {
|
pub struct DfsResult<V: Copy> {
|
||||||
@@ -197,12 +204,12 @@ where
|
|||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
{
|
{
|
||||||
let mut predecessors = graph.vertex_map(None);
|
let mut predecessors = graph.vertex_map(None);
|
||||||
let (visited, _) = dfs_impl(graph, source, |neighbor, predecessor| {
|
let result = dfs_impl(graph, source, |neighbor, predecessor| {
|
||||||
predecessors[neighbor] = Some(predecessor);
|
predecessors[neighbor] = Some(predecessor);
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
DfsResult {
|
DfsResult {
|
||||||
visited,
|
visited: result.visited,
|
||||||
predecessors,
|
predecessors,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,7 +218,7 @@ pub fn dfs_visited<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, bool
|
|||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
{
|
{
|
||||||
dfs_impl(graph, source, |_, _| true).0
|
dfs_impl(graph, source, |_, _| true).visited
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
@@ -229,14 +236,15 @@ where
|
|||||||
if predicate(source) {
|
if predicate(source) {
|
||||||
return Some(source);
|
return Some(source);
|
||||||
}
|
}
|
||||||
dfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).1
|
dfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).found
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dfs_impl<G, F>(
|
struct DfsImplResult<V: Copy> {
|
||||||
graph: &G,
|
visited: VertexMap<V, bool>,
|
||||||
source: G::Vertex,
|
found: Option<V>,
|
||||||
mut on_discover: F,
|
}
|
||||||
) -> (VertexMap<G::Vertex, bool>, Option<G::Vertex>)
|
|
||||||
|
fn dfs_impl<G, F>(graph: &G, source: G::Vertex, mut on_discover: F) -> DfsImplResult<G::Vertex>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
F: FnMut(G::Vertex, G::Vertex) -> bool,
|
F: FnMut(G::Vertex, G::Vertex) -> bool,
|
||||||
@@ -248,7 +256,10 @@ where
|
|||||||
while let Some((v, predecessor)) = stack.pop() {
|
while let Some((v, predecessor)) = stack.pop() {
|
||||||
if let Some(p) = predecessor {
|
if let Some(p) = predecessor {
|
||||||
if !on_discover(v, p) {
|
if !on_discover(v, p) {
|
||||||
return (visited, Some(v));
|
return DfsImplResult {
|
||||||
|
visited,
|
||||||
|
found: Some(v),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for neighbor in graph.adjacent_vertices(v) {
|
for neighbor in graph.adjacent_vertices(v) {
|
||||||
@@ -258,17 +269,20 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(visited, None)
|
DfsImplResult {
|
||||||
|
visited,
|
||||||
|
found: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn 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,
|
||||||
{
|
{
|
||||||
find_path_where(graph, source, |v| v == target)
|
dfs_find_path_where(graph, source, |v| v == target)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn 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,
|
||||||
P: Fn(G::Vertex) -> bool,
|
P: Fn(G::Vertex) -> bool,
|
||||||
|
|||||||
+133
@@ -1,6 +1,139 @@
|
|||||||
|
//! Graph models and algorithms.
|
||||||
|
//!
|
||||||
|
//! This crate provides data structures to model graphs and to perform algorithms on these data
|
||||||
|
//! structures. The functionality presented is designed to be easy to use, flexible, and performant.
|
||||||
|
//!
|
||||||
|
//! Currently supported are:
|
||||||
|
//!
|
||||||
|
//! * Undirected graph types [`Graph`] and [`AppendGraph`] built on a flat, index-based adjacency
|
||||||
|
//! list,
|
||||||
|
//! * [`VertexMap`] and [`EdgeMap`] to freely associate custom data with vertices and edges,
|
||||||
|
//! * Connectivity and pathing algorithms: [Dijkstra's algorithm], [DFS], and [BFS] in different
|
||||||
|
//! variants,
|
||||||
|
//! * Exposed traits to implement custom graph data structures or algorithms.
|
||||||
|
//!
|
||||||
|
//! # Usage example
|
||||||
|
//!
|
||||||
|
//! This example demonstrates some of this library's features.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! // Brings commonly used traits into scope.
|
||||||
|
//! use grapherity::prelude::*;
|
||||||
|
//! use grapherity::models::Graph;
|
||||||
|
//! use grapherity::algorithms;
|
||||||
|
//!
|
||||||
|
//! let mut graph = Graph::new();
|
||||||
|
//! // Creates a map for edge weights with a default weight.
|
||||||
|
//! let mut weights = graph.edge_map(1);
|
||||||
|
//! // Adds three vertices and two edges.
|
||||||
|
//! let v1 = graph.add_vertex();
|
||||||
|
//! let v2 = graph.add_vertex();
|
||||||
|
//! let v3 = graph.add_vertex();
|
||||||
|
//! graph.add_edge(v1, v2);
|
||||||
|
//! let e = graph.add_edge(v2, v3);
|
||||||
|
//! // Sets a non-default weight for the second edge.
|
||||||
|
//! weights[e] = 5;
|
||||||
|
//!
|
||||||
|
//! // Checks the number of vertices.
|
||||||
|
//! assert_eq!(graph.vertex_count(), 3);
|
||||||
|
//! // Checks the number of edges.
|
||||||
|
//! assert_eq!(graph.edge_count(), 2);
|
||||||
|
//! // Checks that v1 and v2 are adjacent.
|
||||||
|
//! assert!(graph.are_adjacent(v1, v2));
|
||||||
|
//! // Checks that v1 and v3 are not adjacent.
|
||||||
|
//! assert!(!graph.are_adjacent(v1, v3));
|
||||||
|
//! // Checks the sum of all vertex degrees.
|
||||||
|
//! let sum: usize = graph.vertices().map(|v| graph.degree(v)).sum();
|
||||||
|
//! assert_eq!(sum, 1 + 2 + 1);
|
||||||
|
//!
|
||||||
|
//! // Calls Dijkstra's algorithm using the edge weights to find the shortest path from v1 to v3.
|
||||||
|
//! let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
|
||||||
|
//! assert_eq!(result.distances[v3], Some(1 + 5));
|
||||||
|
//! assert_eq!(result.predecessors[v3], Some(v2));
|
||||||
|
//!
|
||||||
|
//! // Deletes the middle vertex and its incident edges.
|
||||||
|
//! graph.delete_vertex(v2);
|
||||||
|
//!
|
||||||
|
//! // Calls Dijkstra's algorithm again on the now disconnected graph.
|
||||||
|
//! let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
|
||||||
|
//! assert_eq!(result.distances[v3], None);
|
||||||
|
//! assert_eq!(result.predecessors[v3], None);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Graph types
|
||||||
|
//!
|
||||||
|
//! [`Graph`] and [`AppendGraph`] both implement [`GraphTopology`] using a flat, index-based adjacency
|
||||||
|
//! list to store edges, which means that vertex and edge insertions happen in `O(1)`. Degree
|
||||||
|
//! lookups are also `O(1)`.
|
||||||
|
//!
|
||||||
|
//! [`Graph`] additionally implements [`GraphTopologyDeletion`], thereby supporting deletion of
|
||||||
|
//! vertices and edges.
|
||||||
|
//!
|
||||||
|
//! [`AppendGraph`] does not support deletion, vertices and edges can only be added, allowing it to
|
||||||
|
//! be smaller and more performant compared to [`Graph`] by not requiring the per-element generation
|
||||||
|
//! tracking needed for stable handles after deletion.
|
||||||
|
//!
|
||||||
|
//! # Vertices and edges
|
||||||
|
//!
|
||||||
|
//! Graphs in this library use stable indices [`GraphTopology::Vertex`] and [`GraphTopology::Edge`]
|
||||||
|
//! to denote vertices and edges. This means that vertices and edges returned by graph functions
|
||||||
|
//! never change, even if the underlying graph data structure is mutated. However, vertices and
|
||||||
|
//! edges can become invalid when the denoted element is deleted. Code using an invalid vertex or
|
||||||
|
//! edge in a graph method will panic.
|
||||||
|
//!
|
||||||
|
//! ```should_panic
|
||||||
|
//! # use grapherity::prelude::*;
|
||||||
|
//! # use grapherity::models::Graph;
|
||||||
|
//! let mut graph = Graph::new();
|
||||||
|
//! let v = graph.add_vertex();
|
||||||
|
//! graph.delete_vertex(v);
|
||||||
|
//! // This will panic because 'v' was deleted and is not valid anymore.
|
||||||
|
//! graph.degree(v);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [`VertexMap`] and [`EdgeMap`] are not required to panic if provided with an invalid
|
||||||
|
//! vertex or edge.
|
||||||
|
//!
|
||||||
|
//! Vertices and edges are provided by the graph and not intended to be generated by users.
|
||||||
|
//!
|
||||||
|
//! # Loops and multi-edges
|
||||||
|
//!
|
||||||
|
//! Loops and multi edges are supported as well. In the following example, `v1` will have two loops
|
||||||
|
//! and two edges to `v2`.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use grapherity::prelude::*;
|
||||||
|
//! # use grapherity::models::Graph;
|
||||||
|
//! let mut graph = Graph::new();
|
||||||
|
//! let v1 = graph.add_vertex();
|
||||||
|
//! let v2 = graph.add_vertex();
|
||||||
|
//! for _ in 0..2 {
|
||||||
|
//! graph.add_edge(v1, v1);
|
||||||
|
//! graph.add_edge(v1, v2);
|
||||||
|
//! }
|
||||||
|
//! assert_eq!(graph.edge_count(), 4);
|
||||||
|
//! assert_eq!(graph.degree(v1), 6);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [BFS]: crate::algorithms::bfs
|
||||||
|
//! [Dijkstra's algorithm]: crate::algorithms::dijkstra
|
||||||
|
//! [DFS]: crate::algorithms::dfs
|
||||||
|
//! [`EdgeMap`]: crate::maps::EdgeMap
|
||||||
|
//! [`VertexMap`]: crate::maps::VertexMap
|
||||||
|
//! [`AppendGraph`]: crate::models::AppendGraph
|
||||||
|
//! [`Graph`]: crate::models::Graph
|
||||||
|
//! [`GraphTopology`]: crate::traits::GraphTopology
|
||||||
|
//! [`GraphTopology::Edge`]: crate::traits::GraphTopology::Edge
|
||||||
|
//! [`GraphTopology::Vertex`]: crate::traits::GraphTopology::Vertex
|
||||||
|
//! [`GraphTopologyDeletion`]: crate::traits::GraphTopologyDeletion
|
||||||
|
|
||||||
pub mod algorithms;
|
pub mod algorithms;
|
||||||
pub mod maps;
|
pub mod maps;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
|
|
||||||
|
pub mod prelude {
|
||||||
|
pub use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
|
||||||
|
}
|
||||||
|
|
||||||
mod testing;
|
mod testing;
|
||||||
|
|||||||
+7
-7
@@ -13,9 +13,9 @@ impl<V: Copy, T: Clone> VertexMap<V, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads beyond len() are valid and return the default value.
|
// Reads beyond 'capacity' are valid and return the default value.
|
||||||
pub fn len(&self) -> usize {
|
pub fn capacity(&self) -> usize {
|
||||||
self.inner.len()
|
self.inner.capacity()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sync<G: GraphTopology<Vertex = V>>(&mut self, graph: &G) {
|
pub fn sync<G: GraphTopology<Vertex = V>>(&mut self, graph: &G) {
|
||||||
@@ -48,9 +48,9 @@ impl<E: Copy, T: Clone> EdgeMap<E, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads beyond len() are valid and return the default value.
|
// Reads beyond 'capacity' are valid and return the default value.
|
||||||
pub fn len(&self) -> usize {
|
pub fn capacity(&self) -> usize {
|
||||||
self.inner.len()
|
self.inner.capacity()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sync<G: GraphTopology<Edge = E>>(&mut self, graph: &G) {
|
pub fn sync<G: GraphTopology<Edge = E>>(&mut self, graph: &G) {
|
||||||
@@ -87,7 +87,7 @@ impl<E: Copy, T: Clone> EntityMap<E, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn capacity(&self) -> usize {
|
||||||
self.data.len()
|
self.data.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,7 @@
|
|||||||
pub mod append_graph;
|
pub mod append_graph;
|
||||||
pub mod graph;
|
pub mod graph;
|
||||||
|
|
||||||
|
// TODO: Compressed-sparse-row graph model.
|
||||||
|
|
||||||
|
pub use append_graph::{AppendGraph, AppendGraphEdgeMap, AppendGraphVertexMap};
|
||||||
|
pub use graph::{Graph, GraphEdgeMap, GraphVertexMap};
|
||||||
|
|||||||
+17
-14
@@ -5,9 +5,12 @@ use crate::traits::{GraphTopology, IncidenceCursor};
|
|||||||
pub struct Vertex(usize);
|
pub struct Vertex(usize);
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Incidence(usize);
|
pub struct Edge(usize);
|
||||||
|
|
||||||
impl Incidence {
|
pub type AppendGraphVertexMap<T> = VertexMap<Vertex, T>;
|
||||||
|
pub type AppendGraphEdgeMap<T> = EdgeMap<Edge, T>;
|
||||||
|
|
||||||
|
impl Edge {
|
||||||
fn normalize(&self) -> Self {
|
fn normalize(&self) -> Self {
|
||||||
Self(self.0 & !1)
|
Self(self.0 & !1)
|
||||||
}
|
}
|
||||||
@@ -15,22 +18,22 @@ impl Incidence {
|
|||||||
|
|
||||||
struct VertexIncidenceHeader {
|
struct VertexIncidenceHeader {
|
||||||
incidence_count: usize,
|
incidence_count: usize,
|
||||||
first_incidence: Option<Incidence>,
|
first_incidence: Option<Edge>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct IncidenceEntry {
|
struct IncidenceEntry {
|
||||||
next: Option<Incidence>,
|
next: Option<Edge>,
|
||||||
adjacent: Vertex,
|
adjacent: Vertex,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct AppendGraphIncidenceCursor {
|
pub struct AppendGraphIncidenceCursor {
|
||||||
incidence: Option<Incidence>,
|
incidence: Option<Edge>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IncidenceCursor<AppendGraph> for AppendGraphIncidenceCursor {
|
impl IncidenceCursor<AppendGraph> for AppendGraphIncidenceCursor {
|
||||||
fn next(&mut self, graph: &AppendGraph) -> Option<(Vertex, Incidence)> {
|
fn next(&mut self, graph: &AppendGraph) -> Option<(Vertex, Edge)> {
|
||||||
graph.step_incidence(&mut self.incidence)
|
graph.step_incidence(&mut self.incidence)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,15 +59,15 @@ impl AppendGraph {
|
|||||||
adjacent: v2,
|
adjacent: v2,
|
||||||
});
|
});
|
||||||
self.vertices[v1.0].incidence_count += 1;
|
self.vertices[v1.0].incidence_count += 1;
|
||||||
self.vertices[v1.0].first_incidence = Some(Incidence(self.incidences.len() - 1));
|
self.vertices[v1.0].first_incidence = Some(Edge(self.incidences.len() - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn raw_incidences(&self, v: Vertex) -> impl Iterator<Item = (Vertex, Incidence)> {
|
fn raw_incidences(&self, v: Vertex) -> impl Iterator<Item = (Vertex, Edge)> {
|
||||||
let mut incidence = self.vertices[v.0].first_incidence;
|
let mut incidence = self.vertices[v.0].first_incidence;
|
||||||
std::iter::from_fn(move || self.step_incidence(&mut incidence))
|
std::iter::from_fn(move || self.step_incidence(&mut incidence))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_incidence(&self, incidence: &mut Option<Incidence>) -> Option<(Vertex, Incidence)> {
|
fn step_incidence(&self, incidence: &mut Option<Edge>) -> Option<(Vertex, Edge)> {
|
||||||
let current = (*incidence)?;
|
let current = (*incidence)?;
|
||||||
let entry = self.incidences[current.0];
|
let entry = self.incidences[current.0];
|
||||||
*incidence = entry.next;
|
*incidence = entry.next;
|
||||||
@@ -80,7 +83,7 @@ impl Default for AppendGraph {
|
|||||||
|
|
||||||
impl GraphTopology for AppendGraph {
|
impl GraphTopology for AppendGraph {
|
||||||
type Vertex = Vertex;
|
type Vertex = Vertex;
|
||||||
type Edge = Incidence;
|
type Edge = Edge;
|
||||||
type IncidenceCursor = AppendGraphIncidenceCursor;
|
type IncidenceCursor = AppendGraphIncidenceCursor;
|
||||||
|
|
||||||
fn vertex_count(&self) -> usize {
|
fn vertex_count(&self) -> usize {
|
||||||
@@ -88,7 +91,7 @@ impl GraphTopology for AppendGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn vertex_capacity(&self) -> usize {
|
fn vertex_capacity(&self) -> usize {
|
||||||
self.vertices.len()
|
self.vertices.capacity()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T> {
|
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T> {
|
||||||
@@ -130,7 +133,7 @@ impl GraphTopology for AppendGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn edges(&self) -> impl Iterator<Item = Self::Edge> {
|
fn edges(&self) -> impl Iterator<Item = Self::Edge> {
|
||||||
(0..self.incidences.len()).step_by(2).map(Incidence)
|
(0..self.incidences.len()).step_by(2).map(Edge)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge> {
|
fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge> {
|
||||||
@@ -158,7 +161,7 @@ impl GraphTopology for AppendGraph {
|
|||||||
fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge {
|
fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge {
|
||||||
self.add_incidence(v1, v2);
|
self.add_incidence(v1, v2);
|
||||||
self.add_incidence(v2, v1);
|
self.add_incidence(v2, v1);
|
||||||
Incidence(self.incidences.len() - 2)
|
Edge(self.incidences.len() - 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +178,7 @@ mod tests {
|
|||||||
let v1 = graph.add_vertex();
|
let v1 = graph.add_vertex();
|
||||||
let v2 = graph.add_vertex();
|
let v2 = graph.add_vertex();
|
||||||
let e = graph.add_edge(v1, v2);
|
let e = graph.add_edge(v1, v2);
|
||||||
let f = Incidence(e.0 + 1);
|
let f = Edge(e.0 + 1);
|
||||||
let (u1, u2) = graph.incident_vertices(f);
|
let (u1, u2) = graph.incident_vertices(f);
|
||||||
assert!(
|
assert!(
|
||||||
(u1 == v1 && u2 == v2) || (u1 == v2 && u2 == v1),
|
(u1 == v1 && u2 == v2) || (u1 == v2 && u2 == v1),
|
||||||
|
|||||||
+4
-2
@@ -3,9 +3,11 @@ use typed_generational_arena::{Arena, Index};
|
|||||||
use crate::maps::{EdgeMap, VertexMap};
|
use crate::maps::{EdgeMap, VertexMap};
|
||||||
use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
|
use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
|
||||||
|
|
||||||
type Vertex = Index<VertexIncidenceHeader, usize, usize>;
|
pub type Vertex = Index<VertexIncidenceHeader, usize, usize>;
|
||||||
|
pub type Edge = Index<IncidenceEntry, usize, usize>;
|
||||||
|
|
||||||
type Edge = Index<IncidenceEntry, usize, usize>;
|
pub type GraphVertexMap<T> = VertexMap<Vertex, T>;
|
||||||
|
pub type GraphEdgeMap<T> = EdgeMap<Edge, T>;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
struct VertexSlot(usize);
|
struct VertexSlot(usize);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! bfs_tests {
|
macro_rules! bfs_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! dfs_tests {
|
macro_rules! dfs_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! dijkstra_tests {
|
macro_rules! dijkstra_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! find_path_tests {
|
macro_rules! find_path_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
@@ -7,7 +8,7 @@ macro_rules! find_path_tests {
|
|||||||
let mut graph = <$T>::new();
|
let mut graph = <$T>::new();
|
||||||
let v = graph.add_vertex();
|
let v = graph.add_vertex();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
$crate::algorithms::find_path(&graph, v, v),
|
$crate::algorithms::dfs_find_path(&graph, v, v),
|
||||||
Some(vec![]),
|
Some(vec![]),
|
||||||
"path from source to itself should be empty"
|
"path from source to itself should be empty"
|
||||||
);
|
);
|
||||||
@@ -17,7 +18,7 @@ macro_rules! find_path_tests {
|
|||||||
fn find_path_disconnected() {
|
fn find_path_disconnected() {
|
||||||
let (graph, vertices) = make_test_graph_disconnected();
|
let (graph, vertices) = make_test_graph_disconnected();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
$crate::algorithms::find_path(&graph, vertices[0], vertices[1]),
|
$crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[1]),
|
||||||
None,
|
None,
|
||||||
"no path should exist to disconnected vertex"
|
"no path should exist to disconnected vertex"
|
||||||
);
|
);
|
||||||
@@ -30,7 +31,7 @@ macro_rules! find_path_tests {
|
|||||||
let v1 = graph.add_vertex();
|
let v1 = graph.add_vertex();
|
||||||
let v2 = graph.add_vertex();
|
let v2 = graph.add_vertex();
|
||||||
let e = graph.add_edge(v1, v2);
|
let e = graph.add_edge(v1, v2);
|
||||||
let path = $crate::algorithms::find_path(&graph, v1, v2)
|
let path = $crate::algorithms::dfs_find_path(&graph, v1, v2)
|
||||||
.expect("path should exist between adjacent vertices");
|
.expect("path should exist between adjacent vertices");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
path.len(),
|
path.len(),
|
||||||
@@ -43,7 +44,7 @@ macro_rules! find_path_tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn find_path() {
|
fn find_path() {
|
||||||
let (graph, vertices, _, _) = make_test_graph();
|
let (graph, vertices, _, _) = make_test_graph();
|
||||||
let path = $crate::algorithms::find_path(&graph, vertices[0], vertices[9])
|
let path = $crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[9])
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"path should exist between connected vertices {:?} and {:?}",
|
"path should exist between connected vertices {:?} and {:?}",
|
||||||
vertices[0], vertices[9]
|
vertices[0], vertices[9]
|
||||||
@@ -57,7 +58,7 @@ macro_rules! find_path_tests {
|
|||||||
let mut graph = <$T>::new();
|
let mut graph = <$T>::new();
|
||||||
let v = graph.add_vertex();
|
let v = graph.add_vertex();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
$crate::algorithms::find_path_where(&graph, v, |u| u == v),
|
$crate::algorithms::dfs_find_path_where(&graph, v, |u| u == v),
|
||||||
Some(vec![]),
|
Some(vec![]),
|
||||||
"path from source to itself should be empty"
|
"path from source to itself should be empty"
|
||||||
);
|
);
|
||||||
@@ -67,7 +68,7 @@ macro_rules! find_path_tests {
|
|||||||
fn find_path_where_disconnected() {
|
fn find_path_where_disconnected() {
|
||||||
let (graph, vertices) = make_test_graph_disconnected();
|
let (graph, vertices) = make_test_graph_disconnected();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
$crate::algorithms::find_path_where(&graph, vertices[0], |v| v == vertices[1]),
|
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[1]),
|
||||||
None,
|
None,
|
||||||
"no path should exist to disconnected vertex"
|
"no path should exist to disconnected vertex"
|
||||||
);
|
);
|
||||||
@@ -77,7 +78,7 @@ macro_rules! find_path_tests {
|
|||||||
fn find_path_where_no_match() {
|
fn find_path_where_no_match() {
|
||||||
let (graph, vertices, _, _) = make_test_graph();
|
let (graph, vertices, _, _) = make_test_graph();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
$crate::algorithms::find_path_where(&graph, vertices[0], |_| false),
|
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |_| false),
|
||||||
None,
|
None,
|
||||||
"no path should exist when predicate never matches"
|
"no path should exist when predicate never matches"
|
||||||
);
|
);
|
||||||
@@ -87,7 +88,7 @@ macro_rules! find_path_tests {
|
|||||||
fn find_path_where() {
|
fn find_path_where() {
|
||||||
let (graph, vertices, _, _) = make_test_graph();
|
let (graph, vertices, _, _) = make_test_graph();
|
||||||
let path =
|
let path =
|
||||||
$crate::algorithms::find_path_where(&graph, vertices[0], |v| v == vertices[9])
|
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[9])
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"path should exist between connected vertices {:?} and {:?}",
|
"path should exist between connected vertices {:?} and {:?}",
|
||||||
vertices[0], vertices[9]
|
vertices[0], vertices[9]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! graph_topology_test_fixtures {
|
macro_rules! graph_topology_test_fixtures {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
@@ -101,6 +102,7 @@ macro_rules! graph_topology_test_fixtures {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! graph_topology_tests {
|
macro_rules! graph_topology_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
@@ -748,6 +750,7 @@ macro_rules! graph_topology_tests {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! graph_topology_deletion_tests {
|
macro_rules! graph_topology_deletion_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
|
|||||||
+14
-10
@@ -1,3 +1,4 @@
|
|||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! vertex_map_tests {
|
macro_rules! vertex_map_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
@@ -52,16 +53,16 @@ macro_rules! vertex_map_tests {
|
|||||||
let mut graph = <$T>::new();
|
let mut graph = <$T>::new();
|
||||||
graph.add_vertex();
|
graph.add_vertex();
|
||||||
let mut map = graph.vertex_map(42);
|
let mut map = graph.vertex_map(42);
|
||||||
let initial_len = map.len();
|
let capacity_before = map.capacity();
|
||||||
while graph.vertex_capacity() <= initial_len {
|
while graph.vertex_capacity() <= capacity_before {
|
||||||
graph.add_vertex();
|
graph.add_vertex();
|
||||||
}
|
}
|
||||||
assert!(
|
assert!(
|
||||||
map.len() < graph.vertex_capacity(),
|
map.capacity() < graph.vertex_capacity(),
|
||||||
"precondition: map is stale before sync"
|
"precondition: map is stale before sync"
|
||||||
);
|
);
|
||||||
map.sync(&graph);
|
map.sync(&graph);
|
||||||
assert_eq!(map.len(), graph.vertex_capacity());
|
assert_eq!(map.capacity(), graph.vertex_capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -78,6 +79,7 @@ macro_rules! vertex_map_tests {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! vertex_map_deletion_tests {
|
macro_rules! vertex_map_deletion_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
@@ -107,7 +109,7 @@ macro_rules! vertex_map_deletion_tests {
|
|||||||
let capacity_before = graph.vertex_capacity();
|
let capacity_before = graph.vertex_capacity();
|
||||||
graph.delete_vertex(v2);
|
graph.delete_vertex(v2);
|
||||||
map.sync(&graph);
|
map.sync(&graph);
|
||||||
assert_eq!(map.len(), capacity_before);
|
assert_eq!(map.capacity(), capacity_before);
|
||||||
assert_eq!(map[v1], 5);
|
assert_eq!(map[v1], 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +132,7 @@ macro_rules! vertex_map_deletion_tests {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! edge_map_tests {
|
macro_rules! edge_map_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
@@ -194,16 +197,16 @@ macro_rules! edge_map_tests {
|
|||||||
let v2 = graph.add_vertex();
|
let v2 = graph.add_vertex();
|
||||||
graph.add_edge(v1, v2);
|
graph.add_edge(v1, v2);
|
||||||
let mut map = graph.edge_map(42);
|
let mut map = graph.edge_map(42);
|
||||||
let initial_len = map.len();
|
let capacity_before = map.capacity();
|
||||||
while graph.edge_capacity() <= initial_len {
|
while graph.edge_capacity() <= capacity_before {
|
||||||
graph.add_edge(v1, v2);
|
graph.add_edge(v1, v2);
|
||||||
}
|
}
|
||||||
assert!(
|
assert!(
|
||||||
map.len() < graph.edge_capacity(),
|
map.capacity() < graph.edge_capacity(),
|
||||||
"precondition: map is stale before sync"
|
"precondition: map is stale before sync"
|
||||||
);
|
);
|
||||||
map.sync(&graph);
|
map.sync(&graph);
|
||||||
assert_eq!(map.len(), graph.edge_capacity());
|
assert_eq!(map.capacity(), graph.edge_capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -222,6 +225,7 @@ macro_rules! edge_map_tests {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! edge_map_deletion_tests {
|
macro_rules! edge_map_deletion_tests {
|
||||||
($T:ty) => {
|
($T:ty) => {
|
||||||
@@ -255,7 +259,7 @@ macro_rules! edge_map_deletion_tests {
|
|||||||
let capacity_before = graph.edge_capacity();
|
let capacity_before = graph.edge_capacity();
|
||||||
graph.delete_edge(e2);
|
graph.delete_edge(e2);
|
||||||
map.sync(&graph);
|
map.sync(&graph);
|
||||||
assert_eq!(map.len(), capacity_before);
|
assert_eq!(map.capacity(), capacity_before);
|
||||||
assert_eq!(map[e1], 5);
|
assert_eq!(map[e1], 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+215
-3
@@ -2,35 +2,247 @@ use crate::maps::{EdgeMap, VertexMap};
|
|||||||
|
|
||||||
// TODO: Add functions to reserve memory for vertices and edges.
|
// TODO: Add functions to reserve memory for vertices and edges.
|
||||||
// TODO: Split out GraphTopologyAddition trait.
|
// TODO: Split out GraphTopologyAddition trait.
|
||||||
|
/// A trait representing an undirected graph topology.
|
||||||
|
///
|
||||||
|
/// An undirected graph is a set of vertices and undirected edges, where each edge connects either
|
||||||
|
/// exactly two vertices or one vertex with itself (loop edge). This trait provides methods for
|
||||||
|
/// querying a graph topology, iterating over vertices and edges, and adding new vertices and edges.
|
||||||
|
///
|
||||||
|
/// # Vertices and Edges
|
||||||
|
///
|
||||||
|
/// Vertices and edges are identified by opaque handles ([`Vertex`] and [`Edge`]) that implement
|
||||||
|
/// [`Copy`] and [`Eq`]. Handles remain valid for the lifetime of the graph unless the graph also
|
||||||
|
/// implements [`GraphTopologyDeletion`] and the item identified by the handle is explicitly
|
||||||
|
/// deleted.
|
||||||
|
///
|
||||||
|
/// Methods accepting vertices or edges as parameters panic if the handle was invalidated by a
|
||||||
|
/// deletion, and return incorrect results if the handle was not produced by this graph instance.
|
||||||
|
///
|
||||||
|
/// # Deletion
|
||||||
|
///
|
||||||
|
/// This trait covers graph construction and querying only. To delete vertices and edges, see
|
||||||
|
/// [`GraphTopologyDeletion`].
|
||||||
|
///
|
||||||
|
/// [`Edge`]: GraphTopology::Edge
|
||||||
|
/// [`Vertex`]: GraphTopology::Vertex
|
||||||
pub trait GraphTopology {
|
pub trait GraphTopology {
|
||||||
|
/// An opaque, stable handle identifying a vertex.
|
||||||
type Vertex: Copy + Eq;
|
type Vertex: Copy + Eq;
|
||||||
type Edge: Copy + Eq;
|
|
||||||
type IncidenceCursor: IncidenceCursor<Self> + Copy;
|
|
||||||
|
|
||||||
|
/// An opaque, stable handle identifying an edge.
|
||||||
|
type Edge: Copy + Eq;
|
||||||
|
|
||||||
|
/// A resumable position in the incidence list of a vertex.
|
||||||
|
///
|
||||||
|
/// Prefer [`incidences`](Self::incidences) for straightforward iteration. A cursor is useful
|
||||||
|
/// when an algorithm needs to pause traversal, perform other graph queries or mutations, and
|
||||||
|
/// then continue from where it left off. This cursor type can be obtained via
|
||||||
|
/// [`incidence_cursor`](Self::incidence_cursor).
|
||||||
|
type IncidenceCursor: IncidenceCursor<Self>;
|
||||||
|
|
||||||
|
/// Returns the number of vertices in the graph.
|
||||||
fn vertex_count(&self) -> usize;
|
fn vertex_count(&self) -> usize;
|
||||||
|
|
||||||
|
/// Returns the total number of vertices the graph can hold without reallocating.
|
||||||
fn vertex_capacity(&self) -> usize;
|
fn vertex_capacity(&self) -> usize;
|
||||||
|
|
||||||
|
/// Creates and returns a [`VertexMap`] with every slot initialised to `default`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use grapherity::prelude::*;
|
||||||
|
/// # use grapherity::models::Graph;
|
||||||
|
/// let mut graph = Graph::new();
|
||||||
|
/// let v1 = graph.add_vertex();
|
||||||
|
/// let mut labels = graph.vertex_map("z");
|
||||||
|
/// assert_eq!(labels[v1], "z");
|
||||||
|
/// labels[v1] = "a";
|
||||||
|
/// assert_eq!(labels[v1], "a");
|
||||||
|
///
|
||||||
|
/// // A new vertex is immediately available for read and write.
|
||||||
|
/// let v2 = graph.add_vertex();
|
||||||
|
/// assert_eq!(labels[v2], "z");
|
||||||
|
/// labels[v2] = "b";
|
||||||
|
/// assert_eq!(labels[v2], "b");
|
||||||
|
/// ```
|
||||||
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T>;
|
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T>;
|
||||||
|
|
||||||
|
/// Returns the number of edges in the graph.
|
||||||
fn edge_count(&self) -> usize;
|
fn edge_count(&self) -> usize;
|
||||||
|
|
||||||
|
/// Returns the total number of edges the graph can hold without reallocating.
|
||||||
fn edge_capacity(&self) -> usize;
|
fn edge_capacity(&self) -> usize;
|
||||||
|
|
||||||
|
/// Creates and returns an [`EdgeMap`] with every slot initialised to `default`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use grapherity::prelude::*;
|
||||||
|
/// # use grapherity::models::Graph;
|
||||||
|
/// let mut graph = Graph::new();
|
||||||
|
/// let v1 = graph.add_vertex();
|
||||||
|
/// let v2 = graph.add_vertex();
|
||||||
|
/// let e1 = graph.add_edge(v1, v2);
|
||||||
|
/// let mut weights = graph.edge_map(5);
|
||||||
|
/// assert_eq!(weights[e1], 5);
|
||||||
|
/// weights[e1] = 1;
|
||||||
|
/// assert_eq!(weights[e1], 1);
|
||||||
|
///
|
||||||
|
/// // A new edge is immediately available for read and write.
|
||||||
|
/// let e2 = graph.add_edge(v1, v2);
|
||||||
|
/// assert_eq!(weights[e2], 5);
|
||||||
|
/// weights[e2] = 2;
|
||||||
|
/// assert_eq!(weights[e2], 2);
|
||||||
|
/// ```
|
||||||
fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T>;
|
fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T>;
|
||||||
|
|
||||||
|
/// Returns the degree of `v`, i.e. the number of incident edges, where each loop contributes 2.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `v` is not a valid vertex of this graph.
|
||||||
fn degree(&self, v: Self::Vertex) -> usize;
|
fn degree(&self, v: Self::Vertex) -> usize;
|
||||||
|
|
||||||
|
/// Returns `true` if there is at least one edge between `v1` and `v2`, and `false` otherwise.
|
||||||
|
///
|
||||||
|
/// A vertex is adjacent to itself if and only if it has a loop edge.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `v1` or `v2` is not a valid vertex of this graph.
|
||||||
fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool;
|
fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool;
|
||||||
|
|
||||||
|
/// Returns an iterator over all vertices in the graph.
|
||||||
fn vertices(&self) -> impl Iterator<Item = Self::Vertex>;
|
fn vertices(&self) -> impl Iterator<Item = Self::Vertex>;
|
||||||
|
|
||||||
|
/// Returns an iterator over all vertices adjacent to `v`.
|
||||||
|
///
|
||||||
|
/// Algorithms may prefer this over [`incidences`](Self::incidences) if the edges are not
|
||||||
|
/// required, since implementors may be able to provide an iterator faster than the trivial
|
||||||
|
/// mapping. The complexity of this method must not exceed that of
|
||||||
|
/// [`incidences`](Self::incidences).
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `v` is not a valid vertex of this graph.
|
||||||
fn adjacent_vertices(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Vertex>;
|
fn adjacent_vertices(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Vertex>;
|
||||||
|
|
||||||
|
/// Returns the two endpoints of edge `e`, which are identical if and only if `e` is a loop
|
||||||
|
/// edge.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `e` is not a valid edge of this graph.
|
||||||
fn incident_vertices(&self, e: Self::Edge) -> (Self::Vertex, Self::Vertex);
|
fn incident_vertices(&self, e: Self::Edge) -> (Self::Vertex, Self::Vertex);
|
||||||
|
|
||||||
|
/// Returns an iterator over all edges in the graph.
|
||||||
fn edges(&self) -> impl Iterator<Item = Self::Edge>;
|
fn edges(&self) -> impl Iterator<Item = Self::Edge>;
|
||||||
|
|
||||||
|
/// Returns an iterator over all edges incident to `v`.
|
||||||
|
///
|
||||||
|
/// Algorithms may prefer this over [`incidences`](Self::incidences) if the vertices are not
|
||||||
|
/// required, since implementors may be able to provide an iterator faster than the trivial
|
||||||
|
/// mapping. The complexity of this method must not exceed that of
|
||||||
|
/// [`incidences`](Self::incidences).
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `v` is not a valid vertex of this graph.
|
||||||
fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge>;
|
fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge>;
|
||||||
|
|
||||||
|
/// Returns an iterator over all incidences of `v`, i.e. vertex-edge pairs `(u, e)` such that
|
||||||
|
/// `u` is adjacent to `v` and `e` is an edge between them.
|
||||||
|
///
|
||||||
|
/// Use [`incidence_cursor`](Self::incidence_cursor) instead for a traversal that needs to
|
||||||
|
/// suspend and resume across other state updates.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `v` is not a valid vertex of this graph.
|
||||||
fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)>;
|
fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)>;
|
||||||
|
|
||||||
|
/// Returns a cursor over all incidences of `v`, analogously to
|
||||||
|
/// [`incidences`](Self::incidences), initially positioned before the first one.
|
||||||
|
///
|
||||||
|
/// See [`IncidenceCursor`](Self::IncidenceCursor) for when to prefer a cursor over
|
||||||
|
/// [`incidences`](Self::incidences).
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `v` is not a valid vertex of this graph.
|
||||||
fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor;
|
fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor;
|
||||||
|
|
||||||
|
/// Adds a new isolated vertex and returns its handle.
|
||||||
fn add_vertex(&mut self) -> Self::Vertex;
|
fn add_vertex(&mut self) -> Self::Vertex;
|
||||||
|
|
||||||
|
/// Adds a new edge between `v1` and `v2` and returns its handle.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `v1` or `v2` is not a valid vertex of this graph.
|
||||||
fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge;
|
fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait that adds deletion operations to an undirected graph topology.
|
||||||
|
///
|
||||||
|
/// This trait provides methods for deletion of vertices and edges in an undirected graph. These
|
||||||
|
/// operations will invalidate handles to all deleted vertices and edges. Methods accepting invalid
|
||||||
|
/// vertices or edges as parameters panic.
|
||||||
pub trait GraphTopologyDeletion: GraphTopology {
|
pub trait GraphTopologyDeletion: GraphTopology {
|
||||||
|
/// Deletes the vertex `v` and all its incident edges from the graph. Note that this also
|
||||||
|
/// invalidates the handles of all edges incident to `v`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `v` is not a valid vertex of this graph.
|
||||||
fn delete_vertex(&mut self, v: Self::Vertex);
|
fn delete_vertex(&mut self, v: Self::Vertex);
|
||||||
|
|
||||||
|
/// Deletes the edge `e` from the graph. This operation only invalidates `e` and no other vertex
|
||||||
|
/// or edge handles.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `e` is not a valid edge of this graph.
|
||||||
fn delete_edge(&mut self, e: Self::Edge);
|
fn delete_edge(&mut self, e: Self::Edge);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait IncidenceCursor<G: GraphTopology + ?Sized> {
|
/// A cursor for traversing the incidences of a vertex one step at a time.
|
||||||
|
///
|
||||||
|
/// Because the cursor is [`Copy`], its state can be saved and restored to replay or branch a
|
||||||
|
/// traversal.
|
||||||
|
///
|
||||||
|
/// Cursors are obtained via [`GraphTopology::incidence_cursor`]. See
|
||||||
|
/// [`GraphTopology::IncidenceCursor`] for guidance on when to prefer a cursor over
|
||||||
|
/// [`GraphTopology::incidences`].
|
||||||
|
pub trait IncidenceCursor<G: GraphTopology + ?Sized>: Copy {
|
||||||
|
/// Advances the cursor and returns the next incidence as `Some((u, e))`, or `None` if the
|
||||||
|
/// traversal is exhausted.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use grapherity::prelude::*;
|
||||||
|
/// # use grapherity::models::Graph;
|
||||||
|
/// // Constructs a graph with two vertices connected to `v`.
|
||||||
|
/// let mut graph = Graph::new();
|
||||||
|
/// let v = graph.add_vertex();
|
||||||
|
/// for _ in 0..2 {
|
||||||
|
/// let u = graph.add_vertex();
|
||||||
|
/// graph.add_edge(u, v);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Iterates over the incidences of `v` with a cursor.
|
||||||
|
/// let mut c1 = graph.incidence_cursor(v);
|
||||||
|
/// assert!(c1.next(&graph).is_some());
|
||||||
|
/// let mut c2 = c1;
|
||||||
|
/// // Continues iteration with original cursor.
|
||||||
|
/// assert!(c1.next(&graph).is_some());
|
||||||
|
/// assert!(c1.next(&graph).is_none());
|
||||||
|
/// // Iterates over the last incidence again with the copied cursor.
|
||||||
|
/// assert!(c2.next(&graph).is_some());
|
||||||
|
/// assert!(c2.next(&graph).is_none());
|
||||||
|
/// ```
|
||||||
fn next(&mut self, graph: &G) -> Option<(G::Vertex, G::Edge)>;
|
fn next(&mut self, graph: &G) -> Option<(G::Vertex, G::Edge)>;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
mod append_graph_tests {
|
mod append_graph_tests {
|
||||||
use grapherity::models::append_graph::AppendGraph;
|
use grapherity::models::AppendGraph;
|
||||||
|
|
||||||
grapherity::graph_topology_test_fixtures!(AppendGraph);
|
grapherity::graph_topology_test_fixtures!(AppendGraph);
|
||||||
grapherity::bfs_tests!(AppendGraph);
|
grapherity::bfs_tests!(AppendGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
mod graph_tests {
|
mod graph_tests {
|
||||||
use grapherity::models::graph::Graph;
|
use grapherity::models::Graph;
|
||||||
|
|
||||||
grapherity::graph_topology_test_fixtures!(Graph);
|
grapherity::graph_topology_test_fixtures!(Graph);
|
||||||
grapherity::bfs_tests!(Graph);
|
grapherity::bfs_tests!(Graph);
|
||||||
|
|||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
mod append_graph_tests {
|
mod append_graph_tests {
|
||||||
use grapherity::models::append_graph::AppendGraph;
|
use grapherity::models::AppendGraph;
|
||||||
|
|
||||||
grapherity::graph_topology_test_fixtures!(AppendGraph);
|
grapherity::graph_topology_test_fixtures!(AppendGraph);
|
||||||
grapherity::dfs_tests!(AppendGraph);
|
grapherity::dfs_tests!(AppendGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
mod graph_tests {
|
mod graph_tests {
|
||||||
use grapherity::models::graph::Graph;
|
use grapherity::models::Graph;
|
||||||
|
|
||||||
grapherity::graph_topology_test_fixtures!(Graph);
|
grapherity::graph_topology_test_fixtures!(Graph);
|
||||||
grapherity::dfs_tests!(Graph);
|
grapherity::dfs_tests!(Graph);
|
||||||
|
|||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
mod append_graph_tests {
|
mod append_graph_tests {
|
||||||
use grapherity::models::append_graph::AppendGraph;
|
use grapherity::models::AppendGraph;
|
||||||
|
|
||||||
grapherity::graph_topology_test_fixtures!(AppendGraph);
|
grapherity::graph_topology_test_fixtures!(AppendGraph);
|
||||||
grapherity::dijkstra_tests!(AppendGraph);
|
grapherity::dijkstra_tests!(AppendGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
mod graph_tests {
|
mod graph_tests {
|
||||||
use grapherity::models::graph::Graph;
|
use grapherity::models::Graph;
|
||||||
|
|
||||||
grapherity::graph_topology_test_fixtures!(Graph);
|
grapherity::graph_topology_test_fixtures!(Graph);
|
||||||
grapherity::dijkstra_tests!(Graph);
|
grapherity::dijkstra_tests!(Graph);
|
||||||
|
|||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
mod append_graph_tests {
|
mod append_graph_tests {
|
||||||
use grapherity::models::append_graph::AppendGraph;
|
use grapherity::models::AppendGraph;
|
||||||
|
|
||||||
grapherity::graph_topology_test_fixtures!(AppendGraph);
|
grapherity::graph_topology_test_fixtures!(AppendGraph);
|
||||||
grapherity::find_path_tests!(AppendGraph);
|
grapherity::find_path_tests!(AppendGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
mod graph_tests {
|
mod graph_tests {
|
||||||
use grapherity::models::graph::Graph;
|
use grapherity::models::Graph;
|
||||||
|
|
||||||
grapherity::graph_topology_test_fixtures!(Graph);
|
grapherity::graph_topology_test_fixtures!(Graph);
|
||||||
grapherity::find_path_tests!(Graph);
|
grapherity::find_path_tests!(Graph);
|
||||||
|
|||||||
+4
-4
@@ -1,24 +1,24 @@
|
|||||||
mod append_graph_vertex_map_tests {
|
mod append_graph_vertex_map_tests {
|
||||||
use grapherity::models::append_graph::AppendGraph;
|
use grapherity::models::AppendGraph;
|
||||||
|
|
||||||
grapherity::vertex_map_tests!(AppendGraph);
|
grapherity::vertex_map_tests!(AppendGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
mod append_graph_edge_map_tests {
|
mod append_graph_edge_map_tests {
|
||||||
use grapherity::models::append_graph::AppendGraph;
|
use grapherity::models::AppendGraph;
|
||||||
|
|
||||||
grapherity::edge_map_tests!(AppendGraph);
|
grapherity::edge_map_tests!(AppendGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
mod graph_vertex_map_tests {
|
mod graph_vertex_map_tests {
|
||||||
use grapherity::models::graph::Graph;
|
use grapherity::models::Graph;
|
||||||
|
|
||||||
grapherity::vertex_map_tests!(Graph);
|
grapherity::vertex_map_tests!(Graph);
|
||||||
grapherity::vertex_map_deletion_tests!(Graph);
|
grapherity::vertex_map_deletion_tests!(Graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
mod graph_edge_map_tests {
|
mod graph_edge_map_tests {
|
||||||
use grapherity::models::graph::Graph;
|
use grapherity::models::Graph;
|
||||||
|
|
||||||
grapherity::edge_map_tests!(Graph);
|
grapherity::edge_map_tests!(Graph);
|
||||||
grapherity::edge_map_deletion_tests!(Graph);
|
grapherity::edge_map_deletion_tests!(Graph);
|
||||||
|
|||||||
Reference in New Issue
Block a user