//! Generators for common graphs. use crate::models::AppendGraph; use crate::traits::{GraphTopology, GraphTopologyAddition, GraphTopologyConversion}; /// Builds a graph of type `G` by running a generator on a temporary graph. /// /// `generate_as` fills a temporary [`AppendGraph`] and converts it to `G`. It returns the handles /// of the elements it created as handles of the resulting graph. This makes it possible to obtain /// graph types directly that do not implement [`GraphTopologyAddition`], such as [`FrozenGraph`]. /// /// # Examples /// /// ``` /// use grapherity::prelude::*; /// use grapherity::generators; /// use grapherity::models::FrozenGraph; /// /// // Generates a parameterless Petersen graph as a FrozenGraph model. /// let (graph, vertices, edges) = generators::generate_as::(generators::petersen); /// assert_eq!(graph.vertex_count(), 10); /// assert!(graph.vertices().all(|v| graph.degree(v) == 3)); /// /// // Generates a parameterized complete graph as a FrozenGraph model. /// let (graph, vertices, edges) = generators::generate_as::(|g| generators::complete(g, 5)); /// assert_eq!(graph.vertex_count(), 5); /// assert!(graph.vertices().all(|v| graph.degree(v) == 4)); /// ``` /// /// [`FrozenGraph`]: crate::models::FrozenGraph pub fn generate_as( generator: impl FnOnce( &mut AppendGraph, ) -> ( Vec<::Vertex>, Vec<::Edge>, ), ) -> (G, Vec, Vec) where G: GraphTopologyConversion, { let mut builder = AppendGraph::new(); let (vertices, edges) = generator(&mut builder); let (graph, iso) = G::convert(&builder); ( graph, vertices.into_iter().map(|v| iso.vertex(v)).collect(), edges.into_iter().map(|e| iso.edge(e)).collect(), ) } /// Generates a path graph. /// /// Generates a [path graph] *P(n)* with `n` vertices and `n - 1` edges in `graph` as a new /// connected component and returns the new vertices *v0, v1, ..., v(n - 1)* and edges /// *e0, e1, ..., e(n - 2)* in order, i.e. *e0 = (v0, v1)*, *e1 = (v1, v2)*, etc. and *v0* and /// *v(n-1)* being the two terminal vertices /// /// For `n == 0`, nothing is added. For `n == 1`, only a single vertex and no edges are added. /// /// [Path graph]: https://en.wikipedia.org/wiki/Path_graph pub fn path(graph: &mut G, n: usize) -> (Vec, Vec) { let edge_capacity = n.saturating_sub(1); graph.reserve_vertices(n); graph.reserve_edges(edge_capacity); let vertices: Vec<_> = (0..n).map(|_| graph.add_vertex()).collect(); let edges = (0..edge_capacity) .map(|i| graph.add_edge(vertices[i], vertices[i + 1])) .collect(); (vertices, edges) } /// Generates a cycle graph. /// /// Generates a [cycle graph] *C(n)* with `n` vertices and `n` edges in `graph` as a new connected /// component and returns the new vertices *v0, v1, ..., v(n - 1)* and edges *e0, e1, ..., e(n - 1)* /// in order, i.e. *e0 = (v0, v1), e1 = (v1, v2), ..., e(n - 1) = (v(n - 1), v0)*. /// /// For `n == 0`, nothing is added. For `n == 1`, a single vertex with a loop is added. For /// `n == 2`, two vertices and two edges between them (multi-edge) are added. /// /// [Cycle graph]: https://en.wikipedia.org/wiki/Cycle_graph pub fn cycle(graph: &mut G, n: usize) -> (Vec, Vec) { graph.reserve_vertices(n); graph.reserve_edges(n); let vertices: Vec<_> = (0..n).map(|_| graph.add_vertex()).collect(); let edges = (0..n) .map(|i| graph.add_edge(vertices[i], vertices[if i < n - 1 { i + 1 } else { 0 }])) .collect(); (vertices, edges) } /// Generates a complete graph. /// /// Generates a [complete graph] *K(n)* with `n` vertices and `n * (n - 1) / 2` edges in `graph` as /// a new connected component and returns the new vertices and edges. /// /// For `n == 0`, nothing is added. For `n == 1`, only a single vertex is added. /// /// [Complete graph]: https://en.wikipedia.org/wiki/Complete_graph pub fn complete( graph: &mut G, n: usize, ) -> (Vec, Vec) { let edge_capacity = n * n.saturating_sub(1) / 2; graph.reserve_vertices(n); graph.reserve_edges(edge_capacity); let vertices: Vec<_> = (0..n).map(|_| graph.add_vertex()).collect(); let mut edges = Vec::with_capacity(edge_capacity); for (i, &u) in vertices.iter().enumerate() { for &v in &vertices[i + 1..] { edges.push(graph.add_edge(u, v)); } } (vertices, edges) } /// Generates a Petersen graph. /// /// Generates a [Petersen graph] in `graph` as a new connected component and returns the new /// vertices and edges. This graph is also the Kneser graph *KG(5, 2)*. /// /// [Petersen graph]: https://en.wikipedia.org/wiki/Petersen_graph pub fn petersen(graph: &mut G) -> (Vec, Vec) { const N: usize = 5; const K: usize = 2; graph.reserve_vertices(10); graph.reserve_edges(15); let vertices: Vec<_> = (0..10).map(|_| graph.add_vertex()).collect(); let edges = (0..15) .map(|j| { let i = j / 3; match j % 3 { 0 => { let outer = if i < N - 1 { i + 1 } else { 0 }; graph.add_edge(vertices[i], vertices[outer]) } 1 => { let inner = if i < N - K { i + N + K } else { i + K }; graph.add_edge(vertices[i + N], vertices[inner]) } _ => graph.add_edge(vertices[i], vertices[i + N]), } }) .collect(); (vertices, edges) } #[cfg(test)] mod tests { use std::fmt::Debug; use super::*; use crate::testing::fixtures::MakeTestGraph; #[derive(Debug, Clone, Copy)] enum Spec { Path(usize), Cycle(usize), Complete(usize), Petersen, } impl Spec { fn generate( &self, graph: &mut G, ) -> (Vec, Vec) { match *self { Spec::Path(n) => path(graph, n), Spec::Cycle(n) => cycle(graph, n), Spec::Complete(n) => complete(graph, n), Spec::Petersen => petersen(graph), } } fn assert(&self, built: &Built) where G::Vertex: Debug, G::Edge: Debug, { match *self { Spec::Path(n) => assert_path(built, n), Spec::Cycle(n) => assert_cycle(built, n), Spec::Complete(n) => assert_complete(built, n), Spec::Petersen => assert_petersen(built), } } } struct Built { graph: G, vertices: Vec, edges: Vec, initial_vertex_count: usize, initial_edge_count: usize, } /// Generates the test for the `append_graph` harness for the given `Spec` expression. macro_rules! min_harness_tests { ($spec:expr) => { #[test] fn append_graph() { run($spec, add_to_empty::<$crate::models::AppendGraph>); } }; } /// Generates one test per harness for the given `Spec` expression. macro_rules! all_harness_tests { ($spec:expr) => { min_harness_tests!($spec); #[test] fn append_graph_non_empty() { run($spec, add_to_standard::<$crate::models::AppendGraph>); } #[test] fn graph() { run($spec, add_to_empty::<$crate::models::Graph>); } #[test] fn graph_non_empty() { run($spec, add_to_standard::<$crate::models::Graph>); } #[test] fn frozen_graph() { run($spec, conv_generate_as::<$crate::models::FrozenGraph>); } }; } /// Generates the module tree `::::` for each generator case. /// /// The `all_harnesses` cases run on every harness, the `min_harnesses` cases only on a reduced /// set of harnesses. macro_rules! generator_tests { ($($group:ident { all { $($all_case:ident => $all_generator:expr),+ $(,)? } min { $($min_case:ident => $min_generator:expr),* $(,)? } })+) => { $( mod $group { use super::*; $( mod $all_case { use super::*; all_harness_tests!($all_generator); } )+ $( mod $min_case { use super::*; min_harness_tests!($min_generator); } )* } )+ }; } generator_tests! { path { all { small => Spec::Path(4), } min { empty => Spec::Path(0), single_vertex => Spec::Path(1), single_edge => Spec::Path(2), large => Spec::Path(21), } } cycle { all { small_even => Spec::Cycle(4), } min { empty => Spec::Cycle(0), loop_edge => Spec::Cycle(1), multi_edge => Spec::Cycle(2), large_odd => Spec::Cycle(21), } } complete { all { small_even => Spec::Complete(4), } min { empty => Spec::Complete(0), single_vertex => Spec::Complete(1), single_edge => Spec::Complete(2), large_odd => Spec::Complete(13), } } } mod petersen { use super::*; all_harness_tests!(Spec::Petersen); } /// Builds a graph with `harness` and asserts that it matches what `spec` should produce. fn run(spec: Spec, harness: impl FnOnce(&Spec) -> Built) where G::Vertex: Debug, G::Edge: Debug, { let built = harness(&spec); spec.assert(&built); } /// Runs `spec` on an empty graph of type `G`. fn add_to_empty(spec: &Spec) -> Built { let mut graph = G::default(); let (vertices, edges) = spec.generate(&mut graph); Built { graph, vertices, edges, initial_vertex_count: 0, initial_edge_count: 0, } } /// Runs `spec` on a non-empty test graph of type `G`. fn add_to_standard(spec: &Spec) -> Built { let (mut graph, initial_vertices, initial_edges, _) = G::standard(); let (vertices, edges) = spec.generate(&mut graph); Built { graph, vertices, edges, initial_vertex_count: initial_vertices.len(), initial_edge_count: initial_edges.len(), } } /// Runs `spec` within `generate_as` for type `G`. fn conv_generate_as(spec: &Spec) -> Built { let (graph, vertices, edges) = generate_as::(|graph| spec.generate(graph)); Built { graph, vertices, edges, initial_vertex_count: 0, initial_edge_count: 0, } } fn assert_path(built: &Built, n: usize) where G::Vertex: Debug, G::Edge: Debug, { assert_element_counts(built, n, n.saturating_sub(1)); assert_handles(built.graph.vertices(), &built.vertices, "vertex"); assert_handles(built.graph.edges(), &built.edges, "edge"); assert_no_loops(built); assert_no_multi_edges(built); match n { 0 => {} 1 => assert_eq!( built.graph.degree(built.vertices[0]), 0, "path n == 1 should be an isolated vertex" ), _ => { for (i, &v) in built.vertices.iter().enumerate() { assert_eq!( built.graph.degree(v), if i == 0 || i == n - 1 { 1 } else { 2 }, "unexpected degree of vertex {v:?} in path n == {n}" ); } } } assert_path_order(built); } fn assert_cycle(built: &Built, n: usize) where G::Vertex: Debug, G::Edge: Debug, { assert_element_counts(built, n, n); assert_handles(built.graph.vertices(), &built.vertices, "vertex"); assert_handles(built.graph.edges(), &built.edges, "edge"); if n != 1 { assert_no_loops(built); } if n != 2 { assert_no_multi_edges(built); } assert!( built.vertices.iter().all(|&v| built.graph.degree(v) == 2), "all new vertices should have degree 2" ); assert_path_order(built); // Asserts the closing edge e(n - 1) == (v(n - 1), v0). if n > 0 { // For n == 1, u == v and this validates the loop. For n == 2, the above asserted one of // the two multi-edges, and this asserts the other. let e = built.edges[n - 1]; let u = built.vertices[0]; let v = built.vertices[n - 1]; let incident_vertices = built.graph.incident_vertices(e); assert!( incident_vertices == (u, v) || incident_vertices == (v, u), "edge {e:?} should have incident vertices {u:?} and {v:?}", ); } } fn assert_complete(built: &Built, n: usize) where G::Vertex: Debug, G::Edge: Debug, { assert_element_counts(built, n, n * n.saturating_sub(1) / 2); assert_handles(built.graph.vertices(), &built.vertices, "vertex"); assert_handles(built.graph.edges(), &built.edges, "edge"); // The predicate in `all` is only executed if `vertices` has any elements, i.e. for n > 0. assert!( built .vertices .iter() .all(|&v| built.graph.degree(v) == n - 1), "all new vertices should have degree {}", n - 1 ); // Asserts that all pairs of vertices have exactly one edge between them. This also includes // checks for loops and multi-edges. If there are no duplicates, then this implies that all // expected edges were also matched due to the validated counts from above. let mut seen = vec![false; n * n]; let position = |v, e| { built .vertices .iter() .position(|&x| x == v) .unwrap_or_else(|| panic!("edge {e:?} has an invalid incident vertex {v:?}")) }; for &e in &built.edges { let (u, v) = built.graph.incident_vertices(e); assert_ne!(u, v, "unexpected loop edge {e:?} at vertex {u:?}"); let (i, j) = (position(u, e), position(v, e)); let (i, j) = (i.min(j), i.max(j)); assert!( !seen[i * n + j], "unexpected multi-edge {e:?} between vertices {u:?} and {v:?}" ); seen[i * n + j] = true; } } fn assert_petersen(built: &Built) where G::Vertex: Debug, G::Edge: Debug, { assert_element_counts(built, 10, 15); assert_handles(built.graph.vertices(), &built.vertices, "vertex"); assert_handles(built.graph.edges(), &built.edges, "edge"); assert_no_loops(built); assert_no_multi_edges(built); assert!( built.vertices.iter().all(|&v| built.graph.degree(v) == 3), "all new vertices should have degree 3" ); // Checks that each pair of vertices (u, v) has no common neighbor if they are adjacent, and // exactly one common neighbor otherwise. for (i, &v) in built.vertices.iter().enumerate() { for &u in &built.vertices[i + 1..] { if built.graph.are_adjacent(v, u) { assert!( built.graph.common_neighbors(u, v).next().is_none(), "adjacent vertices {u:?} and {v:?} should not have a common neighbor" ); } else { assert_eq!( built.graph.common_neighbors(u, v).count(), 1, "non-adjacent vertices {u:?} and {v:?} should have exactly one common neighbor" ); } } } } fn assert_element_counts( built: &Built, expected_vertex_count: usize, expected_edge_count: usize, ) { assert_eq!( built.graph.vertex_count(), built.initial_vertex_count + expected_vertex_count, "unexpected number of added vertices" ); assert_eq!( built.vertices.len(), expected_vertex_count, "unexpected number of returned vertices" ); assert_eq!( built.graph.edge_count(), built.initial_edge_count + expected_edge_count, "unexpected number of added edges" ); assert_eq!( built.edges.len(), expected_edge_count, "unexpected number of returned edges" ); } /// Asserts that every handle in `returned` is unique and in `all`. fn assert_handles( all: impl Iterator, returned: &[T], kind: &str, ) { let mut remaining: Vec = all.collect(); for e in returned { let Some(position) = remaining.iter().position(|x| x == e) else { panic!("returned {kind} {e:?} is not in the graph or appears more than once"); }; remaining.swap_remove(position); } } fn assert_no_loops(built: &Built) where G::Vertex: Debug, G::Edge: Debug, { for &e in &built.edges { let (u, v) = built.graph.incident_vertices(e); assert_ne!(u, v, "unexpected loop edge {e:?} at vertex {u:?}"); } } fn assert_no_multi_edges(built: &Built) where G::Vertex: Debug, { for &v in &built.vertices { let mut neighbors = Vec::new(); let mut loop_count = 0; for u in built.graph.adjacent_vertices(v) { if u == v { loop_count += 1; assert!(loop_count < 3, "unexpected multi-edge loop at vertex {v:?}"); } else { assert!( !neighbors.contains(&u), "unexpected multi-edge between vertices {v:?} and {u:?}" ); neighbors.push(u); } } } } /// Asserts that each returned edge *ei* connects the returned vertices *vi* and *v(i + 1)*. fn assert_path_order(built: &Built) where G::Vertex: Debug, G::Edge: Debug, { for (w, &e) in built.vertices.windows(2).zip(&built.edges) { let incident_vertices = built.graph.incident_vertices(e); assert!( incident_vertices == (w[0], w[1]) || incident_vertices == (w[1], w[0]), "edge {e:?} should have incident vertices {:?} and {:?}", w[0], w[1] ); } } }