1 /* labelg.c version 2.0; B D McKay, Jun 2015 */
2
3 #define USAGE "labelg [-q] [-sgz | -C#W#] [-fxxx] [-S|-t] \n\
4 [-i# -I#:# -K#] [infile [outfile]]"
5
6 #define HELPTEXT \
7 " Canonically label a file of graphs or digraphs.\n\
8 \n\
9 -s force output to sparse6 format\n\
10 -g force output to graph6 format\n\
11 -z force output to digraph6 format\n\
12 If neither -s, -g or -z are given, the output format is\n\
13 determined by the header or, if there is none, by the\n\
14 format of the first input graph. As an exception, digraphs\n\
15 are always written in digraph6 format.\n\
16 -S Use sparse representation internally.\n\
17 Note that this changes the canonical labelling.\n\
18 Multiple edges are not supported. One loop per vertex is ok.\n\
19 -t Use Traces.\n\
20 Note that this changes the canonical labelling.\n\
21 Multiple edges and loops are not supported, nor invariants.\n\
22 \n\
23 -C# Make an invariant in 0..#-1 and output the number of graphs\n\
24 with each value of the invariant. Don't write graphs unless\n\
25 -W too.\n\
26 -W# (requires -C) Output the graphs with this invariant value,\n\
27 in their original labelling. Don't write the table.\n\
28 \n\
29 The output file will have a header if and only if the input file does.\n\
30 \n\
31 -fxxx Specify a partition of the point set. xxx is any\n\
32 string of ASCII characters except nul. This string is\n\
33 considered extended to infinity on the right with the\n\
34 character 'z'. One character is associated with each point,\n\
35 in the order given. The labelling used obeys these rules:\n\
36 (1) the new order of the points is such that the associated\n\
37 characters are in ASCII ascending order\n\
38 (2) if two graphs are labelled using the same string xxx,\n\
39 the output graphs are identical iff there is an\n\
40 associated-character-preserving isomorphism between them.\n\
41 No option can be concatenated to the right of -f.\n\
42 \n\
43 -i# select an invariant (1 = twopaths, 2 = adjtriang(K), 3 = triples,\n\
44 4 = quadruples, 5 = celltrips, 6 = cellquads, 7 = cellquins,\n\
45 8 = distances(K), 9 = indsets(K), 10 = cliques(K), 11 = cellcliq(K),\n\
46 12 = cellind(K), 13 = adjacencies, 14 = cellfano, 15 = cellfano2,\n\
47 16 = refinvar(K))\n\
48 -I#:# select mininvarlevel and maxinvarlevel (default 1:1)\n\
49 -K# select invararg (default 3)\n\
50 \n\
51 -q suppress auxiliary information\n"
52
53
54 /*************************************************************************/
55
56 #include "gtools.h"
57 #include "nautinv.h"
58 #include "gutils.h"
59 #include "traces.h"
60
61 static struct invarrec
62 {
63 void (*entrypoint)(graph*,int*,int*,int,int,int,int*,
64 int,boolean,int,int);
65 void (*entrypoint_sg)(graph*,int*,int*,int,int,int,int*,
66 int,boolean,int,int);
67 char *name;
68 } invarproc[]
69 = {{NULL, NULL, "none"},
70 {twopaths, NULL, "twopaths"},
71 {adjtriang, NULL, "adjtriang"},
72 {triples, NULL, "triples"},
73 {quadruples, NULL, "quadruples"},
74 {celltrips, NULL, "celltrips"},
75 {cellquads, NULL, "cellquads"},
76 {cellquins, NULL, "cellquins"},
77 {distances, distances_sg, "distances"},
78 {indsets, NULL, "indsets"},
79 {cliques, NULL, "cliques"},
80 {cellcliq, NULL, "cellcliq"},
81 {cellind, NULL, "cellind"},
82 {adjacencies, adjacencies_sg, "adjacencies"},
83 {cellfano, NULL, "cellfano"},
84 {cellfano2, NULL, "cellfano2"},
85 {refinvar, NULL, "refinvar"}
86 };
87
88 #define NUMINVARS ((int)(sizeof(invarproc)/sizeof(struct invarrec)))
89
90 static nauty_counter orbtotal;
91 static double unorbtotal;
92 extern int gt_numorbits;
93
94 /**************************************************************************/
95
96 int
main(int argc,char * argv[])97 main(int argc, char *argv[])
98 {
99 graph *g;
100 sparsegraph sg,sh;
101 int m,n,codetype;
102 int argnum,j,outcode;
103 char *arg,sw,*fmt;
104 boolean badargs,digraph;
105 boolean sswitch,gswitch,qswitch,fswitch,Oswitch;
106 boolean iswitch,Iswitch,Kswitch,Mswitch,Sswitch;
107 boolean uswitch,tswitch,Cswitch,Wswitch,zswitch;
108 boolean dooutput;
109 int tabsize,outinvar;
110 int inv,mininvarlevel,maxinvarlevel,invararg;
111 long minil,maxil;
112 double t;
113 char *infilename,*outfilename;
114 FILE *infile,*outfile;
115 nauty_counter nin,nout;
116 int ii,secret,loops;
117 DEFAULTOPTIONS_TRACES(traces_opts);
118 TracesStats traces_stats;
119 #if MAXN
120 graph h[MAXN*MAXM];
121 int lab[MAXN],ptn[MAXN],orbits[MAXN];
122 #else
123 DYNALLSTAT(graph,h,h_sz);
124 DYNALLSTAT(int,lab,lab_sz);
125 DYNALLSTAT(int,ptn,ptn_sz);
126 DYNALLSTAT(int,orbits,orbits_sz);
127 #endif
128 DYNALLSTAT(nauty_counter,tab,tab_sz);
129
130 HELP; PUTVERSION;
131
132 nauty_check(WORDSIZE,1,1,NAUTYVERSIONID);
133
134 sswitch = gswitch = qswitch = FALSE;
135 fswitch = Oswitch = Mswitch = FALSE;
136 iswitch = Iswitch = Kswitch = FALSE;
137 uswitch = Sswitch = tswitch = FALSE;
138 zswitch = Cswitch = Wswitch = FALSE;
139 infilename = outfilename = NULL;
140 inv = 0;
141
142 argnum = 0;
143 badargs = FALSE;
144 for (j = 1; !badargs && j < argc; ++j)
145 {
146 arg = argv[j];
147 if (arg[0] == '-' && arg[1] != '\0')
148 {
149 ++arg;
150 while (*arg != '\0')
151 {
152 sw = *arg++;
153 SWBOOLEAN('s',sswitch)
154 else SWBOOLEAN('g',gswitch)
155 else SWBOOLEAN('z',zswitch)
156 else SWBOOLEAN('u',uswitch)
157 else SWBOOLEAN('q',qswitch)
158 else SWBOOLEAN('O',Oswitch)
159 else SWBOOLEAN('S',Sswitch)
160 else SWBOOLEAN('t',tswitch)
161 else SWINT('C',Cswitch,tabsize,"labelg -C")
162 else SWINT('W',Wswitch,outinvar,"labelg -W")
163 else SWINT('i',iswitch,inv,"labelg -i")
164 else SWINT('K',Kswitch,invararg,"labelg -K")
165 else SWRANGE('k',":-",Iswitch,minil,maxil,"labelg -k")
166 else SWRANGE('I',":-",Iswitch,minil,maxil,"labelg -I")
167 else if (sw == 'f')
168 {
169 fswitch = TRUE;
170 fmt = arg;
171 break;
172 }
173 else SWINT('M',Mswitch,secret,"labelg -M")
174 else badargs = TRUE;
175 }
176 }
177 else
178 {
179 ++argnum;
180 if (argnum == 1) infilename = arg;
181 else if (argnum == 2) outfilename = arg;
182 else badargs = TRUE;
183 }
184 }
185
186 if ((sswitch!=0) + (zswitch!=0) + (gswitch!=0) + (uswitch!=0) > 1)
187 gt_abort(">E labelg: -szgu are incompatible\n");
188
189 if (tswitch && (fswitch || Sswitch))
190 gt_abort(">E labelg: -t is incompatible with -S and -f \n");
191
192 if (iswitch && inv == 0) iswitch = FALSE;
193
194 if (!Sswitch && iswitch && (inv > NUMINVARS))
195 gt_abort(">E labelg: -i value must be 0..16\n");
196 if (tswitch && iswitch)
197 gt_abort(">E labelg: invariants are not available with -t\n");
198 if (Wswitch && (sswitch || gswitch || zswitch || uswitch || !Cswitch))
199 gt_abort(">E labelg: -W forbids -sgzu and requires -C\n");
200 if (Cswitch && tabsize <= 0)
201 gt_abort(">E labelg: bad value for -C\n");
202 if (Wswitch && (outinvar < 0 || outinvar >= tabsize))
203 gt_abort(">E labelg: value of -W is not valid\n");
204 if (Sswitch && iswitch && invarproc[inv].entrypoint_sg == NULL)
205 gt_abort(
206 ">E labelg: that invariant is not available in sparse mode\n");
207
208
209 if (iswitch)
210 {
211 if (Iswitch)
212 {
213 mininvarlevel = minil;
214 maxinvarlevel = maxil;
215 }
216 else
217 mininvarlevel = maxinvarlevel = 1;
218 if (!Kswitch) invararg = 3;
219 }
220
221 if (!Mswitch) secret = 1;
222
223 if (badargs || argnum > 2)
224 {
225 fprintf(stderr,">E Usage: %s\n",USAGE);
226 GETHELP;
227 exit(1);
228 }
229
230 if (!qswitch)
231 {
232 fprintf(stderr,">A labelg");
233 if (sswitch || gswitch || fswitch || iswitch || zswitch
234 || tswitch || Sswitch || Cswitch || Wswitch)
235 fprintf(stderr," -");
236 if (sswitch) fprintf(stderr,"s");
237 if (gswitch) fprintf(stderr,"g");
238 if (zswitch) fprintf(stderr,"z");
239 if (Sswitch) fprintf(stderr,"S");
240 if (tswitch) fprintf(stderr,"t");
241 if (Cswitch) fprintf(stderr,"C%d",tabsize);
242 if (Wswitch) fprintf(stderr,"W%d",outinvar);
243 if (iswitch)
244 fprintf(stderr,"i=%s[%d:%d,%d]",invarproc[inv].name,
245 mininvarlevel,maxinvarlevel,invararg);
246 if (fswitch) fprintf(stderr,"f%s",fmt);
247 if (argnum > 0) fprintf(stderr," %s",infilename);
248 if (argnum > 1) fprintf(stderr," %s",outfilename);
249 fprintf(stderr,"\n");
250 fflush(stderr);
251 }
252
253 if (Cswitch & !Wswitch)
254 {
255 DYNALLOC1(nauty_counter,tab,tab_sz,tabsize,"table");
256 for (j = 0; j < tabsize; ++j) tab[j] = 0;
257 }
258
259 if (infilename && infilename[0] == '-') infilename = NULL;
260 infile = opengraphfile(infilename,&codetype,FALSE,1);
261
262 if (!infile) exit(1);
263 if (!infilename) infilename = "stdin";
264
265 if (!outfilename || outfilename[0] == '-')
266 {
267 outfilename = "stdout";
268 outfile = stdout;
269 }
270 else if ((outfile = fopen(outfilename,"w")) == NULL)
271 {
272 fprintf(stderr,"Can't open output file %s\n",outfilename);
273 gt_abort(NULL);
274 }
275
276 if (uswitch)
277 outcode = 0;
278 else if (Cswitch && !Wswitch)
279 outcode = -1;
280 else if (Cswitch && Wswitch)
281 outcode = -2;
282 else if (gswitch)
283 outcode = GRAPH6;
284 else if (sswitch)
285 outcode = SPARSE6;
286 else if (zswitch)
287 outcode = DIGRAPH6;
288 else if ((codetype&DIGRAPH6))
289 outcode = DIGRAPH6;
290 else if ((codetype&GRAPH6))
291 outcode = GRAPH6;
292 else if ((codetype&SPARSE6))
293 outcode = SPARSE6;
294 else
295 {
296 outcode = GRAPH6;
297 fprintf(stderr,
298 ">W labelg doesn't handle this graph format, writing graph6.\n");
299 }
300
301 dooutput = (outcode == GRAPH6 || outcode == DIGRAPH6 || outcode == SPARSE6);
302
303 if (!fswitch) fmt = NULL;
304
305 if (!uswitch && (codetype&HAS_HEADER))
306 {
307 if (outcode == SPARSE6) writeline(outfile,SPARSE6_HEADER);
308 else if (outcode == DIGRAPH6) writeline(outfile,DIGRAPH6_HEADER);
309 else writeline(outfile,GRAPH6_HEADER);
310 }
311
312 nin = nout = 0;
313 orbtotal = 0;
314 unorbtotal = 0.0;
315 t = CPUTIME;
316 if (Sswitch)
317 {
318 SG_INIT(sg);
319 SG_INIT(sh);
320 while (TRUE)
321 {
322 if (read_sgg_loops(infile,&sg,&loops,&digraph) == NULL) break;
323 ++nin;
324 n = sg.nv;
325 m = (n + WORDSIZE - 1) / WORDSIZE;
326 SG_ALLOC(sh,n,sg.nde,"labelg");
327 for (ii = 0; ii < secret; ++ii)
328 fcanonise_inv_sg(&sg,m,n,&sh,fmt,invarproc[inv].entrypoint_sg,
329 mininvarlevel,maxinvarlevel,invararg,loops>0||digraph);
330 sortlists_sg(&sh);
331 if (n > 0)
332 {
333 orbtotal += gt_numorbits;
334 unorbtotal += 1.0 / gt_numorbits;
335 }
336 if (dooutput && (outcode == DIGRAPH6 || digraph)) writed6_sg(outfile,&sh);
337 else if (outcode == SPARSE6) writes6_sg(outfile,&sh);
338 else if (outcode == GRAPH6) writeg6_sg(outfile,&sh);
339 else if (outcode == -1)
340 ++tab[hashgraph_sg(&sh,137)%tabsize];
341 else if (outcode == -2 && (hashgraph_sg(&sh,137)%tabsize) == outinvar)
342 {
343 writelast(outfile);
344 ++nout;
345 }
346 }
347 }
348 else if (tswitch)
349 {
350 SG_INIT(sg);
351 SG_INIT(sh);
352 traces_opts.getcanon = TRUE;
353 traces_opts.writeautoms = FALSE;
354 traces_opts.verbosity = 0;
355 traces_opts.outfile = stdout;
356
357 while (TRUE)
358 {
359 if (read_sgg_loops(infile,&sg,&loops,&digraph) == NULL) break;
360 if (loops > 0 || digraph)
361 gt_abort(">E Traces does not allow loops or directed edges\n");
362 ++nin;
363 n = sg.nv;
364 DYNALLOC1(int,lab,lab_sz,n,"traces@labelg");
365 DYNALLOC1(int,ptn,ptn_sz,n,"traces@labelg");
366 DYNALLOC1(int,orbits,orbits_sz,n,"traces@labelg");
367 SG_ALLOC(sh,n,sg.nde,"labelg");
368 sh.nv = sg.nv;
369 sh.nde = sg.nde;
370 if (n > 0)
371 {
372 for (ii = 0; ii < n; ++ii) { lab[ii] = ii; ptn[ii] = 1; }
373 ptn[n-1] = 0;
374 for (ii = 0; ii < secret; ++ii)
375 Traces(&sg,lab,ptn,orbits,&traces_opts,&traces_stats,&sh);
376 sortlists_sg(&sh);
377 orbtotal += gt_numorbits;
378 unorbtotal += 1.0 / gt_numorbits;
379 }
380 if (outcode == SPARSE6) writes6_sg(outfile,&sh);
381 else if (outcode == GRAPH6) writeg6_sg(outfile,&sh);
382 else if (outcode == -1)
383 ++tab[hashgraph_sg(&sh,137)%tabsize];
384 else if (outcode == -2 && (hashgraph_sg(&sh,137)%tabsize) == outinvar)
385 {
386 writelast(outfile);
387 ++nout;
388 }
389 SG_FREE(sh);
390 }
391 }
392 else
393 {
394 while (TRUE)
395 {
396 if ((g = readgg(infile,NULL,0,&m,&n,&digraph)) == NULL) break;
397 ++nin;
398 #if !MAXN
399 DYNALLOC2(graph,h,h_sz,n,m,"labelg");
400 #endif
401 loops = loopcount(g,m,n);
402 for (ii = 0; ii < secret; ++ii)
403 fcanonise_inv(g,m,n,h,fmt,invarproc[inv].entrypoint,
404 mininvarlevel,maxinvarlevel,invararg,loops>0||digraph);
405 if (n > 0)
406 {
407 orbtotal += gt_numorbits;
408 unorbtotal += 1.0 / gt_numorbits;
409 }
410 if (dooutput && (outcode == DIGRAPH6 || digraph)) writed6(outfile,h,m,n);
411 else if (outcode == SPARSE6) writes6(outfile,h,m,n);
412 else if (outcode == GRAPH6) writeg6(outfile,h,m,n);
413 else if (outcode == -1)
414 ++tab[hashgraph(h,m,n,137)%tabsize];
415 else if (outcode == -2 && (hashgraph(h,m,n,137)%tabsize) == outinvar)
416 {
417 writelast(outfile);
418 ++nout;
419 }
420 FREES(g);
421 }
422 }
423 t = CPUTIME - t;
424
425 if (Oswitch)
426 fprintf(stderr,">C orbit totals = " COUNTER_FMT " %15.8f\n",
427 orbtotal,unorbtotal);
428 if (!qswitch)
429 {
430 if (outcode == -1)
431 {
432 for (j = 0; j < tabsize; ++j)
433 fprintf(stderr,">C %5d " COUNTER_FMT "\n",j,tab[j]);
434 fprintf(stderr,">Z " COUNTER_FMT " total from %s in %3.2f sec.\n",
435 nin,infilename,t);
436 }
437 else if (outcode == -2)
438 {
439 fprintf(stderr,
440 ">Z " COUNTER_FMT " out of " COUNTER_FMT
441 " graphs copied from %s to %s in %3.2f sec.\n",
442 nout,nin,infilename,outfilename,t);
443 }
444 else
445 {
446 fprintf(stderr,
447 ">Z " COUNTER_FMT
448 " graphs labelled from %s to %s in %3.2f sec.\n",
449 nin,infilename,outfilename,t);
450 }
451 }
452
453 exit(0);
454 }
455