//! [`AppendGraph`], an undirected graph topology supporting addition only. use crate::maps::{EdgeMap, VertexMap}; use crate::traits::{GraphTopology, IncidenceCursor}; /// An opaque handle identifying a vertex in an [`AppendGraph`]. /// /// Handles are stable for the lifetime of the graph. Obtain via graph methods like /// [`AppendGraph::add_vertex`] and [`AppendGraph::vertices`]. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Vertex(usize); /// An opaque handle identifying an edge in an [`AppendGraph`]. /// /// Handles are stable for the lifetime of the graph. Obtain via graph methods like /// [`AppendGraph::add_edge`] and [`AppendGraph::edges`]. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Edge(usize); /// A [`VertexMap`] for [`AppendGraph`] vertices. pub type AppendGraphVertexMap = VertexMap; /// An [`EdgeMap`] for [`AppendGraph`] edges. pub type AppendGraphEdgeMap = EdgeMap; impl Edge { fn normalize(&self) -> Self { Self(self.0 & !1) } } // TODO: Check if VertexIncidenceHeader and IncidenceEntry can be made smaller. Currently they both take 24 bytes (on 64bit), see https://stackoverflow.com/a/79653173 struct VertexIncidenceHeader { incidence_count: usize, first_incidence: Option, } #[derive(Copy, Clone)] struct IncidenceEntry { next: Option, adjacent: Vertex, } /// A resumable cursor over the incidences of a single vertex in an [`AppendGraph`]. /// /// Obtain via [`AppendGraph::incidence_cursor`]. See [`IncidenceCursor`] on usage guidance. #[derive(Copy, Clone)] pub struct AppendGraphIncidenceCursor { incidence: Option, } impl IncidenceCursor for AppendGraphIncidenceCursor { fn next(&mut self, graph: &AppendGraph) -> Option<(Vertex, Edge)> { graph .step_incidence(&mut self.incidence) .map(|(v, e)| (v, e.normalize())) } } /// An undirected graph that supports adding vertices and edges, but not deleting them. /// /// `AppendGraph` is optimised for workloads that incrementally build a graph and query it /// repeatedly. [`Vertex`] and [`Edge`] handles are never invalidated. Use [`Graph`] instead if you /// need to remove vertices or edges. /// /// Incidences are stored as interleaved adjacency lists in a single flat [`Vec`]. In general, /// vertex neighborhood traversals result in scattered index jumps. /// /// # Examples /// /// ``` /// use grapherity::prelude::*; /// use grapherity::models::AppendGraph; /// /// let mut graph = AppendGraph::new(); /// let v1 = graph.add_vertex(); /// let v2 = graph.add_vertex(); /// let e = graph.add_edge(v1, v2); /// assert!(graph.are_adjacent(v1, v2)); /// ``` /// /// # Time and space complexity /// /// Both [`add_vertex`] and [`add_edge`] run in amortised *O(1)* time. [`degree`] runs in *O(1)* /// time since vertex degrees are stored. Space complexity is *O(|V| + |E|)*. /// /// [`add_vertex`]: Self::add_vertex /// [`add_edge`]: Self::add_edge /// [`degree`]: Self::degree /// [`Graph`]: crate::models::Graph pub struct AppendGraph { vertices: Vec, incidences: Vec, } impl AppendGraph { /// Creates an empty graph instance with no vertices or edges. pub fn new() -> Self { Self { vertices: vec![], incidences: vec![], } } /// Adds a single incidence of an edge, which is composed by two such incidences, to the /// incidences vector. fn add_incidence(&mut self, v1: Vertex, v2: Vertex) { self.incidences.push(IncidenceEntry { next: self.vertices[v1.0].first_incidence.take(), adjacent: v2, }); self.vertices[v1.0].incidence_count += 1; self.vertices[v1.0].first_incidence = Some(Edge(self.incidences.len() - 1)); } fn raw_incidences(&self, v: Vertex) -> impl Iterator { let mut incidence = self.vertices[v.0].first_incidence; std::iter::from_fn(move || self.step_incidence(&mut incidence)) } fn step_incidence(&self, incidence: &mut Option) -> Option<(Vertex, Edge)> { let current = (*incidence)?; let entry = self.incidences[current.0]; *incidence = entry.next; Some((entry.adjacent, current)) } } impl Default for AppendGraph { fn default() -> Self { Self::new() } } impl GraphTopology for AppendGraph { type Vertex = Vertex; type Edge = Edge; type IncidenceCursor = AppendGraphIncidenceCursor; fn vertex_count(&self) -> usize { self.vertices.len() } fn vertex_capacity(&self) -> usize { self.vertices.capacity() } fn vertex_map(&self, default: T) -> VertexMap { VertexMap::new(default, |v| v.0, self.vertex_capacity()) } fn edge_count(&self) -> usize { self.incidences.len() / 2 } fn edge_capacity(&self) -> usize { self.incidences.capacity() / 2 } fn edge_map(&self, default: T) -> EdgeMap { EdgeMap::new(default, |e| e.0 / 2, self.edge_capacity()) } fn degree(&self, v: Self::Vertex) -> usize { self.vertices[v.0].incidence_count } fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool { self.adjacent_vertices(v1).any(|x| x == v2) } fn vertices(&self) -> impl Iterator { (0..self.vertices.len()).map(Vertex) } fn adjacent_vertices(&self, v: Self::Vertex) -> impl Iterator { self.raw_incidences(v).map(|(v, _)| v) } fn incident_vertices(&self, e: Self::Edge) -> (Self::Vertex, Self::Vertex) { let v2 = self.incidences[e.0].adjacent; let v1 = self.incidences[e.0 ^ 1].adjacent; (v1, v2) } fn edges(&self) -> impl Iterator { (0..self.incidences.len()).step_by(2).map(Edge) } fn incident_edges(&self, v: Self::Vertex) -> impl Iterator { 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 incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor { AppendGraphIncidenceCursor { incidence: self.vertices[v.0].first_incidence, } } fn add_vertex(&mut self) -> Self::Vertex { self.vertices.push(VertexIncidenceHeader { incidence_count: 0, first_incidence: None, }); Vertex(self.vertices.len() - 1) } fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge { self.add_incidence(v1, v2); self.add_incidence(v2, v1); Edge(self.incidences.len() - 2) } } #[cfg(test)] mod tests { use super::*; crate::graph_topology_test_fixtures!(AppendGraph); crate::graph_topology_tests!(AppendGraph); #[test] fn incident_vertices_paired_index() { let mut graph = AppendGraph::new(); let v1 = graph.add_vertex(); let v2 = graph.add_vertex(); let e = graph.add_edge(v1, v2); let f = Edge(e.0 + 1); let (u1, u2) = graph.incident_vertices(f); assert!( (u1 == v1 && u2 == v2) || (u1 == v2 && u2 == v1), "unexpected incident vertices {u1:?} and {u2:?} for edge {f:?}" ); } }