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
+2 -1
View File
@@ -145,7 +145,8 @@ pub mod traits;
/// Convenience re-exports of graph topology traits for common use.
pub mod prelude {
pub use crate::traits::{
GraphTopology, GraphTopologyAddition, GraphTopologyDeletion, Incidence, IncidenceCursor,
FromGraph, GraphTopology, GraphTopologyAddition, GraphTopologyDeletion, Incidence,
IncidenceCursor,
};
}
+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;
+1 -1
View File
@@ -2,7 +2,7 @@ use std::fmt::Debug;
use crate::maps::ElementMap;
use crate::models::{AppendGraph, FrozenGraph};
use crate::traits::{GraphTopology, GraphTopologyAddition, Incidence};
use crate::traits::{FromGraph, GraphTopology, GraphTopologyAddition, Incidence};
pub trait MakeTestGraph: GraphTopology + Sized {
fn standard() -> (
+33
View File
@@ -237,6 +237,39 @@ pub trait GraphTopologyDeletion: GraphTopology {
fn delete_edge(&mut self, e: Self::Edge);
}
/// A trait for constructing an undirected graph topology by converting another graph
/// implementation to this one.
pub trait FromGraph: GraphTopology {
/// Creates a new graph instance that is isomorphic to `source`.
///
/// Returns the copied graph instance and two mappings from the vertices and edges of `source`
/// to the corresponding vertices and edges of the returned graph.
///
/// # Examples
///
/// ```
/// use grapherity::prelude::*;
/// use grapherity::models::{AppendGraph, FrozenGraph};
///
/// let mut source = AppendGraph::new();
/// let u = source.add_vertex();
/// let v = source.add_vertex();
/// let e = source.add_edge(u, v);
/// let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&source);
///
/// assert!(graph.are_adjacent(vertex_map[u], vertex_map[v]));
/// ```
fn from_graph<G: GraphTopology>(
source: &G,
) -> (
Self,
ElementMap<G::Vertex, Self::Vertex>,
ElementMap<G::Edge, Self::Edge>,
)
where
Self: Sized;
}
/// A cursor for traversing the incidences of a vertex one step at a time.
///
/// Because the cursor is [`Copy`], its state can be saved and restored to replay or branch a