Renamed FromGraph to GraphTopologyConversion to align with existing traits and separate from From trait

This commit is contained in:
2026-09-29 23:16:57 +02:00
parent 9d43f0cb8d
commit c8855daddf
11 changed files with 125 additions and 98 deletions
+2 -2
View File
@@ -145,8 +145,8 @@ pub mod traits;
/// Convenience re-exports of graph topology traits for common use. /// Convenience re-exports of graph topology traits for common use.
pub mod prelude { pub mod prelude {
pub use crate::traits::{ pub use crate::traits::{
FromGraph, GraphTopology, GraphTopologyAddition, GraphTopologyDeletion, Incidence, GraphTopology, GraphTopologyAddition, GraphTopologyConversion, GraphTopologyDeletion,
IncidenceCursor, Incidence, IncidenceCursor,
}; };
} }
+5 -4
View File
@@ -17,14 +17,15 @@ pub use graph::{Graph, GraphEdgeMap, GraphIncidence, GraphVertexMap};
/// Builds a new graph isomorphic to `source`. /// Builds a new graph isomorphic to `source`.
/// ///
/// The resulting graph type `G` must implement [`GraphTopologyAddition`] which is used to add /// The resulting graph type `G` implements [`GraphTopologyAddition`] which is used to add
/// vertices and edges. `on_edge` is called for each edge in `source` together with the /// vertices and edges. `on_edge` is called for each edge in `source` together with the
/// corresponding new edge of the new graph, which callers can use to construct an edge mapping. /// corresponding new edge of the new graph, which callers can use to construct an edge mapping.
/// ///
/// Used by the [`GraphTopologyAddition`] models' `From<&H>` and [`FromGraph`] implementations. /// Used by the [`GraphTopologyAddition`] models' `From<&H>` and [`GraphTopologyConversion`]
/// implementations.
/// ///
/// [`FromGraph`]: crate::traits::FromGraph /// [`GraphTopologyConversion`]: crate::traits::GraphTopologyConversion
pub(crate) fn build_from_topology<G: GraphTopologyAddition, H: GraphTopology>( pub(crate) fn convert_to_addition<G: GraphTopologyAddition, H: GraphTopology>(
source: &H, source: &H,
vertex_default: G::Vertex, vertex_default: G::Vertex,
mut on_edge: impl FnMut(H::Edge, G::Edge), mut on_edge: impl FnMut(H::Edge, G::Edge),
+15 -12
View File
@@ -4,7 +4,9 @@ use std::num::NonZeroUsize;
use crate::maps::ElementMap; use crate::maps::ElementMap;
use crate::models; use crate::models;
use crate::traits::{FromGraph, GraphTopology, GraphTopologyAddition, Incidence, IncidenceCursor}; use crate::traits::{
GraphTopology, GraphTopologyAddition, GraphTopologyConversion, Incidence, IncidenceCursor,
};
/// An opaque handle identifying a vertex in an [`AppendGraph`]. /// An opaque handle identifying a vertex in an [`AppendGraph`].
/// ///
@@ -260,14 +262,8 @@ impl GraphTopologyAddition for AppendGraph {
} }
} }
impl<H: GraphTopology> From<&H> for AppendGraph { impl GraphTopologyConversion for AppendGraph {
fn from(source: &H) -> Self { fn convert<H: GraphTopology>(
models::build_from_topology::<Self, _>(source, Vertex(usize::MAX), |_, _| {}).0
}
}
impl FromGraph for AppendGraph {
fn from_graph<H: GraphTopology>(
source: &H, source: &H,
) -> ( ) -> (
Self, Self,
@@ -276,11 +272,17 @@ impl FromGraph for AppendGraph {
) { ) {
let mut edge_map = source.edge_map(Edge(NonZeroUsize::MAX)); let mut edge_map = source.edge_map(Edge(NonZeroUsize::MAX));
let (result, vertex_map) = let (result, vertex_map) =
models::build_from_topology(source, Vertex(usize::MAX), |e, new_e| edge_map[e] = new_e); models::convert_to_addition(source, Vertex(usize::MAX), |e, new_e| edge_map[e] = new_e);
(result, vertex_map, edge_map) (result, vertex_map, edge_map)
} }
} }
impl<H: GraphTopology> From<&H> for AppendGraph {
fn from(source: &H) -> Self {
models::convert_to_addition::<Self, _>(source, Vertex(usize::MAX), |_, _| {}).0
}
}
#[cfg(test)] #[cfg(test)]
mod trait_tests { mod trait_tests {
use super::AppendGraph; use super::AppendGraph;
@@ -290,8 +292,9 @@ mod trait_tests {
crate::graph_topology_addition_tests!(AppendGraph); crate::graph_topology_addition_tests!(AppendGraph);
crate::graph_topology_clone_tests!(AppendGraph); crate::graph_topology_clone_tests!(AppendGraph);
crate::graph_topology_addition_clone_tests!(AppendGraph); crate::graph_topology_addition_clone_tests!(AppendGraph);
crate::graph_topology_from_graph_tests!(AppendGraph, Graph); crate::graph_topology_conversion_tests!(AppendGraph, Graph);
crate::graph_topology_addition_from_graph_tests!(AppendGraph, Graph); crate::graph_topology_addition_conversion_tests!(AppendGraph, Graph);
crate::from_tests!(AppendGraph, Graph);
} }
#[cfg(test)] #[cfg(test)]
+12 -11
View File
@@ -3,7 +3,7 @@
use std::ops::IndexMut; use std::ops::IndexMut;
use crate::maps::ElementMap; use crate::maps::ElementMap;
use crate::traits::{FromGraph, GraphTopology, Incidence, IncidenceCursor}; use crate::traits::{GraphTopology, GraphTopologyConversion, Incidence, IncidenceCursor};
/// An opaque handle identifying a vertex in a [`FrozenGraph`]. /// An opaque handle identifying a vertex in a [`FrozenGraph`].
/// ///
@@ -77,7 +77,7 @@ impl IncidenceCursor<FrozenGraph> for FrozenGraphIncidenceCursor {
/// let u = graph.add_vertex(); /// let u = graph.add_vertex();
/// let v = graph.add_vertex(); /// let v = graph.add_vertex();
/// let e = graph.add_edge(u, v); /// let e = graph.add_edge(u, v);
/// let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&graph); /// let (graph, vertex_map, edge_map) = FrozenGraph::convert(&graph);
/// ///
/// // Queries the FrozenGraph instance via the returned map. /// // Queries the FrozenGraph instance via the returned map.
/// assert!(graph.are_adjacent(vertex_map[u], vertex_map[v])); /// assert!(graph.are_adjacent(vertex_map[u], vertex_map[v]));
@@ -191,14 +191,8 @@ impl GraphTopology for FrozenGraph {
} }
} }
impl<G: GraphTopology> From<&G> for FrozenGraph { impl GraphTopologyConversion for FrozenGraph {
fn from(graph: &G) -> Self { fn convert<G: GraphTopology>(
Self::from_graph(graph).0
}
}
impl FromGraph for FrozenGraph {
fn from_graph<G: GraphTopology>(
source: &G, source: &G,
) -> ( ) -> (
Self, Self,
@@ -253,6 +247,12 @@ impl FromGraph for FrozenGraph {
} }
} }
impl<G: GraphTopology> From<&G> for FrozenGraph {
fn from(graph: &G) -> Self {
Self::convert(graph).0
}
}
#[cfg(test)] #[cfg(test)]
mod trait_tests { mod trait_tests {
use super::FrozenGraph; use super::FrozenGraph;
@@ -260,7 +260,8 @@ mod trait_tests {
crate::graph_topology_tests!(FrozenGraph); crate::graph_topology_tests!(FrozenGraph);
crate::graph_topology_clone_tests!(FrozenGraph); crate::graph_topology_clone_tests!(FrozenGraph);
crate::graph_topology_from_graph_tests!(FrozenGraph, AppendGraph); crate::graph_topology_conversion_tests!(FrozenGraph, AppendGraph);
crate::from_tests!(FrozenGraph, AppendGraph);
} }
#[cfg(test)] #[cfg(test)]
+19 -18
View File
@@ -6,8 +6,8 @@ use typed_generational_arena::{Arena, Index};
use crate::maps::ElementMap; use crate::maps::ElementMap;
use crate::models; use crate::models;
use crate::traits::{ use crate::traits::{
FromGraph, GraphTopology, GraphTopologyAddition, GraphTopologyDeletion, Incidence, GraphTopology, GraphTopologyAddition, GraphTopologyConversion, GraphTopologyDeletion,
IncidenceCursor, Incidence, IncidenceCursor,
}; };
/// An opaque handle identifying a vertex in a [`Graph`]. /// An opaque handle identifying a vertex in a [`Graph`].
@@ -400,19 +400,8 @@ impl GraphTopologyDeletion for Graph {
} }
} }
impl<H: GraphTopology> From<&H> for Graph { impl GraphTopologyConversion for Graph {
fn from(source: &H) -> Self { fn convert<H: GraphTopology>(
models::build_from_topology::<Self, _>(
source,
Vertex::new(usize::MAX, usize::MAX),
|_, _| {},
)
.0
}
}
impl FromGraph for Graph {
fn from_graph<H: GraphTopology>(
source: &H, source: &H,
) -> ( ) -> (
Self, Self,
@@ -421,13 +410,24 @@ impl FromGraph for Graph {
) { ) {
let mut edge_map = source.edge_map(Edge::new(usize::MAX, usize::MAX)); let mut edge_map = source.edge_map(Edge::new(usize::MAX, usize::MAX));
let (result, vertex_map) = let (result, vertex_map) =
models::build_from_topology(source, Vertex::new(usize::MAX, usize::MAX), |e, new_e| { models::convert_to_addition(source, Vertex::new(usize::MAX, usize::MAX), |e, new_e| {
edge_map[e] = new_e edge_map[e] = new_e
}); });
(result, vertex_map, edge_map) (result, vertex_map, edge_map)
} }
} }
impl<H: GraphTopology> From<&H> for Graph {
fn from(source: &H) -> Self {
models::convert_to_addition::<Self, _>(
source,
Vertex::new(usize::MAX, usize::MAX),
|_, _| {},
)
.0
}
}
#[cfg(test)] #[cfg(test)]
mod trait_tests { mod trait_tests {
use super::Graph; use super::Graph;
@@ -439,8 +439,9 @@ mod trait_tests {
crate::graph_topology_addition_deletion_tests!(Graph); crate::graph_topology_addition_deletion_tests!(Graph);
crate::graph_topology_clone_tests!(Graph); crate::graph_topology_clone_tests!(Graph);
crate::graph_topology_addition_clone_tests!(Graph); crate::graph_topology_addition_clone_tests!(Graph);
crate::graph_topology_from_graph_tests!(Graph, AppendGraph); crate::graph_topology_conversion_tests!(Graph, AppendGraph);
crate::graph_topology_addition_from_graph_tests!(Graph, AppendGraph); crate::graph_topology_addition_conversion_tests!(Graph, AppendGraph);
crate::from_tests!(Graph, AppendGraph);
} }
#[cfg(test)] #[cfg(test)]
+3 -2
View File
@@ -1,11 +1,12 @@
//! Test fixture and test macros for graph topology and algorithm implementations. //! Test fixture and test macros for graph topology and algorithm implementations.
pub mod fixtures; pub mod fixtures;
pub mod from_testing;
pub mod graph_topology_addition_clone_testing; pub mod graph_topology_addition_clone_testing;
pub mod graph_topology_addition_conversion_testing;
pub mod graph_topology_addition_deletion_testing; pub mod graph_topology_addition_deletion_testing;
pub mod graph_topology_addition_from_graph_testing;
pub mod graph_topology_addition_testing; pub mod graph_topology_addition_testing;
pub mod graph_topology_clone_testing; pub mod graph_topology_clone_testing;
pub mod graph_topology_conversion_testing;
pub mod graph_topology_deletion_testing; pub mod graph_topology_deletion_testing;
pub mod graph_topology_from_graph_testing;
pub mod graph_topology_testing; pub mod graph_topology_testing;
+9 -9
View File
@@ -2,7 +2,7 @@ use std::fmt::Debug;
use crate::maps::ElementMap; use crate::maps::ElementMap;
use crate::models::{AppendGraph, FrozenGraph}; use crate::models::{AppendGraph, FrozenGraph};
use crate::traits::{FromGraph, GraphTopology, GraphTopologyAddition, Incidence}; use crate::traits::{GraphTopology, GraphTopologyAddition, GraphTopologyConversion, Incidence};
pub trait MakeTestGraph: GraphTopology + Sized { pub trait MakeTestGraph: GraphTopology + Sized {
fn standard() -> ( fn standard() -> (
@@ -175,7 +175,7 @@ impl MakeTestGraph for FrozenGraph {
[Vec<Incidence<Self::Vertex, Self::Edge>>; 10], [Vec<Incidence<Self::Vertex, Self::Edge>>; 10],
) { ) {
let (build, vertices, edges, incidences) = AppendGraph::standard(); let (build, vertices, edges, incidences) = AppendGraph::standard();
let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); let (graph, vertex_map, edge_map) = FrozenGraph::convert(&build);
( (
graph, graph,
vertices.map(|v| vertex_map[v]), vertices.map(|v| vertex_map[v]),
@@ -197,31 +197,31 @@ impl MakeTestGraph for FrozenGraph {
fn single_vertex() -> (Self, Self::Vertex) { fn single_vertex() -> (Self, Self::Vertex) {
let (build, v) = AppendGraph::single_vertex(); let (build, v) = AppendGraph::single_vertex();
let (graph, vertex_map, _) = FrozenGraph::from_graph(&build); let (graph, vertex_map, _) = FrozenGraph::convert(&build);
(graph, vertex_map[v]) (graph, vertex_map[v])
} }
fn single_edge() -> (Self, [Self::Vertex; 2], Self::Edge) { fn single_edge() -> (Self, [Self::Vertex; 2], Self::Edge) {
let (build, vertices, e) = AppendGraph::single_edge(); let (build, vertices, e) = AppendGraph::single_edge();
let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); let (graph, vertex_map, edge_map) = FrozenGraph::convert(&build);
(graph, vertices.map(|v| vertex_map[v]), edge_map[e]) (graph, vertices.map(|v| vertex_map[v]), edge_map[e])
} }
fn disconnected() -> (Self, [Self::Vertex; 3]) { fn disconnected() -> (Self, [Self::Vertex; 3]) {
let (build, vertices) = AppendGraph::disconnected(); let (build, vertices) = AppendGraph::disconnected();
let (graph, vertex_map, _) = FrozenGraph::from_graph(&build); let (graph, vertex_map, _) = FrozenGraph::convert(&build);
(graph, vertices.map(|v| vertex_map[v])) (graph, vertices.map(|v| vertex_map[v]))
} }
fn loop_edge() -> (Self, Self::Vertex, Self::Edge) { fn loop_edge() -> (Self, Self::Vertex, Self::Edge) {
let (build, v, e) = AppendGraph::loop_edge(); let (build, v, e) = AppendGraph::loop_edge();
let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); let (graph, vertex_map, edge_map) = FrozenGraph::convert(&build);
(graph, vertex_map[v], edge_map[e]) (graph, vertex_map[v], edge_map[e])
} }
fn multiple_edges<const K: usize>() -> (Self, [Self::Vertex; 2], [Self::Edge; K]) { fn multiple_edges<const K: usize>() -> (Self, [Self::Vertex; 2], [Self::Edge; K]) {
let (build, vertices, edges) = AppendGraph::multiple_edges(); let (build, vertices, edges) = AppendGraph::multiple_edges();
let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); let (graph, vertex_map, edge_map) = FrozenGraph::convert(&build);
( (
graph, graph,
vertices.map(|v| vertex_map[v]), vertices.map(|v| vertex_map[v]),
@@ -231,7 +231,7 @@ impl MakeTestGraph for FrozenGraph {
fn two_edge_path() -> (Self, [Self::Vertex; 3], [Self::Edge; 2]) { fn two_edge_path() -> (Self, [Self::Vertex; 3], [Self::Edge; 2]) {
let (build, vertices, edges) = AppendGraph::two_edge_path(); let (build, vertices, edges) = AppendGraph::two_edge_path();
let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); let (graph, vertex_map, edge_map) = FrozenGraph::convert(&build);
( (
graph, graph,
vertices.map(|v| vertex_map[v]), vertices.map(|v| vertex_map[v]),
@@ -241,7 +241,7 @@ impl MakeTestGraph for FrozenGraph {
fn two_edge_path_with_loops() -> (Self, [Self::Vertex; 3], [Self::Edge; 4]) { fn two_edge_path_with_loops() -> (Self, [Self::Vertex; 3], [Self::Edge; 4]) {
let (build, vertices, edges) = AppendGraph::two_edge_path_with_loops(); let (build, vertices, edges) = AppendGraph::two_edge_path_with_loops();
let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); let (graph, vertex_map, edge_map) = FrozenGraph::convert(&build);
( (
graph, graph,
vertices.map(|v| vertex_map[v]), vertices.map(|v| vertex_map[v]),
@@ -1,16 +1,12 @@
use std::fmt::Debug;
use crate::testing::fixtures;
use crate::testing::fixtures::MakeTestGraph; use crate::testing::fixtures::MakeTestGraph;
use crate::traits::{FromGraph, GraphTopology}; use crate::traits::GraphTopology;
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! graph_topology_from_graph_tests { macro_rules! from_tests {
($T:ty, $U:ty) => { ($T:ty, $U:ty) => {
$crate::graph_topology_from_graph_tests!(@wrap $T, $U, $crate::from_tests!(@wrap $T, $U,
from, from,
from_graph,
); );
}; };
@@ -18,7 +14,7 @@ macro_rules! graph_topology_from_graph_tests {
$( $(
#[test] #[test]
fn $name() { fn $name() {
$crate::testing::graph_topology_from_graph_testing::$name::<$T, $U>(); $crate::testing::from_testing::$name::<$T, $U>();
} }
)* )*
}; };
@@ -40,22 +36,3 @@ where
actual.sort(); actual.sort();
assert_eq!(actual, expected, "degree sequence should match"); assert_eq!(actual, expected, "degree sequence should match");
} }
pub fn from_graph<G: FromGraph, H: MakeTestGraph>()
where
G::Vertex: Debug,
G::Edge: Debug,
H::Vertex: Debug,
H::Edge: Debug,
{
let (source, vertices, edges, _) = H::standard();
let (graph, vertex_map, edge_map) = G::from_graph(&source);
fixtures::assert_isomorphic(
&source,
&graph,
&vertices,
|v| vertex_map[v],
&edges,
|e| edge_map[e],
);
}
@@ -1,13 +1,13 @@
use crate::testing::fixtures; use crate::testing::fixtures;
use crate::testing::fixtures::MakeTestGraph; use crate::testing::fixtures::MakeTestGraph;
use crate::traits::{FromGraph, GraphTopologyAddition}; use crate::traits::{GraphTopologyAddition, GraphTopologyConversion};
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! graph_topology_addition_from_graph_tests { macro_rules! graph_topology_addition_conversion_tests {
($T:ty, $U:ty) => { ($T:ty, $U:ty) => {
$crate::graph_topology_addition_from_graph_tests!(@wrap $T, $U, $crate::graph_topology_addition_conversion_tests!(@wrap $T, $U,
from_graph_mutation_independent, convert_mutation_independent,
); );
}; };
@@ -15,18 +15,18 @@ macro_rules! graph_topology_addition_from_graph_tests {
$( $(
#[test] #[test]
fn $name() { fn $name() {
$crate::testing::graph_topology_addition_from_graph_testing::$name::<$T, $U>(); $crate::testing::graph_topology_addition_conversion_testing::$name::<$T, $U>();
} }
)* )*
}; };
} }
pub fn from_graph_mutation_independent<G, H>() pub fn convert_mutation_independent<G, H>()
where where
G: FromGraph + GraphTopologyAddition, G: GraphTopologyConversion + GraphTopologyAddition,
H: MakeTestGraph + GraphTopologyAddition, H: MakeTestGraph + GraphTopologyAddition,
{ {
let (mut source, vertices, _) = H::single_edge(); let (mut source, vertices, _) = H::single_edge();
let (mut graph, vertex_map, _) = G::from_graph(&source); let (mut graph, vertex_map, _) = G::convert(&source);
fixtures::assert_mutation_independent(&mut source, &mut graph, &vertices, |v| vertex_map[v]); fixtures::assert_mutation_independent(&mut source, &mut graph, &vertices, |v| vertex_map[v]);
} }
@@ -0,0 +1,43 @@
use std::fmt::Debug;
use crate::testing::fixtures;
use crate::testing::fixtures::MakeTestGraph;
use crate::traits::GraphTopologyConversion;
#[doc(hidden)]
#[macro_export]
macro_rules! graph_topology_conversion_tests {
($T:ty, $U:ty) => {
$crate::graph_topology_conversion_tests!(@wrap $T, $U,
convert,
);
};
(@wrap $T:ty, $U:ty, $($name:ident),* $(,)?) => {
$(
#[test]
fn $name() {
$crate::testing::graph_topology_conversion_testing::$name::<$T, $U>();
}
)*
};
}
pub fn convert<G: GraphTopologyConversion, H: MakeTestGraph>()
where
G::Vertex: Debug,
G::Edge: Debug,
H::Vertex: Debug,
H::Edge: Debug,
{
let (source, vertices, edges, _) = H::standard();
let (graph, vertex_map, edge_map) = G::convert(&source);
fixtures::assert_isomorphic(
&source,
&graph,
&vertices,
|v| vertex_map[v],
&edges,
|e| edge_map[e],
);
}
+5 -5
View File
@@ -291,11 +291,11 @@ pub struct Incidence<V, E> {
// TODO-0.3.0: Provide and document a way to transform an ElementMap for source to an ElementMap for the copy. // TODO-0.3.0: Provide and document a way to transform an ElementMap for source to an ElementMap for the copy.
/// A trait for constructing an undirected graph topology by converting another graph /// A trait for constructing an undirected graph topology by converting another graph
/// implementation to this one. /// implementation to this one.
pub trait FromGraph: GraphTopology { pub trait GraphTopologyConversion: GraphTopology {
/// Creates a new graph instance that is isomorphic to `source`. /// 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` /// Returns the converted graph instance and two mappings from the vertices and edges of
/// to the corresponding vertices and edges of the returned graph. /// `source` to the corresponding vertices and edges of the returned graph.
/// ///
/// # Examples /// # Examples
/// ///
@@ -307,11 +307,11 @@ pub trait FromGraph: GraphTopology {
/// let u = source.add_vertex(); /// let u = source.add_vertex();
/// let v = source.add_vertex(); /// let v = source.add_vertex();
/// let e = source.add_edge(u, v); /// let e = source.add_edge(u, v);
/// let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&source); /// let (graph, vertex_map, edge_map) = FrozenGraph::convert(&source);
/// ///
/// assert!(graph.are_adjacent(vertex_map[u], vertex_map[v])); /// assert!(graph.are_adjacent(vertex_map[u], vertex_map[v]));
/// ``` /// ```
fn from_graph<G: GraphTopology>( fn convert<G: GraphTopology>(
source: &G, source: &G,
) -> ( ) -> (
Self, Self,