16 Commits

Author SHA1 Message Date
warrence 53a6bed932 Add todos 2026-07-31 10:24:51 +02:00
warrence afaf161344 Rename 'weight' parameter in Dijkstra's algorithm to 'weights' 2026-07-31 10:23:47 +02:00
warrence 0169343ac8 Merge branch 'main' into release-0.3.0 2026-07-31 10:16:03 +02:00
warrence 399b36bb16 Add documentation for Graph and AppendGraph EntityMap types 2026-07-24 10:13:15 +02:00
warrence 3708c1e0d1 Fix formatting 2026-07-22 13:45:46 +02:00
warrence 9608796f23 Move dfs_find_path tests into dfs_testing 2026-07-22 13:44:32 +02:00
warrence c253e4a840 Add tests for GraphTopology::vertex_map and edge_map 2026-07-22 13:31:15 +02:00
warrence d711e68577 Replace VertexMap and EdgeMap with now public EntityMap 2026-07-22 13:30:09 +02:00
warrence 8db7a86187 Add Petersen graph generator 2026-07-18 23:38:08 +02:00
warrence ad9850800d Update IncidenceCursor trait doc with note on Copy 2026-07-17 11:30:45 +02:00
warrence a14202effc Merge branch 'main' into release-0.3.0 2026-07-17 11:29:41 +02:00
warrence 913a9e0baa Fix AppendGraph::capacity to actually use the underlying vec's capacity 2026-07-17 09:54:16 +02:00
warrence 8c6c98859c Add Copy to IncidenceCursor trait 2026-07-17 09:53:05 +02:00
warrence 24c08438d6 Add IncidenceCursor to prelude 2026-07-17 09:52:35 +02:00
warrence bf6a7142f3 Fix readme typo 2026-07-17 09:50:43 +02:00
warrence fb9dfe8280 Renamed len() to capacity() in EntityMap, VertexMap, and EdgeMap
len() is generally expected to describe the number of elements in a collection,
but in this case elements beyond len() are accessible without restriction.
capacity() is a better name because it describes data storage size.
2026-07-10 11:58:45 +02:00
16 changed files with 314 additions and 495 deletions
+24 -22
View File
@@ -31,7 +31,7 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{BinaryHeap, VecDeque}; use std::collections::{BinaryHeap, VecDeque};
use crate::maps::VertexMap; use crate::maps::EntityMap;
use crate::traits::{GraphTopology, IncidenceCursor}; use crate::traits::{GraphTopology, IncidenceCursor};
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
@@ -55,16 +55,17 @@ 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: EntityMap<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: EntityMap<V, Option<V>>,
} }
// TODO: Generalize the return type of the weight function. // TODO: Generalize the return type of the weight function.
// TODO: Add complexity information for Dijkstra's algorithm variants.
/// [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 /// 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 /// by `weights` 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 /// of each vertex on some shortest path from `source` to that vertex. Returns `None` for any vertex
/// not connected to `source`. /// not connected to `source`.
/// ///
@@ -91,13 +92,13 @@ pub struct DijkstraResult<V: Copy> {
/// ``` /// ```
/// ///
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm /// [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, weights: W) -> DijkstraResult<G::Vertex>
where where
G: GraphTopology, G: GraphTopology,
W: Fn(G::Edge) -> u32, W: Fn(G::Edge) -> u32,
{ {
let mut predecessors = graph.vertex_map(None); let mut predecessors = graph.vertex_map(None);
let distances = dijkstra_impl(graph, source, weight, |adjacent, predecessor| { let distances = dijkstra_impl(graph, source, weights, |adjacent, predecessor| {
predecessors[adjacent] = Some(predecessor); predecessors[adjacent] = Some(predecessor);
}); });
DijkstraResult { DijkstraResult {
@@ -109,7 +110,7 @@ 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 /// 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 /// by `weights` function. Returns the distances from `source` to each vertex. Returns `None` for any
/// vertex not connected to `source`. /// vertex not connected to `source`.
/// ///
/// # Panics /// # Panics
@@ -137,13 +138,13 @@ where
pub fn dijkstra_distances<G, W>( pub fn dijkstra_distances<G, W>(
graph: &G, graph: &G,
source: G::Vertex, source: G::Vertex,
weight: W, weights: W,
) -> VertexMap<G::Vertex, Option<u32>> ) -> EntityMap<G::Vertex, Option<u32>>
where where
G: GraphTopology, G: GraphTopology,
W: Fn(G::Edge) -> u32, W: Fn(G::Edge) -> u32,
{ {
dijkstra_impl(graph, source, weight, |_, _| {}) dijkstra_impl(graph, source, weights, |_, _| {})
} }
/// [Dijkstra's algorithm] with unit edge weights, returns minimum distances and predecessors. /// [Dijkstra's algorithm] with unit edge weights, returns minimum distances and predecessors.
@@ -213,7 +214,7 @@ where
pub fn dijkstra_distances_unweighted<G>( pub fn dijkstra_distances_unweighted<G>(
graph: &G, graph: &G,
source: G::Vertex, source: G::Vertex,
) -> VertexMap<G::Vertex, Option<u32>> ) -> EntityMap<G::Vertex, Option<u32>>
where where
G: GraphTopology, G: GraphTopology,
{ {
@@ -223,9 +224,9 @@ where
fn dijkstra_impl<G, W, F>( fn dijkstra_impl<G, W, F>(
graph: &G, graph: &G,
source: G::Vertex, source: G::Vertex,
weight: W, weights: W,
mut on_relax: F, mut on_relax: F,
) -> VertexMap<G::Vertex, Option<u32>> ) -> EntityMap<G::Vertex, Option<u32>>
where where
G: GraphTopology, G: GraphTopology,
W: Fn(G::Edge) -> u32, W: Fn(G::Edge) -> u32,
@@ -242,7 +243,7 @@ where
while let Some(v) = heap.pop() { while let Some(v) = heap.pop() {
for incidence in graph.incidences(v.vertex) { for incidence in graph.incidences(v.vertex) {
let new_distance = distances[v.vertex].unwrap() + weight(incidence.1); let new_distance = distances[v.vertex].unwrap() + weights(incidence.1);
if match distances[incidence.0] { if match distances[incidence.0] {
None => true, None => true,
Some(old_distance) if old_distance > new_distance => true, Some(old_distance) if old_distance > new_distance => true,
@@ -263,9 +264,9 @@ where
/// Return data type for [`bfs`]. /// 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. /// Vertex map of minimum distances from a given `source` vertex.
pub distances: VertexMap<V, Option<u32>>, pub distances: EntityMap<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: EntityMap<V, Option<V>>,
} }
/// [Breadth-first search] traversal from `source`, returns distances and predecessors. /// [Breadth-first search] traversal from `source`, returns distances and predecessors.
@@ -341,7 +342,7 @@ where
/// ``` /// ```
/// ///
/// [Breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search /// [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) -> EntityMap<G::Vertex, Option<u32>>
where where
G: GraphTopology, G: GraphTopology,
{ {
@@ -423,7 +424,7 @@ where
} }
struct BfsImplResult<V: Copy> { struct BfsImplResult<V: Copy> {
distances: VertexMap<V, Option<u32>>, distances: EntityMap<V, Option<u32>>,
found: Option<(V, u32)>, found: Option<(V, u32)>,
} }
@@ -459,12 +460,13 @@ where
} }
} }
// TODO: 'visited' is already encoded in 'predecessors', except for 'source', which is visited, but has no predecessor.
/// Return data type for [`dfs`]. /// 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. /// Vertex map indicating which vertices were visited during the search.
pub visited: VertexMap<V, bool>, pub visited: EntityMap<V, bool>,
/// Vertex map of predecessors on the DFS tree path from a given `source` vertex. /// Vertex map of predecessors on the DFS tree path from a given `source` vertex.
pub predecessors: VertexMap<V, Option<V>>, pub predecessors: EntityMap<V, Option<V>>,
} }
/// [Depth-first search] traversal from `source`, returns visited vertices and predecessors. /// [Depth-first search] traversal from `source`, returns visited vertices and predecessors.
@@ -540,7 +542,7 @@ where
/// ``` /// ```
/// ///
/// [Depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search /// [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) -> EntityMap<G::Vertex, bool>
where where
G: GraphTopology, G: GraphTopology,
{ {
@@ -620,7 +622,7 @@ where
} }
struct DfsImplResult<V: Copy> { struct DfsImplResult<V: Copy> {
visited: VertexMap<V, bool>, visited: EntityMap<V, bool>,
found: Option<V>, found: Option<V>,
} }
+69
View File
@@ -0,0 +1,69 @@
use crate::traits::GraphTopology;
pub fn petersen<G>(
graph: &mut G,
) -> (
[<G as GraphTopology>::Vertex; 10],
[<G as GraphTopology>::Edge; 15],
)
where
G: GraphTopology,
{
const N: usize = 5;
const K: usize = 2;
let vertices = core::array::from_fn(|_| graph.add_vertex());
let edges = core::array::from_fn(|j| {
let i = j / 3;
match j % 3 {
0 => {
let outer = if i < N - 1 { i + 1 } else { 0 };
graph.add_edge(vertices[i], vertices[outer])
}
1 => {
let inner = if i < N - K { i + N + K } else { i + K };
graph.add_edge(vertices[i + N], vertices[inner])
}
_ => graph.add_edge(vertices[i], vertices[i + N]),
}
});
(vertices, edges)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::AppendGraph;
#[test]
fn petersen() {
let mut graph = AppendGraph::new();
let (vertices, edges) = super::petersen(&mut graph);
assert_eq!(
graph.vertex_count(),
10,
"unexpected number of added vertices"
);
assert_eq!(graph.edge_count(), 15, "unexpected number of added edges");
assert!(
graph.vertices().all(|v| graph.degree(v) == 3),
"all vertices should have degree 3"
);
assert_handles_in_graph(&graph, &vertices, &edges);
}
fn assert_handles_in_graph<G: GraphTopology>(
graph: &G,
vertices: &[G::Vertex],
edges: &[G::Edge],
) {
assert!(
vertices.iter().all(|&v| graph.vertices().any(|u| u == v)),
"all returned vertices should be in the graph"
);
assert!(
edges.iter().all(|&e| graph.edges().any(|f| f == e)),
"all returned edges should be in the graph"
);
}
}
+4 -4
View File
@@ -7,7 +7,7 @@
//! //!
//! * Undirected graph types [`Graph`] and [`AppendGraph`] built on a flat, index-based adjacency //! * Undirected graph types [`Graph`] and [`AppendGraph`] built on a flat, index-based adjacency
//! list, //! list,
//! * [`VertexMap`] and [`EdgeMap`] to freely associate custom data with vertices and edges, //! * [`EntityMap`] to freely associate custom data with vertices and edges,
//! * Connectivity and pathing algorithms: [Dijkstra's algorithm], [DFS], and [BFS] in different //! * Connectivity and pathing algorithms: [Dijkstra's algorithm], [DFS], and [BFS] in different
//! variants, //! variants,
//! * Exposed traits to implement custom graph data structures or algorithms. //! * Exposed traits to implement custom graph data structures or algorithms.
@@ -118,8 +118,7 @@
//! [BFS]: crate::algorithms::bfs //! [BFS]: crate::algorithms::bfs
//! [Dijkstra's algorithm]: crate::algorithms::dijkstra //! [Dijkstra's algorithm]: crate::algorithms::dijkstra
//! [DFS]: crate::algorithms::dfs //! [DFS]: crate::algorithms::dfs
//! [`EdgeMap`]: crate::maps::EdgeMap //! [`EntityMap`]: crate::maps::EntityMap
//! [`VertexMap`]: crate::maps::VertexMap
//! [`AppendGraph`]: crate::models::AppendGraph //! [`AppendGraph`]: crate::models::AppendGraph
//! [`Graph`]: crate::models::Graph //! [`Graph`]: crate::models::Graph
//! [`GraphTopology`]: crate::traits::GraphTopology //! [`GraphTopology`]: crate::traits::GraphTopology
@@ -130,13 +129,14 @@
#![warn(missing_docs)] #![warn(missing_docs)]
pub mod algorithms; pub mod algorithms;
pub mod generators;
pub mod maps; pub mod maps;
pub mod models; pub mod models;
pub mod traits; pub mod traits;
/// Convenience re-exports of graph topology traits for common use. /// Convenience re-exports of graph topology traits for common use.
pub mod prelude { pub mod prelude {
pub use crate::traits::{GraphTopology, GraphTopologyDeletion}; pub use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
} }
mod testing; mod testing;
+23 -140
View File
@@ -1,156 +1,32 @@
//! Provides maps to associate custom data to graph vertices and edges. //! Provides maps to associate custom data to graph vertices and edges.
//! //!
//! [`VertexMap`] and [`EdgeMap`] provide copy-on-write, [`Vec`]-backed maps to associate data to //! [`EntityMap`] provides a copy-on-write, [`Vec`]-backed map to associate data to either all
//! all vertices or all edges in a graph, respectively. //! vertices or all edges in a graph.
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use crate::traits::GraphTopology; /// A map to associate custom data to graph vertices or edges.
/// A map to associate custom data to graph vertices.
/// ///
/// This map uses raw entity indices to associate homogenous custom data of type `T` to graph /// This map uses raw entity indices to associate homogenous custom data of type `T` to graph
/// vertices. The implementation uses a [`Vec`], allocating contiguous slots for the data, which /// vertices or edges. The implementation uses a [`Vec`], allocating contiguous slots for the data,
/// means that the provided index conversion function should map vertices to contiguous indices, or /// which means that the provided index conversion function should map entities to contiguous
/// indices with relatively few gaps. /// indices, or indices with relatively few gaps.
/// ///
/// Data allocation happens as copy-on-write, i.e. the backing [`Vec`] is only resized if a value /// Data allocation happens as copy-on-write, i.e. the backing [`Vec`] is only resized if a value is
/// beyond current capacity is written, but the `default` value can transparently be read. No bound /// written beyond current capacity, but the `default` value can transparently be read. No bound or
/// or validity checks are performed by the map on the provided vertex handles. /// validity checks are performed by the map on the provided entity handles.
/// ///
/// Use [`GraphTopology::vertex_map`] to obtain a `VertexMap`. /// Use [`GraphTopology::vertex_map`] or [`GraphTopology::edge_map`] to obtain an `EntityMap` for
pub struct VertexMap<V: Copy, T: Clone> { /// vertices or edges, respectively.
inner: EntityMap<V, T>, pub struct EntityMap<E: Copy, T: Clone> {
}
impl<V: Copy, T: Clone> VertexMap<V, T> {
/// Creates a new map with the given `default` value, an index conversion function `to_index`,
/// and an initial `capacity`.
pub fn new(default: T, to_index: fn(V) -> usize, capacity: usize) -> Self {
Self {
inner: EntityMap::new(default, to_index, capacity),
}
}
/// Returns the total number of data entries the map can write without reallocating. Reads
/// beyond `capacity` are valid and return the default value.
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[deprecated(since = "0.2.2", note = "use 'capacity' instead")]
#[allow(missing_docs)]
pub fn len(&self) -> usize {
self.capacity()
}
/// Expands the internal data storage capacity of the map to `capacity`. Does nothing if
/// capacity is already sufficient.
///
/// Use this before writing data for new graph vertices to avoid incremental growth on the first
/// write to each new vertex.
pub fn expand(&mut self, capacity: usize) {
self.inner.expand(capacity);
}
#[deprecated(
since = "0.2.4",
note = "use 'expand(graph.vertex_capacity())' instead"
)]
#[allow(missing_docs)]
pub fn sync<G: GraphTopology<Vertex = V>>(&mut self, graph: &G) {
self.inner.expand(graph.vertex_capacity());
}
}
impl<V: Copy, T: Clone> Index<V> for VertexMap<V, T> {
type Output = T;
fn index(&self, v: V) -> &T {
&self.inner[v]
}
}
impl<V: Copy, T: Clone> IndexMut<V> for VertexMap<V, T> {
fn index_mut(&mut self, v: V) -> &mut T {
&mut self.inner[v]
}
}
/// A map to associate custom data to graph edges.
///
/// This map uses raw entity indices to associate homogenous custom data of type `T` to graph edges.
/// The implementation uses a [`Vec`], allocating contiguous slots for the data, which means that
/// the provided index conversion function should map edges to contiguous indices, or indices with
/// relatively few gaps.
///
/// Data allocation happens as copy-on-write, i.e. the backing [`Vec`] is only resized if a value
/// beyond current capacity is written, but the `default` value can transparently be read. No bound
/// or validity checks are performed by the map on the provided edge handles.
///
/// Use [`GraphTopology::edge_map`] to obtain an `EdgeMap`.
pub struct EdgeMap<E: Copy, T: Clone> {
inner: EntityMap<E, T>,
}
impl<E: Copy, T: Clone> EdgeMap<E, T> {
/// Creates a new map with the given `default` value, an index conversion function `to_index`,
/// and an initial `capacity`.
pub fn new(default: T, to_index: fn(E) -> usize, capacity: usize) -> Self {
Self {
inner: EntityMap::new(default, to_index, capacity),
}
}
/// Returns the total number of data entries the map can write without reallocating. Reads
/// beyond `capacity` are valid and return the default value.
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[deprecated(since = "0.2.2", note = "use 'capacity' instead")]
#[allow(missing_docs)]
pub fn len(&self) -> usize {
self.capacity()
}
/// Expands the internal data storage capacity of the map to `capacity`. Does nothing if
/// capacity is already sufficient.
///
/// Use this before writing data for new graph edges to avoid incremental growth on the first
/// write to each new edge.
pub fn expand(&mut self, capacity: usize) {
self.inner.expand(capacity);
}
#[deprecated(since = "0.2.4", note = "use 'expand(graph.edge_capacity())' instead")]
#[allow(missing_docs)]
pub fn sync<G: GraphTopology<Edge = E>>(&mut self, graph: &G) {
self.inner.expand(graph.edge_capacity());
}
}
impl<E: Copy, T: Clone> Index<E> for EdgeMap<E, T> {
type Output = T;
fn index(&self, e: E) -> &T {
&self.inner[e]
}
}
impl<E: Copy, T: Clone> IndexMut<E> for EdgeMap<E, T> {
fn index_mut(&mut self, e: E) -> &mut T {
&mut self.inner[e]
}
}
struct EntityMap<E: Copy, T: Clone> {
data: Vec<T>, data: Vec<T>,
default: T, default: T,
to_index: fn(E) -> usize, to_index: fn(E) -> usize,
} }
impl<E: Copy, T: Clone> EntityMap<E, T> { impl<E: Copy, T: Clone> EntityMap<E, T> {
/// Creates a new map with the given `default` value, an index conversion function `to_index`,
/// and an initial `capacity`.
pub fn new(default: T, to_index: fn(E) -> usize, capacity: usize) -> Self { pub fn new(default: T, to_index: fn(E) -> usize, capacity: usize) -> Self {
Self { Self {
data: vec![default.clone(); capacity], data: vec![default.clone(); capacity],
@@ -159,12 +35,19 @@ impl<E: Copy, T: Clone> EntityMap<E, T> {
} }
} }
/// Returns the total number of data entries the map can write without reallocating. Reads
/// beyond `capacity` are valid and return the default value.
pub fn capacity(&self) -> usize { pub fn capacity(&self) -> usize {
self.data.len() self.data.capacity()
} }
/// Expands the internal data storage capacity of the map to `capacity`, Does nothing if
/// capacity is already sufficient.
///
/// Use this before writing data for new graph entities to avoid incremental growth on the first
/// write to each new entity.
pub fn expand(&mut self, capacity: usize) { pub fn expand(&mut self, capacity: usize) {
if capacity > self.data.capacity() { if capacity > self.data.len() {
self.data.resize(capacity, self.default.clone()); self.data.resize(capacity, self.default.clone());
} }
} }
+9 -9
View File
@@ -1,6 +1,6 @@
//! [`AppendGraph`], an undirected graph topology supporting addition only. //! [`AppendGraph`], an undirected graph topology supporting addition only.
use crate::maps::{EdgeMap, VertexMap}; use crate::maps::EntityMap;
use crate::traits::{GraphTopology, IncidenceCursor}; use crate::traits::{GraphTopology, IncidenceCursor};
/// An opaque handle identifying a vertex in an [`AppendGraph`]. /// An opaque handle identifying a vertex in an [`AppendGraph`].
@@ -17,11 +17,11 @@ pub struct Vertex(usize);
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Edge(usize); pub struct Edge(usize);
/// A [`VertexMap`] for [`AppendGraph`] vertices. /// An [`EntityMap`] for [`AppendGraph`] vertices.
pub type AppendGraphVertexMap<T> = VertexMap<Vertex, T>; pub type AppendGraphVertexMap<T> = EntityMap<Vertex, T>;
/// An [`EdgeMap`] for [`AppendGraph`] edges. /// An [`EntityMap`] for [`AppendGraph`] edges.
pub type AppendGraphEdgeMap<T> = EdgeMap<Edge, T>; pub type AppendGraphEdgeMap<T> = EntityMap<Edge, T>;
impl Edge { impl Edge {
fn normalize(&self) -> Self { fn normalize(&self) -> Self {
@@ -145,8 +145,8 @@ impl GraphTopology for AppendGraph {
self.vertices.capacity() self.vertices.capacity()
} }
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T> { fn vertex_map<T: Clone>(&self, default: T) -> EntityMap<Self::Vertex, T> {
VertexMap::new(default, |v| v.0, self.vertex_capacity()) EntityMap::new(default, |v| v.0, self.vertex_capacity())
} }
fn edge_count(&self) -> usize { fn edge_count(&self) -> usize {
@@ -157,8 +157,8 @@ impl GraphTopology for AppendGraph {
self.incidences.capacity() / 2 self.incidences.capacity() / 2
} }
fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T> { fn edge_map<T: Clone>(&self, default: T) -> EntityMap<Self::Edge, T> {
EdgeMap::new(default, |e| e.0 / 2, self.edge_capacity()) EntityMap::new(default, |e| e.0 / 2, self.edge_capacity())
} }
fn degree(&self, v: Self::Vertex) -> usize { fn degree(&self, v: Self::Vertex) -> usize {
+9 -9
View File
@@ -2,7 +2,7 @@
use typed_generational_arena::{Arena, Index}; use typed_generational_arena::{Arena, Index};
use crate::maps::{EdgeMap, VertexMap}; use crate::maps::EntityMap;
use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor}; use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor};
/// An opaque handle identifying a vertex in a [`Graph`]. /// An opaque handle identifying a vertex in a [`Graph`].
@@ -17,11 +17,11 @@ pub type Vertex = Index<VertexIncidenceHeader, usize, usize>;
/// deleted. Obtain via graph methods like [`Graph::add_edge`] and [`Graph::edges`]. /// deleted. Obtain via graph methods like [`Graph::add_edge`] and [`Graph::edges`].
pub type Edge = Index<IncidenceEntry, usize, usize>; pub type Edge = Index<IncidenceEntry, usize, usize>;
/// A [`VertexMap`] for [`Graph`] vertices. /// An [`EntityMap`] for [`Graph`] vertices.
pub type GraphVertexMap<T> = VertexMap<Vertex, T>; pub type GraphVertexMap<T> = EntityMap<Vertex, T>;
/// An [`EdgeMap`] for [`Graph`] edges. /// An [`EntityMap`] for [`Graph`] edges.
pub type GraphEdgeMap<T> = EdgeMap<Edge, T>; pub type GraphEdgeMap<T> = EntityMap<Edge, T>;
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
struct VertexSlot(usize); struct VertexSlot(usize);
@@ -240,8 +240,8 @@ impl GraphTopology for Graph {
self.vertices.capacity() self.vertices.capacity()
} }
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T> { fn vertex_map<T: Clone>(&self, default: T) -> EntityMap<Self::Vertex, T> {
VertexMap::new(default, |v| v.arr_idx(), self.vertex_capacity()) EntityMap::new(default, |v| v.arr_idx(), self.vertex_capacity())
} }
fn edge_count(&self) -> usize { fn edge_count(&self) -> usize {
@@ -252,8 +252,8 @@ impl GraphTopology for Graph {
self.incidences.capacity() / 2 self.incidences.capacity() / 2
} }
fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T> { fn edge_map<T: Clone>(&self, default: T) -> EntityMap<Self::Edge, T> {
EdgeMap::new(default, |e| e.arr_idx() / 2, self.edge_capacity()) EntityMap::new(default, |e| e.arr_idx() / 2, self.edge_capacity())
} }
fn degree(&self, v: Self::Vertex) -> usize { fn degree(&self, v: Self::Vertex) -> usize {
-1
View File
@@ -3,6 +3,5 @@
pub(crate) mod bfs_testing; pub(crate) mod bfs_testing;
pub(crate) mod dfs_testing; pub(crate) mod dfs_testing;
pub(crate) mod dijkstra_testing; pub(crate) mod dijkstra_testing;
pub(crate) mod find_path_testing;
pub(crate) mod graph_topology_testing; pub(crate) mod graph_topology_testing;
pub(crate) mod maps_testing; pub(crate) mod maps_testing;
+2 -2
View File
@@ -194,7 +194,7 @@ macro_rules! bfs_tests {
} }
fn assert_bfs_distances( fn assert_bfs_distances(
distances: &$crate::maps::VertexMap< distances: &$crate::maps::EntityMap<
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
Option<u32>, Option<u32>,
>, >,
@@ -222,7 +222,7 @@ macro_rules! bfs_tests {
} }
fn assert_bfs_predecessors( fn assert_bfs_predecessors(
predecessors: &$crate::maps::VertexMap< predecessors: &$crate::maps::EntityMap<
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
Option<<$T as $crate::traits::GraphTopology>::Vertex>, Option<<$T as $crate::traits::GraphTopology>::Vertex>,
>, >,
+122 -3
View File
@@ -163,7 +163,7 @@ macro_rules! dfs_tests {
} }
fn assert_dfs_visited( fn assert_dfs_visited(
visited: &$crate::maps::VertexMap<<$T as $crate::traits::GraphTopology>::Vertex, bool>, visited: &$crate::maps::EntityMap<<$T as $crate::traits::GraphTopology>::Vertex, bool>,
vertices: &[<$T as $crate::traits::GraphTopology>::Vertex], vertices: &[<$T as $crate::traits::GraphTopology>::Vertex],
) { ) {
for i in 0..10 { for i in 0..10 {
@@ -177,8 +177,8 @@ macro_rules! dfs_tests {
fn assert_dfs_predecessors( fn assert_dfs_predecessors(
graph: &$T, graph: &$T,
visited: &$crate::maps::VertexMap<<$T as $crate::traits::GraphTopology>::Vertex, bool>, visited: &$crate::maps::EntityMap<<$T as $crate::traits::GraphTopology>::Vertex, bool>,
predecessors: &$crate::maps::VertexMap< predecessors: &$crate::maps::EntityMap<
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
Option<<$T as $crate::traits::GraphTopology>::Vertex>, Option<<$T as $crate::traits::GraphTopology>::Vertex>,
>, >,
@@ -202,5 +202,124 @@ macro_rules! dfs_tests {
); );
} }
} }
#[test]
fn dfs_find_path_source_equals_target() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v = graph.add_vertex();
assert_eq!(
$crate::algorithms::dfs_find_path(&graph, v, v),
Some(vec![]),
"path from source to itself should be empty"
);
}
#[test]
fn dfs_find_path_disconnected() {
let (graph, vertices) = make_test_graph_disconnected();
assert_eq!(
$crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[1]),
None,
"no path should exist to disconnected vertex"
);
}
#[test]
fn dfs_find_path_adjacent() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let e = graph.add_edge(v1, v2);
let path = $crate::algorithms::dfs_find_path(&graph, v1, v2)
.expect("path should exist between adjacent vertices");
assert_eq!(
path.len(),
1,
"unexpected path length between adjacent vertices"
);
assert_eq!(path[0], e, "path should use the connecting edge");
}
#[test]
fn dfs_find_path() {
let (graph, vertices, _, _) = make_test_graph();
let path = $crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[9])
.expect(&format!(
"path should exist between connected vertices {:?} and {:?}",
vertices[0], vertices[9]
));
assert_valid_path(&graph, &path, vertices[0], vertices[9]);
}
#[test]
fn dfs_find_path_where_source_matches() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v = graph.add_vertex();
assert_eq!(
$crate::algorithms::dfs_find_path_where(&graph, v, |u| u == v),
Some(vec![]),
"path from source to itself should be empty"
);
}
#[test]
fn dfs_find_path_where_disconnected() {
let (graph, vertices) = make_test_graph_disconnected();
assert_eq!(
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[1]),
None,
"no path should exist to disconnected vertex"
);
}
#[test]
fn dfs_find_path_where_no_match() {
let (graph, vertices, _, _) = make_test_graph();
assert_eq!(
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |_| false),
None,
"no path should exist when predicate never matches"
);
}
#[test]
fn dfs_find_path_where() {
let (graph, vertices, _, _) = make_test_graph();
let path =
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[9])
.expect(&format!(
"path should exist between connected vertices {:?} and {:?}",
vertices[0], vertices[9]
));
assert_valid_path(&graph, &path, vertices[0], vertices[9]);
}
fn assert_valid_path(
graph: &$T,
path: &[<$T as $crate::traits::GraphTopology>::Edge],
source: <$T as $crate::traits::GraphTopology>::Vertex,
target: <$T as $crate::traits::GraphTopology>::Vertex,
) {
use $crate::traits::GraphTopology;
assert!(!path.is_empty(), "path should be non-empty");
// Walks the path: tracks current vertex, confirm each edge is incident to it.
let mut current = source;
for (i, &e) in path.iter().enumerate() {
let (v1, v2) = graph.incident_vertices(e);
assert_ne!(v1, v2, "path should not contain loop edge {e:?}");
assert!(
v1 == current || v2 == current,
"path edge {e:?} (index {i}, from {v1:?} to {v2:?}) is not incident to current path vertex {current:?}"
);
current = if v1 == current { v2 } else { v1 };
}
assert_eq!(
current, target,
"path should end at target {target:?}, but ended at {current:?}"
);
}
}; };
} }
+5 -5
View File
@@ -131,7 +131,7 @@ macro_rules! dijkstra_tests {
} }
fn assert_distances_single_vertex( fn assert_distances_single_vertex(
distances: &$crate::maps::VertexMap< distances: &$crate::maps::EntityMap<
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
Option<u32>, Option<u32>,
>, >,
@@ -160,7 +160,7 @@ macro_rules! dijkstra_tests {
} }
fn assert_distances_disconnected( fn assert_distances_disconnected(
distances: &$crate::maps::VertexMap< distances: &$crate::maps::EntityMap<
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
Option<u32>, Option<u32>,
>, >,
@@ -204,7 +204,7 @@ macro_rules! dijkstra_tests {
} }
fn assert_distances_test_graph( fn assert_distances_test_graph(
distances: &$crate::maps::VertexMap< distances: &$crate::maps::EntityMap<
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
Option<u32>, Option<u32>,
>, >,
@@ -261,7 +261,7 @@ macro_rules! dijkstra_tests {
} }
fn assert_distances_unweighted_test_graph( fn assert_distances_unweighted_test_graph(
distances: &$crate::maps::VertexMap< distances: &$crate::maps::EntityMap<
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
Option<u32>, Option<u32>,
>, >,
@@ -300,7 +300,7 @@ macro_rules! dijkstra_tests {
<$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex,
<$T as $crate::traits::GraphTopology>::Edge, <$T as $crate::traits::GraphTopology>::Edge,
)>; 10], )>; 10],
$crate::maps::EdgeMap<<$T as $crate::traits::GraphTopology>::Edge, u32>, $crate::maps::EntityMap<<$T as $crate::traits::GraphTopology>::Edge, u32>,
) { ) {
use $crate::traits::GraphTopology; use $crate::traits::GraphTopology;
let (graph, vertices, edges, incidences) = make_test_graph(); let (graph, vertices, edges, incidences) = make_test_graph();
-124
View File
@@ -1,124 +0,0 @@
#[doc(hidden)]
#[macro_export]
macro_rules! find_path_tests {
($T:ty) => {
#[test]
fn find_path_source_equals_target() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v = graph.add_vertex();
assert_eq!(
$crate::algorithms::dfs_find_path(&graph, v, v),
Some(vec![]),
"path from source to itself should be empty"
);
}
#[test]
fn find_path_disconnected() {
let (graph, vertices) = make_test_graph_disconnected();
assert_eq!(
$crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[1]),
None,
"no path should exist to disconnected vertex"
);
}
#[test]
fn find_path_adjacent() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let e = graph.add_edge(v1, v2);
let path = $crate::algorithms::dfs_find_path(&graph, v1, v2)
.expect("path should exist between adjacent vertices");
assert_eq!(
path.len(),
1,
"unexpected path length between adjacent vertices"
);
assert_eq!(path[0], e, "path should use the connecting edge");
}
#[test]
fn find_path() {
let (graph, vertices, _, _) = make_test_graph();
let path = $crate::algorithms::dfs_find_path(&graph, vertices[0], vertices[9])
.expect(&format!(
"path should exist between connected vertices {:?} and {:?}",
vertices[0], vertices[9]
));
assert_valid_path(&graph, &path, vertices[0], vertices[9]);
}
#[test]
fn find_path_where_source_matches() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v = graph.add_vertex();
assert_eq!(
$crate::algorithms::dfs_find_path_where(&graph, v, |u| u == v),
Some(vec![]),
"path from source to itself should be empty"
);
}
#[test]
fn find_path_where_disconnected() {
let (graph, vertices) = make_test_graph_disconnected();
assert_eq!(
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[1]),
None,
"no path should exist to disconnected vertex"
);
}
#[test]
fn find_path_where_no_match() {
let (graph, vertices, _, _) = make_test_graph();
assert_eq!(
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |_| false),
None,
"no path should exist when predicate never matches"
);
}
#[test]
fn find_path_where() {
let (graph, vertices, _, _) = make_test_graph();
let path =
$crate::algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[9])
.expect(&format!(
"path should exist between connected vertices {:?} and {:?}",
vertices[0], vertices[9]
));
assert_valid_path(&graph, &path, vertices[0], vertices[9]);
}
fn assert_valid_path(
graph: &$T,
path: &[<$T as $crate::traits::GraphTopology>::Edge],
source: <$T as $crate::traits::GraphTopology>::Vertex,
target: <$T as $crate::traits::GraphTopology>::Vertex,
) {
use $crate::traits::GraphTopology;
assert!(!path.is_empty(), "path should be non-empty");
// Walks the path: tracks current vertex, confirm each edge is incident to it.
let mut current = source;
for (i, &e) in path.iter().enumerate() {
let (v1, v2) = graph.incident_vertices(e);
assert_ne!(v1, v2, "path should not contain loop edge {e:?}");
assert!(
v1 == current || v2 == current,
"path edge {e:?} (index {i}, from {v1:?} to {v2:?}) is not incident to current path vertex {current:?}"
);
current = if v1 == current { v2 } else { v1 };
}
assert_eq!(
current, target,
"path should end at target {target:?}, but ended at {current:?}"
);
}
};
}
+28
View File
@@ -128,6 +128,19 @@ macro_rules! graph_topology_tests {
assert_eq!(graph.vertex_count(), 10, "unexpected vertex count"); assert_eq!(graph.vertex_count(), 10, "unexpected vertex count");
} }
#[test]
fn vertex_map() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let mut map = graph.vertex_map(27);
assert_eq!(map[v1], 27, "unexpected value from default map read");
map[v1] = 9;
assert_eq!(map[v1], 9, "unexpected value from map after write");
let v2 = graph.add_vertex();
assert_eq!(map[v2], 27, "unexpected value from default map read for new vertex");
}
#[test] #[test]
fn add_edge() { fn add_edge() {
use $crate::traits::GraphTopology; use $crate::traits::GraphTopology;
@@ -152,6 +165,21 @@ macro_rules! graph_topology_tests {
assert_eq!(graph.edge_count(), 18, "unexpected edge count"); assert_eq!(graph.edge_count(), 18, "unexpected edge count");
} }
#[test]
fn edge_map() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let e1 = graph.add_edge(v1, v2);
let mut map = graph.edge_map(27);
assert_eq!(map[e1], 27, "unexpected value from default map read");
map[e1] = 9;
assert_eq!(map[e1], 9, "unexpected value from map after write");
let e2 = graph.add_edge(v1, v2);
assert_eq!(map[e2], 27, "unexpected value from default map read for new vertex");
}
#[test] #[test]
fn degree_zero() { fn degree_zero() {
use $crate::traits::GraphTopology; use $crate::traits::GraphTopology;
+3 -137
View File
@@ -1,6 +1,6 @@
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! vertex_map_tests { macro_rules! entity_map_tests {
($T:ty) => { ($T:ty) => {
#[test] #[test]
fn initial_values_are_default() { fn initial_values_are_default() {
@@ -81,7 +81,7 @@ macro_rules! vertex_map_tests {
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! vertex_map_deletion_tests { macro_rules! entity_map_deletion_tests {
($T:ty) => { ($T:ty) => {
#[test] #[test]
fn surviving_vertex_readable_after_delete() { fn surviving_vertex_readable_after_delete() {
@@ -108,144 +108,10 @@ macro_rules! vertex_map_deletion_tests {
map[v1] = 99; map[v1] = 99;
graph.delete_vertex(v1); graph.delete_vertex(v1);
let v2 = graph.add_vertex(); let v2 = graph.add_vertex();
// VertexMap uses raw indices, not vertex identity. A new vertex v2 reusing the slot // EntityMap uses raw indices, not vertex identity. A new vertex v2 reusing the slot
// of previously deleted v1 sees the old value. Callers must reinitialize stale slots // of previously deleted v1 sees the old value. Callers must reinitialize stale slots
// after deletion. // after deletion.
assert_eq!(map[v2], 99); assert_eq!(map[v2], 99);
} }
}; };
} }
#[doc(hidden)]
#[macro_export]
macro_rules! edge_map_tests {
($T:ty) => {
#[test]
fn initial_values_are_default() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let e1 = graph.add_edge(v1, v2);
let e2 = graph.add_edge(v1, v2);
let map = graph.edge_map(42);
assert_eq!(map[e1], 42);
assert_eq!(map[e2], 42);
}
#[test]
fn write_and_read() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let e1 = graph.add_edge(v1, v2);
let e2 = graph.add_edge(v1, v2);
let mut map = graph.edge_map(0);
map[e1] = 7;
assert_eq!(map[e1], 7);
assert_eq!(map[e2], 0);
}
#[test]
fn lazy_growth_on_read() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
graph.add_edge(v1, v2);
let map = graph.edge_map(99);
let e = graph.add_edge(v1, v2);
assert_eq!(map[e], 99);
}
#[test]
fn lazy_growth_on_write() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let e1 = graph.add_edge(v1, v2);
let mut map = graph.edge_map(0);
let e2 = graph.add_edge(v1, v2);
map[e2] = 7;
assert_eq!(map[e1], 0);
assert_eq!(map[e2], 7);
}
#[test]
fn expand_to_new_edges() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
graph.add_edge(v1, v2);
let mut map = graph.edge_map(42);
let capacity_before = map.capacity();
while graph.edge_capacity() <= capacity_before {
graph.add_edge(v1, v2);
}
assert!(
map.capacity() < graph.edge_capacity(),
"precondition: map is stale before expand"
);
map.expand(graph.edge_capacity());
assert_eq!(map.capacity(), graph.edge_capacity());
}
#[test]
fn expand_does_not_overwrite_existing_values() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let e = graph.add_edge(v1, v2);
let mut map = graph.edge_map(0);
map[e] = 5;
graph.add_edge(v1, v2);
map.expand(graph.edge_capacity());
assert_eq!(map[e], 5);
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! edge_map_deletion_tests {
($T:ty) => {
#[test]
fn surviving_edge_readable_after_delete() {
use $crate::traits::GraphTopology;
use $crate::traits::GraphTopologyDeletion;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let e1 = graph.add_edge(v1, v2);
let e2 = graph.add_edge(v1, v2);
let mut map = graph.edge_map(0);
map[e1] = 1;
map[e2] = 2;
graph.delete_edge(e2);
assert_eq!(map[e1], 1);
}
#[test]
fn reused_slot_returns_old_value() {
use $crate::traits::GraphTopology;
use $crate::traits::GraphTopologyDeletion;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
graph.add_edge(v1, v2);
let e1 = graph.add_edge(v1, v2);
let mut map = graph.edge_map(0);
map[e1] = 99;
graph.delete_edge(e1);
let e2 = graph.add_edge(v1, v2);
// EdgeMap uses raw indices, not edge identity. Because to_index uses arr_idx/2,
// both halves of a deleted edge pair map to the same index, so a new edge reusing
// either slot sees the old value. Callers must reinitialize stale slots after deletion.
assert_eq!(map[e2], 99);
}
};
}
+11 -8
View File
@@ -1,6 +1,6 @@
//! Core traits for undirected graph topologies. //! Core traits for undirected graph topologies.
use crate::maps::{EdgeMap, VertexMap}; use crate::maps::EntityMap;
// 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.
@@ -41,7 +41,7 @@ pub trait GraphTopology {
/// when an algorithm needs to pause traversal, perform other graph queries or mutations, and /// 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 /// then continue from where it left off. This cursor type can be obtained via
/// [`incidence_cursor`](Self::incidence_cursor). /// [`incidence_cursor`](Self::incidence_cursor).
type IncidenceCursor: IncidenceCursor<Self> + Copy; type IncidenceCursor: IncidenceCursor<Self>;
/// Returns the number of vertices in the graph. /// Returns the number of vertices in the graph.
fn vertex_count(&self) -> usize; fn vertex_count(&self) -> usize;
@@ -49,7 +49,8 @@ pub trait GraphTopology {
/// Returns the total number of vertices the graph can hold without reallocating. /// 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`. /// Creates and returns an [`EntityMap`] for the vertices with every slot initialised to
/// `default`.
/// ///
/// # Examples /// # Examples
/// ///
@@ -69,7 +70,7 @@ pub trait GraphTopology {
/// labels[v2] = "b"; /// labels[v2] = "b";
/// assert_eq!(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) -> EntityMap<Self::Vertex, T>;
/// Returns the number of edges in the graph. /// Returns the number of edges in the graph.
fn edge_count(&self) -> usize; fn edge_count(&self) -> usize;
@@ -77,7 +78,7 @@ pub trait GraphTopology {
/// Returns the total number of edges the graph can hold without reallocating. /// 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`. /// Creates and returns an [`EntityMap`] for the edges with every slot initialised to `default`.
/// ///
/// # Examples /// # Examples
/// ///
@@ -99,7 +100,7 @@ pub trait GraphTopology {
/// weights[e2] = 2; /// weights[e2] = 2;
/// assert_eq!(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) -> EntityMap<Self::Edge, T>;
/// Returns the degree of `v`, i.e. the number of incident edges, where each loop contributes 2. /// Returns the degree of `v`, i.e. the number of incident edges, where each loop contributes 2.
/// ///
@@ -213,10 +214,13 @@ pub trait GraphTopologyDeletion: GraphTopology {
/// A cursor for traversing the incidences of a vertex one step at a time. /// 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 /// Cursors are obtained via [`GraphTopology::incidence_cursor`]. See
/// [`GraphTopology::IncidenceCursor`] for guidance on when to prefer a cursor over /// [`GraphTopology::IncidenceCursor`] for guidance on when to prefer a cursor over
/// [`GraphTopology::incidences`]. /// [`GraphTopology::incidences`].
pub trait IncidenceCursor<G: GraphTopology + ?Sized> { pub trait IncidenceCursor<G: GraphTopology + ?Sized>: Copy {
/// Advances the cursor and returns the next incidence as `Some((u, e))`, or `None` if the /// Advances the cursor and returns the next incidence as `Some((u, e))`, or `None` if the
/// traversal is exhausted. /// traversal is exhausted.
/// ///
@@ -225,7 +229,6 @@ pub trait IncidenceCursor<G: GraphTopology + ?Sized> {
/// ``` /// ```
/// # use grapherity::prelude::*; /// # use grapherity::prelude::*;
/// # use grapherity::models::Graph; /// # use grapherity::models::Graph;
/// # use grapherity::traits::IncidenceCursor;
/// // Constructs a graph with two vertices connected to `v`. /// // Constructs a graph with two vertices connected to `v`.
/// let mut graph = Graph::new(); /// let mut graph = Graph::new();
/// let v = graph.add_vertex(); /// let v = graph.add_vertex();
-13
View File
@@ -1,13 +0,0 @@
mod append_graph_tests {
use grapherity::models::AppendGraph;
grapherity::graph_topology_test_fixtures!(AppendGraph);
grapherity::find_path_tests!(AppendGraph);
}
mod graph_tests {
use grapherity::models::Graph;
grapherity::graph_topology_test_fixtures!(Graph);
grapherity::find_path_tests!(Graph);
}
+5 -18
View File
@@ -1,25 +1,12 @@
mod append_graph_vertex_map_tests { mod append_graph_entity_map_tests {
use grapherity::models::AppendGraph; use grapherity::models::AppendGraph;
grapherity::vertex_map_tests!(AppendGraph); grapherity::entity_map_tests!(AppendGraph);
} }
mod append_graph_edge_map_tests { mod graph_entity_map_tests {
use grapherity::models::AppendGraph;
grapherity::edge_map_tests!(AppendGraph);
}
mod graph_vertex_map_tests {
use grapherity::models::Graph; use grapherity::models::Graph;
grapherity::vertex_map_tests!(Graph); grapherity::entity_map_tests!(Graph);
grapherity::vertex_map_deletion_tests!(Graph); grapherity::entity_map_deletion_tests!(Graph);
}
mod graph_edge_map_tests {
use grapherity::models::Graph;
grapherity::edge_map_tests!(Graph);
grapherity::edge_map_deletion_tests!(Graph);
} }