Nug 26
Nug 26
Nug 26
6)
Brendan D. McKay
Adolfo Piperno
Departimento di Informatica
Sapienza Universit`a di Roma
Rome, Italy
[email protected]
Contents
0.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
nauty (no automorphisms, yes?) is a set of procedures for determining the automorphism
group of a vertex-coloured graph, and for testing graphs for isomorphism. Traces is an
alternative program for these operations.
The dreadnaut program provides sufficient functionality that most simple applications can be managed without the need to write any programs. Section 2 is intended to
be a fairly self-contained introduction to that level of use. You should start by reading
Section 1 and Section 2.
nauty and Traces also come with a set of utilities suitable for processing files of
graphs; these are described in Section 15.
For other serious purposes, you will need to write a program that calls nauty or
Traces. In that case you dont have much choice but to read this Guide from start to
finish. However, it isnt really as hard as it sounds; see the sample programs in this guide
for a constructive proof.
The current versions of nauty and Traces are available at
http://cs.anu.edu.au/bdm/nauty and http://pallini.di.uniroma1.it. There is
also a mailing list you can subscribe to if you want to discuss nauty and Traces and
receive upgrade notices: http://mailman.anu.edu.au/mailman/listinfo/nauty.
nauty and Traces are written in a highly portable subset of the language C. Modern
C compilers for most types of computer should be able to handle them without difficulty.
The theoretical basis of the original edition of nauty first appeared in [9]. An updated
account, and a detailed description of Traces appears in [10].
Introduction
nauty and Traces come with a primitive interactive interface dreadnaut which will
suffice for most one-off computations. This chapter describes the basic concepts and gives
examples of dreadnaut usage. Later chapters will describe the programming interface.
A graph for our purposes has a finite set of vertices, and a finite set of edges. Most of
the time when we write graph we mean simple undirected graph, which implies that
each edge is an unordered pair vw of distinct vertices (so multiple edges and loops are
not included).
The following shows a graph with 8 vertices and 12 edges.
edges remains the same. In the above graph we can interchange vertex labels 0,1 and
interchange vertex labels 2,3, and this preserves the edge set (for example, 2 is adjacent
to 5 before and after, while 0 is not adjacent to 4 before or after). This means that
(0 1)(2 3) is an automorphism.
(0 1)(2 3)
1
The application of two automorphisms one after the other is an automorphism too.
The set of all automorphisms, including the trivial one (that moves no labels at all), is
called the automorphism group of the graph. The automorphism group of the graph above
has 8 automorphisms:
(1)
(0 1)(2 3)
(4 5)(6 7)
(0 1)(2 3)(4 5)(6 7)
row, the results are identical (note that the edges of the two graphs are the same, even
though the drawings differ).
different
canonize
canonize
same
6
first colour
second colour
There are now only 4 automorphisms, namely those which preserve the colouring:
(1)
(4 5)(6 7)
(0 1)(2 3)
(0 1)(2 3)(4 5)(6 7)
nauty and Traces consider the colours to come in some order; i.e., there is a 1st
colour, a 2nd colour, etc.. This doesnt matter with regard to automorphisms, but it
plays an important part in canonical labelling: the new vertex labels are in order of
colour. The vertices of the first colour are labelled first, of the second colour next, and so
on. This rule means that the canonical labelling can be used to determine if two coloured
graphs are isomorphic via an isomorphism that maps each vertex of one graph onto a
vertex of the same colour in the other graph.
A colouring of the vertices is also referred to as a partition, and the colour classes as
the cells of the partition.
different
canonize
canonize
different
nauty can also handle directed graphs and loops, but Traces currently only handles
simple undirected graphs.
dreadnaut
dreadnaut is a simple program which can read graphs and execute nauty or Traces. It
is a rather primitive interface with few facilities.
Input is taken from the standard input and output is sent to the standard output, but
this can be changed by using the < and > commands. Commands may appear any
number per line separated by white space, commas, semicolons or nothing. They consist
of single characters, except when they consist of two characters. Sometimes commands
are followed by parameters.
At any point of time, dreadnaut knows the following information:
(a) The mode, which is one of dense (for using the dense version of nauty; this is
the default), sparse (for using the sparse version of nauty) and Traces (for using
Traces).
(b) The number of vertices, n.
(c) The current graph g, if defined.
(d) The current partition . If it is not defined, it is assumed equal to the partition
with every vertex in the same cell (i.e., with the same colour).
(e) The orbits of the (coloured) graph (g, ), if defined.
(f) The canonically labelled isomorph of g, called h, if defined. (Also called canong.)
(g) An extra graph called h0 , if defined. (Also called savedg.)
(h) Values for each of a variety of options.
In the following # is an integer and = is always optional.
5
Change the mode to sparse. The adjacency list data structure will be used for g
and the sparse version of nauty will be used for the x command. The graphs g, h,
h0 and the partition become undefined.
At
Change the mode to Traces. The adjacency list data structure will be used for g
and Traces will be used if the x command. The graphs g, h, h0 and the partition
become undefined.
Edit the graph g. The available subcommands are the same as for the g command.
This is only available in dense mode.
R . . . ; This is the same as r except that unspecified vertices are not filled in. Instead,
a subgraph corresponding to the given vertices is formed and replaces g. If the
command is given as -R, the given vertices are deleted instead. The partition is
6
Perform the doubling operation E(g) defined in [8]. The result in g is a regular
graph with order 2n + 2 and degree n.
s =#
Generate graph (or digraph) g at random with independent edge probabilities 1/i,
where i is the integer specified.
sr = #
Generate random regular graph g of degree i, where i is the integer specified. This
is only available in sparse and traces modes, and i cannot be more than 8.
(underscore) Replace the graph g by its complement. If there are any loops, the set
of loops is complemented too; otherwise, no loops are introduced.
(two underscores) If g is a digraph, take its converse (which reverses the direction
of all the edges). Otherwise do the same as .
Type the graph g, in an obvious format. The value of option linelength (see
l command) is taken into account. The format used is consistent with the input
format allowed by the g command. To examine just some of the graph, you can
use the ? subcommand within the e command.
This is exactly like t except that a line of the form n=n $=l g is written first,
where n is the number of vertices and l is the number of the first vertex, and a line
of the form $$ is written afterwards. This enables you to save a graph to a file and
easily restore it later: >newgraph.dre T -> will save g to the file newgraph.dre,
while <newgraph.dre will restore it.
v,vv
Display the degrees of each vertex of the graph g, if defined. For vv, a count of
how many vertices have each degree is given instead. For digraphs, the outdegrees
are displayed. Loops count as 1.
Specify a partition.
-f selects the partition with only one cell, which is the default.
f = # selects the partition with one cell containing just the vertex named and one
cell containing every other vertex.
f = [ . . . ] selects an arbitrary partition. Replace . . . by a list of cells separated
by |. You can use the abbreviation x:y for the range x, x+1, . . . , y. Any
vertices not named are put in a cell of their own at the end.
Example: If n = 10, then f=[3:7 | 0,2] establishes the partition
[3, 4, 5, 6, 7 | 0, 2 | 1, 8, 9].
F =#
Make the partition finer by placing the specified vertex in a cell of its own just
before the remains of the cell it was in before.
FF
Identify the cell which would be first individualized by the chosen algorithm (according to the mode), and place one vertex of that cell in a cell of its own just before
the remains of the cell it was in before. This is not likely to be meaningful unless
the partition has been refined first (see the i command). The rule for choosing a
7
cell is the one that nauty or Traces uses at the top of the search tree; this means
that repeated use of FF doesnt necessarily follow a path that nauty or Traces
would follow.
i
If the orbits of the automorphism group are known (as by executing the x command),
they are converted into a partition . The cells of are the orbits, and they are
arranged in order of their least elements.
OO
This is like O, except that the orbits are placed in increasing order of size and equalsized orbits are combined into single cells. This means the resulting partition is an
isomorphism invariant.
$$
Restore the vertex numbering origin to what it was just before the last $ command.
Only one previous value is remembered.
l =#
Set value of option linelength : the length of the longest line permitted for output.
The default value is installation-dependent (typically 78). A value of 0 indicates no
limit.
w =#
Set value of worksize : the amount of space provided for nauty to store automorphism data. The amount provided is enough to store i automorphisms, where i is
the integer provided. Traces does not use this option.
d,-d
Set option digraph to TRUE or FALSE, respectively. You must set it to TRUE if
you wish to define g to be a digraph or a graph with loops. The default is FALSE.
Changing it from TRUE to FALSE also causes the graph g to become undefined,
as a safety measure. This version of Traces cant handle either directed edges or
loops.
c,-c
Set option getcanon to TRUE or FALSE, respectively. This tells nauty or Traces
whether to find a canonical labelling or just the automorphism group. The default
is FALSE.
a,-a
m,-m
p,-p
y =#
Set the value of option tc level. A value of # tells nauty to use an advanced, but
expensive, algorithm for choosing target cells in the top k levels of the search tree.
See Section 7 for a more detailed description. The default is 100, but setting it to 0
might speed up the average time for easy graphs. Traces does not use this option.
G =#
S =#
=#
k = # # (Two integer arguments.) Define values for the options mininvarlevel and
maxinvarlevel. These tell nauty the minimum and maximum levels of the tree
at which it is to apply the vertex-invariant. The root of the tree is at level 1. See
Section 7 for a little more information about these options. The default is k = 0 1,
which causes the invariant to be applied only at the top of the search tree. Traces
does not use invariants itself, but invariants will be applied before calling Traces if
mininvarlevel 1 maxinvarlevel.
K =#
Give a value to the invararg option. This number is passed to the vertex-invariant
by the I command and by nauty. See Section 10 for the meaning of this option for
each available vertex-invariant. The default value depends on the invariant; see the
command.
V =#
Specify a verbosity level for Traces. A value of 0 means that no output will be
written except for group generators (according to the c command) and the summary
at the end. Values greater than 0 produce more and more output during execution,
see Section 8. The default is 0.
u =#
Type the mode, and the current values of m, n, worksize, most of the options, the
number of edges in g, and the number of cells in . If output has been directed
away from stdout using the > command, some of this information is also written
to stdout.
&
&&
Same as &, except that the quotient of g with respect to is also written. Say
10
Turn on, respectively off, the facility to provide known automophisms to Traces.
If this is turned on, the PP command (below) can be used to input automorphisms,
and the group generators (including the extra ones found by Traces) are kept when
Traces exits. The generators will be deleted if the graph changes. To delete them
manually, use -P P (with the space!).
PP...; Set on the facility to provide known automophisms to Traces (if it isnt on
already) and read in one automorphism. Use of this command one or more times
before command x in Traces mode allows known automorphisms to be given to
Traces. When Traces runs, it checks whether the permutation is in fact an automorphism, and dies if it is not. The format of the input is a list of n distinct integers
comprising a permutation. Note that the automorphisms you give to Traces are
not written by it; only extra generators are written.
Example: If n = 7 then PP 4 0 2 3 6 5 1; is valid input.
(E) Commands which execute nauty or Traces or use the results.
x
Type the canonical label and the canonically labelled graph. The canonical label is
given in the form of a list of the vertices of g in canonical order. Only possible after
x with option getcanon selected.
Type three 8-digit hex numbers whose value depends only on h. This allows
quick comparison between graphs. Isomorphic graphs give the same value. Nonisomorphic graphs may also give the same value, though this is rare. Only possible
after x with option getcanon selected.
Compare the labelled graphs h and h0 . Both must have been already defined (using
x and @). The complete process for testing two graphs g1 and g2 for isomorphism is
this:
enter g1
c x @
(select getcanon option, execute nauty or Traces, copy h to h0 );
enter g2
x #
(execute nauty or Traces, compare h to h0 ).
11
##
This is the same as # except that, if h is identical to h0 , you will also be given an
isomorphism from g1 to g2 . This is in the form of a sequence of pairs vi -wi , where
vi is a vertex of g1 and wi is a vertex of g2 . The vertex-numbering origin in force
when h0 was created is used for g1 , whilst the origin now in force is used for g2 .
Type the orbits of the group. Only possible after x. For orbits longer than one
vertex, the orbit size is shown in parentheses.
M = #,M = #/#,-M Each call to nauty or Traces is repeated until either the number of
repetitions exceeds the first value or (if the second part is included) the cpu time
exceeds the number of seconds in the second value. Each limit can be turned off by
setting the value to 0. The cpu time is then reported accurately. This is for doing
timing tests with easy graphs. Output is suppressed except for the first execution.
This also effects the i command. -M and M=1 turn this feature off.
(F) Miscellaneous commands.
h,H
"..." Anything between the quotes is simply copied to the output. The ligatures \n
(newline), \t (tab), \b (backspace), \r (carriage return), \f (formfeed), \\
(backslash), \ (single quote) and \" (double quote) are recognised. Other occurrences of \ are ignored.
!
Ignore anything else on this input line. Note that this is a command, not a comment
character in the usual sense, so you cant use it in the middle of other commands.
<
Begin reading input from another file. The name of the file starts at the first nonwhite character after the < and ends before the next white character, unless the
first non-white character is " in which case the name ends at the next " or
newline. If such a file cannot be found, another attempt is made with the string
.dre appended to the name. When end-of-file is encountered on that file, continue
from the current input file. The allowed level of nesting is configurable (usually 10).
>,>>
Close the existing output file unless it is the standard output, then begin writing
output to another file. The name of the file starts at the first non-white character
after the > and ends before the next white character, unless the first non-white
character is " in which case the name ends at the next " or newline. For >
the file starts off empty. For >>, if an existing file of the right name exists, it is
written to starting at the current end-of-file.
->
If an output file other than standard output is in use, close it and direct output
back to the standard output.
->>
B,-B
Turn flushing on or off. Initially it is off. When flushing is on, the output will be
flushed at the end of every command.
Quit. dreadnaut will exit irrespective of which level of input nesting it is on.
12
2.1
Several sample dreadnaut sessions are shown below. The underlined characters are those
typed by the user.
3
2
g =
4
5
6
7
> n=8 g
0: 1 3 4;
1: 2 5;
2: 3 6;
3: 7;
8 vertices
enter the graph
13
4: 5 7;
5: 6;
6: 7.
> f=2 x
fix vertex 2; execute
[fixing partition]
(0 5)(3 6)
level 2: 6 orbits; 3 fixed; index 2
(1 3)(5 7)
level 1: 4 orbits; 1 fixed; index 3
4 orbits; grpsize=6; 2 gens; 6 nodes; maxlev=3
cpu time = 0.00 seconds
> o
show the orbits
0 5 7 (3); 1 3 6 (3); 2; 4;
> q
quit
The next problem solved is to determine an isomorphism between the following two
graphs. We turn off the writing of automorphisms to save some space, and this time we
will use Traces.
11
8
g1 =
2
11
10
3
2
4
g2 =
10
> At
use Traces mode
> c -a V=0
turn getcanon on, group writing and verbosity off
> n=12 g
enter the first graph
0: 1; 2; 0;
3: 4; 5; 6; 3;
7: 8; 9; 10; 11; 7.
> x @
execute, save the result
3 orbits; grpsize=480; 4 gens; 41 nodes (1 interrupted); maxlev=6;
canupdates=1; cpu time = 0.00 seconds
> g
enter the second graph
0: 1; 2; 3; 4; 0;
5: 6; 7; 8; 5;
9: 10; 11; 9.
> x
execute
3 orbits; grpsize=480; 5 gens; 35 nodes (2 interrupted); maxlev=6;
canupdates=1; cpu time = 0.00 seconds
compare to saved graph
> ##
14
As a third example, we consider a simple block design. nauty and Traces can compute automorphisms and canonical labellings of block designs by the common method of
converting the design to an equivalent coloured graph. Suppose a design D has varieties
x1 , x2 , . . ., xv and blocks B1 , B2 , . . ., Bb . Define G(D) to be the graph with vertex set
{x1 , . . . , xv , B1 , . . . , Bb }, with each x-vertex having one colour and each B-vertex having
a second colour, and edge set {xi Bj | xi Bj }. The following theorem is elementary.
Theorem 1.
(a) The automorphism group of D is isomorphic to the automorphism group of G(D).
(b) If D1 and D2 are designs, D1 and D2 are isomorphic if and only if G(D1 ) and
G(D2 ) are isomorphic.
Consider the design D = { {1, 2, 4}, {1, 3}, {2, 3, 4} }. Label G(D) so that the varieties
of D correspond to vertices 14, while the blocks correspond to vertices 57. This time
we will do it with the sparse version of nauty.
1
2
g =
3
4
5
6
7
> $=1 As
label vertices starting at 1, sparse mode
> n=7 g
1: 5:
go to vertex 5 (block 1), the character is a colon
5: 1 2 4;
6: 1 3;
7: 2 3 4.
> f=[1:4]
fix the varieties setwise
> cx
run nauty
[fixing partition]
(2 4)
group generators
level 2: 6 orbits; 2 fixed; index 2
(1 3)(5 7)
level 1: 4 orbits; 1 fixed; index 2
4 orbits; grpsize=4; 2 gens; 6 nodes; maxlev=3
canupdates=1; cpu time = 0.00 seconds
> o
display the orbits
1 3 (2); 2 4 (2); 5 7 (2); 6;
> b
display the canonical labelling
2 4 1 3 6 7 5
the vertices in canonical order
15
1
2
3
4
5
6
7
> q
:
:
:
:
:
:
:
6
6
5
5
3
1
1
7;
7;
7;
6;
4;
2 4;
2 3;
quit
Looking at the vertices 5,6,7 which represent the blocks, we see that the canonically
labelled block design is { {3, 4}, {1, 2, 4}, {1, 2, 3} }.
Data Structures
In this section we will describe the basic data structures required for programs that call
nauty or Traces.
Data structure for graphs.
There are two graph data structures supported. One is the dense form (also called the
packed form) used only by the dense version of nauty. The other is the sparse form used
by the sparse version of nauty and by Traces. The vertices of a graph are numbered
0, 1, . . . , n 1.
The dense form of a graph is an adjacency matrix with one bit per entry. A setword
is an unsigned integer type of either 16, 32 or 64 bits, depending on the compile-time
parameter WORDSIZE. (By default, WORDSIZE is 32 unless the size of type long int
is greater than 32, in which case WORDSIZE is 64, but this test can be overridden at
configuration time, see Section 16.)
A set (by which we always mean a subset of V = {0, 1, . . . , n1}) is represented by
an array of m setwords, where m is some number such that WORDSIZE m n. The
bits of a set are numbered 0, 1, . . . , n1 left to right (within each setword: high order
to low order). Bits which dont get numbers are called unnumbered and are assumed
permanently zero. A set represents the subset { i | bit i is 1 }.
A graph represented in dense form uses the type graph. It is stored as an array of n
sets (so it has mn setwords altogether). The i-th set gives the vertices to which vertex
i is adjacent, for 0 i < n.
The C types setword, set and graph are actually the same, so a graph in dense form
is really represented by a 1-dimensional array of length mn, not by an array of arrays.
A graph represented in sparse form uses the type sparsegraph. It is stored as a
structure with the following fields:
int nv: the number of vertices
size t nde: the number of directed edges (loops count as 1, other undirected edges as 2)
size t v: pointer to an array of length at least nv
16
2
0
0110 0
1000 0
1000 0
2
0
1
0010 0
1100 0
1000 0
nv
nde
d
v
e
dlen
vlen
elen
3
4
nv
nde
d
v
e
dlen
vlen
elen
3
4
2 1 1
0 3 5
5
3
7
&
1 2
1 2 1
0 2 4
4
3
5
&
0 1 0
Figure 1: Dense and sparse data structures for graphs and digraphs.
In Figure 1, the graph on the left is represented in dense form by the array in the
centre (we show three words of type setword). On the right is a possible sparse form for
the same graph. Note that loops are only allowed for directed graphs, and contribute 1
to the vertex degree.
Before the sparesgraph structure can be used, it needs to be initialised. Fields
d,v,e,w should be set to NULL, and dlen,vlen,elen,wlen should be set to 0. After
initialisation, the sizes of the fields will be automatically adjusted as required, so you
dont need to initialise it again.
Data structure for permutations, orbits, and colourings.
A permutation of V is represented by an array of n integers, type int, with the i-th
entry giving the image of i under the permutation.
17
4 7 8
0 0 2 2 0 0
6 2 6
3 5 6 1 0 4
7 8
ptn: 0
0 1 1 1 0 1
1 0
Note that colours come in a particular order. In the example, there are 4 colours listed
left to right. However, each colour class by itself is an unordered set of vertices so it makes
no difference which order they are listed in. Also, values in ptn which are not 0 can be
any positive value. (Advanced hint: this is not true internally to nauty, probably you
dont need to know this.) So, for example, exactly the same partition is represented by
the following.
lab: 2
3 1 6 5 0 7
8 4
ptn: 0
0 1 2 2 0 8
3 0
The type boolean is a synonym for int, but the different name is intended to encourage
you to restrict the values to either TRUE or FALSE (which are defined as 1 and 0,
respectively).
Size limits
There are several ways to compile nauty, leading to differences in types and the size of
graph that can be processed. These are selected by preprocessor variables.
(1) If type int has less than 32 bits (very rare these days), there is an absolute limit of
215 3 = 32765.
(2) If type int has at least 32 bits, there is an absolute limit of 2 109 on the order of
a graph.
In addition, there is a choice between static and dynamic memory allocation for the
larger data objects. This is selected by the value of the preprocessor variable MAXN.
18
(a) If MAXN is defined as 0, the limit on the order of a graph is given in (1)(2) above
and objects are dynamically allocated. Of course, if you dont have enough memory,
dynamic allocation may fail. This is the default.
(b) If MAXN is defined as a positive integer, that is the limit on the order of a graph.
It cant be greater than the absolute limit given in (1)(2) above. In this case most,
but not all objects are statically allocated, so space is wasted if MAXN is much
larger than what is actually used.
A special case of option (b) is 0 < MAXN WORDSIZE, which implies that a set
consists of a single setword. Some of the critical routines in nauty have special code to
optimize performance in that case. The recommended way to compile for this case is to
define MAXN to be the name WORDSIZE.
Traces is limited to the same number of vertices as nauty, including the restriction
to MAXN if that is non-zero.
Various options are provided to nauty or Traces by means of options structures. The
type optionblk is used for nauty and the type TracesOptions for Traces.
In all cases, it is strongly recommended that the values be first set to their defaults
by using one of the provided macros:
DEFAULTOPTIONS GRAPH : for undirected graphs in dense nauty
DEFAULTOPTIONS DIGRAPH : for digraphs in dense nauty
DEFAULTOPTIONS SPARSEGRAPH : for undirected graphs in sparse nauty
DEFAULTOPTIONS SPARSEDIGRAPH : for digraphs in sparse nauty
DEFAULTOPTIONS TRACES : for undirected graphs in Traces
If any of the defaults are not suitable, change them using assignment statements. In this
way you will only need to recompile if the option structures change in the future.
We first describe optionblk, used by nauty (both dense and sparse versions).
boolean getcanon: If this is TRUE, the canonically labelled graph is produced as well as
the automorphism group. Otherwise, only the automorphism group is determined.
Default FALSE.
boolean digraph: This must be TRUE if the graph has any directed edges or loops. If no
directed edges or loops are present, selecting this option is legal but may degrade the
performance slightly and the canonical labelling might be different. Default TRUE
for DEFAULTOPTIONS DIGRAPH and DEFAULTOPTIONS SPARSEDIGRAPH,
otherwise FALSE.
boolean writeautoms: If this is TRUE, generators of the automorphism group will be
written to the file outfile (see below). The format will depend on the settings
of options cartesian and linelength (see below, again). More details on what
19
boolean digraph: Unused, must be FALSE. This release of Traces cannot handle digraphs.
boolean defaultptn: If this is TRUE, it is assumed that all vertices of the graph have
the same colour (so the initial values of the parameters lab and ptn are ignored).
If it is FALSE, the initial colouring of the vertices is determined by lab and ptn as
described above. Default TRUE.
int linelength: The value of this variable specifies the maximum number of characters
per line (excluding end-of-line characters) which may be written to the file outfile
(see below). Default 0.
FILE outfile: This is the file to which the output selected by the options writeautoms
and verbosity is sent. It must be already open and writable. The null pointer
NULL is equivalent to stdout (the standard output). Default NULL.
int strategy: Must be 0 in this version.
int verbosity: A level of verbosity of messages while Traces is running. A value of
0 means that no output will be written (except that automorphisms are written if
the writeautoms option requests them). Larger values produce greater information about the execution, though its interpretation requires some knowledge of the
algorithm. Default 0.
permnode generators: This can be used to provide known automorphisms to Traces
and receive the automorphisms from Traces when it is finished. If it is NULL when
Traces is called, Traces does not change it. If it is non-NULL, it is expected to
point to a (perhaps empty) circular list of known automorphisms. (It is an error to
give a permutation that is not an automorphism of the input coloured graph.) In this
case, Traces will add automorphisms to the list so that the whole automorphism
group is generated. See Section 18 for detailed instructions and examples. Default
NULL.
void (userautomproc) (int,int,int): This is a pointer to a user-defined procedure
which is to be called for each generator. Section 9 has details. No calls will be made
if the value is NULL. Default NULL.
Structured types for receiving statistics
nauty and Traces provide some statistics on output. These values do not play a part
in the computation, so it isnt an error if some of the counts exceed the capacity of the
fields they are stored in.
The various fields of a structure of type statsblk are set by nauty. Their meanings
are as follows:
double grpsize1, int grpsize2: Within rounding error, the order of the automorphism group is equal to grpsize1 10grpsize2 . If the exact size of a very large
group is needed, it can be calculated from the output selected by the writemarkers
option, or you can compute it with your own multiprecision arithmetic using the
userlevelproc feature. See Section 8.
int numorbits: The number of orbits of the automorphism group.
22
In this section, we describe simplified interfaces to nauty and the main interface to
Traces. The hairy details of calling nauty directly will be left to Section 7.
A call to the dense version of nauty can be made as follows.
densenauty(g, lab, ptn, orbits, options, stats, m, n, canong)
graph g: The input graph. Read-only.
int lab,ptn: Two arrays of n entries. Their use depends on the values of several
options. If options.defaultptn = TRUE, the input values are ignored; otherwise, they define the initial colouring of the graph (see above for the format). If
options.getcanon = TRUE, the value of lab on return is the canonical labelling
of the graph. Precisely, it lists the vertices of g in the order in which they need to
be relabelled to give canong. Irrespective of options.getcanon, neither lab nor
ptn is changed by enough to change the colouring. (Recall that the order of the
vertices within the cells is irrelevant.) Read-Write.
int orbits: An array of n entries to hold the orbits of the automorphism group. When
densenauty returns, orbits[i] is the number of the least-numbered vertex in the
same orbit as i, for 0 i n1. Write-only.
optionblk options: A structure giving a list of options to the procedure. See above
for their meanings. It should be declared using DEFAULTOPTIONS GRAPH or
DEFAULTOPTIONS DIGRAPH, but options other than dispatch can be changed.
Read-only.
statsblk stats: A structure used by nauty to provide some statistics about what it
did. See above for their meanings. Write-only.
int m, n: The number of setwords in sets and the number of vertices, respectively.
It must be the case that 1 n m WORDSIZE. If nauty is compiled with
MAXN > 0, it must also be the case that n MAXN and m MAXM, where
MAXM = dMAXN/WORDSIZEe. Read-only.
24
graph canong: The canonically labelled isomorph of g produced by nauty. This argument is ignored if options.getcanon = FALSE, in which case the nil pointer
NULL can be given as the actual parameter. Write-only.
A call to the sparse version of nauty can be made as follows.
sparsenauty(g, lab, ptn, orbits, options, stats, canong)
The parameters are the same as for densenauty except:
(a) Parameters g and canong have type sparsegraph*. If options.getcanon = TRUE,
then canong should have been initialised (see Section 12). The fields will be automatically expanded if they arent large enough.
(b) options should be declared using either DEFAULTOPTIONS SPARSEGRAPH or
DEFAULTOPTIONS SPARSEDIGRAPH, but options other than dispatch can be
changed.
A call to Traces looks like this:
Traces(g, lab, ptn, orbits, toptions, tstats, canong)
sparsegraph g: The input graph. Read-only.
int lab,ptn: Two arrays of n entries. Their use depends on the values of several
options. If toptions.defaultptn = TRUE, the input values are ignored (every
vertex has the same colour); otherwise, they define the initial colouring of the graph
(see above for the format). If toptions.getcanon = TRUE, the value of lab on
return is the canonical labelling of the graph. Precisely, it lists the vertices of g in
the order in which they need to be relabelled to give canong. Read-write.
int orbits: Returns the orbits of the automorphism group, as described above. Writeonly.
TracesOptions toptions: A structure giving a list of options to the procedure. See
above for their meanings. Read-only.
TracesStats tstats: A structure used by Traces to provide some statistics about
what it did. See above for their meanings. Write-only.
sparsegraph canong: The canonically labelled graph, if toptions.getcanon = TRUE.
Otherwise it can be NULL. Write-only.
25
For most applications, the simplified interface to nauty described in the previous section
is recommended. This section describes the low-level interface used both for dense graphs
and sparse graphs. nauty knows which type you are using from the dispatch field of the
options argument.
A call to nauty has the form
nauty (g, lab, ptn, active, orbits, options, stats, workspace, worksize, m, n,
canong)
where the parameters have meanings as defined below.
graph or sparsegraph g: The input graph. Read-only. The actual parameter has type
graph, so your compiler might like you to cast a sparsegraph argument to that
type if you are using a sparse graph.
int lab,ptn: Two arrays of n entries. Their use depends on the values of several
options. If options.defaultptn = TRUE, the input values are ignored; otherwise,
they define the initial colouring of the graph (see below). If options.getcanon =
TRUE, the value of lab on return is the canonical labelling of the graph. Precisely,
it lists the vertices of g in the order in which they need to be relabelled to give
canong. Irrespective of options.getcanon, neither lab nor ptn is changed by
enough to change the colouring. (Recall that the order of the vertices within the
cells is irrelevant.) Read-Write.
set active: An array of m setwords specifying the colours which are initially active.
A brief outline of what this means is given below. This argument is rarely used;
nauty will always work correctly if given the nil pointer NULL. Read-only.
int orbits: An array of n entries to hold the orbits of the automorphism group. When
nauty returns, orbits[i] is the number of the least-numbered vertex in the same
orbit as i, for 0 i n1. Write-only.
optionblk options: A structure giving a list of options to the procedure. See Section 5
for their meanings. Read-only.
statsblk stats: A structure used by nauty to provide some statistics about what it
did. See Section 5 for their meanings. Write-only.
setword workspace, worksize: The address and length of an array used by nauty for
working storage. The length is given in units of setword (differently from the w
command in dreadnaut). There is no minimum requirement for correct operation,
but the efficiency may suffer if not much is provided. A value of worksize 50m
is recommended. Write-only and read-only, respectively.
int m, n: The number of setwords in sets and the number of vertices, respectively.
It must be the case that 1 n m WORDSIZE. If nauty is compiled with
MAXN > 0, it must also be the case that n MAXN and m MAXM, where
MAXM = dMAXN/WORDSIZEe. Read-only.
graph or sparsegraph canong The canonically labelled isomorph of g produced by
26
8
8.1
1
(k)
2
.
.
.
(k)
tk
level k:
(k1)
1
(k1)
2
.
.
.
(k1)
tk1
level k1:
.
.
.
level 2:
(1)
1
(1)
2
.
.
.
(1)
t1
level 1:
28
(j)
are
for l j k and 1 i tj .
1
2
g =
4
7
29
= {(1)}
= h1 i with 6 orbits and order 2
= h1 , 2 i with 4 orbits and order 2 3 = 6
= h1 , 2 , 3 i with 1 orbit and order 6 8 = 48.
The values of stats.grpsize1 and stats.grpsize2 show that | | = 48 100 = 48. The
value of stats.numorbits shows there is only one orbit, which is also seen from orbits.
Example 2:
0
1
2
g =
4
11
g =
0
10
level 4:
(3 4)(5 6)
level 3:
(1 2)
level 2:
(0 1)
level 1:
10
h =
9
11 1
6
7
Example 4:
11
3
4
g =
9
10
2
0
11
h =
9
10
8.2
Traces output
Output from Traces is controlled by the writeautoms, cartesian, and verbosity options.
2
1
6
g =
5
0
31
(0 4 3 7 5 1)(2 9 8)
(0 2)(3 4)(6 9)(7 8)
3 cells; target cell: 10; 1 orbit; 3 nodes (1 kept); 1 update;
(1 4 9)(2 8 7)(3 6 5)
(3 6)(4 9)(7 8)
(1 9 4)(2 6 8 5 7 3)
7 cells; target cell: 6; 1 orbit; 5 nodes (2 kept); 1 update;
10 cells; target cell: 2; 1 orbit; 3 nodes (1 kept); 1 update;
The lines starting Gen are generators for the group. Note that in Traces there is no
base and the generators do not in general form a strong generating set. The lines starting
level, which are turned on by the verbosity option, describe the levels in the search
tree:
(a) the level number (level 1 has the children of the root of the tree);
(b) the number of cells, and the size of the target cell, for the nodes on this level that
are the best;
(c) the number of orbits in the group generated by the generators found by the time this
level is finished;
(d) the number of nodes created on this level, and the number which are judged to be
best;
(e) the number of times a node was found on this level better than the previous nodes
on this level.
If known automorphisms are given to Traces via toptions.generators, these are
not written.
User-defined procedures
nauty makes provision for up to five procedures specified by the user to be called at
various times during the processing. This will be done if pointers to them are passed
in the userrefproc, userautomproc, usernodeproc, userlevelproc or usercanonproc
fields of options (see Section 7). In all cases, a value of NULL will cause no call to be
made.
Traces currently has two such procedures, userautomproc and usercanonproc.
Proceedures for nauty.
These procedures have many parameters in common; we will describe the most important of these here. Unless the individual procedure descriptions specify otherwise, they
should be treated as read-only.
graph g; int m, n: These are the arguments of the same name passed to nauty. nauty
has not changed them. See Section 7 for their meanings. If the sparse version
32
of nauty is being used, the argument passed to parameter g actually has type
sparsegraph*. Correct practice is declare it as type graph* but cast it to type
sparsegraph* before use.
int level: The level of the current node. The root of the search tree has level one.
int lab, ptn: Arrays of length n giving partitions associated with each of the nodes
along the path from the root of the tree to the current node. These are the parameters of the same name passed to nauty, but nauty has modified their contents as
described below.
Suppose that we are currently at level l of the search tree. Let 1 , 2 , . . . , l be the
path in the tree from the root 1 to the current node l . The partition at level i is a
partition i associated with node i . The partition originally passed to nauty, implicitly
or explicitly, is the partition at level 0, denoted by 0 . The complete partition nest
0 , 1 , . . . , l is held in lab and ptn thus:
(a) lab holds a permutation of {0, 1, . . . , n1}.
(b) For 0 t l, the partition t has as cells all the sets of the form {lab[i], lab[i+1],
. . . , lab[j]}, where [i, j] is a maximal subinterval of [0, n1] such that ptn[k] > t for
i k < j and ptn[j] t.
(c) Every entry of ptn which is not less than or equal to l is equal to NAUTY INFINITY.
(NAUTY INFINITY is a large constant defined in nauty.h.)
For example, say n = 10, l = 3, 0 = [0, 2, 4, 5, 6, 7, 8, 9|1, 3], 1 = [0, 2, 4, 6|5, 7, 8, 9|1, 3],
2 = [0, 2, 4, 6|8|5, 7, 9|3|1], and 3 = [4, 6|0, 2|8|5, 7, 9|3|1]. Then the contents of lab and
ptn may be
lab: 4 6 2 0 8 7
ptn: 3 1 2
5 9 3 1
0 2 0
name passed to nauty, but has the same meaning and purpose. It can be changed
without affecting nauty behaviour. See Section 7.
int code: This must be set to a labelling-independent value which is an invariant of the
partition at this level before or after refinement. (Example: the number of cells.)
It is essential that equivalent nodes have the same code. The value assigned must
be less than NAUTY INFINITY.
The operation of refining the current partition involves permuting the vertices (i.e.,
entries of lab) within a cell, and then breaking it into subcells by changing the appropriate
entries of ptn to level.
The validity of nauty requires that the operation performed be entirely independent
of the labelling of the graph. Thus, if userrefproc is called with g and lab relabelled
consistently and the same values of ptn and active, then the final values of ptn and
active should be the same, and the final value of lab should be the same but relabelled
in the same way (remembering always that the order of vertices within the cells doesnt
matter). It is also necessary that nodes of the tree which may be equivalent must be
treated equivalently. To be safe, regard any nodes on the same level as possibly equivalent.
It is desirable (but not compulsory) that the partition returned is equitable. If necessary, this can be done by calling the default refinement procedure refine, which has the
same parameter list. If equitablility cannot be ensured, make sure that nauty is called
with options.digraph = TRUE.
The usefulness of userrefproc has declined since vertex-invariants were introduced
(see Section 10).
(b) usernodeproc(g, lab, ptn, level, numcells, tc, code, m, n)
This is called once for every node of the search tree, after the partition has been
refined.
The parameters passed are as follows. Treat all of them as read-only.
g,m,n,lab,ptn,level: As above.
int numcells: The number of cells in the current partition.
int tc: If nauty has determined that children of this node need to be explored, tc is
the index in lab of where the target cell starts. Otherwise, it is 1.
int code: This is the code produced by the refinement and vertex-invariant procedures
while refining this partition.
34
35
36
10
Vertex-invariants
The operation of nauty and Traces is driven by a procedure which accepts partitions
and attempts to make them strictly finer without separating equivalent vertices. For some
families of difficult graphs, the built-in refinement procedure is insufficiently powerful,
resulting in excessively large search trees. In many cases, this problem can be dramatically
reduced by using some sort of invariant to assist the refinement procedure.
Traces does not have the facility to use invariants during its operation, though dreadnaut allows an invariant to be applied before Traces is called.
Formally, let G be the set of all labelled graphs (or digraphs) with vertex set V =
{0, 1, . . . , n1}, and let be the set of partitions of V . As always, the order of the cells
of a partition is significant, but the order of the elements of the cells is not. Let Z be the
integers. A vertex-invariant is defined to be a mapping
: G V Z
such that (G , , v ) = (G, , v) for every G G, , v V and permutation .
Informally, this says that the values of are independent of the labelling of G.
A great number of vertex-invariants have been proposed in the literature, but very few
of them are suitable for use with nauty. Most of them are either insufficiently powerful
or require excessive amounts of time or space to compute. Even amongst the vertexinvariants which are known to be useful, their usefulness varies so much with the type
of graph they are applied to, or the levels of the search tree at which they are applied,
that intelligent automatic selection of a vertex-invariant by nauty would seem to be a
task beyond our current capabilities. Consequently, the choice of vertex-invariant (or the
choice not to use one) has been left up to the user.
The options parameter of nauty has four fields relevant to vertex-invariants, namely
invarproc, mininvarlevel, maxinvarlevel and invararg. These are fully described in
Section 7. The I command in dreadnaut may be useful in investigating which of the
supplied vertex-invariants are useful for your problem. Experience shows that it is nearly
always best to apply the invariant at just one level in the search tree, with levels 1 and 2
being the most likely candidates.
We now describe the vertex-invariants which are provided with nauty. Information
on how to write a new vertex-invariant procedure can be found in the file nautinv.c. We
will assume that g is a graph on V = {0, 1, . . . , n1}, and that = (V0 , V1 , . . . , Vk ) is a
partition of V . This partition will be equitable unless options.digraph = TRUE. One of
the cells of will be designated V . If the procedure is called by nauty at level 1 (i.e.
at the root of the search tree), or directly by dreadnaut (I command), this will be the
first cell V0 ; otherwise, V will be the singleton cell containing the vertex fixed in order
to create this node from its parent.
Unless otherwise specified, these invariants are only available for graphs in dense form.
Trying to use them with sparse form will cause a disaster.
twopaths.
Each vertex v is given a code depending on the cells to which belong the
vertices reachable from v along a path of length 2. invararg is not used. This is a cheap
37
invariant suitable for graphs which are regular but otherwise have no particular structure
(for example).
adjtriang. Each vertex v is given a code depending on the number of common neighbours between each pair {v1 , v2 } of neighbours of v, and which cells v1 and v2 belong to.
v1 must be adjacent to v2 if invararg = 0 and not adjacent if invararg = 1 . This is a
fairly cheap invariant which can often break up the vertex sets of strongly-regular graphs.
triples.
Each vertex v is given a code depending on the set of weights w(v, v1 , v2 ),
where {v1 , v2 } ranges over the set of all pairs of vertices distinct from v such that at
least one of {v, v1 , v2 } lies in V . The weight w(v, v1 , v2 ) depends on the number of
vertices adjacent to an odd number of {v, v1 , v2 } and to the cells that v, v1 and v2 belong
to. invararg is not used. This invariant often works on strongly-regular graphs that
adjtriang fails on, but is more expensive.
quadruples. Each vertex v is given a code depending on the set of weights w(v, v1 , v2 , v3 ),
where {v1 , v2 , v3 } ranges over the set of all pairs of vertices distinct from v such that at
least one of {v, v1 , v2 , v3 } lies in V . The weight w(v, v1 , v2 , v3 ) depends on the number of
vertices adjacent to an odd number of {v, v1 , v2 , v3 } and to the cells that v, v1 , v2 and v3
belong to. invararg is not used. This is an expensive invariant which can sometimes be
of use for graphs with a particularly regular structure.
celltrips. Each vertex v is given a code depending on the set of weights w(v, v1 , v2 ),
where w(v, v1 , v2 ) depends on the number of vertices adjacent to an odd number of
{v, v1 , v2 }. These three vertices are constrained to belong to the same cell. The cells
of are tried in increasing order of size until one splits. invararg is not used. This
invariant can sometimes split the bipartite graphs derived from block designs, and other
graphs of moderate difficulty.
cellquads. Each vertex v is given a code depending on the set of weights w(v, v1 , v2 , v3 ),
where w(v, v1 , v2 , v3 ) depends on the number of vertices adjacent to an odd number of
{v, v1 , v2 , v3 }. These four vertices are constrained to belong to the same cell. The cells of
are tried in increasing order of size until one splits. invararg is not used. This invariant
is powerful enough to split many difficult graphs, such as hadamard-matrix graphs (where
it is best applied at level 2).
cellquins. Each vertex v is given a code depending on the set of weights
w(v, v1 , v2 , v3 , v4 ), where w(v, v1 , v2 , v3 , v4 ) depends on the number of vertices adjacent to
an odd number of {v, v1 , v2 , v3 , v4 }. These five vertices are constrained to belong to the
same cell. The cells of are tried in increasing order of size until one splits. invararg is
not used. We know of no good use for this very powerful but very expensive invariant.
distances. Each vertex v is given a code depending on the number of vertices at each
distance from v, and what cells they belong to. If a cell is found that splits, no further
cells are tried. invararg specifies an upper bound on which distance to investigate, with
0 indicating no limit. This is a fairly cheap invariant suitable for things like regular
graphs for which twopaths doesnt work. Use the smallest value of invararg that gives
satisfactory results.
This is like distances but works for sparse form rather than dense
distances sg.
form. It is in the file nausparse.c rather than nautyinv.c.
38
indsets. Each vertex v is given a code depending on the number of independent sets
of size invararg which include v, and the cells containing the other vertices of those sets.
The value of invararg is limited to 10. This can often split the vertex sets of stronglyregular graphs and bipartite design graphs, though it becomes expensive if invararg is
large. A value of 4 is sometimes sufficient.
cliques.
Each vertex v is given a code depending on the number of cliques of size
invararg which include v, and the cells containing the other vertices of those cliques. The
value of invararg is limited to 10. This can often split the vertex sets of strongly-regular
graphs, though it becomes expensive if invararg is large. A value of 4 is sometimes
sufficient.
cellcliq.
Each vertex v is given a code depending on the number of cliques of size
invararg which include v and lie within the cell containing v. The value of invararg is
limited to 10. The cells are tried in increasing order of size, and the process stops as soon
as a cell splits. This invariant applied at level 2 can be very successful on difficult vertextransitive graphs. A value of 3 can sometimes work even on strongly-regular graphs.
cellind.
Each vertex v is given a code depending on the number of independent
sets of size invararg which include v and lie within the cell containing v. The value of
invararg is limited to 10. The cells are tried in increasing order of size, and the process
stops as soon as a cell splits. This invariant applied at level 2 can be very successful on
difficult vertex-transitive graphs.
adjacencies. This is an invariant for digraphs and is not useful for graphs. The standard refinement procedure alone can sometimes give very poor performance for directed
graphs, especially those which are not strongly connected. This invariant tries to correct
the poor behaviour. Applying it to multiple levels may be necessary.
This is like adjacencies but works for sparse form rather than
adjacencies sg.
dense form. It is in the file nausparse.c rather than nautyinv.c.
cellfano.
This invariant is intended for projective plane graphs but can be applied
to any graphs. It is very expensive.
cellfano2.
This invariant is intended for projective plane graphs but can be applied
to any graphs. It is very expensive, but maybe less than cellfano for genuine projective
plane graphs. In the latter case, it can be thought of as counting the Fano subplanes
according to which cells they involve. Another class of graph that this invariant can help
with is the graphs derived from Latin squares as in Section 14.
refinvar.
Each vertex is given a code that depends on the result of refining the
partition resulting from individualization of that vertex. The refinement is not necessarily
complete; its completeness depends on the value of invararg. Use the smallest value of
invararg that gives satisfactory results. This is good for regular graphs that are not
strongly regular, and similar graphs.
39
11
Programs which call the dense version of nauty should include the file nauty.h and be
linked with nauty.c, nautil.c, naugraph.c, schreier.c, and naurng.c. If a built-in
invariant is used, the file nautinv.h should be included too, and nautinv.c should be
linked.
The simplest way to link with the necessary object files is to use the library nauty.a.
Suppose that m and n have meanings as usual.
There are two general approaches to storage management. The first, the simplest if a
prior limit is known on the graph size, is to define MAXN to be that limit before nauty.h
is included. nauty.h will define MAXM, and then MAXM and MAXN can be used to
declare variables. For example:
set s[MAXM]; /* a set */
graph g[MAXN*MAXM]; /* a graph */
int xy[MAXN]; /* an array */
The second method is more complicated but does not require a prior bound on the
graph size. In this method, each variable whose size is unknown is dynamically allocated.
Of course you can do this yourself using malloc( ) but nauty.h provides macros for doing
it in a convenient and efficient way. First there are static declarations:
DYNALLSTAT(set,s,s sz);
DYNALLSTAT(graph,g,g sz);
DYNALLSTAT(int,xy,xy sz);
Before the variables are used, they are set to the right size using the dynamic allocation
macros:
DYNALLOC1(set,s,s sz,m,"malloc");
DYNALLOC2(graph,g,g sz,m,n,"malloc");
DYNALLOC1(int,xy,xy sz,n,"malloc");
To take the first variable as an example, the result of the macro will be that s has
a value of type set which points to an array of length at least m. If DYNALLOC1 or
DYNALLOC2 is used again for the same variable, it is freed and allocated again only
if the new requested size is larger than the previous size. Otherwise the same space is
reused. This is intended to be more efficient that repeated unnecessary calls to malloc( )
and free( ). In case it is desired to free the object allocated by DYNALLOC1, use, for
example, DYNFREE(s,s sz). There is also CONDYNFREE that frees objects if they are
bigger than a given size.
In the case of g, we used DYNALLOC2 instead of DYNALLOC1. This is slightly
better as it covers the possibility that mn is too large for an int. We could also use
DYNALLOC1(graph,g,g sz,m*(size t)n,"malloc");
The last parameter of DYNALLOC1 and DYNALLOC2 is a string used in an error
message in the event that the allocation fails.
nauty.h also defines a number of macros that are useful for programming with the
40
nauty data structures. Some of the more useful macros are as follows.
SETWORDSNEEDED(n) : the least number of setwords needed for n bits.
ADDELEMENT(s,i) : add element i to set s.
DELELEMENT(s,i) : delete element i from set s.
FLIPELEMENT(s,i) : delete element i from set s if it is present, or insert it if it is
absent.
ISELEMENT(s,i) : test if i is an element of the set s (0 i n1).
EMPTYSET(s,m) : make the set s equal to the empty set.
POPCOUNT(x) : the number of 1-bits in the setword x.
Use ((x)?POPCOUNT(x):0) in circumstances where x is most often zero. Note that
the fastest version of this macro may need a compiler switch: see the value of
CFLAGS in makefile after configuration.
FIRSTBIT(x) : the position (0 to WORDSIZE 1) of the first (least-numbered) 1-bit in
the setword x, or WORDSIZE if there is none.
FIRSTBITNZ(x) : This is a faster version of FIRSTBIT, but it assumes that x 6= 0.
TAKEBIT(i,x) : If the setword x is not 0, set i to the position (0 to WORDSIZE 1)
of the first (least-numbered) 1-bit in x, and remove that bit from x.
ALLBITS : A setword constant with the first WORDSIZE bits set (this is usually all
the bits).
BITMASK(i) : A setword constant with the first i + 1 bits unset and the other
WORDSIZE i 1 numbered bits set, for 0 i < WORDSIZE. Thus, ANDing a
setword with BITMASK(i) deletes bits 0..i.
ALLMASK(i) : A setword constant with the first i bits set and all other bits unset, for
0 i WORDSIZE.
GRAPHROW(g,v,m) : The address of the row of graph g corresponding to the neighbours
of vertex v.
EMPTYGRAPH(g,m,n) : Makes a graph empty (i.e., no edges).
ADDONEARC(g,v,w,m) : Add one directed edge to a graph.
ADDONEEDGE(g,v,w,m) : Add one undirected edge to a graph.
SETWORD SHORT, SETWORD INT, SETWORD LONG, SETWORD LONGLONG :
Exactly one of these is defined, according to which unsigned integer type is the same
as setword.
SETWORDSNEEDED(n) : Calculates dn/WORDSIZEe, the number of setwords needed
to hold a subset of {0, 1, . . . , n1}.
SETWORD FORMAT : A string suitable for writing a value of type setword with
fprintf. For example it might be "%08lx".
41
Some of the procedures in nautil.c or naugraph.c may be useful. They are declared
in nauty.h. See the source code for the parameter list and semantics of these:
nextelement : find the position of the next element in a set following a specified position.
The recommended way to do something for each element of the set s is like this:
for (i = -1; (i = nextelement(s,m,i)) >= 0;)
{Process element i }
If you just want to do something for each bit in a setword x, it is more efficient to
do it like this:
tmp = x;
while (tmp)
{
TAKEBIT(i,tmp);
Process element i;
}
permset : apply a permutation to a set.
orbjoin : update the orbits of a group according to a new generator.
writeperm : write a permutation to a file.
isautom : test if a permutation is an automorphism.
updatecan : (for samerows = 0) relabel a graph.
refine : find coarsest equitable partition not coarser than given partition.
refine1 : produces exactly the same results as refine, but assumes m = 1 for greater
speed.
The file naututil.c contains procedures which are used by the dreadnaut program
(see Section 2). Many of these are also useful to programs which call nauty. If your
program uses them, include naututil.h as well as nauty.h.
Some of the more useful procedures are:
setsize : find cardinality of set.
setinter : find cardinality of intersection of two sets.
putset : write a set to a file.
putgraph : write a graph to a file.
putorbits : write a set of orbits to a file.
putptn : write a partition to a file.
readgraph : read a graph from a file.
readptn : read a partition from a file.
ranperm : generate a random permutation.
rangraph : generate a random graph.
42
43
11.1
int
main(int argc, char *argv[])
{
graph g[MAXN*MAXM];
int lab[MAXN],ptn[MAXN],orbits[MAXN];
static DEFAULTOPTIONS_GRAPH(options);
statsblk stats;
int n,m,v;
/* Default options are set by the DEFAULTOPTIONS_GRAPH macro above.
Here we change those options that we want to be different from the
defaults. writeautoms=TRUE causes automorphisms to be written.
*/
options.writeautoms = TRUE;
while (1)
{
printf("\nenter n : ");
if (scanf("%d",&n) != 1 || n <= 0)
break;
if (n > MAXN)
{
printf("n must be in the range 1..%d\n",MAXN);
exit(1);
}
/* The nauty parameter m is a value such that an array of
m setwords is sufficient to hold n bits. The type setword
is defined in nauty.h. The number of bits in a setword is
WORDSIZE, which is 16, 32 or 64. Here we calculate
m = ceiling(n/WORDSIZE).
*/
m = SETWORDSNEEDED(n);
44
ADDONEEDGE(g,v,(v+1)%n,m);
45
11.2
46
47
11.3
9
5
2
9
4
6
0
48
printf("\nenter n : ");
if (scanf("%d",&n) == 1 && n > 0)
{
if (n%2 != 0)
{
fprintf(stderr,"Sorry, n must be even\n");
continue;
}
m = SETWORDSNEEDED(n);
nauty_check(WORDSIZE,m,n,NAUTYVERSIONID);
DYNALLOC1(int,lab1,lab1_sz,n,"malloc");
DYNALLOC1(int,lab2,lab2_sz,n,"malloc");
DYNALLOC1(int,ptn,ptn_sz,n,"malloc");
DYNALLOC1(int,orbits,orbits_sz,n,"malloc");
DYNALLOC1(int,map,map_sz,n,"malloc");
/* Now make the first graph */
DYNALLOC2(graph,g1,g1_sz,n,m,"malloc");
EMPTYGRAPH(g1,m,n);
for (i = 0; i < n; i += 2)
ADDONEEDGE(g1,i,i+1,m);
/* Spokes */
49
densenauty(g2,lab2,ptn,orbits,&options,&stats,m,n,cg2);
/* Compare canonically labelled graphs */
for (k = 0; k < m*(size_t)n; ++k)
if (cg1[k] != cg2[k]) break;
if (k == m*(size_t)n)
{
printf("Isomorphic.\n");
if (n <= 1000)
{
/* Write the isomorphism. For each i, vertex lab1[i]
of sg1 maps onto vertex lab2[i] of sg2. We compute
the map in order of labelling because it looks better. */
for (i = 0; i < n; ++i) map[lab1[i]] = lab2[i];
for (i = 0; i < n; ++i) printf(" %d-%d",i,map[i]);
printf("\n");
}
}
else
printf("Not isomorphic.\n");
}
else
break;
}
exit(0);
}
12
The basic data structure for sparse representation is the structure sparsegraph defined in Section 3. Programs using it should include nausparse.h and link with the
file nausparse.c.
As described in Section 3, the sparse representation of a graph uses a structure of type
sparsegraph with the following fields:
int nv: the number of vertices
int nde: the number of directed edges (loops count as 1)
int v: pointer to an array of length at least nv
int d: pointer to an array of length at least nv
int e: pointer to an array of length at least nde
50
51
12.1
int
main(int argc, char *argv[])
{
DYNALLSTAT(int,lab,lab_sz);
DYNALLSTAT(int,ptn,ptn_sz);
DYNALLSTAT(int,orbits,orbits_sz);
static DEFAULTOPTIONS_SPARSEGRAPH(options);
statsblk stats;
sparsegraph sg;
/* Declare sparse graph structure */
int n,m,i;
options.writeautoms = TRUE;
/* Initialise sparse graph structure. */
SG_INIT(sg);
while (1)
{
printf("\nenter n : ");
if (scanf("%d",&n) == 1 && n > 0)
{
m = SETWORDSNEEDED(n);
nauty_check(WORDSIZE,m,n,NAUTYVERSIONID);
DYNALLOC1(int,lab,lab_sz,n,"malloc");
DYNALLOC1(int,ptn,ptn_sz,n,"malloc");
DYNALLOC1(int,orbits,orbits_sz,n,"malloc");
/* SG_ALLOC makes sure that the v,d,e fields of a sparse graph
structure point to arrays that are large enough. This only
works if the structure has been initialised. */
SG_ALLOC(sg,n,2*n,"malloc");
sg.nv = n;
sg.nde = 2*n;
/* Number of vertices */
/* Number of directed edges */
52
/* edge i->i-1 */
/* edge i->i+1 */
53
12.2
int
main(int argc, char *argv[])
{
DYNALLSTAT(int,lab1,lab1_sz);
DYNALLSTAT(int,lab2,lab2_sz);
DYNALLSTAT(int,ptn,ptn_sz);
DYNALLSTAT(int,orbits,orbits_sz);
DYNALLSTAT(int,map,map_sz);
static DEFAULTOPTIONS_SPARSEGRAPH(options);
statsblk stats;
/* Declare and initialize sparse graph structures */
SG_DECL(sg1); SG_DECL(sg2);
SG_DECL(cg1); SG_DECL(cg2);
int n,m,i;
/* Select option for canonical labelling */
options.getcanon = TRUE;
/* Read the number of vertices and process it */
while (1)
{
printf("\nenter n : ");
if (scanf("%d",&n) == 1 && n > 0)
{
if (n%2 != 0)
{
fprintf(stderr,"Sorry, n must be even\n");
continue;
}
m = SETWORDSNEEDED(n);
nauty_check(WORDSIZE,m,n,NAUTYVERSIONID);
DYNALLOC1(int,lab1,lab1_sz,n,"malloc");
DYNALLOC1(int,lab2,lab2_sz,n,"malloc");
DYNALLOC1(int,ptn,ptn_sz,n,"malloc");
54
DYNALLOC1(int,orbits,orbits_sz,n,"malloc");
DYNALLOC1(int,map,map_sz,n,"malloc");
/* Now make the first graph */
SG_ALLOC(sg1,n,3*n,"malloc");
sg1.nv = n;
/* Number of vertices */
sg1.nde = 3*n;
/* Number of directed edges */
for (i = 0; i < n; ++i)
{
sg1.v[i] = 3*i;
/* Position of vertex i in v array */
sg1.d[i] = 3;
/* Degree of vertex i */
}
for (i = 0; i < n; i += 2)
{
sg1.e[sg1.v[i]] = i+1;
sg1.e[sg1.v[i+1]] = i;
}
/* Spokes */
55
/* Clockwise */
sg2.e[sg2.v[i]+1] = (i+n-1) % n;
sg2.e[sg2.v[i]+2] = (i+n/2) % n;
/* Anti-clockwise */
/* Diagonals */
}
/* Label sg1, result in cg1 and labelling in lab1; similarly sg2.
It is not necessary to pre-allocate space in cg1 and cg2, but
they have to be initialised as we did above. */
sparsenauty(&sg1,lab1,ptn,orbits,&options,&stats,&cg1);
sparsenauty(&sg2,lab2,ptn,orbits,&options,&stats,&cg2);
/* Compare canonically labelled graphs */
if (aresame_sg(&cg1,&cg2))
{
printf("Isomorphic.\n");
if (n <= 1000)
{
/* Write the isomorphism. For each i, vertex lab1[i]
of sg1 maps onto vertex lab2[i] of sg2. We compute
the map in order of labelling because it looks better. */
for (i = 0; i < n; ++i) map[lab1[i]] = lab2[i];
for (i = 0; i < n; ++i) printf(" %d-%d",i,map[i]);
printf("\n");
}
}
else
printf("Not isomorphic.\n");
}
else
break;
}
exit(0);
}
13
Traces uses the same data structures for graphs, partitions, permutations and orbits as
sparse nauty, so the functions for manipulating sparse graphs can be used unchanged.
Here we give the previous program again, using Traces.
56
13.1
57
DYNALLOC1(int,orbits,orbits_sz,n,"malloc");
DYNALLOC1(int,map,map_sz,n,"malloc");
/* Now make the first graph */
SG_ALLOC(sg1,n,3*n,"malloc");
sg1.nv = n;
/* Number of vertices */
sg1.nde = 3*n;
/* Number of directed edges */
for (i = 0; i < n; ++i)
{
sg1.v[i] = 3*i;
/* Position of vertex i in v array */
sg1.d[i] = 3;
/* Degree of vertex i */
}
for (i = 0; i < n; i += 2)
{
sg1.e[sg1.v[i]] = i+1;
sg1.e[sg1.v[i+1]] = i;
}
/* Spokes */
58
/* Clockwise */
sg2.e[sg2.v[i]+1] = (i+n-1) % n;
sg2.e[sg2.v[i]+2] = (i+n/2) % n;
/* Anti-clockwise */
/* Diagonals */
}
/* Label sg1, result in cg1 and labelling in lab1; similarly sg2.
It is not necessary to pre-allocate space in cg1 and cg2, but
they have to be initialised as we did above. */
Traces(&sg1,lab1,ptn,orbits,&options,&stats,&cg1);
Traces(&sg2,lab2,ptn,orbits,&options,&stats,&cg2);
/* Compare canonically labelled graphs */
if (aresame_sg(&cg1,&cg2))
{
printf("Isomorphic.\n");
if (n <= 1000)
{
/* Write the isomorphism. For each i, vertex lab1[i]
of sg1 maps onto vertex lab2[i] of sg2. We compute
the map in order of labelling because it looks better. */
for (i = 0; i < n; ++i) map[lab1[i]] = lab2[i];
for (i = 0; i < n; ++i) printf(" %d-%d",i,map[i]);
printf("\n");
}
}
else
printf("Not isomorphic.\n");
}
else
break;
}
exit(0);
}
59
14
Variations
As mentioned, nauty and Traces can handle graphs with coloured vertices. In this
section, we describe how several other types of isomorphism problem can be solved by
mapping them onto a problem for vertex-coloured graphs. (But recall that Traces cant
handle directed edges.)
Isomorphism of edge-coloured graphs. Isomorphism of two graphs, each with both
vertices and edges coloured, is defined in the obvious way. An example of such a graph
appears at the left of Figure 3.
1
3
0
2
2
1
0
0
0
0
3
0
1
0
0
2
0
graph has O(n log k) vertices. This can be improved to O(n log k) vertices by also using
edges that are not horizontal, but this needs care.
Exchangeable vertex colours. The vertex colours known to nauty and Traces are
distinguishable: vertices can only be mapped onto vertices of the same colour. In some
applications, entire colour classes can also be exchanged.
In the left side of Figure 4 is a graph with three exchangeable vertex colours. To
process this problem with nauty or Traces, we recolour the vertices to be all the same,
then indicate the original colour classes with additional vertices of a new colour, as in the
graph on the right. It is easy to see how this idea can be extended to allow some colours
to be exchangeable and some not.
60
1
1
1 1 0 0 0
0 1 1 0 0
0 0 1 1 1
2
3
4
1 1
1 1
1 1
w1
w2
w3
w10
w20
w30
v1
v2
v3
v10
v20
v30
1
1
1
Isotopy of matrices. Two matrices over some symbol set S are called isotopic if one
can be obtained from the other by permuting the rows, permuting the columns, and
permuting the symbols. This equivalence relation is important in the study of Latin
squares, quasigroups, and other subjects.
c1
c2
c3
r1
r2
1 3 2
2 1 3
3 2 1
r3
s3
s2
s1
62
entry. Other related equivalences, such as paratopy (main class isotopy) can be handled
in similar fashion [5].
15
Utilities
The nauty package includes a suite of programs called gtools that provide efficient
processing of files of graphs stored in graph6 or sparse6 format. These formats are
defined in Section 19.
Most of the gtools programs will run on any system with a modern C compiler, but
a few need Unix-like facilities. For example, the program shortg requires a program
compatible with the Unix sort program, as well as pipes.
A general principle is that data is sent to stdout (unless an alternative output file is
named) and diagnostic output is sent to stderr.
All the gtools programs are self-documenting: just execute with the option --help
to see an explanation of all the features. We only list the basic functions of the programs
here; see Section 23 for more details.
geng : generate small graphs
genbg : generate small bicoloured graphs
gentourng : generate small tournaments
gentreeg : generate trees
directg : generate small digraphs with given underlying graph
watercluster2 : a faster alternative to directg written by Gunnar Brinkmann
multig : generate small multigraphs with given underlying graph
vcolg : colour the vertices of graphs in all distinct ways
genrang : generate random graphs
genquartic : generate quartic graphs
genspecialg : generate special graphs, like cycles and complete graphs
copyg : convert format and select subset
labelg : canonically label graphs
shortg : remove isomorphs from a file of graphs
listg : display graphs in a variety of forms
showg : a stand-alone subset of listg
amtog : read graphs in adjacency matrix form
dretog : read graphs in dreadnaut form
complg : complement graphs
converseg : converse digraphs
63
16
nauty is prepared using the autoconf configuration system. You need a C compiler and
library at least recent enough to support the basic commands of ANSI C.
If you have a Unix-like operating system, which includes Linux, MacOSX or a Windows environment such as cygwin, first run the configuration script like this:
./configure
That will examine the facilities available on your computer and generate a custom makefile
and custom definition files nauty.h, gtools.h and naututil.h. Then run
make
to compile the basic files of nauty and Traces.
This will generate the executable file dreadnaut and a lot of object files and libraries.
For compiling your own programs, the most convenient way to link with nauty or Traces
is to use the static library nauty.a. For example, with the gcc compiler, a simple compilation might be
gcc -o myprog myprog.c nauty.a
64
Execution of make without arguments will also generate the library nauty1.a, which is
the same except that MAXN is defined to be equal to WORDSIZE. This is more efficient
if you only want to process small graphs.
For both nauty.a and nauty1.a, WORDSIZE is determined automatically as specified
in Section 3. However, there may be times when you want to use a particular word size,
so you can also make the following variants by, for example, make nautyL1.a :
nautyW.a: WORDSIZE = 32, unlimited MAXN
nautyW1.a: WORDSIZE = MAXN = 32
nautyL.a: WORDSIZE = 64, unlimited MAXN
nautyL1.a: WORDSIZE = MAXN=64
The last two variants can only be used if your compiler supports either unsigned
long or unsigned long long type as a 64-bit integer.
To use these versions, you need to compile your program in the same way. For example:
gcc -o myprog -DWORDSIZE=64 -DMAXN=WORDSIZE myprog.c nautyL1.a
Using the function nauty check( ) in your program is a way to check that you compiled it correctly; see Section 11.
There are some test files included in the package. To run these, just use
make checks
followed by
./runalltests
which should not produce any output that looks like an error message.
If you are on a system where configure doesnt work or make is not available,
you should start by editing the definitions near the start of nauty.h, naututil.h and
gtools.h. (Most should be OK already.) Then you can compile using the commands in
makefile.basic as a guide.
Some compilation options are provided at the configure stage. Run
./configure --help to see the syntax and more information.
(a) The choice of compiler, compiler options, linker options, libraries and include files
can be influenced by the environment variables CC, CFLAGS, LDFLAGS, LIBS and
CPPFLAGS. For example, to compile a 32-bit program on a 64-bit Intel system, this
might work (assuming a Bourne-type shell such as bash):
CFLAGS=-m32 LDFLAGS=-m32 ./configure
(Note that the hardware 32/64-bit mode is entirely independent of the value of
WORDSIZE.)
(b) Section 3 describes how WORDSIZE is chosen when no explicit definition of it appears. To override this default rule, you can use
./configure --enable-wordsize=NN
where NN is 16, 32 or 64. However, an explicit definition of WORDSIZE at compile
time takes precedence even over this.
65
(c) Generally nauty and Traces are not thread-safe. However, if your compiler supports
thread-local storage, configuring with
./configure --enable-tls
will mark static and external variables as thread-local. (The most common syntax is
to add the attribute thread.) This means that nauty or Traces can be invoked
at the same time by different threads. This may slow it down slightly if you arent
using threads. There are sample programs nauthread1.c and nauthread2.c in the
distribution.
(d) Some C compilers and processors support commands like builtin clz( ) that locate the first 1-bit in a word faster than nautys standard macros can do it. These are
detected automatically and influence the definition of the FIRSTBIT and TAKEBIT
macros. However, if this causes trouble for some reason, you can turn off this feature
using
./configure --disable-clz
(e) Many modern processors have an instruction POPCNT that counts 1-bits. It will
be used to define the macro POPCOUNT if possible. To disable this feauture, use
./configure --disable-popcnt
(f) The output written by Traces and some of the output written by dreadnaut will
look prettier on terminals that support ANSI control sequences if you use
./configure --enable-ansicontrols
Dont use this if you plan to read dreadnaut output with a program.
(g) If your operating system allows it, dreadnaut establishes a signal handler for catching
SIGINT when nauty or Traces is running. To disable this feature, use
./configure --disable-interrupt
This only effects dreadnaut.
17
Recent changes
See the file changes24-26.txt for a list of changes made since version 2.4.
18
nauty and Traces use the Random Schreier Method to process the automorphism group
as it is found. For nauty, this is optional: see the field schreier of the options. Difficult
graphs with substantial automorphism groups will benefit the most from this addition.
The Random Schreier Method is a probabilistic algorithm that determines information
about the group only with some probability. However, this nondeterminism does not effect
the result of nauty or Traces. Occasionally a different set of generators will be found,
but the group generated will be the same and the canonical labelling will be unaffected.
There is a parameter for tuning the method, which can be changed using the function
schreier fails( ). See the file schreier.txt for documentation of this and the other
66
group functions. The default value is 10, but smaller values may be better if the group is
very large. In dreadnaut, the G command sets this parameter.
18.1
The automorphism group of a graph can be exceedingly large, so trying to list it all
might be a bad idea. However, it can be done using the code in the files naugroup.h and
naugroup.c, as illustrated by the following program.
This works with sparse nauty as well, but not with Traces.
67
18.2
68
nauty_check(WORDSIZE,m,n,NAUTYVERSIONID);
DYNALLOC2(graph,g,g_sz,m,n,"malloc");
DYNALLOC1(int,lab,lab_sz,n,"malloc");
DYNALLOC1(int,ptn,ptn_sz,n,"malloc");
DYNALLOC1(int,orbits,orbits_sz,n,"malloc");
EMPTYGRAPH(g,m,n);
for (v = 0; v < n; ++v) ADDONEEDGE(g,v,(v+1)%n,m);
printf("Automorphisms of C[%d]:\n",n);
densenauty(g,lab,ptn,orbits,&options,&stats,m,n,NULL);
/* Get a pointer to the structure in which the group information
has been stored. If you use TRUE as an argument, the
structure will be "cut loose" so that it wont be used
again the next time nauty() is called. Otherwise, as
here, the same structure is used repeatedly. */
group = groupptr(FALSE);
/* Expand the group structure to include a full set of coset
representatives at every level. This step is necessary
if allgroup() is to be called. */
makecosetreps(group);
/* Call the procedure writeautom() for every element of the group.
The first call is always for the identity. */
allgroup(group,writeautom);
}
else
break;
}
exit(0);
}
18.3
Traces provides the possibility of giving it known automorphisms. This is only likely to
be useful for very regular graphs that have automorphisms which are difficult to discover.
The method is illustrated in the following sample program.
69
18.4
70
DYNALLOC1(int,p,p_sz,n,"malloc");
DYNALLOC1(boolean,issquare,issquare_sz,n,"malloc");
/* Initialise list of automorphisms */
gens = NULL;
/* Find the squares and the degree */
for (i = 0; i < n; ++i) issquare[i] = FALSE;
for (i = 0; i < n; ++i) issquare[(i*i)%n] = TRUE;
if (!issquare[n-1])
{
printf("-1 must be a square mod n; try again\n");
continue;
}
deg = 0;
for (i = 1; i < n; ++i) if (issquare[i]) ++deg;
/* Now make the graph */
SG_ALLOC(sg,n,n*deg,"malloc");
sg.nv = n;
/* Number of vertices */
sg.nde = n*deg;
/* Number of directed edges */
for (i = 0; i < n; ++i)
{
sg.v[i] = i*deg;
sg.d[i] = deg;
}
71
72
18.5
Traces uses a different tree-traversal method when it only wants to compute the automorphism group only, without canonical labelling. For some types of difficult graphs, this
can be much faster than canonical labelling.
Since Traces can also make use of known automorphisms, it is sometimes faster to
compute the group first and then use the group for computing the canonical labelling. In
dreadnaut this is achieved with the sequence P -c x c x. The following illustrates how
to achieve the same in a program.
An extension (not shown) would be to use the orbit sizes from the first computation
to partition the graph before the second computation.
73
if (n%2 != 0)
{
fprintf(stderr,"Sorry, n must be even\n");
continue;
}
m = SETWORDSNEEDED(n);
nauty_check(WORDSIZE,m,n,NAUTYVERSIONID);
DYNALLOC1(int,lab1,lab1_sz,n,"malloc");
DYNALLOC1(int,lab2,lab2_sz,n,"malloc");
DYNALLOC1(int,ptn,ptn_sz,n,"malloc");
DYNALLOC1(int,orbits,orbits_sz,n,"malloc");
DYNALLOC1(int,map,map_sz,n,"malloc");
/* Now make the first graph */
SG_ALLOC(sg1,n,3*n,"malloc");
sg1.nv = n;
/* Number of vertices */
sg1.nde = 3*n;
/* Number of directed edges */
for (i = 0; i < n; ++i)
{
sg1.v[i] = 3*i;
/* Position of vertex i in v array */
sg1.d[i] = 3;
/* Degree of vertex i */
}
for (i = 0; i < n; i += 2)
{
sg1.e[sg1.v[i]] = i+1;
sg1.e[sg1.v[i+1]] = i;
}
/* Spokes */
74
/* Clockwise */
/* Anti-clockwise */
/* Diagonals */
75
printf("Isomorphic.\n");
if (n <= 1000)
{
/* Write the isomorphism. For each i, vertex lab1[i]
of sg1 maps onto vertex lab2[i] of sg2. We compute
the map in order of labelling because it looks better. */
for (i = 0; i < n; ++i) map[lab1[i]] = lab2[i];
for (i = 0; i < n; ++i) printf(" %d-%d",i,map[i]);
printf("\n");
}
}
else
printf("Not isomorphic.\n");
}
else
break;
}
exit(0);
}
76
19
77
N(30) = 93
N(12345) = N(000011 000000 111001) = 126 66 63 120
N(460175067) = N(000000 011011 011011 011011 011011 011011)
= 126 126 63 90 90 90 90 90
N(n) R(x).
Example:
Suppose n=5 and G has edges 0-2, 0-4, 1-3 and 3-4.
x = 0 10 010 1001
Then N(n) = 68 and R(x) = R(010010 100100) = 81 99.
So, the graph is 68 81 99.
78
79
v = 0
for i from 0 to m do
if b[i] = 1 then v = v+1 endif;
if x[i] > v then v = x[i] else output {x[i],v} endif
endfor
In decoding, an incomplete (b,x) pair at the end is discarded.
Example:
:Fa@x^
: indicates sparse6 format.
Subtract 63 from the other bytes and write them in binary,
six bits each.
000111 100010 000001 111001 011111
The first byte is not 63, so it is n. n=7
n-1 needs 3 bits (k=3). Write the other bits in groups
of 1 and k:
1 000
1 000
0 001
1 110
0 101
1 111
This is the b/x sequence 1,0 1,0 0,1 1,6 0,5 1,7.
The 1,7 at the end is just padding.
The remaining parts give the edges 0-1 0-2 1-2 5-6.
80
63
65
79
63.
For a description of the planarcode and edgecode formats, see the plantri documentation at http://cs.anu.edu.au/bdm/plantri.
20
If you want to use nauty in a richer interactive environment, some of your choices are:
(a) Magma: http://magma.maths.usyd.edu.au/magma
(b) GAP with GRAPE: http://www.maths.qmul.ac.uk/leonard/grape/
(c) LINK: http://dimacs.rutgers.edu/berryj/LINK.html
(d) Sage-combinat: http://mupad-combinat.sourceforge.net/
(e) Macaulay2: http://mupad-combinat.sourceforge.net/
81
21
Licence details
For the copyright status of items in the package, see the file COPYRIGHT. They are free
for use with very limited restrictions.
If you use nauty or Traces in your published research, please cite our paper [10].
22
Acknowledgements
So many people have made contributions to nauty that listing them all would be futile.
Bill Kocay, Kevin Malysiak, Andrew Kirk and Gordon Royle stand out, but many people
have made large and small contributions. Some code is derived from other peoples programs: thanks to Frank Ruskey in regard to gentreeg and to Narjess Afzaly in regard to
genquarticg. Heart-felt thanks to all of you.
The authors would appreciate receiving any comments about the program and/or this
Guide, especially about apparent bugs.
82
23
83
-v
Summarize to stderr.
-L
-l
-q
84
The output file has a header if and only if the input file does.
Undirected graphs are passed through without change, while
directed graphs are written in digraph6 format.
-q
85
number of vertices
-e# number of edges
number of loops
-C
strongly connected
minimum (out-)degree
-D# maximum (out-)degree
vertices of min (out-)degree -M# vertices of max (out-)degree
minimum (in-)degree
-U# maximum (in-)degree
vertices of min (out-)degree -S# vertices of max (out-)degree
regular
-b
bipartite
radius
-Z# diameter
girth (0=acyclic)
-Y# total number of cycles
number of triangles
-K# number of maximal cliques
smallest side of some bipartition (0 if none)
number of induced cycles
Eulerian (all degrees are even, connectivity not required)
group size -o# orbits -F# fixed points -t vertex-transitive
connectivity (only implemented for 0,1,2).
min common nbrs of adjacent vertices;
-I# maximum
min common nbrs of non-adjacent vertices; -J# maximum
Sort keys:
Counts are made for all graphs passing the constraints. Counts
are given separately for each combination of values occurring for
the properties listed as sort keys. A sort key is introduced by
-- and uses one of the letters known as constraints. These can
be combined: --n --e --r is the same as --ne --r and --ner.
The order of sort keys is significant.
The output format matches the input, except that sparse6 is used
to output an incremental graph whose predecessor is not output.
===== cubhamg ======================================================
>E Usage: cubhamg [-#] [-v | -V] [-n#-#] [-y#-#] [infile [outfile]]
86
87
-s#/#
-q
88
-s
-h
-q
89
-u
-v
-l
-q
:
:
:
:
:
-m
-d#
-D#
-v
-l
:
:
:
:
-u
-g
-s
-y
-h
:
:
:
:
:
-q
90
:
:
:
:
:
:
:
Usage: genspecialg
[-s|-g|-z|-d] [-q] [-p#|-c#|-e#|-k#|-b#,#|-Q#|-f#|-J#,#|-P#,#|C#,#...|G#,#...|T#,#...] [outfile
91
:
:
:
:
:
:
:
:
:
-u
: do not output any graphs, just generate and count them
-g
: use graph6 output (lower triangle)
-s
: use sparse6 output (lower triangle)
-z
: use digraph6 output
-h
: write a header (only with -g or -s)
Default output is upper triangle row-by-row in ascii
-q
92
:
:
:
:
-q
-u
The output file will have a header if and only if the input file does.
-p
-v
-L#
-t#
-q
93
-S
-t
94
95
96
-q
number of vertices
-e# number of edges
number of loops
-C
strongly connected
minimum (out-)degree
-D# maximum (out-)degree
vertices of min (out-)degree -M# vertices of max (out-)degree
minimum (in-)degree
-U# maximum (in-)degree
vertices of min (out-)degree -S# vertices of max (out-)degree
regular
-b
bipartite
radius
-Z# diameter
girth (0=acyclic)
-Y# total number of cycles
number of triangles
-K# number of maximal cliques
smallest side of some bipartition (0 if none)
number of induced cycles
Eulerian (all degrees are even, connectivity not required)
group size -o# orbits -F# fixed points -t vertex-transitive
connectivity (only implemented for 0,1,2).
min common nbrs of adjacent vertices;
-I# maximum
min common nbrs of non-adjacent vertices; -J# maximum
97
Sort keys:
Counts are made for all graphs passing the constraints. Counts
are given separately for each combination of values occurring for
the properties listed as sort keys. A sort key is introduced by
-- and uses one of the letters known as constraints. These can
be combined: --n --e --r is the same as --ne --r and --ner.
The order of sort keys is significant.
The output format matches the input, except that sparse6 is used
to output an incremental graph whose predecessor is not output.
===== planarg ======================================================
Usage: planarg [-v] [-nVq] [-p|-u] [infile [outfile]]
For each input, write to output if planar.
The output file has a header if and only if the input file does.
-v
-V
-u
-p
-k
-n
-q
98
-S
-t
-k
output graphs have the same labelling and format as the inputs.
Without -k, output graphs have canonical labelling.
-s, -g, -z are ineffective if -k is given.
-v
-d
99
(2) if two graphs are labelled using the same string xxx,
the output graphs are identical iff there is an
associated-character-preserving isomorphism between them.
-i# select an invariant (1 = twopaths, 2 = adjtriang(K), 3 = triples,
4 = quadruples, 5 = celltrips, 6 = cellquads, 7 = cellquins,
8 = distances(K), 9 = indsets(K), 10 = cliques(K), 11 = cellcliq(K),
12 = cellind(K), 13 = adjacencies, 14 = cellfano, 15 = cellfano2,
16 = refinvar(K))
-I#:# select mininvarlevel and maxinvarlevel (default 1:1)
-K#
select invararg (default 3)
-u
Write no output, just report how many graphs it would have output.
In this case, outfile is not permitted.
-Tdir Specify that directory "dir" will be used for temporary disk
space by the sort subprocess. The default is usually /tmp.
-q Suppress auxiliary output
:
:
:
:
:
-o#
-t
-F
-l#
:
:
:
:
-q
100
The output file will have a header if and only if the input file does.
-p
Read a cubic graph and use its prism. Vertex i of the input becomes
vertices 2*i,2*i+1 in the prism.
-x Test for decompositions using each 2-path
-X As -x but only output if two 2-paths are missed at some vertex
-y Test for decompositions using each non-triangular 3-path
-t# With -x and -X, consider only paths with center #
With -y, consider only paths starting at #
-Y With -p, only consider paths whose central edge is vertical
-v Give a partition for those graphs who have one and a message
for those which dont. With -x, list exceptional 2-paths.
-L# Limit to 1000*# iterations; write with message if timeout.
Graphs that time out are written to the output.
-q
101
-e#
-m#
-f#
-T
-u
-q
102
References
[1] J. M. Boyer and W. J. Myrvold, On the cutting edge: simplified O(n) planarity by
edge addition, J. Graph Alg. Appl., 8 (2004) 241273.
[2] P. T. Darga, M. H. Liffiton, K. A. Sakallah, and I. L. Markov, Exploiting Structure in
Symmetry Generation for CNF, Proceedings of the 41st Design Automation Conference, 2004, 530534. Source code at http://vlsicad.eecs.umich.edu/BK/SAUCY.
[3] B. D. McKay, Hadamard equivalence via graph isomorphism, Discrete Math., 27
(1979) 213214.
[4] A. Kirk, Efficiency considerations in the canonical labelling of graphs, Technical
report TR-CS-85-05, Computer Science Department, Australian National University
(1985).
[5] B. D. McKay, A. Meynert and W. Myrvold, Small Latin squares, quasigroups and
loops, J. Combin. Designs, to appear.
[6] T. Miyazaki, The complexity of McKays canonical labelling algorithm, in Groups
and Computation, II, DIMACS Ser. Discrete Math. Theoret. Comput. Sci., 28, Amer.
Math. Soc. (1997) 239256.
[7] K. E. Malysiak, Graph Isomorphism, Canonical Labelling and Invariants, Honours
Thesis, Computer Science Department, Australian National University (1987).
[8] R. Mathon, Sample graphs for isomorphism testing, Congressus Numerantium, 21
(1978) 499517.
[9] B. D. McKay, Practical graph isomorphism, Congressus Numerantium, 30 (1981)
4587. Available at http://cs.anu.edu.au/bdm/nauty/PGI.
[10] B. D. McKay and Adolfo Piperno, Practical graph isomorphism II, J. Symbolic Comput., 60 (2014) 94112.
103