use petgraph::data::{Element, FromElements}; use petgraph::graph::{DiGraph, UnGraph}; use petgraph::{Directed, EdgeType, Graph, Undirected}; /// Tests graph isomorphism enumeration on directed graphs. #[test] fn isomorphisms_directed() { let query = DiGraph::<(), ()>::from_edges([(0, 2), (1, 2), (2, 3)]); let data = DiGraph::<(), ()>::from_edges([(0, 2), (1, 2), (2, 3)]); let isomorphisms = vf2::isomorphisms(&query, &data).vec(); assert_eq!(isomorphisms, vec![vec![0, 1, 2, 3], vec![1, 0, 2, 3]]); } /// Tests graph isomorphism enumeration on undirected graphs. #[test] fn isomorphisms_undirected() { let query = UnGraph::<(), ()>::from_edges([(0, 2), (1, 2), (2, 3)]); let data = UnGraph::<(), ()>::from_edges([(0, 2), (1, 2), (2, 3)]); let isomorphisms = vf2::isomorphisms(&query, &data).vec(); assert_eq!( isomorphisms, vec![ vec![0, 1, 2, 3], vec![0, 3, 2, 1], vec![1, 0, 2, 3], vec![1, 3, 2, 0], vec![3, 0, 2, 1], vec![3, 1, 2, 0], ] ); } /// Tests subgraph isomorphism enumeration on directed graphs. #[test] fn subgraph_isomorphisms_directed() { let (query, data) = small_graphs::(); let isomorphisms = vf2::subgraph_isomorphisms(&query, &data).vec(); assert_eq!( isomorphisms, vec![ vec![0, 1, 3, 4, 5], vec![0, 2, 3, 4, 5], vec![1, 0, 3, 4, 5], vec![1, 2, 3, 4, 5], vec![2, 0, 3, 4, 5], vec![2, 1, 3, 4, 5], ] ); } /// Tests subgraph isomorphism enumeration on undirected graphs. #[test] fn subgraph_isomorphisms_undirected() { let (query, data) = small_graphs::(); let isomorphisms = vf2::subgraph_isomorphisms(&query, &data).vec(); assert_eq!( isomorphisms, vec![ vec![0, 1, 3, 4, 5], vec![0, 1, 3, 6, 7], vec![0, 2, 3, 4, 5], vec![0, 2, 3, 6, 7], vec![0, 4, 3, 1, 2], vec![0, 4, 3, 2, 1], vec![0, 4, 3, 6, 7], vec![0, 6, 3, 1, 2], vec![0, 6, 3, 2, 1], vec![0, 6, 3, 4, 5], vec![1, 0, 3, 4, 5], vec![1, 0, 3, 6, 7], vec![1, 2, 3, 4, 5], vec![1, 2, 3, 6, 7], vec![1, 4, 3, 6, 7], vec![1, 6, 3, 4, 5], vec![2, 0, 3, 4, 5], vec![2, 0, 3, 6, 7], vec![2, 1, 3, 4, 5], vec![2, 1, 3, 6, 7], vec![2, 4, 3, 6, 7], vec![2, 6, 3, 4, 5], vec![4, 0, 3, 1, 2], vec![4, 0, 3, 2, 1], vec![4, 0, 3, 6, 7], vec![4, 1, 3, 6, 7], vec![4, 2, 3, 6, 7], vec![4, 6, 3, 1, 2], vec![4, 6, 3, 2, 1], vec![6, 0, 3, 1, 2], vec![6, 0, 3, 2, 1], vec![6, 0, 3, 4, 5], vec![6, 1, 3, 4, 5], vec![6, 2, 3, 4, 5], vec![6, 4, 3, 1, 2], vec![6, 4, 3, 2, 1], ] ); } /// Tests induced subgraph isomorphism enumeration on directed graphs. #[test] fn induced_subgraph_isomorphisms_directed() { let (query, data) = small_graphs::(); let isomorphisms = vf2::induced_subgraph_isomorphisms(&query, &data).vec(); assert_eq!( isomorphisms, vec![ vec![0, 1, 3, 4, 5], vec![0, 2, 3, 4, 5], vec![1, 0, 3, 4, 5], vec![2, 0, 3, 4, 5], ] ); } /// Tests induced subgraph isomorphism enumeration on undirected graphs. #[test] fn induced_subgraph_isomorphisms_undirected() { let (query, data) = small_graphs::(); let isomorphisms = vf2::induced_subgraph_isomorphisms(&query, &data).vec(); assert_eq!( isomorphisms, vec![ vec![0, 1, 3, 4, 5], vec![0, 1, 3, 6, 7], vec![0, 2, 3, 4, 5], vec![0, 2, 3, 6, 7], vec![0, 4, 3, 6, 7], vec![0, 6, 3, 4, 5], vec![1, 0, 3, 4, 5], vec![1, 0, 3, 6, 7], vec![1, 4, 3, 6, 7], vec![1, 6, 3, 4, 5], vec![2, 0, 3, 4, 5], vec![2, 0, 3, 6, 7], vec![2, 4, 3, 6, 7], vec![2, 6, 3, 4, 5], vec![4, 0, 3, 6, 7], vec![4, 1, 3, 6, 7], vec![4, 2, 3, 6, 7], vec![6, 0, 3, 4, 5], vec![6, 1, 3, 4, 5], vec![6, 2, 3, 4, 5], ] ); } /// Tests that node and edge labels are not compared by default. #[test] fn no_eq_by_default() { let (query, data) = small_labeled_graphs::(); let isomorphisms = vf2::induced_subgraph_isomorphisms(&query, &data).vec(); assert_eq!( isomorphisms, vec![ vec![0, 1, 3, 4, 5], vec![0, 2, 3, 4, 5], vec![1, 0, 3, 4, 5], vec![2, 0, 3, 4, 5], ] ); } /// Tests default equality functions on directed graphs. #[test] fn default_eq_directed() { let (query, data) = small_labeled_graphs::(); let isomorphisms = vf2::induced_subgraph_isomorphisms(&query, &data) .default_eq() .vec(); assert_eq!(isomorphisms, vec![vec![0, 2, 3, 4, 5]]); } /// Tests default equality functions on undirected graphs. #[test] fn default_eq_undirected() { let (query, data) = small_labeled_graphs::(); let isomorphisms = vf2::induced_subgraph_isomorphisms(&query, &data) .default_eq() .vec(); assert_eq!( isomorphisms, vec![ vec![0, 2, 3, 4, 5], vec![0, 2, 3, 6, 7], vec![4, 2, 3, 6, 7], vec![6, 2, 3, 4, 5], ] ); } /// Tests custom equality functions. #[test] fn custom_eq() { let (query, data) = small_labeled_graphs::(); let isomorphisms = vf2::induced_subgraph_isomorphisms(&query, &data) .node_eq(|left, right| left == right) .edge_eq(|left, right| left == right) .vec(); assert_eq!(isomorphisms, vec![vec![0, 2, 3, 4, 5]]); } /// Tests enumeration on disconnected graphs. #[test] fn disconnected() { let query = DiGraph::<(), ()>::from_edges([(0, 1), (2, 3)]); let data = DiGraph::<(), ()>::from_edges([(0, 1), (1, 2), (3, 4)]); let isomorphisms = vf2::subgraph_isomorphisms(&query, &data).vec(); assert_eq!( isomorphisms, vec![ vec![0, 1, 3, 4], vec![1, 2, 3, 4], vec![3, 4, 0, 1], vec![3, 4, 1, 2], ] ); } /// Tests that an empty query results in a panic. #[test] #[should_panic] fn empty_query() { let query = DiGraph::<(), ()>::new(); let data = DiGraph::<(), ()>::from_edges([(0, 1), (1, 2)]); // Should panic since query is empty. vf2::induced_subgraph_isomorphisms(&query, &data).vec(); } /// Tests that query and data graphs must be the same size /// when finding graph isomorphisms. #[test] #[should_panic] fn isomorphisms_same_size() { let query = DiGraph::<(), ()>::from_edges([(0, 1)]); let data = DiGraph::<(), ()>::from_edges([(0, 1), (1, 2)]); // Should panic since query and data are not the same size. vf2::isomorphisms(&query, &data).vec(); } /// Tests that [`Debug`] is implemented for [`Vf2Builder`]. /// /// [`Vf2Builder`]: vf2::Vf2Builder #[test] fn builder_debug() { let (query, data) = small_graphs::(); let builder = vf2::subgraph_isomorphisms(&query, &data); // This should not panic due to missing debug implementation. let debug = format!("{builder:#?}"); assert!(!debug.is_empty()); } /// Tests that [`Debug`] is implemented for [`IsomorphismIter`]. /// /// [`IsomorphismIter`]: vf2::IsomorphismIter #[test] fn iter_debug() { let (query, data) = small_graphs::(); let iter = vf2::subgraph_isomorphisms(&query, &data).iter(); // This should not panic due to missing debug implementation. let debug = format!("{iter:#?}"); assert!(!debug.is_empty()); } /// Tests finding only the first isomorphism. #[test] fn first() { let (query, data) = small_graphs::(); let first = vf2::subgraph_isomorphisms(&query, &data).first(); assert_eq!(first, Some(vec![0, 1, 3, 4, 5])); } /// Tests collecting isomorphisms into a vector. #[test] fn vec() { let (query, data) = small_graphs::(); let vec = vf2::subgraph_isomorphisms(&query, &data).vec(); assert!(!vec.is_empty()); } /// Tests getting an iterator of isomorphisms. #[test] fn iter() { let (query, data) = small_graphs::(); let mut iter = vf2::subgraph_isomorphisms(&query, &data).iter(); assert!(iter.next().is_some()); } /// Tests getting a reference to the next isomorphism. #[test] fn iter_next_ref() { let (query, data) = small_graphs::(); let mut iter = vf2::subgraph_isomorphisms(&query, &data).iter(); let next_ref = iter.next_ref(); assert_eq!(next_ref, Some(&vec![0, 1, 3, 4, 5])); } /// Tests converting the iterator into the next isomorphism. #[test] fn iter_into_next() { let (query, data) = small_graphs::(); let iter = vf2::subgraph_isomorphisms(&query, &data).iter(); let next = iter.into_next(); assert_eq!(next, Some(vec![0, 1, 3, 4, 5])); } /// Returns small query and data graphs used across tests. fn small_graphs() -> (Graph<(), (), D>, Graph<(), (), D>) { let query = Graph::<(), (), D>::from_edges([(0, 2), (1, 2), (2, 3), (3, 4)]); let data = Graph::<(), (), D>::from_edges([ (0, 3), (1, 3), (2, 3), (1, 2), (3, 4), (4, 5), (3, 6), (7, 6), ]); (query, data) } /// Returns small query and data graphs, /// with node and edge labels, used across tests. #[rustfmt::skip] fn small_labeled_graphs() -> (Graph, Graph) { let query = Graph::::from_elements([ Element::Node { weight: Color::Black, }, Element::Node { weight: Color::White, }, Element::Node { weight: Color::White, }, Element::Node { weight: Color::Black, }, Element::Node { weight: Color::White, }, Element::Edge { source: 0, target: 2, weight: Color::White }, Element::Edge { source: 1, target: 2, weight: Color::Black }, Element::Edge { source: 2, target: 3, weight: Color::White }, Element::Edge { source: 3, target: 4, weight: Color::Black }, ]); let data = Graph::::from_elements([ Element::Node { weight: Color::Black }, Element::Node { weight: Color::White }, Element::Node { weight: Color::White }, Element::Node { weight: Color::White }, Element::Node { weight: Color::Black }, Element::Node { weight: Color::White }, Element::Node { weight: Color::Black }, Element::Node { weight: Color::White }, Element::Edge { source: 0, target: 3, weight: Color::White }, Element::Edge { source: 1, target: 3, weight: Color::White }, Element::Edge { source: 2, target: 3, weight: Color::Black }, Element::Edge { source: 1, target: 2, weight: Color::White }, Element::Edge { source: 3, target: 4, weight: Color::White }, Element::Edge { source: 4, target: 5, weight: Color::Black }, Element::Edge { source: 3, target: 6, weight: Color::White }, Element::Edge { source: 7, target: 6, weight: Color::Black }, ]); (query, data) } /// A color enum used as node and edge labels. #[derive(Copy, Clone, Debug, Eq, PartialEq)] enum Color { White, Black, }