507 lines
17 KiB
Rust
507 lines
17 KiB
Rust
use std::fmt::Debug;
|
|
|
|
use crate::maps::ElementMap;
|
|
use crate::models::{AppendGraph, FrozenGraph};
|
|
use crate::traits::{GraphTopology, GraphTopologyAddition, GraphTopologyConversion, Incidence};
|
|
|
|
pub trait MakeTestGraph: GraphTopology + Sized {
|
|
/// Returns a versatile, pre-defined standard test graph.
|
|
// Since this is only a test fixture, the complex return type here is deemed acceptable.
|
|
#[allow(clippy::type_complexity)]
|
|
fn standard() -> (
|
|
Self,
|
|
[Self::Vertex; 10],
|
|
[(Self::Edge, Self::Vertex, Self::Vertex); 18],
|
|
[Vec<Incidence<Self::Vertex, Self::Edge>>; 10],
|
|
);
|
|
|
|
/// Returns an empty graph.
|
|
fn empty() -> Self;
|
|
|
|
/// Returns a graph with a single vertex and no edges.
|
|
fn single_vertex() -> (Self, Self::Vertex);
|
|
|
|
/// Returns a graph with two vertices connected by a single edge.
|
|
fn single_edge() -> (Self, [Self::Vertex; 2], Self::Edge);
|
|
|
|
/// Returns a graph with vertices *v0*, *v1*, and *v2*, where *v0* is isolated, and the other
|
|
/// two are connected by a single edge.
|
|
fn disconnected() -> (Self, [Self::Vertex; 3]);
|
|
|
|
/// Returns a graph with a single vertex that has a single loop edge.
|
|
fn loop_edge() -> (Self, Self::Vertex, Self::Edge);
|
|
|
|
/// Returns a graph with two vertices that are connected by `K` multi-edges.
|
|
fn multiple_edges<const K: usize>() -> (Self, [Self::Vertex; 2], [Self::Edge; K]);
|
|
|
|
/// Returns a graph that is a path with three vertices and two edges between them, returned in
|
|
/// order.
|
|
fn two_edge_path() -> (Self, [Self::Vertex; 3], [Self::Edge; 2]);
|
|
|
|
/// Returns a graph that is a path with three vertices *v0*, *v1*, and *v2*, and two edges
|
|
/// *e0 = (v0, v1)* and *e1 = (v1, v2)* between them, and two loops on the first two vertices
|
|
/// *e2 = (v0, v0)* and *e3 = (v1, v1)*.
|
|
fn two_edge_path_with_loops() -> (Self, [Self::Vertex; 3], [Self::Edge; 4]);
|
|
}
|
|
|
|
impl<G: GraphTopologyAddition> MakeTestGraph for G {
|
|
fn standard() -> (
|
|
Self,
|
|
[Self::Vertex; 10],
|
|
[(Self::Edge, Self::Vertex, Self::Vertex); 18],
|
|
[Vec<Incidence<Self::Vertex, Self::Edge>>; 10],
|
|
) {
|
|
let mut graph = Self::default();
|
|
graph.reserve_vertices(10);
|
|
graph.reserve_edges(18);
|
|
let vertices = core::array::from_fn(|_| graph.add_vertex());
|
|
let edges = [
|
|
(vertices[0], vertices[1]),
|
|
(vertices[0], vertices[1]),
|
|
(vertices[1], vertices[2]),
|
|
(vertices[1], vertices[3]),
|
|
(vertices[1], vertices[4]),
|
|
(vertices[2], vertices[2]),
|
|
(vertices[2], vertices[4]),
|
|
(vertices[2], vertices[4]),
|
|
(vertices[2], vertices[5]),
|
|
(vertices[2], vertices[6]),
|
|
(vertices[3], vertices[6]),
|
|
(vertices[4], vertices[4]),
|
|
(vertices[4], vertices[7]),
|
|
(vertices[4], vertices[8]),
|
|
(vertices[5], vertices[9]),
|
|
(vertices[6], vertices[9]),
|
|
(vertices[7], vertices[8]),
|
|
(vertices[7], vertices[9]),
|
|
]
|
|
.map(|(u, v)| (graph.add_edge(u, v), u, v));
|
|
let i = |vertex, edge| Incidence { vertex, edge };
|
|
let incidences = [
|
|
vec![i(vertices[1], edges[0].0), i(vertices[1], edges[1].0)],
|
|
vec![
|
|
i(vertices[0], edges[0].0),
|
|
i(vertices[0], edges[1].0),
|
|
i(vertices[2], edges[2].0),
|
|
i(vertices[3], edges[3].0),
|
|
i(vertices[4], edges[4].0),
|
|
],
|
|
vec![
|
|
i(vertices[2], edges[5].0),
|
|
i(vertices[2], edges[5].0),
|
|
i(vertices[1], edges[2].0),
|
|
i(vertices[4], edges[6].0),
|
|
i(vertices[4], edges[7].0),
|
|
i(vertices[5], edges[8].0),
|
|
i(vertices[6], edges[9].0),
|
|
],
|
|
vec![i(vertices[1], edges[3].0), i(vertices[6], edges[10].0)],
|
|
vec![
|
|
i(vertices[1], edges[4].0),
|
|
i(vertices[2], edges[6].0),
|
|
i(vertices[2], edges[7].0),
|
|
i(vertices[4], edges[11].0),
|
|
i(vertices[4], edges[11].0),
|
|
i(vertices[7], edges[12].0),
|
|
i(vertices[8], edges[13].0),
|
|
],
|
|
vec![i(vertices[2], edges[8].0), i(vertices[9], edges[14].0)],
|
|
vec![
|
|
i(vertices[2], edges[9].0),
|
|
i(vertices[3], edges[10].0),
|
|
i(vertices[9], edges[15].0),
|
|
],
|
|
vec![
|
|
i(vertices[4], edges[12].0),
|
|
i(vertices[8], edges[16].0),
|
|
i(vertices[9], edges[17].0),
|
|
],
|
|
vec![i(vertices[4], edges[13].0), i(vertices[7], edges[16].0)],
|
|
vec![
|
|
i(vertices[5], edges[14].0),
|
|
i(vertices[6], edges[15].0),
|
|
i(vertices[7], edges[17].0),
|
|
],
|
|
];
|
|
(graph, vertices, edges, incidences)
|
|
}
|
|
|
|
fn empty() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
fn single_vertex() -> (Self, Self::Vertex) {
|
|
let mut graph = Self::default();
|
|
let v = graph.add_vertex();
|
|
(graph, v)
|
|
}
|
|
|
|
fn single_edge() -> (Self, [Self::Vertex; 2], Self::Edge) {
|
|
let mut graph = Self::default();
|
|
graph.reserve_vertices(2);
|
|
let vertices = core::array::from_fn(|_| graph.add_vertex());
|
|
let e = graph.add_edge(vertices[0], vertices[1]);
|
|
(graph, vertices, e)
|
|
}
|
|
|
|
fn disconnected() -> (Self, [Self::Vertex; 3]) {
|
|
let mut graph = Self::default();
|
|
graph.reserve_vertices(3);
|
|
let vertices = core::array::from_fn(|_| graph.add_vertex());
|
|
graph.add_edge(vertices[1], vertices[2]);
|
|
(graph, vertices)
|
|
}
|
|
|
|
fn loop_edge() -> (Self, Self::Vertex, Self::Edge) {
|
|
let mut graph = Self::default();
|
|
let v = graph.add_vertex();
|
|
let e = graph.add_edge(v, v);
|
|
(graph, v, e)
|
|
}
|
|
|
|
fn multiple_edges<const K: usize>() -> (Self, [Self::Vertex; 2], [Self::Edge; K]) {
|
|
let mut graph = Self::default();
|
|
graph.reserve_vertices(2);
|
|
graph.reserve_edges(K);
|
|
let vertices = core::array::from_fn(|_| graph.add_vertex());
|
|
let edges = core::array::from_fn(|_| graph.add_edge(vertices[0], vertices[1]));
|
|
(graph, vertices, edges)
|
|
}
|
|
|
|
fn two_edge_path() -> (Self, [Self::Vertex; 3], [Self::Edge; 2]) {
|
|
let mut graph = Self::default();
|
|
graph.reserve_vertices(3);
|
|
graph.reserve_edges(2);
|
|
let vertices = core::array::from_fn(|_| graph.add_vertex());
|
|
let edges = core::array::from_fn(|i| graph.add_edge(vertices[i], vertices[i + 1]));
|
|
(graph, vertices, edges)
|
|
}
|
|
|
|
fn two_edge_path_with_loops() -> (Self, [Self::Vertex; 3], [Self::Edge; 4]) {
|
|
let mut graph = Self::default();
|
|
graph.reserve_vertices(3);
|
|
graph.reserve_edges(4);
|
|
let vertices = core::array::from_fn(|_| graph.add_vertex());
|
|
// Path edges.
|
|
let e0 = graph.add_edge(vertices[0], vertices[1]);
|
|
let e1 = graph.add_edge(vertices[1], vertices[2]);
|
|
// Loops.
|
|
let e2 = graph.add_edge(vertices[0], vertices[0]);
|
|
let e3 = graph.add_edge(vertices[1], vertices[1]);
|
|
(graph, vertices, [e0, e1, e2, e3])
|
|
}
|
|
}
|
|
|
|
impl MakeTestGraph for FrozenGraph {
|
|
fn standard() -> (
|
|
Self,
|
|
[Self::Vertex; 10],
|
|
[(Self::Edge, Self::Vertex, Self::Vertex); 18],
|
|
[Vec<Incidence<Self::Vertex, Self::Edge>>; 10],
|
|
) {
|
|
let (build, vertices, edges, incidences) = AppendGraph::standard();
|
|
let (graph, iso) = FrozenGraph::convert(&build);
|
|
(
|
|
graph,
|
|
vertices.map(|v| iso.vertex(v)),
|
|
edges.map(|(e, u, v)| (iso.edge(e), iso.vertex(u), iso.vertex(v))),
|
|
incidences.map(|vec| {
|
|
vec.iter()
|
|
.map(|i| Incidence {
|
|
vertex: iso.vertex(i.vertex),
|
|
edge: iso.edge(i.edge),
|
|
})
|
|
.collect()
|
|
}),
|
|
)
|
|
}
|
|
|
|
fn empty() -> Self {
|
|
FrozenGraph::from(&AppendGraph::empty())
|
|
}
|
|
|
|
fn single_vertex() -> (Self, Self::Vertex) {
|
|
let (build, v) = AppendGraph::single_vertex();
|
|
let (graph, iso) = FrozenGraph::convert(&build);
|
|
(graph, iso.vertex(v))
|
|
}
|
|
|
|
fn single_edge() -> (Self, [Self::Vertex; 2], Self::Edge) {
|
|
let (build, vertices, e) = AppendGraph::single_edge();
|
|
let (graph, iso) = FrozenGraph::convert(&build);
|
|
(graph, vertices.map(|v| iso.vertex(v)), iso.edge(e))
|
|
}
|
|
|
|
fn disconnected() -> (Self, [Self::Vertex; 3]) {
|
|
let (build, vertices) = AppendGraph::disconnected();
|
|
let (graph, iso) = FrozenGraph::convert(&build);
|
|
(graph, vertices.map(|v| iso.vertex(v)))
|
|
}
|
|
|
|
fn loop_edge() -> (Self, Self::Vertex, Self::Edge) {
|
|
let (build, v, e) = AppendGraph::loop_edge();
|
|
let (graph, iso) = FrozenGraph::convert(&build);
|
|
(graph, iso.vertex(v), iso.edge(e))
|
|
}
|
|
|
|
fn multiple_edges<const K: usize>() -> (Self, [Self::Vertex; 2], [Self::Edge; K]) {
|
|
let (build, vertices, edges) = AppendGraph::multiple_edges();
|
|
let (graph, iso) = FrozenGraph::convert(&build);
|
|
(
|
|
graph,
|
|
vertices.map(|v| iso.vertex(v)),
|
|
edges.map(|e| iso.edge(e)),
|
|
)
|
|
}
|
|
|
|
fn two_edge_path() -> (Self, [Self::Vertex; 3], [Self::Edge; 2]) {
|
|
let (build, vertices, edges) = AppendGraph::two_edge_path();
|
|
let (graph, iso) = FrozenGraph::convert(&build);
|
|
(
|
|
graph,
|
|
vertices.map(|v| iso.vertex(v)),
|
|
edges.map(|e| iso.edge(e)),
|
|
)
|
|
}
|
|
|
|
fn two_edge_path_with_loops() -> (Self, [Self::Vertex; 3], [Self::Edge; 4]) {
|
|
let (build, vertices, edges) = AppendGraph::two_edge_path_with_loops();
|
|
let (graph, iso) = FrozenGraph::convert(&build);
|
|
(
|
|
graph,
|
|
vertices.map(|v| iso.vertex(v)),
|
|
edges.map(|e| iso.edge(e)),
|
|
)
|
|
}
|
|
}
|
|
|
|
pub fn assert_standard_unweighted_distances_v0<V, F>(actual_for: F, vertices: &[V])
|
|
where
|
|
V: Debug + Copy,
|
|
F: Fn(V) -> Option<u32>,
|
|
{
|
|
let expected = [0, 1, 2, 2, 2, 3, 3, 3, 3, 4];
|
|
for (i, v) in vertices.iter().enumerate() {
|
|
assert_eq!(
|
|
actual_for(*v),
|
|
Some(expected[i]),
|
|
"unexpected distance from vertex {:?} to vertex {:?}",
|
|
vertices[0],
|
|
vertices[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
pub fn assert_standard_unweighted_predecessors_v0<V: Debug + Copy + PartialEq>(
|
|
actual: &ElementMap<V, Option<V>>,
|
|
vertices: &[V],
|
|
) {
|
|
let expected = [
|
|
vec![None],
|
|
vec![Some(vertices[0])],
|
|
vec![Some(vertices[1])],
|
|
vec![Some(vertices[1])],
|
|
vec![Some(vertices[1])],
|
|
vec![Some(vertices[2])],
|
|
vec![Some(vertices[2]), Some(vertices[3])],
|
|
vec![Some(vertices[4])],
|
|
vec![Some(vertices[4])],
|
|
vec![Some(vertices[5]), Some(vertices[6]), Some(vertices[7])],
|
|
];
|
|
assert_eq!(vertices.len(), expected.len());
|
|
for (&v, exp) in vertices.iter().zip(&expected) {
|
|
assert!(
|
|
exp.contains(&actual[v]),
|
|
"unexpected predecessor {:?} of vertex {v:?}",
|
|
actual[v]
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Asserts that the path given as `path_option` is a valid path in `graph`
|
|
///
|
|
/// Walks the path and asserts that it is `Some`, that it is empty if and only if
|
|
/// `source == target`, that its edges are pairwise incident in the order given, and that it starts
|
|
/// at `source` and ends at `target`.
|
|
pub fn assert_valid_path<G: GraphTopology>(
|
|
graph: &G,
|
|
path_option: Option<Vec<G::Edge>>,
|
|
source: G::Vertex,
|
|
targets: &[G::Vertex],
|
|
) -> Vec<G::Edge>
|
|
where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
let path = path_option.unwrap_or_else(|| {
|
|
panic!(
|
|
"path should exist between source vertex {source:?} and connected targets {targets:?}"
|
|
)
|
|
});
|
|
|
|
if targets.contains(&source) {
|
|
assert!(
|
|
path.is_empty(),
|
|
"path from source to itself should be empty"
|
|
);
|
|
} else {
|
|
assert!(!path.is_empty(), "path should not be empty");
|
|
// Walks the path: tracks current vertex, confirms each edge is incident to it.
|
|
let mut current = source;
|
|
for (i, &e) in path.iter().enumerate() {
|
|
let (u, v) = graph.incident_vertices(e);
|
|
assert_ne!(u, v, "path should not contain loop edge {e:?}");
|
|
assert!(
|
|
u == current || v == current,
|
|
"path edge {e:?} (index {i}, vertices {u:?} to {v:?}) not incident to vertex {current:?}"
|
|
);
|
|
current = if u == current { v } else { u };
|
|
}
|
|
assert!(
|
|
targets.contains(¤t),
|
|
"path should end at a target vertex ({targets:?}), but ended at {current:?}"
|
|
);
|
|
}
|
|
path
|
|
}
|
|
|
|
/// Asserts that the two graphs are isomorphic.
|
|
///
|
|
/// `vertices` must contain all vertices of the `original` graph, and `vertex_map` must for any
|
|
/// vertex in the `original` graph return the corresponding vertex in the `other` graph.
|
|
///
|
|
/// `edges` must contain all edges and their endpoints of the `original` graph, and `edge_map` must
|
|
/// for any edge in the `original` graph return the corresponding edge in the `other` graph.
|
|
pub fn assert_isomorphic<G: GraphTopology, H: GraphTopology>(
|
|
original: &G,
|
|
other: &H,
|
|
vertices: &[G::Vertex],
|
|
vertex_map: impl Fn(G::Vertex) -> H::Vertex,
|
|
edges: &[(G::Edge, G::Vertex, G::Vertex)],
|
|
edge_map: impl Fn(G::Edge) -> H::Edge,
|
|
) where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
H::Vertex: Debug,
|
|
H::Edge: Debug,
|
|
{
|
|
assert_eq!(
|
|
original.vertex_count(),
|
|
vertices.len(),
|
|
"precondition: all vertices of the original must be provided"
|
|
);
|
|
assert_eq!(
|
|
original.edge_count(),
|
|
edges.len(),
|
|
"precondition: all edges of the original must be provided"
|
|
);
|
|
assert_eq!(
|
|
other.vertex_count(),
|
|
original.vertex_count(),
|
|
"other graph should have the same vertex count as the original"
|
|
);
|
|
assert_eq!(
|
|
other.edge_count(),
|
|
original.edge_count(),
|
|
"other graph should have the same edge count as the original"
|
|
);
|
|
for &v in vertices {
|
|
assert_eq!(
|
|
other.degree(vertex_map(v)),
|
|
original.degree(v),
|
|
"vertex {v:?} should have the same degree in the other graph as in the original"
|
|
);
|
|
let mut expected: Vec<_> = original.incidences(v).collect();
|
|
for incidence in other.incidences(vertex_map(v)) {
|
|
let pos = expected
|
|
.iter()
|
|
.position(|x| {
|
|
vertex_map(x.vertex) == incidence.vertex && edge_map(x.edge) == incidence.edge
|
|
})
|
|
.unwrap_or_else(|| {
|
|
panic!("unexpected incidence {incidence:?} of vertex {v:?} in the other graph")
|
|
});
|
|
expected.swap_remove(pos);
|
|
}
|
|
assert!(
|
|
expected.is_empty(),
|
|
"expected incidences {expected:?} of vertex {v:?} are missing in the other graph"
|
|
);
|
|
}
|
|
for &(e, u, v) in edges {
|
|
assert_eq!(
|
|
other.incident_vertices(edge_map(e)),
|
|
(vertex_map(u), vertex_map(v)),
|
|
"edge {e:?} should have the same incident vertices in the other graph as in the original"
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Asserts that mutation of the two graphs happens independently.
|
|
///
|
|
/// `vertices` must contain all vertices of the `original` graph, which must not be empty, and
|
|
/// `vertex_map` must for any vertex in the `original` graph return the corresponding vertex in the
|
|
/// `other` graph.
|
|
pub fn assert_mutation_independent<G: GraphTopologyAddition, H: GraphTopologyAddition>(
|
|
original: &mut G,
|
|
other: &mut H,
|
|
vertices: &[G::Vertex],
|
|
vertex_map: impl Fn(G::Vertex) -> H::Vertex,
|
|
) {
|
|
let edge_count = original.edge_count();
|
|
assert_eq!(
|
|
original.vertex_count(),
|
|
vertices.len(),
|
|
"precondition: all vertices of the original must be provided"
|
|
);
|
|
assert!(
|
|
!vertices.is_empty(),
|
|
"precondition: at least one vertex must be provided"
|
|
);
|
|
let u = other.add_vertex();
|
|
other.add_edge(vertex_map(vertices[0]), u);
|
|
assert_eq!(
|
|
other.vertex_count(),
|
|
vertices.len() + 1,
|
|
"precondition: vertex count of other graph"
|
|
);
|
|
assert_eq!(
|
|
other.edge_count(),
|
|
edge_count + 1,
|
|
"precondition: edge count of other graph"
|
|
);
|
|
assert_eq!(
|
|
original.vertex_count(),
|
|
vertices.len(),
|
|
"mutating the other graph should not affect the vertex count of the original"
|
|
);
|
|
assert_eq!(
|
|
original.edge_count(),
|
|
edge_count,
|
|
"mutating the other graph should not affect the edge count of the original"
|
|
);
|
|
|
|
let v = original.add_vertex();
|
|
original.add_edge(vertices[0], v);
|
|
assert_eq!(
|
|
original.vertex_count(),
|
|
vertices.len() + 1,
|
|
"precondition: vertex count of original"
|
|
);
|
|
assert_eq!(
|
|
original.edge_count(),
|
|
edge_count + 1,
|
|
"precondition: edge count of original"
|
|
);
|
|
assert_eq!(
|
|
other.vertex_count(),
|
|
vertices.len() + 1,
|
|
"mutating the original should not affect the vertex count of the other graph"
|
|
);
|
|
assert_eq!(
|
|
other.edge_count(),
|
|
edge_count + 1,
|
|
"mutating the original should not affect the edge count of the other graph"
|
|
);
|
|
}
|