diff --git a/src/algorithms.rs b/src/algorithms.rs index 2386965..2da4d09 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -34,7 +34,7 @@ use std::collections::VecDeque; use std::hash::Hash; use crate::maps::EntityMap; -use crate::traits::{GraphTopology, IncidenceCursor}; +use crate::traits::{GraphTopology, Incidence, IncidenceCursor}; /// Return data type for [`dijkstra`] and [`dijkstra_unweighted`]. pub struct DijkstraResult { @@ -236,16 +236,16 @@ where heap.push(source, Reverse(0u32)); while let Some((v, Reverse(v_distance))) = heap.pop() { - for incidence in graph.incidences(v) { - let new_distance = v_distance + weights(incidence.1); - if match distances[incidence.0] { + for Incidence { vertex: u, edge: e } in graph.incidences(v) { + let new_distance = v_distance + weights(e); + if match distances[u] { None => true, Some(old_distance) if old_distance > new_distance => true, _ => false, } { - distances[incidence.0] = Some(new_distance); - on_relax(incidence.0, v); - heap.push_increase(incidence.0, Reverse(new_distance)); + distances[u] = Some(new_distance); + on_relax(u, v); + heap.push_increase(u, Reverse(new_distance)); } } } @@ -741,7 +741,10 @@ where None => { stack.pop(); } - Some((neighbor, edge)) => { + Some(Incidence { + vertex: neighbor, + edge, + }) => { if predicate(neighbor) { let mut path: Vec = stack.iter().filter_map(|f| f.arrival_edge).collect(); diff --git a/src/lib.rs b/src/lib.rs index 062db19..e902d00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,7 +136,7 @@ pub mod traits; /// Convenience re-exports of graph topology traits for common use. pub mod prelude { - pub use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor}; + pub use crate::traits::{GraphTopology, GraphTopologyDeletion, Incidence, IncidenceCursor}; } mod testing; diff --git a/src/models.rs b/src/models.rs index 4eb163e..1f302c8 100644 --- a/src/models.rs +++ b/src/models.rs @@ -5,5 +5,7 @@ pub mod graph; // TODO: Compressed-sparse-row graph model. -pub use append_graph::{AppendGraph, AppendGraphEdgeMap, AppendGraphVertexMap}; -pub use graph::{Graph, GraphEdgeMap, GraphVertexMap}; +pub use append_graph::{ + AppendGraph, AppendGraphEdgeMap, AppendGraphIncidence, AppendGraphVertexMap, +}; +pub use graph::{Graph, GraphEdgeMap, GraphIncidence, GraphVertexMap}; diff --git a/src/models/append_graph.rs b/src/models/append_graph.rs index bc0d76a..00c2d36 100644 --- a/src/models/append_graph.rs +++ b/src/models/append_graph.rs @@ -1,7 +1,7 @@ //! [`AppendGraph`], an undirected graph topology supporting addition only. use crate::maps::EntityMap; -use crate::traits::{GraphTopology, IncidenceCursor}; +use crate::traits::{GraphTopology, Incidence, IncidenceCursor}; /// An opaque handle identifying a vertex in an [`AppendGraph`]. /// @@ -23,6 +23,9 @@ pub type AppendGraphVertexMap = EntityMap; /// An [`EntityMap`] for [`AppendGraph`] edges. pub type AppendGraphEdgeMap = EntityMap; +/// An [`Incidence`] for [`AppendGraph`]. +pub type AppendGraphIncidence = Incidence; + impl Edge { fn normalize(&self) -> Self { Self(self.0 & !1) @@ -50,10 +53,13 @@ pub struct AppendGraphIncidenceCursor { } impl IncidenceCursor for AppendGraphIncidenceCursor { - fn next(&mut self, graph: &AppendGraph) -> Option<(Vertex, Edge)> { + fn next(&mut self, graph: &AppendGraph) -> Option { graph .step_incidence(&mut self.incidence) - .map(|(v, e)| (v, e.normalize())) + .map(|(v, e)| Incidence { + vertex: v, + edge: e.normalize(), + }) } } @@ -191,8 +197,11 @@ impl GraphTopology for AppendGraph { self.raw_incidences(v).map(|(_, e)| e.normalize()) } - fn incidences(&self, v: Self::Vertex) -> impl Iterator { - self.raw_incidences(v).map(|(v, e)| (v, e.normalize())) + fn incidences(&self, v: Self::Vertex) -> impl Iterator { + self.raw_incidences(v).map(|(v, e)| Incidence { + vertex: v, + edge: e.normalize(), + }) } fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor { diff --git a/src/models/graph.rs b/src/models/graph.rs index e07ddd6..39454eb 100644 --- a/src/models/graph.rs +++ b/src/models/graph.rs @@ -3,7 +3,7 @@ use typed_generational_arena::{Arena, Index}; use crate::maps::EntityMap; -use crate::traits::{GraphTopology, GraphTopologyDeletion, IncidenceCursor}; +use crate::traits::{GraphTopology, GraphTopologyDeletion, Incidence, IncidenceCursor}; /// An opaque handle identifying a vertex in a [`Graph`]. /// @@ -23,6 +23,9 @@ pub type GraphVertexMap = EntityMap; /// An [`EntityMap`] for [`Graph`] edges. pub type GraphEdgeMap = EntityMap; +/// An [`Incidence`] for [`Graph`]. +pub type GraphIncidence = Incidence; + #[derive(Copy, Clone, PartialEq, Eq, Debug)] struct VertexSlot(usize); @@ -66,13 +69,13 @@ pub struct GraphIncidenceCursor { } impl IncidenceCursor for GraphIncidenceCursor { - fn next(&mut self, graph: &Graph) -> Option<(Vertex, Edge)> { - graph.step_incidence(&mut self.incidence).map(|(vs, e)| { - ( - graph.vertices.get_idx(vs.0).unwrap(), - graph.normalize_edge(e), - ) - }) + fn next(&mut self, graph: &Graph) -> Option { + graph + .step_incidence(&mut self.incidence) + .map(|(vs, e)| Incidence { + vertex: graph.vertices.get_idx(vs.0).unwrap(), + edge: graph.normalize_edge(e), + }) } } @@ -297,9 +300,11 @@ impl GraphTopology for Graph { self.raw_incidences(v).map(|(_, e)| self.normalize_edge(e)) } - fn incidences(&self, v: Self::Vertex) -> impl Iterator { - self.raw_incidences(v) - .map(|(vs, e)| (self.vertices.get_idx(vs.0).unwrap(), self.normalize_edge(e))) + fn incidences(&self, v: Self::Vertex) -> impl Iterator { + self.raw_incidences(v).map(|(vs, e)| Incidence { + vertex: self.vertices.get_idx(vs.0).unwrap(), + edge: self.normalize_edge(e), + }) } fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor { diff --git a/src/testing/dijkstra_testing.rs b/src/testing/dijkstra_testing.rs index f53eee1..3b06326 100644 --- a/src/testing/dijkstra_testing.rs +++ b/src/testing/dijkstra_testing.rs @@ -296,10 +296,12 @@ macro_rules! dijkstra_tests { <$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex, ); 18], - [Vec<( - <$T as $crate::traits::GraphTopology>::Vertex, - <$T as $crate::traits::GraphTopology>::Edge, - )>; 10], + [Vec< + $crate::traits::Incidence< + <$T as $crate::traits::GraphTopology>::Vertex, + <$T as $crate::traits::GraphTopology>::Edge, + >, + >; 10], $crate::maps::EntityMap<<$T as $crate::traits::GraphTopology>::Edge, u32>, ) { use $crate::traits::GraphTopology; diff --git a/src/testing/graph_topology_testing.rs b/src/testing/graph_topology_testing.rs index 0faf451..5ee3be3 100644 --- a/src/testing/graph_topology_testing.rs +++ b/src/testing/graph_topology_testing.rs @@ -10,12 +10,12 @@ macro_rules! graph_topology_test_fixtures { <$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Vertex, ); 18], - [Vec<( + [Vec<$crate::traits::Incidence< <$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Edge, - )>; 10], + >>; 10], ) { - use $crate::traits::GraphTopology; + use $crate::traits::{GraphTopology, Incidence}; let mut graph = <$T>::new(); let vertices: [<$T as $crate::traits::GraphTopology>::Vertex; 10] = core::array::from_fn(|_| graph.add_vertex()); @@ -40,50 +40,54 @@ macro_rules! graph_topology_test_fixtures { (vertices[7], vertices[9]), ] .map(|(v1, v2)| (graph.add_edge(v1, v2), v1, v2)); - let incidences: [Vec<_>; 10] = [ - vec![(vertices[1], edges[0].0), (vertices[1], edges[1].0)], + let i = |vertex, edge| Incidence { vertex, edge }; + let incidences: [Vec::Vertex, + <$T as GraphTopology>::Edge, + >>; 10] = [ + vec![i(vertices[1], edges[0].0), i(vertices[1], edges[1].0)], vec![ - (vertices[0], edges[0].0), - (vertices[0], edges[1].0), - (vertices[2], edges[2].0), - (vertices[3], edges[3].0), - (vertices[4], edges[4].0), + i(vertices[0], edges[0].0), + i(vertices[0], edges[1].0), + i(vertices[2], edges[2].0), + i(vertices[3], edges[3].0), + i(vertices[4], edges[4].0), ], vec![ - (vertices[2], edges[5].0), - (vertices[2], edges[5].0), - (vertices[1], edges[2].0), - (vertices[4], edges[6].0), - (vertices[4], edges[7].0), - (vertices[5], edges[8].0), - (vertices[6], edges[9].0), + i(vertices[2], edges[5].0), + i(vertices[2], edges[5].0), + i(vertices[1], edges[2].0), + i(vertices[4], edges[6].0), + i(vertices[4], edges[7].0), + i(vertices[5], edges[8].0), + i(vertices[6], edges[9].0), ], - vec![(vertices[1], edges[3].0), (vertices[6], edges[10].0)], + vec![i(vertices[1], edges[3].0), i(vertices[6], edges[10].0)], vec![ - (vertices[1], edges[4].0), - (vertices[2], edges[6].0), - (vertices[2], edges[7].0), - (vertices[4], edges[11].0), - (vertices[4], edges[11].0), - (vertices[7], edges[12].0), - (vertices[8], edges[13].0), + i(vertices[1], edges[4].0), + i(vertices[2], edges[6].0), + i(vertices[2], edges[7].0), + i(vertices[4], edges[11].0), + i(vertices[4], edges[11].0), + i(vertices[7], edges[12].0), + i(vertices[8], edges[13].0), ], - vec![(vertices[2], edges[8].0), (vertices[9], edges[14].0)], + vec![i(vertices[2], edges[8].0), i(vertices[9], edges[14].0)], vec![ - (vertices[2], edges[9].0), - (vertices[3], edges[10].0), - (vertices[9], edges[15].0), + i(vertices[2], edges[9].0), + i(vertices[3], edges[10].0), + i(vertices[9], edges[15].0), ], vec![ - (vertices[4], edges[12].0), - (vertices[8], edges[16].0), - (vertices[9], edges[17].0), + i(vertices[4], edges[12].0), + i(vertices[8], edges[16].0), + i(vertices[9], edges[17].0), ], - vec![(vertices[4], edges[13].0), (vertices[7], edges[16].0)], + vec![i(vertices[4], edges[13].0), i(vertices[7], edges[16].0)], vec![ - (vertices[5], edges[14].0), - (vertices[6], edges[15].0), - (vertices[7], edges[17].0), + i(vertices[5], edges[14].0), + i(vertices[6], edges[15].0), + i(vertices[7], edges[17].0), ], ]; (graph, vertices, edges, incidences) @@ -540,7 +544,7 @@ macro_rules! graph_topology_tests { "unexpected incident edge count for vertex {:?}", vertices[i] ); - let mut expected: Vec<_> = incidences[i].iter().map(|(_, e)| *e).collect(); + let mut expected: Vec<_> = incidences[i].iter().map(|x| x.edge).collect(); for e in graph.incident_edges(vertices[i]) { let pos = expected .iter() @@ -634,7 +638,7 @@ macro_rules! graph_topology_tests { for incidence in graph.incidences(vertices[i]) { let pos = expected .iter() - .position(|(v, e)| *v == incidence.0 && *e == incidence.1) + .position(|x| *x == incidence) .expect(&format!( "unexpected incidence {incidence:?} of vertex {:?} from iterator", vertices[i] @@ -657,17 +661,21 @@ macro_rules! graph_topology_tests { // For loop edges (u == v), the edge must appear exactly twice in the incidences of u. let (graph, _, _, _) = make_test_graph(); for u in graph.vertices() { - for (v, e) in graph.incidences(u) { - if u == v { + for incidence in graph.incidences(u) { + if u == incidence.vertex { assert_eq!( - graph.incidences(u).filter(|(_, f)| *f == e).count(), + graph.incidences(u).filter(|x| x.edge == incidence.edge).count(), 2, - "loop edge {e:?} should appear exactly twice in incidences of vertex {u:?}" + "loop edge {:?} should appear exactly twice in incidences of vertex {u:?}", + incidence.edge ); } else { assert!( - graph.incidences(v).any(|(w, f)| w == u && f == e), - "edge {e:?} from incidences of vertex {u:?} missing in incidences of adjacent vertex {v:?}" + graph + .incidences(incidence.vertex) + .any(|x| x.vertex == u && x.edge == incidence.edge), + "edge {:?} from incidences of vertex {u:?} missing in incidences of adjacent vertex {:?}", + incidence.edge, incidence.vertex ); } } @@ -676,19 +684,19 @@ macro_rules! graph_topology_tests { #[test] fn incidences_loop_edge() { - use $crate::traits::GraphTopology; + use $crate::traits::{GraphTopology, Incidence}; let mut graph = <$T>::new(); let v = graph.add_vertex(); let e = graph.add_edge(v, v); let mut iter = graph.incidences(v); assert_eq!( iter.next(), - Some((v, e)), + Some(Incidence { vertex: v, edge: e }), "vertex should be adjacent to itself" ); assert_eq!( iter.next(), - Some((v, e)), + Some(Incidence { vertex: v, edge: e }), "vertex should be adjacent to itself twice" ); assert_eq!( @@ -716,16 +724,16 @@ macro_rules! graph_topology_tests { vertices[i] )); assert_eq!( - current.0, + current.vertex, vertices[1 - i], "unexpected adjacent vertex of vertex {:?} in incidence {j}", vertices[i] ); assert_eq!( - edges.iter().filter(|e| **e == current.1).count(), + edges.iter().filter(|e| **e == current.edge).count(), 1, "unexpected incident edge {:?} of vertex {:?}", - current.1, + current.edge, vertices[i], ); } @@ -761,7 +769,7 @@ macro_rules! graph_topology_tests { while let Some(incidence) = cursor.next(&graph) { let pos = expected .iter() - .position(|(v, e)| *v == incidence.0 && *e == incidence.1) + .position(|x| *x == incidence) .expect(&format!( "unexpected incidence {incidence:?} of vertex {:?} from cursor", vertices[i] @@ -779,19 +787,19 @@ macro_rules! graph_topology_tests { #[test] fn incidence_cursor_loop_edge() { - use $crate::traits::{GraphTopology, IncidenceCursor}; + use $crate::traits::{GraphTopology, Incidence, IncidenceCursor}; let mut graph = <$T>::new(); let v = graph.add_vertex(); let e = graph.add_edge(v, v); let mut cursor = graph.incidence_cursor(v); assert_eq!( cursor.next(&graph), - Some((v, e)), + Some(Incidence { vertex: v, edge: e }), "vertex should be adjacent to itself" ); assert_eq!( cursor.next(&graph), - Some((v, e)), + Some(Incidence { vertex: v, edge: e }), "vertex should be adjacent to itself twice" ); assert_eq!( @@ -819,16 +827,16 @@ macro_rules! graph_topology_tests { vertices[i] )); assert_eq!( - current.0, + current.vertex, vertices[1 - i], "unexpected adjacent vertex of vertex {:?} in incidence {j}", vertices[i] ); assert_eq!( - edges.iter().filter(|e| **e == current.1).count(), + edges.iter().filter(|e| **e == current.edge).count(), 1, "unexpected incident edge {:?} of vertex {:?}", - current.1, + current.edge, vertices[i], ); } @@ -868,11 +876,12 @@ macro_rules! graph_topology_tests { use $crate::traits::GraphTopology; let (graph, _, _, _) = make_test_graph(); for u in graph.vertices() { - for (v, e) in graph.incidences(u) { - let (w1, w2) = graph.incident_vertices(e); + for incidence in graph.incidences(u) { + let (w1, w2) = graph.incident_vertices(incidence.edge); assert!( - (w1 == u && w2 == v) || (w1 == v && w2 == u), - "incident vertices {w1:?} and {w2:?} of edge {e:?} are inconsistent with incidence ({v:?}, {e:?}) of vertex {u:?}" + (w1 == u && w2 == incidence.vertex) || (w1 == incidence.vertex && w2 == u), + "edge endpoints {w1:?} and {w2:?} are inconsistent with incidence ({:?}, {:?}) of vertex {u:?}", + incidence.vertex, incidence.edge ); } } @@ -883,7 +892,7 @@ macro_rules! graph_topology_tests { use $crate::traits::GraphTopology; let (graph, _, _, _) = make_test_graph(); for u in graph.vertices() { - let mut expected: Vec<_> = graph.incidences(u).map(|(_, e)| e).collect(); + let mut expected: Vec<_> = graph.incidences(u).map(|x| x.edge).collect(); for e in graph.incident_edges(u) { let pos = expected .iter() @@ -1256,7 +1265,7 @@ macro_rules! graph_topology_deletion_tests { for i in 0..10 { let mut expected: Vec<_> = incidences[i] .iter() - .filter_map(|(_, e)| (*e != edges[2].0).then_some(*e)) + .filter_map(|x| (x.edge != edges[2].0).then_some(x.edge)) .collect(); assert_eq!( graph.incident_edges(vertices[i]).count(), @@ -1289,7 +1298,7 @@ macro_rules! graph_topology_deletion_tests { for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] { let mut expected: Vec<_> = incidences[i] .iter() - .filter_map(|(v, e)| (*v != vertices[2]).then_some(*e)) + .filter_map(|x| (x.vertex != vertices[2]).then_some(x.edge)) .collect(); assert_eq!( graph.incident_edges(vertices[i]).count(), @@ -1321,7 +1330,7 @@ macro_rules! graph_topology_deletion_tests { for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] { let remaining = incidences[i] .iter() - .filter(|(v, _)| *v != vertices[2]) + .filter(|x| x.vertex != vertices[2]) .cloned() .collect(); assert_vertex_incidences(&graph, vertices[i], remaining); @@ -1337,7 +1346,7 @@ macro_rules! graph_topology_deletion_tests { for i in 0..10 { let remaining = incidences[i] .iter() - .filter(|(_, e)| *e != edges[2].0) + .filter(|x| x.edge != edges[2].0) .cloned() .collect(); assert_vertex_incidences(&graph, vertices[i], remaining); @@ -1347,10 +1356,10 @@ macro_rules! graph_topology_deletion_tests { fn assert_vertex_incidences( graph: &$T, v: <$T as $crate::traits::GraphTopology>::Vertex, - mut expected: Vec<( + mut expected: Vec<$crate::traits::Incidence< <$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Edge, - )>, + >>, ) { use $crate::traits::GraphTopology; assert_eq!( @@ -1362,7 +1371,7 @@ macro_rules! graph_topology_deletion_tests { for incidence in graph.incidences(v) { let pos = expected .iter() - .position(|(u, e)| *u == incidence.0 && *e == incidence.1) + .position(|x| *x == incidence) .expect(&format!( "unexpected incidence {incidence:?} of vertex {:?} from iterator after delete", v @@ -1385,7 +1394,7 @@ macro_rules! graph_topology_deletion_tests { for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] { let remaining = incidences[i] .iter() - .filter(|(v, _)| *v != vertices[2]) + .filter(|x| x.vertex != vertices[2]) .cloned() .collect(); assert_vertex_incidence_cursor(&graph, vertices[i], remaining); @@ -1401,7 +1410,7 @@ macro_rules! graph_topology_deletion_tests { for i in 0..10 { let remaining = incidences[i] .iter() - .filter(|(_, e)| *e != edges[2].0) + .filter(|x| x.edge != edges[2].0) .cloned() .collect(); assert_vertex_incidence_cursor(&graph, vertices[i], remaining); @@ -1411,17 +1420,17 @@ macro_rules! graph_topology_deletion_tests { fn assert_vertex_incidence_cursor( graph: &$T, v: <$T as $crate::traits::GraphTopology>::Vertex, - mut expected: Vec<( + mut expected: Vec<$crate::traits::Incidence< <$T as $crate::traits::GraphTopology>::Vertex, <$T as $crate::traits::GraphTopology>::Edge, - )>, + >>, ) { use $crate::traits::IncidenceCursor; let mut cursor = graph.incidence_cursor(v); while let Some(incidence) = cursor.next(graph) { let pos = expected .iter() - .position(|(u, e)| *u == incidence.0 && *e == incidence.1) + .position(|x| *x == incidence) .expect(&format!( "unexpected incidence {incidence:?} of vertex {:?} from cursor after delete", v diff --git a/src/traits.rs b/src/traits.rs index 597240e..6e92aff 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -4,7 +4,6 @@ use crate::maps::EntityMap; // TODO: Add functions to reserve memory for vertices and edges. // TODO: Split out GraphTopologyAddition trait. -// TODO: Introduce an Incidence struct. /// A trait representing an undirected graph topology. /// /// An undirected graph is a set of vertices and undirected edges, where each edge connects either @@ -165,7 +164,10 @@ pub trait GraphTopology { /// # Panics /// /// Panics if `v` is not a valid vertex of this graph. - fn incidences(&self, v: Self::Vertex) -> impl Iterator; + fn incidences( + &self, + v: Self::Vertex, + ) -> impl Iterator>; /// Returns a cursor over all incidences of `v`, analogously to /// [`incidences`](Self::incidences), initially positioned before the first one. @@ -221,8 +223,8 @@ pub trait GraphTopologyDeletion: GraphTopology { /// [`GraphTopology::IncidenceCursor`] for guidance on when to prefer a cursor over /// [`GraphTopology::incidences`]. pub trait IncidenceCursor: Copy { - /// Advances the cursor and returns the next incidence as `Some((u, e))`, or `None` if the - /// traversal is exhausted. + /// Advances the cursor and returns the next incidence, or `None` if the traversal is + /// exhausted. /// /// # Examples /// @@ -248,5 +250,17 @@ pub trait IncidenceCursor: Copy { /// 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>; +} + +/// An incidence of some vertex *v* in a graph, i.e. a vertex-edge pair *(u, e)* such that *u* is +/// adjacent to *v* and *e* is an edge between them. +/// +/// Return type of [`GraphTopology::incidences`] and [`IncidenceCursor::next`]. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Incidence { + /// The vertex adjacent to *v*. + pub vertex: V, + /// The edge between *v* and [`vertex`](Self::vertex). + pub edge: E, }