From 37a911dce0be9a65c22472bcddcc6c717d1998fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 23 Apr 2026 18:25:23 +0200 Subject: [PATCH] Change GraphTopology implementation for Graph with generational arena --- Cargo.toml | 1 + src/models/graph.rs | 79 +++++++++++++++++++++++---------------------- 2 files changed, 41 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b338f02..80cdb46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] +typed-generational-arena = "0.2.9" diff --git a/src/models/graph.rs b/src/models/graph.rs index 498e286..5e57c72 100644 --- a/src/models/graph.rs +++ b/src/models/graph.rs @@ -1,67 +1,67 @@ +use typed_generational_arena::{Arena, Index}; + use crate::traits::GraphTopology; -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Vertex(usize); +type Vertex = Index; -impl From for usize { - fn from(v: Vertex) -> usize { - v.0 - } -} +type Edge = Index; #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Incidence(usize); +struct VertexSlot(usize); -struct VertexIncidenceHeader { +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +struct IncidenceSlot(usize); + +pub struct VertexIncidenceHeader { incidence_count: usize, - first_incidence: Option, + first_incidence: Option, } -struct IncidenceEntry { - next: Option, - neighbor: Vertex, +pub struct IncidenceEntry { + next: Option, + neighbor: VertexSlot, } struct VertexNeighborIterator<'a> { graph: &'a Graph, - incidence: Option, + incidence: Option, } impl<'a> Iterator for VertexNeighborIterator<'a> { type Item = Vertex; fn next(&mut self) -> Option { + // TODO: Benchmark storing full Index (one read, larger entries) vs slot + get_idx() (two reads, smaller entries). let incidence = self.incidence?; - let entry = &self.graph.incidences[incidence.0]; + let index = self.graph.incidences.get_idx(incidence.0)?; + let entry = &self.graph.incidences[index]; self.incidence = entry.next; - Some(entry.neighbor) + self.graph.vertices.get_idx(entry.neighbor.0) } } -// TODO: Graph cannot support deleting vertices because of the external indices "Vertex". They could be made stable with e.g. slotmap or generational arena. -// TODO: Add function to delete vertices. -// TODO: Add function to delete edges. pub struct Graph { - // TODO: Index 'vertices' by a 'Vertex' instead of 'Vertex.0'? - vertices: Vec, - incidences: Vec, + // TODO: Arena index and generation types could be externalized to Graph. + vertices: Arena, + incidences: Arena, } impl Graph { pub fn new() -> Self { Self { - vertices: vec![], - incidences: vec![], + vertices: Arena::new(), + incidences: Arena::new(), } } - fn add_incidence(&mut self, v1: Vertex, v2: Vertex) { - self.incidences.push(IncidenceEntry { - next: self.vertices[v1.0].first_incidence.take(), - neighbor: v2, + fn add_incidence(&mut self, v1: Vertex, v2: Vertex) -> Edge { + let edge = self.incidences.insert(IncidenceEntry { + next: self.vertices[v1].first_incidence.take(), + neighbor: VertexSlot(v2.arr_idx()), }); - self.vertices[v1.0].incidence_count += 1; - self.vertices[v1.0].first_incidence = Some(Incidence(self.incidences.len() - 1)); + self.vertices[v1].incidence_count += 1; + self.vertices[v1].first_incidence = Some(IncidenceSlot(edge.arr_idx())); + edge } } @@ -74,7 +74,7 @@ impl Default for Graph { impl GraphTopology for Graph { type Vertex = Vertex; - type Edge = Incidence; + type Edge = Edge; fn vertex_count(&self) -> usize { self.vertices.len() @@ -85,7 +85,7 @@ impl GraphTopology for Graph { } fn degree(&self, v: Self::Vertex) -> usize { - self.vertices[v.0].incidence_count + self.vertices[v].incidence_count } fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool { @@ -93,31 +93,32 @@ impl GraphTopology for Graph { } fn vertices(&self) -> impl Iterator { - (0..self.vertices.len()).map(Vertex) + self.vertices.iter().map(|(i, _)| i) } fn neighbors(&self, v: Self::Vertex) -> impl Iterator { VertexNeighborIterator { graph: self, - incidence: self.vertices[v.0].first_incidence, + incidence: self.vertices[v].first_incidence, } } fn add_vertex(&mut self) -> Self::Vertex { - self.vertices.push(VertexIncidenceHeader { + self.vertices.insert(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); + let first = self.add_incidence(v1, v2); self.add_incidence(v2, v1); - Incidence(self.incidences.len() - 2) + first } } +// TODO: impl GraphTopologyDeletion for Graph. + #[cfg(test)] mod tests { use super::*;