nauty_pet/
lib.rs

1//! Canonical graph labelling.
2//!
3//! Leverages [nauty and Traces](http://pallini.di.uniroma1.it/) to
4//! find [canonical
5//! labellings](https://en.wikipedia.org/wiki/Graph_canonization) and
6//! [graph
7//! automorphisms](https://en.wikipedia.org/wiki/Graph_automorphism)
8//! for [petgraph](https://github.com/petgraph/petgraph) graphs.
9//!
10//! # Example
11//!
12//! ```rust
13//! use petgraph::graph::UnGraph;
14//! use nauty_pet::prelude::*;
15//!
16//! // Two different vertex labellings for the tree graph with two edges
17//! let g1 = UnGraph::<(), ()>::from_edges([(0, 1), (1, 2)]);
18//! let g2 = UnGraph::<(), ()>::from_edges([(0, 1), (0, 2)]);
19//!
20//! // The canonical forms are identical
21//! let c1 = g1.clone().into_canon();
22//! let c2 = g2.clone().into_canon();
23//! assert!(c1.is_identical(&c2));
24//!
25//! // Alternatively, we can use a dedicated `struct` for canonically
26//! // labelled graphs
27//! let c1 = CanonGraph::from(g1.clone());
28//! let c2 = CanonGraph::from(g2);
29//! assert_eq!(c1, c2);
30//!
31//! // `g1` is invariant under the permutation 0 -> 2, 1 -> 1, 2 -> 0.
32//! // we encode it as the vector `[2, 1, 0]`
33//! let automorphisms = g1.try_into_autom_group().unwrap();
34//! assert!(automorphisms.contains(&vec![2, 1, 0]));
35//!
36//! ```
37//!
38//! # Features
39//!
40//! * `serde-1`: Enables serialisation of
41//!              [CanonGraph](graph::CanonGraph) objects using
42//!              [serde](https://crates.io/crates/serde).
43//!
44//! * `stable`: Ensures deterministic behaviour when node or edge
45//!             weights are distinguishable, but compare equal.
46//!
47//! To enable features `feature1`, `feature2` add the following to
48//! your Cargo.toml:
49//! ```toml
50//! [dependencies]
51//! nauty-pet = { version = "0.8", features = ["feature1", "feature2"] }
52//! ```
53pub mod autom;
54pub mod canon;
55mod cmp;
56pub mod error;
57pub mod graph;
58mod nauty_graph;
59pub mod prelude;
60
61pub use canon::IntoCanon;
62pub use canon::{IntoCanonNautySparse, TryIntoCanonTraces};
63pub use cmp::IsIdentical;
64
65#[cfg(test)]
66mod tests {
67    use super::prelude::*;
68    use petgraph::{
69        EdgeType,
70        graph::{Graph, IndexType, UnGraph},
71        prelude::EdgeIndex,
72    };
73
74    fn add_edge<N, E, Ty: EdgeType, Ix: IndexType>(
75        g: &mut Graph<N, E, Ty, Ix>,
76        v1: usize,
77        v2: usize,
78        wt: E,
79    ) -> EdgeIndex<Ix> {
80        use petgraph::visit::NodeIndexable;
81        g.add_edge(g.from_index(v1), g.from_index(v2), wt)
82    }
83
84    #[test]
85    fn nautyex8() {
86        let n_range = (2..20).step_by(2);
87
88        for n in n_range {
89            let mut g1 = UnGraph::<(), ()>::with_capacity(n, 3 * n);
90            for _ in 0..n {
91                g1.add_node(());
92            }
93
94            // Spokes
95            for i in (0..n).step_by(2) {
96                add_edge(&mut g1, i, i + 1, ());
97            }
98            // Cycle
99            for i in 0..n - 2 {
100                add_edge(&mut g1, i, i + 2, ());
101            }
102            add_edge(&mut g1, 1, n - 2, ());
103            add_edge(&mut g1, 0, n - 1, ());
104
105            let mut g2 = UnGraph::<(), ()>::with_capacity(n, 3 * n);
106            for _ in 0..n {
107                g2.add_node(());
108            }
109
110            for i in 0..n {
111                add_edge(&mut g2, i, (i + 1) % n, ()); /* Rim */
112            }
113            for i in 0..(n / 2) {
114                add_edge(&mut g2, i, i + n / 2, ()); /* Diagonals */
115            }
116
117            /* Create canonical graphs */
118
119            let cg1 = g1.into_canon();
120            let cg2 = g2.into_canon();
121
122            assert!(cg1.is_identical(&cg2));
123        }
124    }
125}