Add FromGraph trait, and add implementation for FrozenGraph

This commit is contained in:
2026-09-19 17:53:44 +02:00
parent e593298eba
commit 038e984c97
4 changed files with 99 additions and 70 deletions
+63 -68
View File
@@ -3,7 +3,7 @@
use std::ops::IndexMut;
use crate::maps::ElementMap;
use crate::traits::{GraphTopology, Incidence, IncidenceCursor};
use crate::traits::{FromGraph, GraphTopology, Incidence, IncidenceCursor};
/// An opaque handle identifying a vertex in a [`FrozenGraph`].
///
@@ -103,67 +103,6 @@ pub struct FrozenGraph {
}
impl FrozenGraph {
// TODO: Move this to a new FromGraph trait and adapt the MakeTestGraph impl to use helper functions like "fn fixture_empty<G: FromGraph>() -> G { G::from_graph(&AppendGraph::empty()).0 }".
/// Creates a graph instance as a copy of another graph.
///
/// Returns the copied graph instance and two mappings of the `source` graph vertices and edges
/// to the new ones. Note that the returned mappings cannot support additional growth, i.e. any
/// vertex or edge added to the `source` graph after calling `from_graph` will map to an invalid
/// vertex or edge in the new `FrozenGraph`.
pub fn from_graph<G: GraphTopology>(
source: &G,
) -> (
Self,
ElementMap<G::Vertex, Vertex>,
ElementMap<G::Edge, Edge>,
) {
// Builds vertex list with sentinel and vertex map.
let mut vertex_map = source.vertex_map(Vertex(usize::MAX));
let mut vertices = Vec::with_capacity(source.vertex_count() + 1);
let mut offset = 0;
for (i, v) in source.vertices().enumerate() {
vertex_map[v] = Vertex(i);
vertices.push(offset);
offset += source.degree(v);
}
vertices.push(offset);
// Builds incidence list and edge map. Uses "usize::MAX" as "unset" here: an edge can never
// be normalized to "usize::MAX", since it must be the smaller index of two incidences.
const UNSET: usize = usize::MAX;
let mut edge_map = source.edge_map(Edge(UNSET));
let mut incidences = Vec::with_capacity(source.edge_count() * 2);
for v in source.vertices() {
for Incidence { vertex: u, edge: e } in source.incidences(v) {
let em = edge_map.index_mut(e);
if em.0 == UNSET {
// Adds the first incidence for the edge, but we don't know the opposite, yet.
*em = Edge(incidences.len());
incidences.push(IncidenceEntry {
adjacent: vertex_map[u],
opposite: 0,
});
} else {
// Adds the second incidence for the edge and fills the opposite for the first.
incidences[em.0].opposite = incidences.len();
incidences.push(IncidenceEntry {
adjacent: vertex_map[u],
opposite: em.0,
});
}
}
}
(
Self {
vertices,
incidences,
},
vertex_map,
edge_map,
)
}
fn raw_incidences(&self, v: Vertex) -> impl Iterator<Item = usize> {
self.vertices[v.0]..self.vertices[v.0 + 1]
}
@@ -186,12 +125,6 @@ impl FrozenGraph {
}
}
impl<G: GraphTopology> From<&G> for FrozenGraph {
fn from(graph: &G) -> Self {
Self::from_graph(graph).0
}
}
impl GraphTopology for FrozenGraph {
type Vertex = Vertex;
type Edge = Edge;
@@ -257,6 +190,68 @@ impl GraphTopology for FrozenGraph {
}
}
impl<G: GraphTopology> From<&G> for FrozenGraph {
fn from(graph: &G) -> Self {
Self::from_graph(graph).0
}
}
impl FromGraph for FrozenGraph {
fn from_graph<G: GraphTopology>(
source: &G,
) -> (
Self,
ElementMap<G::Vertex, Vertex>,
ElementMap<G::Edge, Edge>,
) {
// Builds vertex list with sentinel and vertex map.
let mut vertex_map = source.vertex_map(Vertex(usize::MAX));
let mut vertices = Vec::with_capacity(source.vertex_count() + 1);
let mut offset = 0;
for (i, v) in source.vertices().enumerate() {
vertex_map[v] = Vertex(i);
vertices.push(offset);
offset += source.degree(v);
}
vertices.push(offset);
// Builds incidence list and edge map. Uses "usize::MAX" as "unset" here: an edge can never
// be normalized to "usize::MAX", since it must be the smaller index of two incidences.
const UNSET: usize = usize::MAX;
let mut edge_map = source.edge_map(Edge(UNSET));
let mut incidences = Vec::with_capacity(source.edge_count() * 2);
for v in source.vertices() {
for Incidence { vertex: u, edge: e } in source.incidences(v) {
let em = edge_map.index_mut(e);
if em.0 == UNSET {
// Adds the first incidence for the edge, but we don't know the opposite, yet.
*em = Edge(incidences.len());
incidences.push(IncidenceEntry {
adjacent: vertex_map[u],
opposite: 0,
});
} else {
// Adds the second incidence for the edge and fills the opposite for the first.
incidences[em.0].opposite = incidences.len();
incidences.push(IncidenceEntry {
adjacent: vertex_map[u],
opposite: em.0,
});
}
}
}
(
Self {
vertices,
incidences,
},
vertex_map,
edge_map,
)
}
}
#[cfg(test)]
mod trait_tests {
use super::FrozenGraph;