Change GraphTopology implementation for Graph with generational arena

This commit is contained in:
2026-04-23 18:25:23 +02:00
parent 67e16f50cd
commit 37a911dce0
2 changed files with 41 additions and 39 deletions
+1
View File
@@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
typed-generational-arena = "0.2.9"
+40 -39
View File
@@ -1,67 +1,67 @@
use typed_generational_arena::{Arena, Index};
use crate::traits::GraphTopology; use crate::traits::GraphTopology;
#[derive(Copy, Clone, PartialEq, Eq, Debug)] type Vertex = Index<VertexIncidenceHeader, usize, usize>;
pub struct Vertex(usize);
impl From<Vertex> for usize { type Edge = Index<IncidenceEntry, usize, usize>;
fn from(v: Vertex) -> usize {
v.0
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[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, incidence_count: usize,
first_incidence: Option<Incidence>, first_incidence: Option<IncidenceSlot>,
} }
struct IncidenceEntry { pub struct IncidenceEntry {
next: Option<Incidence>, next: Option<IncidenceSlot>,
neighbor: Vertex, neighbor: VertexSlot,
} }
struct VertexNeighborIterator<'a> { struct VertexNeighborIterator<'a> {
graph: &'a Graph, graph: &'a Graph,
incidence: Option<Incidence>, incidence: Option<IncidenceSlot>,
} }
impl<'a> Iterator for VertexNeighborIterator<'a> { impl<'a> Iterator for VertexNeighborIterator<'a> {
type Item = Vertex; type Item = Vertex;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
// TODO: Benchmark storing full Index (one read, larger entries) vs slot + get_idx() (two reads, smaller entries).
let incidence = self.incidence?; 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; 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 { pub struct Graph {
// TODO: Index 'vertices' by a 'Vertex' instead of 'Vertex.0'? // TODO: Arena index and generation types could be externalized to Graph.
vertices: Vec<VertexIncidenceHeader>, vertices: Arena<VertexIncidenceHeader, usize, usize>,
incidences: Vec<IncidenceEntry>, incidences: Arena<IncidenceEntry, usize, usize>,
} }
impl Graph { impl Graph {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
vertices: vec![], vertices: Arena::new(),
incidences: vec![], incidences: Arena::new(),
} }
} }
fn add_incidence(&mut self, v1: Vertex, v2: Vertex) { fn add_incidence(&mut self, v1: Vertex, v2: Vertex) -> Edge {
self.incidences.push(IncidenceEntry { let edge = self.incidences.insert(IncidenceEntry {
next: self.vertices[v1.0].first_incidence.take(), next: self.vertices[v1].first_incidence.take(),
neighbor: v2, neighbor: VertexSlot(v2.arr_idx()),
}); });
self.vertices[v1.0].incidence_count += 1; self.vertices[v1].incidence_count += 1;
self.vertices[v1.0].first_incidence = Some(Incidence(self.incidences.len() - 1)); self.vertices[v1].first_incidence = Some(IncidenceSlot(edge.arr_idx()));
edge
} }
} }
@@ -74,7 +74,7 @@ impl Default for Graph {
impl GraphTopology for Graph { impl GraphTopology for Graph {
type Vertex = Vertex; type Vertex = Vertex;
type Edge = Incidence; type Edge = Edge;
fn vertex_count(&self) -> usize { fn vertex_count(&self) -> usize {
self.vertices.len() self.vertices.len()
@@ -85,7 +85,7 @@ impl GraphTopology for Graph {
} }
fn degree(&self, v: Self::Vertex) -> usize { 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 { fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool {
@@ -93,31 +93,32 @@ impl GraphTopology for Graph {
} }
fn vertices(&self) -> impl Iterator<Item = Self::Vertex> { fn vertices(&self) -> impl Iterator<Item = Self::Vertex> {
(0..self.vertices.len()).map(Vertex) self.vertices.iter().map(|(i, _)| i)
} }
fn neighbors(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Vertex> { fn neighbors(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Vertex> {
VertexNeighborIterator { VertexNeighborIterator {
graph: self, graph: self,
incidence: self.vertices[v.0].first_incidence, incidence: self.vertices[v].first_incidence,
} }
} }
fn add_vertex(&mut self) -> Self::Vertex { fn add_vertex(&mut self) -> Self::Vertex {
self.vertices.push(VertexIncidenceHeader { self.vertices.insert(VertexIncidenceHeader {
incidence_count: 0, incidence_count: 0,
first_incidence: None, first_incidence: None,
}); })
Vertex(self.vertices.len() - 1)
} }
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); let first = self.add_incidence(v1, v2);
self.add_incidence(v2, v1); self.add_incidence(v2, v1);
Incidence(self.incidences.len() - 2) first
} }
} }
// TODO: impl GraphTopologyDeletion for Graph.
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;