use super::canonical::isomorphic_bliss::isomorphic_bliss;
use super::simplify_and_colorize::simplify_and_colorize;
use super::subiso::subisomorphic_vf2;
use super::vf2::isomorphic_vf2;
use crate::algorithms::properties::multiplicity::has_multiple;
use crate::core::{Graph, IgraphError, IgraphResult};
pub fn isomorphic(graph1: &Graph, graph2: &Graph) -> IgraphResult<bool> {
if graph1.is_directed() != graph2.is_directed() {
return Err(IgraphError::InvalidArgument(
"cannot compare directed and undirected graphs for isomorphism".to_owned(),
));
}
if has_multiple(graph1)? || has_multiple(graph2)? {
let r1 = simplify_and_colorize(graph1)?;
let r2 = simplify_and_colorize(graph2)?;
let res = isomorphic_vf2(
&r1.graph,
&r2.graph,
Some(&r1.vertex_color),
Some(&r2.vertex_color),
Some(&r1.edge_color),
Some(&r2.edge_color),
)?;
return Ok(res.iso);
}
if graph1.vcount() != graph2.vcount() || graph1.ecount() != graph2.ecount() {
return Ok(false);
}
Ok(isomorphic_bliss(graph1, graph2, None, None)?.iso)
}
pub fn subisomorphic(graph1: &Graph, graph2: &Graph) -> IgraphResult<bool> {
Ok(subisomorphic_vf2(graph1, graph2, None, None, None, None)?.iso)
}
#[cfg(test)]
mod tests {
use super::*;
fn ring(n: u32, directed: bool) -> Graph {
let mut g = Graph::new(n, directed).expect("graph");
for i in 0..n {
g.add_edge(i, (i + 1) % n).expect("edge");
}
g
}
fn path(n: u32) -> Graph {
let mut g = Graph::new(n, false).expect("graph");
for i in 0..n.saturating_sub(1) {
g.add_edge(i, i + 1).expect("edge");
}
g
}
fn complete(n: u32) -> Graph {
let mut g = Graph::new(n, false).expect("graph");
for a in 0..n {
for b in (a + 1)..n {
g.add_edge(a, b).expect("edge");
}
}
g
}
#[test]
fn relabelled_cycle_is_isomorphic() {
let a = ring(6, false);
let mut b = Graph::new(6, false).expect("graph");
for (u, v) in [(0, 2), (2, 4), (4, 1), (1, 3), (3, 5), (5, 0)] {
b.add_edge(u, v).expect("edge");
}
assert!(isomorphic(&a, &b).expect("iso"));
}
#[test]
fn cycle_is_not_a_path() {
assert!(!isomorphic(&ring(6, false), &path(6)).expect("iso"));
}
#[test]
fn different_edge_count_short_circuits_to_false() {
let c4 = ring(4, false);
let mut three_edges = Graph::new(4, false).expect("graph");
for (u, v) in [(0, 1), (1, 2), (2, 3)] {
three_edges.add_edge(u, v).expect("edge");
}
assert!(!isomorphic(&c4, &three_edges).expect("iso"));
}
#[test]
fn directedness_mismatch_is_an_error() {
assert!(isomorphic(&ring(3, false), &ring(3, true)).is_err());
}
#[test]
fn directed_cycles_respect_orientation() {
assert!(isomorphic(&ring(5, true), &ring(5, true)).expect("iso"));
}
#[test]
fn self_loops_are_supported() {
let mut a = Graph::new(2, false).expect("graph");
a.add_edge(0, 0).expect("loop");
a.add_edge(0, 1).expect("edge");
let mut b = Graph::new(2, false).expect("graph");
b.add_edge(1, 1).expect("loop");
b.add_edge(0, 1).expect("edge");
assert!(isomorphic(&a, &b).expect("iso"));
}
#[test]
fn multigraph_path_uses_colorized_vf2() {
let mut a = Graph::new(3, false).expect("graph");
a.add_edge(0, 1).expect("edge");
a.add_edge(0, 1).expect("edge");
a.add_edge(1, 2).expect("edge");
let mut b = Graph::new(3, false).expect("graph");
b.add_edge(2, 1).expect("edge");
b.add_edge(2, 1).expect("edge");
b.add_edge(1, 0).expect("edge");
assert!(isomorphic(&a, &b).expect("iso"));
}
#[test]
fn multigraph_distinguishes_multiplicity() {
let mut a = Graph::new(3, false).expect("graph");
a.add_edge(0, 1).expect("edge");
a.add_edge(0, 1).expect("edge");
a.add_edge(1, 2).expect("edge");
let mut b = Graph::new(3, false).expect("graph");
b.add_edge(0, 1).expect("edge");
b.add_edge(1, 2).expect("edge");
b.add_edge(2, 0).expect("edge");
assert!(!isomorphic(&a, &b).expect("iso"));
}
#[test]
fn edge_embeds_into_triangle() {
let mut edge = Graph::new(2, false).expect("graph");
edge.add_edge(0, 1).expect("edge");
assert!(subisomorphic(&complete(3), &edge).expect("subiso"));
}
#[test]
fn triangle_does_not_embed_into_path() {
assert!(!subisomorphic(&path(3), &ring(3, false)).expect("subiso"));
}
#[test]
fn whole_graph_embeds_into_itself() {
let g = complete(4);
assert!(subisomorphic(&g, &g).expect("subiso"));
}
}