An implementation of the Traces algorithm in Rust to enable fast graph isomorphish check and the Hash trait on Graph objects. For a complete description of the algorithm, see this publication.
Given a graph (from the awesome petgraph library), this algorithm computes the key associated to the graph. This key is isomorphic-invariant, hence it can be hashed & used for isomorphism test.
use petgraph::{Graph, graph::Undirected};
use graphkey::GraphKey;
fn main() {
let edges : Vec<(u32, u32)> = vec![
(0, 3), (0, 5), (0, 8), (1, 4), (1, 6), (1, 8),
(2, 5), (2, 7), (3, 6), (3, 9),
(4, 7), (4, 9), (5, 8), (7, 9)
];
let g = Graph::<usize, (), Undirected>::from_edges(edges);
let key = GraphKey::new(&g);
}
The resulting key is stable by permutation, thus two keys can be compared in order to perform isomorphism check.
fn main() {
let edges : Vec<(u32, u32)> = vec![
(0, 3), (0, 5), (0, 8), (1, 4), (1, 6), (1, 8),
(2, 5), (2, 7), (3, 6), (3, 9),
(4, 7), (4, 9), (5, 8), (7, 9)
];
let g1 = Graph::<usize, (), Undirected>::from_edges(edges);
let g2 = generate_permutated_graph(&g1);
let key1 = GraphKey::new(&g1);
let key2 = GraphKey::new(&g2);
assert!(key1 == key2);
}
As a wrapper around a Vec, GraphKey implements the Hash trait.
use std::collections::HashSet;
fn main() {
let edges1 : Vec<(u32, u32)> = vec![
(0, 3), (0, 5), (0, 8), (1, 4), (1, 6), (1, 8),
(2, 5), (2, 7), (3, 6), (3, 9),
(4, 7), (4, 9), (5, 8), (7, 9)
];
let edges2 : Vec<(u32, u32)> = vec![
(0, 3), (0, 5), (0, 9), (1, 4), (1, 6), (1, 8),
(2, 5), (2, 7), (3, 6), (3, 9),
(4, 7), (4, 9), (5, 8), (7, 9)
];
let g1 = Graph::<usize, (), Undirected>::from_edges(edges1);
let g2 = generate_permutated_graph(&g1);
let g3 = Graph::<usize, (), Undirected>::from_edges(edges2);
let g4 = generate_permutated_graph(&g3);
let mut s = HashSet::new();
s.insert(GraphKey::new(&g1));
s.insert(GraphKey::new(&g2));
s.insert(GraphKey::new(&g3));
s.insert(GraphKey::new(&g4));
assert_eq!(s.len(), 2);
}
For large graphs (n > 1_000), the key comparison allows to perform an isomorphism check faster than with the algorithm provided currently in petgraph. In particular, it can handle large graphs ( > 10_000 nodes) which is not possible with petgraph::algo::is_isomorphic.
fn main() {
use petgraph::algo::is_isomorphic;
let g1 = generate_random_graph(5000, 0.1);
let g2 = generate_permutated_graph(&g1);
let start = Instant::now();
let key1 = GraphKey::new(&g1);
let key2 = GraphKey::new(&g2);
let are_isomorphic_graphkey = key1 == key2;
let duration_graphkey = start.elapsed();
let start = Instant::now();
let are_isomorphic_petgraph = is_isomorphic(&g1, &g2);
let duration_petgraph = start.elapsed();
println!("Isomorphism check with petgraph : {} ({:?})", are_isomorphic_petgraph, duration_petgraph);
println!("Isomorphism check with graphkey : {} ({:?})", are_isomorphic_graphkey, duration_graphkey);
}
Isomorphism check with petgraph : true (50.6870042s)
Isomorphism check with graphkey : true (1.5694743s)
Note that the complexity of the isomorphism check is highly dependant of the graph structure.
fn generate_permutated_graph(g : &Graph::<usize, (), Undirected>) -> Graph::<usize, (), Undirected> {
use rand::thread_rng;
use rand::seq::SliceRandom;
let n = g.node_count();
let mut perm : Vec<usize> = (0..n).collect();
let mut rng = thread_rng();
perm.shuffle(&mut rng);
let edges : Vec<(usize, usize)> = g.edge_indices()
.map(|e| {
let (u, v) = g.edge_endpoints(e).unwrap();
(perm[u.index()] , perm[v.index()])
})
.collect();
let mut g = UnGraph::<usize, ()>::new_undirected();
g.reserve_nodes(n);
(0..n).for_each(|_| { g.add_node(1); });
g.reserve_edges(edges.len());
edges.into_iter().for_each(|(u, v)| { g.add_edge(NodeIndex::new(u), NodeIndex::new(v), ()); });
g
}
fn generate_random_graph(n : usize, p : f64) -> Graph::<usize, (), Undirected> {
use rand::Rng;
let mut rng = rand::thread_rng();
let mut g = UnGraph::<usize, ()>::new_undirected();
g.reserve_nodes(n);
(0..n).for_each(|i| { g.add_node(i); });
for i in 0..n {
for j in (i+1)..n {
if rng.gen_range((0.)..(1.)) < p {
g.add_edge(NodeIndex::new(i), NodeIndex::new(j), ());
}
}
}
g
}