Skip to content

Commit fc9ec9a

Browse files
authored
full automorphism support (#73)
* full automorphism support * more tests * document generator behavior on order=1 groups * nicer autg printing; doctests
1 parent 93e0206 commit fc9ec9a

7 files changed

Lines changed: 716 additions & 62 deletions

File tree

docs/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[deps]
22
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
3+
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
34

45
[compat]
56
Documenter = "1.15"

docs/make.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
using Documenter, NautyGraphs
1+
using Documenter, NautyGraphs, Graphs
22

3-
DocMeta.setdocmeta!(NautyGraphs, :DocTestSetup, :(using NautyGraphs); recursive=true)
3+
DocMeta.setdocmeta!(NautyGraphs, :DocTestSetup, :(using NautyGraphs, Graphs); recursive=true)
44

55
makedocs(sitename="NautyGraphs.jl";
66
pages = [

docs/src/groups.md

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,87 @@
11
# Graph automorphism groups
2-
To obtain information about a graph's automorphism group, use `nauty(g)`. This will return the canonical permutation as well as an `AutomorphismGroup` object.
3-
Right now, the recorded properties of the automorphism group are very limited and only include the group size and orbits, but this will change in the future.
2+
3+
Use `automorphism_group(g)` to get a graph's [`AutomorphismGroup`](@ref).
4+
It reports the group's [`order`](@ref), its vertex [`orbits`](@ref) and a set of [`generators`](@ref), and leaves `g` untouched.
5+
6+
```jldoctest groups
7+
julia> using NautyGraphs, Graphs
8+
9+
julia> g = NautyGraph(smallgraph(:petersen));
10+
11+
julia> autg = automorphism_group(g)
12+
AutomorphismGroup
13+
order 120
14+
vertices 10
15+
orbits 1
16+
generators 4
17+
18+
julia> order(autg)
19+
120
20+
21+
julia> orbit_partition(autg)
22+
1-element Vector{Vector{Int64}}:
23+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
24+
```
25+
26+
Generators are permutations in one-based form: generator `p` maps vertex `i` to `p[i]`.
27+
Together they generate the whole group, so its `order` is generally much larger than the number of generators.
28+
29+
The identity belongs to every automorphism group and is never listed as a generator.
30+
A graph with no symmetry therefore comes back with an empty generating set, not with a single identity permutation.
31+
32+
```jldoctest groups
33+
julia> frucht = automorphism_group(NautyGraph(smallgraph(:frucht)))
34+
AutomorphismGroup
35+
order 1
36+
vertices 12
37+
orbits 12
38+
generators 0
39+
40+
julia> generators(frucht)
41+
Vector{Int32}[]
42+
```
43+
44+
45+
`orbits(autg)` labels every vertex with the smallest vertex it can be mapped to, so two vertices share an orbit exactly if they carry the same label.
46+
`orbit_partition(autg)` turns that labelling into one vector per orbit, at the cost of an allocation per orbit.
47+
48+
## Computing less
49+
50+
`automorphism_group` computes everything nauty can report.
51+
When that is more than you need, call [`nauty`](@ref) directly and switch the expensive parts off.
52+
It returns the canonical permutation alongside the group, so nothing has to be recomputed:
53+
54+
```jldoctest groups
55+
julia> canonperm, autg = nauty(g; generators=true);
56+
57+
julia> order(autg)
58+
120.0
59+
```
60+
61+
The orbits and an approximate order come for free with every run.
62+
The generators and the exact order each cost an extra callback into Julia, so they are opt-in:
63+
64+
| keyword | what it adds | cost |
65+
| --- | --- | --- |
66+
| none | orbits, `order` as a `Float64` | free |
67+
| `generators=true` | the generating permutations | one callback per generator |
68+
| `exact_order=true` | `order` as an exact `BigInt` | one callback per search-tree level |
69+
| `canonize=true` | canonizes `g` in place | rewrites the group into the new numbering |
70+
71+
Without `exact_order=true`, `order(autg)` is a `Float64`: it carries about 16 significant digits, is exact up to roughly `10^12`, and overflows to `Inf` past `10^308`.
72+
Ask for the exact order whenever you need to count with it, for example when dividing by ``|\mathrm{Aut}(g)|``.
73+
74+
!!! warning "`canonize` renumbers the group as well"
75+
76+
Nauty reports the automorphism group in terms of `g`'s vertex numbering on input.
77+
With `canonize=true` that numbering is replaced, so the returned orbits and generators are rewritten to match the canonized `g` and no longer refer to the graph as it was passed in.
78+
79+
## Working with the group
80+
81+
NautyGraphs deliberately does not implement group theory beyond what nauty reports.
82+
To enumerate elements, test membership or build stabilizer chains, hand the generators to a group theory package:
83+
84+
```julia
85+
using PermutationGroups
86+
group = PermGroup([Perm(p) for p in generators(autg)])
87+
```

src/NautyGraphs.jl

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ include("sparsenautygraph.jl")
1515
include("nauty.jl")
1616
include("graphs_api_extensions.jl")
1717

18+
function __init__()
19+
_GENERATOR_CALLBACK[] = @cfunction(_record_generator, Cvoid,
20+
(Cint, Ptr{Cint}, Ptr{Cint}, Cint, Cint, Cint))
21+
_LEVEL_CALLBACK[] = @cfunction(_record_level, Cvoid,
22+
(Ptr{Cint}, Ptr{Cint}, Cint, Ptr{Cint}, Ptr{Cvoid}, Cint, Cint, Cint, Cint, Cint, Cint))
23+
return
24+
end
25+
1826
export
1927
add_edge!,
2028
rem_edge!,
@@ -40,7 +48,12 @@ export
4048
DenseNautyGraph,
4149
SparseNautyGraph,
4250
AutomorphismGroup,
43-
labels,
51+
automorphism_group,
52+
order,
53+
orbits,
54+
orbit_partition,
55+
generators,
56+
labels,
4457
label,
4558
setlabels!,
4659
setlabel!,

src/graphs_api_extensions.jl

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,8 @@ function count_isomorph(g1::AbstractGraph, g2::AbstractGraph, ::NautyAlg{true};
4040
end
4141

4242
function _count_isomorph(g1, g2)
43-
_, autg = nauty(g1; canonize=true)
44-
if g1 g2
45-
return autg.n
46-
else
47-
return zero(autg.n)
48-
end
43+
g1 g2 || return big(0)
44+
# the number of isomorphisms is the order of the automorphism group, so it has to be exact
45+
_, autg = nauty(g1; exact_order=true)
46+
return order(autg)
4947
end

0 commit comments

Comments
 (0)