// cc -O4 -o water2 -DWORDSIZE=32 -DMAXN=WORDSIZE nauty.c naugraph.c nautil.c gtools.c schreier.c naurng.c watercluster2.c /* Reads graphs in g6 code or multicode (optional) from stdin and directs them options: ix means: the indegree of every vertex may be at most x. oy means: the outdegree of every vertex may be at most y. S means: allow that for every pair of vertices x,y at most one of the edges x-->y and y-->x may be present. By default both of them may be present in the same graph. T means: Output directed graphs in T-code. This is a simple ASCII output format. Every line contains one graph. First the number of vertices, then the number of directed edges and then the list of directed edges with the start first and the end then. E.g.: 3 2 0 1 2 1 means 3 vertices, 2 directed edges: 0-->1 and 2-->1 B means: Output the directed graphs in a binary code. Every item of the code is an unsigned char. The first unsigned char is the number nv of vertices. The vertices are numbered 1..nv Then the list of vertices x for which there is a directed edge 1->x follow. This list is ended by a 0. Then the list of outgoing neighbours of 2 follows -- again ended with a 0, etc. The code is complete with the 0 ending the list of outgoing neighbours of nv. Z means: Output the directed graphs in digraph6 code. See formats.txt for a complete definition. C means: Do really construct all the directed graphs in memory, but don't output them. This is not a big difference in case of restricted in- and outdegrees, because all that is done extra is that edges are directed instead of just keeping track of in- and out-degrees. This option is intended only for testing purposes to test also routines that are normally not used when counting. Things that would speed up the counting also in some cases of restricted in- and out-degrees -- like multiplying the possibilities of assigning directions to edges that can be assigned directions independent of each other (depending on the degrees of the endvertices and overlaps) -- are not included. In case of not restrictive bounds on the in- and out-degree it not really constructing the graphs can be considerably faster. In cases of restricted in- and out-degrees the only difference is that the graph isn't modified... The fact that in case of no output the graph is not modified is mainly to save time for the one case of waterclusters, where large numbers were determined. If large numbers (without output) for other cases shall be determined, one should think about adding the multiplication routines. m read multicode This program uses different labelling routines -- all based on the ideas of G. Brinkmann, Generating water clusters and other directed graphs, Journal of Mathematical Chemistry 46, 1112--1121 (2009) October 10, 2011: corrected error caused by overflow of 32bit int used as hashvalue. Sep, 2012: PROCESS feature added by BDM. Oct, 2017: digraph6 output added by BDM. */ /* PROCESS feature * * If PROCESS is defined, it must expand as the name of a procedure * with prototype like void PROCESS(FILE *f, graph *g, int n). This * procedure will be called for each output graph before it is * output (or before output is suppressed) with f being the output * file (possibly NULL). * It is an error if n > WORDSIZE. * If NOCONVERSE is also defined, the call is only made for one of * each digraph and its converse. */ /* SUMMARY feature * * If SUMMARY is defined, it must expand as the name of a procedure * with prototype void SUMMARY(void). It is called at the end after * the normal summary. */ //#include #include "gtools.h" #include #include #ifdef PROCESS extern void PROCESS(FILE*,graph*,int); nauty_counter dg_nin,dg_nout; #endif #ifdef SUMMARY extern void SUMMARY(void); #endif #define MAX_BOGEN ((MAXN*(MAXN-1))/2) #define INFTY_UCHAR UCHAR_MAX typedef int BOOG[2]; void nontrivlabels(), init_nauty_options(); BOOG edgelist[MAX_BOGEN+1]; /* de lijst van bogen */ BOOG edgelist_final[MAX_BOGEN+1]; /* de lijst van bogen die nadat er eerst nontriviale automorphismen waren die dan verdwenen nog gericht moeten worden.*/ BOOG *laatstepositie; /* The last position in one of these lists. Global in order not to have to copy it every time */ /* all the next variables must be evaluated immediately when orbits are constructed. They are reused in the next iteration */ unsigned char *operations=NULL; int *root_op=NULL, size_root=0, blocklength, orbitblocklength[MAXN], size_operations=0, number_operations=0; /* these arrays will be dynamically allocated and extended. Operations will be an array with "blocks" of length "blocklength=tobedirected[vertex_in_orbit]+3". They represent an operation the following way: the first entry is the central vertex then the list of vertices from which only edges come in -- ended by INFTY_UCHAR, then the list of vertices to which only outgoing edges go -- ended by INFTY_UCHAR. Then the list of vertices to which incoming AND outgoing edges go -- ended by INFTY_UCHAR. root has as many entries as there are blocks in operations. root[i]=j means that when computing the orbits of operations and constructing a union-find-tree, block number j is the root of the tree containing vertex i. */ unsigned char *remember_operations[MAXN]; //the nonequivalent operations that are stored for every iteration int remember_size[MAXN]; // remember_size[i] is the number of characters allocated for remember_operations[i] #define COPYBOOG(a,b) { (a)[0]=(b)[0]; (a)[1]=(b)[1]; } /* OPTIONS */ boolean dummybool; int mingerichtdeg; int remaining_doubles=0; /* hoeveel bogen kunnen ten hoogste nog in allebei richtingen gericht worden ? */ int watermaxedges; /* what is the theoretical maximum for the number of edges which can directed in a way that doesn't give a conflict with the in/out-degree bounds. */ int watermaxdeg; /* maxindeg+maxoutdeg */ int is_gericht[MAX_BOGEN][MAX_BOGEN]={{0}}; /* is_gericht[i][j]==1 als {i,j} is al gericht -- anders 0 */ int virtual_gericht[MAX_BOGEN][MAX_BOGEN]={{0}}; /* virtual_gericht[i][j]==1 als het al vastgelegd is dat {i,j} i->j gericht moet worden, 2 als hij j->i gericht moet worden en 0 als het nog niet vastgelegd is. Vastgelegd betekent door de beperkingen van in- en outgraad moet deze boog zo gericht worden. */ int positie[MAXN][MAXN]; /* the value of positie[i][j] is the position of edge {1,j} in edgelist in case of nontrivial automorphisms of the underlying undirected graph. */ int virtual_indeg[MAXN], virtual_outdeg[MAXN]; /* de graden als de nog niet gerichte maar al vastgelegde bogen meegerekend worden -- dat mag niet voor kanoniciteit gebruikt worden */ int nextstep_depth; #define SWITCHPAR_ORBSIZE 5 // when does serial orbit labelling switch to parallel -- and stay there #define MAXPAR_ORBSIZE 9 // maximum 16, when changing, change following MAXPAROPS too #define MAXPAROPS 19683 // set to minimum 3^MAXPAR_ORBSIZE unsigned int parops[MAXPAROPS]; // in the case of no degree restrictions this could in fact be permanently filled in -- only depending // on the edge orbit size. But the time for filling it in is so small that it is simply not worth it. int number_parops; // an operation is defined as follows: if edge number i in kleinste_orbit must be directed kleinste_orbit[i][0]->kleinste_orbit[i][1], // bits 2i and 2i+1 form the number 0 (so are both 0). For kleinste_orbit[i][0]<-kleinste_orbit[i][1] they form 2, for // a double edge they form 1 ---- so 2-type is always the inverse #define SETOP(op,edge,type) ((op) = ((op) & ~(3<<((edge)<<1))) | (type)<<((edge)<<1)) #define GETTYPE(op,edge) (((op)>>((edge)<<1))&3) /* for the following macros global variables _x_ and _y_ are used */ int _x_, _y_; #define BILDBOOG_UNDIR(startboog,bildboog,permnummer) \ { _x_=generators[permnummer][(startboog)[0]]; _y_=generators[permnummer][(startboog)[1]]; \ if (_x_<_y_) { (bildboog)[0]=_x_; (bildboog)[1]=_y_; } else { (bildboog)[0]=_y_; (bildboog)[1]=_x_; }} #define BILDBOOG(startboog,bildboog,permnummer) \ { (bildboog)[0]=generators[permnummer][(startboog)[0]]; (bildboog)[1]=generators[permnummer][(startboog)[1]]; } #define POSBILD(startboog,permnummer) \ (positie[generators[permnummer][(startboog)[0]]][generators[permnummer][(startboog)[1]]]) /* a macro that sets numbers a and b equivalent to a */ #define SETEQUIV(a,b) { _y_=(b); while ((_x_=number[_y_])!=_y_) { number[_y_]=(a); _y_=_x_; } number[_y_]=(a); } /* zorgt ervoor dat nummer[a] zo is dat nummer[nummer[a]]=nummer[a] */ #define UPDATENUMBER(a) { _y_=(a); while ((_x_=number[_y_])!=_y_) { number[_y_]=number[_x_]; _y_=number[_y_]; } number[a]=number[_y_]; } /* the quality of a directed edge */ #define QUALITY(a,b) (((saturated[a]+saturated[b])<<6)+colour[nextstep_depth][a]) // upper bound for the quality if the in- or out-deg of one of the entries is changed by one #define QUALITY_P1(a,b) (((saturated[a]+saturated[b]+1)<<6)+colour[nextstep_depth][a]) // quality if the in- or out-deg of one of the entries are both changed by one #define QUALITY_P2(a,b) (((saturated[a]+saturated[b]+2)<<6)+colour[nextstep_depth][a]) int _marks[MAX_BOGEN], _markvalue=INT_MAX; #define RESETMARKS { int i; if (_markvalue0) int aantal_toppen, aantal_bogen, aantal_gerichte_bogen; int max_doubles; int double_free[MAXN]={0}; /* hoeveel dubbele bogen kan top [i] nog krijgen? */ long long int addnumber=0; /* How much must the counter be increased if a graph is found. Just relevant in case not all are really constructed and coded. Then this gives a speedup.*/ long long int aantal_grafen_met_triv_group=0LL; long long int aantal_gerichte_grafen=0LL; static DEFAULTOPTIONS(options_directed); static DEFAULTOPTIONS(options_directed_canon); static DEFAULTOPTIONS(options); static DEFAULTOPTIONS(options_final); static statsblk stats; setword workspace[100*MAXN]; int generators[MAXN][MAXN]; int number_of_generators; int group_up_to_date=0; int indeg_free[MAXN]={0}, outdeg_free[MAXN]={0}, saturated[MAXN]={0}; graph all; // the set of all vertices int orbitchoices[MAXN]={0}; #define CSIZE 32768 int _colourmarks[CSIZE], _cmark=INT_MAX; #define RESETMARKS_COLOUR { int i; \ if (_cmark==INT_MAX)\ { for (i=0;i=0;i++) fprintf(stderr,"%d ",list[i]); fprintf(stderr,"\n"); } void writegraph(graph *g) // for graphs as they are used in the vertexorbit routines { int i,j; static int counter=0; fprintf(stderr,"---------------------------------------------------------\n"); fprintf(stderr,"Graph number %d with %d vertices\n",++counter,aantal_toppen); for (i=0; i') /* koennte ein header sein -- oder 'ne 62, also ausreichend fuer unsigned char */ { gepuffert=1; a=getc(fil); if(a==0) nuller++; b=getc(fil); if(b==0) nuller++; /* jetzt wurden 3 Zeichen gelesen */ if ((a=='>') && (b=='m')) /*garantiert header*/ { while ((ucharpuffer=getc(fil)) != '<') {} /* noch zweimal: */ ucharpuffer=getc(fil); if (ucharpuffer!='<') { fprintf(stderr,"Problems with header -- single '<'\n"); exit(1); } if ((knotenzahl=getc(fil))==EOF) return EOF; /* kein graph drin */ } /* else kein header */ } if (knotenzahl > maxknotenzahl) { if (*code) free(*code); *code=(unsigned char *)malloc((knotenzahl*(knotenzahl-1)/2+knotenzahl)*sizeof(unsigned char)); if (code==NULL) { fprintf(stderr,"Do not get memory for code\n"); exit(0); } maxknotenzahl=knotenzahl; } (*code)[0]=knotenzahl; if (gepuffert) { codel=3; (*code)[1]=a; (*code)[2]=b; } while (nullerb AND b->a in the same output graph.\n"); fprintf(stderr,"m means: read multicode instead of g6 code \n"); exit(1); } /**********DECODE_TO_NAUTY****************************************************/ void decode_to_nauty(unsigned char *code, int codelaenge, graph *g, int degree[]) /* Dekodiert multicode nach nauty bitcode */ /* alle knotennamen muessen fuer nauty um 1 nach unten verschoben werden */ { int v1,v2; unsigned char *end; if (code[0]>32) { fprintf(stderr,"Not prepared for %d>32 vertices.\n",code[0]); exit(2); } aantal_toppen=code[0]; aantal_bogen=codelaenge-aantal_toppen; for (v1=0; v132) { fprintf(stderr,"Not prepared for %d>32 vertices.\n",aantal_toppen); exit(2); } aantal_bogen=0; for (i=0; i>1; aantal_gerichte_bogen=0; return; } /***************************FILL_EDGELIST**************************/ void fill_edgelist() /* writes the edges in g into the list in a lexicographic way. Is used if no bounds for in- and out-degree are given or few edges are left. */ { int i,j,end; for (i=j=0; i=0) { for (beste=1;listlen[beste]==0; beste++); (listlen[beste])--; /* zo wordt hij ook verwijderd */ start=list[beste][listlen[beste]]; for (i=0; istart gericht worden */ { WRITEUP_COUNT(); } if (indeg_free[end] && outdeg_free[start]) /* bogen kan start->end gericht worden */ { WRITEUP_COUNT(); } return; } /* else -- niet op laatste positie */ if (indeg_free[start] && outdeg_free[end]) /* bogen kan end->start gericht worden */ { (indeg_free[start])--; (outdeg_free[end])--; trivlabels_nowrite_nodouble(positie+1); (indeg_free[start])++; (outdeg_free[end])++; } if (indeg_free[end] && outdeg_free[start]) /* bogen kan start->end gericht worden */ { (indeg_free[end])--; (outdeg_free[start])--; trivlabels_nowrite_nodouble(positie+1); (indeg_free[end])++; (outdeg_free[start])++; } return; } void trivlabels_nowrite(BOOG *positie) { int start, end, counter; if (!remaining_doubles) { trivlabels_nowrite_nodouble(positie); return; } start=(*positie)[0]; end=(*positie)[1]; if (positie==laatstepositie) { if (indeg_free[start] && outdeg_free[end]) /* bogen kan end->start gericht worden */ { counter=1; WRITEUP_COUNT(); } else counter=0; if (indeg_free[end] && outdeg_free[start]) /* bogen kan start->end gericht worden */ { counter++; WRITEUP_COUNT(); } if (remaining_doubles && (counter==2)) WRITEUP_COUNT(); return; } /* else -- niet op laatste positie */ if (indeg_free[start] && outdeg_free[end]) /* bogen kan end->start gericht worden */ { counter=1; (indeg_free[start])--; (outdeg_free[end])--; trivlabels_nowrite(positie+1); (indeg_free[start])++; (outdeg_free[end])++; } else counter=0; if (indeg_free[end] && outdeg_free[start]) /* bogen kan start->end gericht worden */ { counter++; (indeg_free[end])--; (outdeg_free[start])--; trivlabels_nowrite(positie+1); (indeg_free[end])++; (outdeg_free[start])++; } if (remaining_doubles && (counter==2) && double_free[start] && double_free[end]) { (indeg_free[end])--; (outdeg_free[start])--; (indeg_free[start])--; (outdeg_free[end])--; double_free[start]--; double_free[end]--; remaining_doubles--; trivlabels_nowrite(positie+1); remaining_doubles++; double_free[start]++; double_free[end]++; (indeg_free[end])++; (outdeg_free[start])++; (indeg_free[start])++; (outdeg_free[end])++; } return; } void trivlabels(BOOG *positie) { int start, end, counter; start=(*positie)[0]; end=(*positie)[1]; if (positie==laatstepositie) { if (indeg_free[start] && outdeg_free[end]) /* bogen kan end->start gericht worden */ { counter=1; //(indeg_free[start])--; (outdeg_free[end])--; DELELEMENT(workg+start,end); MAYBEPROCESS; WRITEUP(); ADDELEMENT(workg+start,end); //(indeg_free[start])++; (outdeg_free[end])++; } else counter=0; if (indeg_free[end] && outdeg_free[start]) /* bogen kan start->end gericht worden */ { counter++; //(indeg_free[end])--; (outdeg_free[start])--; DELELEMENT(workg+end,start); MAYBEPROCESS; WRITEUP(); ADDELEMENT(workg+end,start); //(indeg_free[end])++; (outdeg_free[start])++; } if (remaining_doubles && (counter==2)) { remaining_doubles--; MAYBEPROCESS; WRITEUP(); remaining_doubles++; } return; } /* else -- niet op laatste positie */ if (indeg_free[start] && outdeg_free[end]) /* bogen kan end->start gericht worden */ { counter=1; (indeg_free[start])--; (outdeg_free[end])--; DELELEMENT(workg+start,end); trivlabels(positie+1); ADDELEMENT(workg+start,end); (indeg_free[start])++; (outdeg_free[end])++; } else counter=0; if (indeg_free[end] && outdeg_free[start]) /* bogen kan start->end gericht worden */ { counter++; (indeg_free[end])--; (outdeg_free[start])--; DELELEMENT(workg+end,start); trivlabels(positie+1); ADDELEMENT(workg+end,start); (indeg_free[end])++; (outdeg_free[start])++; } if (remaining_doubles && (counter==2) && double_free[start] && double_free[end]) { (indeg_free[end])--; (outdeg_free[start])--; (indeg_free[start])--; (outdeg_free[end])--; double_free[start]--; double_free[end]--; remaining_doubles--; trivlabels(positie+1); remaining_doubles++; double_free[start]++; double_free[end]++; (indeg_free[end])++; (outdeg_free[start])++; (indeg_free[start])++; (outdeg_free[end])++; } return; } void trivlabels_init(BOOG *positie) { int remember; if (!direct_output) { if (nodegbound) { remember=addnumber; if (remaining_doubles) { for (;positie<=laatstepositie;positie++) addnumber*=3;} else { for (;positie<=laatstepositie;positie++) addnumber*=2;} WRITEUP(); addnumber=remember; return; } /* else */ if (!remaining_doubles) trivlabels_nowrite_nodouble(positie); else trivlabels_nowrite(positie); return; } /* else */ trivlabels(positie); return; } /******************************DIRECT_ALL_TRIV********************************/ void direct_all_triv() { int i, start, end; aantal_grafen_met_triv_group++; if (nodegbound) fill_edgelist(); else fill_edgelist_order(); for (i=0;il0 l1>l2 { if (list[0]3) //should hardly ever happen and then "number" is still small -- bubblesort { for (i=number-1; i ; i--) for (j=0;j1) sort_decreasing(in_image,num_in); if (num_out>1) sort_decreasing(out_image,num_out); if (num_double>1) sort_decreasing(double_image,num_double); for (run=1 ; num_in; run++) { num_in--; image[run]=in_image[num_in]; if (original[run]!=image[run]) change=1;} image[run]=INFTY_UCHAR; run++; for ( ; num_out; run++) { num_out--; image[run]=out_image[num_out]; if (original[run]!=image[run]) change=1;} image[run]=INFTY_UCHAR; run++; for ( ; num_double; run++) { num_double--; image[run]=double_image[num_double]; if (original[run]!=image[run]) change=1;} image[run]=INFTY_UCHAR; //for ( ;run=0;i++) if (!decided[i]) { if ( double_free[center] && double_free[list[i]] && ((outdeg[center]+numberout < maxoutdeg) && (indeg[list[i]]lowerlimit_outdeg) outdeg_larger=1; else outdeg_larger=0; } } if (double_free[center]) // then there is still something to decide { if (outdeg[center]+numberout < maxoutdeg) for (i=liststart;list[i]>=0;i++) if (!decided[i]) { if (indeg[list[i]]=mindouble)) // looks complicated but is just the final number of double edges construct_operations_final(list,decided,buffer_op,positie+1, numberin, numberout); return; } //else // all the undecided entries have to be outgoing for (i=liststart;list[i]>=0;i++) if (!decided[i]) { if ((outdeg[center]+numberout < maxoutdeg) && (indeg[list[i]]=0;i++) if (!decided[i]) { if (outdeg[list[i]]=0; still_open++) // number_operations is global, so it must not be passed to the next functions //if (NOT_READY(top)) { if (tobedirected[top]==1) { for (j= FIRSTBIT(workg[top]); READY(j) ; NEXTEL((workg[top]),(j))); if (ISELEMENT(&sameorbit,j) && (tobedirected[j]==1)) construct_operations_one(top, j, 0, 1, double_allowed); else construct_operations_one(top, j, 1, 1, double_allowed); } else if (!(staticg[top] & tbd1)) { for (end=0, j= FIRSTBIT(workg[top]); (j=0; j++) { if (NOT_READY(top)) { //ADDELEMENT(&sameorbit,top); if (tobedirected[top]>=2) ADDELEMENT(&pre_free,top); // will stay free unless is chosen top else ADDELEMENT(&tbd1,top); } else // that is: ready { *readyrun=top; readyrun++; } // minout would not be used } } else { do_double=double_allowed; for (j=0; (top=orbit[j])>=0; j++) { if (NOT_READY(top)) { //ADDELEMENT(&sameorbit,top); if (tobedirected[top]>=2) ADDELEMENT(&pre_free,top); // will stay free unless is chosen top else ADDELEMENT(&tbd1,top); } else // that is: ready { bufferg=(workg[top]&sameorbit); *readyrun=top; readyrun++; if ((bufferg) && (outdeg[top]=0; still_open++) // number_operations is global, so it must not be passed to the next functions //if (NOT_READY(top)) { if (tobedirected[top]==1) // already ready vertices can't have less... { for (j= FIRSTBIT(workg[top]); READY(j) ; NEXTEL((workg[top]),(j))); if (ISELEMENT(&sameorbit,j) && (tobedirected[j]==1)) { bufferg=workg[j]&sameorbit; DELELEMENT(&bufferg,top); if (outdeg[top]outdeg[top])) // otherwise the end would be better { if (outdeg[top]==minout-1) construct_operations_one(top, j, 0, 1, do_double); else construct_operations_one(top, j, 0, 1, double_allowed); } else if ((outdeg[j]==outdeg[top]) && double_allowed) { if (outdeg[top]==minout-1) construct_operations_one(top, j, 0, 0, do_double); else construct_operations_one(top, j, 0, 0, double_allowed); } } } else construct_operations_one(top, j, 1, 1, double_allowed); } else { if (!(staticg[top] & tbd1)) { error=0; freevertices = pre_free | (tbd1 & ~staticg[top]); DELELEMENT1(&freevertices,top); // now freevertices is the set of vertices that will still be free after the addition of top for (lowerlimit_outdeg=mindouble=0, readyrun=readylist; !error && (top2= *readyrun)>=0; readyrun++) { bufferg= freevertices & staticg[top2]; if ((dummy=POPCOUNT(bufferg)))// will be a candidate afterwards { if (dummy < tobedirected[top]) error=1; else { if (dummy == tobedirected[top]) { if (outdeg[top2]>lowerlimit_outdeg) { lowerlimit_outdeg=outdeg[top2]; mindouble=indeg[top2]+outdeg[top2]-deg[top2]; } else if (double_allowed && (outdeg[top2]==lowerlimit_outdeg)) { if (indeg[top2]+outdeg[top2]-deg[top2]>mindouble) mindouble=indeg[top2]+outdeg[top2]-deg[top2]; } } } } } if (!error) { for (end=0, j= FIRSTBIT(workg[top]); (jend in edgelist and returns the index. */ { int min, max, center; min=0; max=length-1; center= (min+max)/2; while (minend smaller or equal { max=center; center= (min+max)/2; } else // start->end strictly larger { min=center+1; center= (min+max)/2; } } return min; } /******************************COMPUTE_ORBITS************************/ int compute_orbits() /* compute the orbits of the group described by the global variable generators[][] on the operations described in the global variable operations. Returns the number of orbits. */ { int i,j,buffer,run, orbits, index; unsigned char image[MAXN+4]; if (number_operations>size_root) { size_root=number_operations; free(root_op); root_op=malloc((size_t)size_root*sizeof(int)); } if (root_op==NULL) { fprintf(stderr,"Can't allocate %d items for root_op -- exiting.\n",size_root); exit(0); } for (i=0; iy or x<->y. // Edges completely in this orbit and obtained by operations center x end y are chosen as follows: // First the starting point x is chosen with minimum outdegree. // For edges with starting points with the same minimal outdegree, edges are preferred which are single. // If the tested operation is double and single operations exist, the operation is rejected. // Among the remaining edges those are chosen where y has minimum indegree. // Among the remaining those are chosen where x is in the orbit of the one with smallest canonical number // (after the operation). y is the neighbour with smallest indegree -- and among those with the same indegree // the one with smallest canonical number (in the same given numbering of course). // Otherwise: // Define a vertex to be free if it is not in an orbit that was already chosen (we call that untouched) // or it is in such an orbit but not all edges to it are already directed (that can only be in the last orbit). // In this case the central vertex of the canonical operation is one that is chosen in steps: // Among all with edges to free vertices it is chosen as one with a minimum number of these. // Among those one with max outdegree is chosen. // Among those one with maximum number of double edges is chosen. // Finally a more complicated artificial criterion is tested -- not suitable for a look ahead during the construction // of possible operations // Among the remaining, one with smallest canonical label is chosen. { int i, j, k, min, top, buffer, candidatelist[MAXN], number_candidates, center; graph free, candidates, bufferset, dummy, sameorbit, bit_startlist; int edgelistcounter, edgelist[2*MAXN*MAXN][2], orb[2*MAXN*MAXN], canoncenter, canonend, endvertex=0; int finished[MAXN], finishedptn[MAXN], testoutdeg; int startlist[MAXN], *startrun; // list of possible starts in case of no candidates int numberends, ends[MAXN][MAXN], endin=0, *run; int *colour; long long int buffer2, k2; free= all & (~touched); // in fact all ready vertices are candidates if one has a non-empty // intersection with this as they are all in the same orbit -- so have the same number of edges // to this set sameorbit=bit_orbit[orbitid]; for (run=vertexorbit; (*run)>=0; run++) if (NOT_READY(*run)) ADDELEMENT(&free,(*run)); center=operation[0]; candidates= (graph)0; number_candidates=0; min=INFTY_UCHAR; for (i=0; (top=vertexorbit[i])>=0; i++) if (READY(top) && (bufferset=(staticg[top] & free))) { buffer=POPCOUNT(bufferset); if (buffer0) { return 0;} else { if (k<0) { DELELEMENT(&candidates,candidatelist[i]); number_candidates--; candidatelist[i]=candidatelist[number_candidates]; } else i++; // otherwise the new element has to be tested } } if (number_candidates==1) { *newgroup=0; return 1; } // end number_candidates>0 if (remaining_doubles != max_doubles) // there are double edges { buffer=indeg[center]+outdeg[center]-deg[center]; // number of double edges starting at center for (i=0; i0) { return 0;} else { if (k<0) { DELELEMENT(&candidates,candidatelist[i]); number_candidates--; candidatelist[i]=candidatelist[number_candidates]; } else i++; // otherwise the new element has to be tested } } } if (number_candidates==1) { *newgroup=0; return 1; } //OK-- last try. A colour that can not really be used to avoid unnecessary operation in advance colour=rememberorbits[orbitid]; dummy=workg[center]&(free | sameorbit); buffer2=1LL; FORALLELEMENTS(dummy,i) { buffer2 *= (tobedirected[i]<<12)+(indeg[i]<<9)+(outdeg[i]<<6)+(colour[i]<<3)+deg[i]+1; buffer2 = buffer2%63241LL; } for (i=0; i0LL) { return 0;} else { if (k2<0LL) { DELELEMENT(&candidates,candidatelist[i]); number_candidates--; candidatelist[i]=candidatelist[number_candidates]; } else i++; // otherwise the new element has to be tested } } if (number_candidates==1) { *newgroup=0; return 1; } } // end number candidates > 0 // OK -- we have to work. First the canonical form must be computed: // Problem: the difference between double edges and edges that have not yet been directed cannot be // detected in the datastructure graph. To this end we put "ready" vertices in an extra partition -- // this way double edges cannot be mapped on undirected edges. This is only necessary if there are double edges. else // (number_candidates==0) // finished vertices in this orbit always have an internal edge! { startrun=startlist; bit_startlist=(graph)0; if (operation[2]==INFTY_UCHAR) // a double edge was added and all single edges starting at same outdeg are better { endvertex= operation[3]; endin=indeg[endvertex]; for (i=0; (k=vertexorbit[i])>=0; i++) if (READY(k) && (workg[k] & sameorbit))// has _outgoing_ edge into same orbit // the end points are automatically ready and all in the same orbit -- otherwise there would have been a candidate { if (outdeg[k]=0; i++) if (READY(k) && (workg[k] & sameorbit))// has _outgoing_ edge into same orbit // the end points are automatically ready and all in the same orbit -- otherwise there would have been a candidate { if (outdeg[k]1) { for (i=0; !ISELEMENT(&candidates,bufferlab[i]); i++); // zoekt candidaat met kleinste kanonische label if (orbits[center]==orbits[bufferlab[i]]) { return 1; } else { return 0; } } // only remaining case: number_candidates=0 -- that is: only one edge between 2 vertices of vertexorbit was added // operation must be outgoing or double edge -- but this is guaranteed by the construction of operations // for (i=0; !(READY(bufferlab[i]) && ISELEMENT(&sameorbit,bufferlab[i]) && (workg[bufferlab[i]] & sameorbit)) for (i=0; !ISELEMENT(&bit_startlist,bufferlab[i]); i++); // Now we have the smallest vertex in vertexorbit with an edge to another vertex in vertexorbit canoncenter=bufferlab[i]; if (orbits[center]!=orbits[canoncenter]) { return 0; } // Now look for the endvertex: min=INFTY_UCHAR; dummy= sameorbit & workg[canoncenter]; // at this point it is known that none of the possible startvertices has an endvertex that // has smaller indegree than the endvertex of the operation tested (that is: endin) // now look for the smallest labelled neighbour: if (operation[2]==INFTY_UCHAR) // a double edge was added and any suitable neighbour is double for (i=0;!(ISELEMENT(&dummy,bufferlab[i]) && (indeg[bufferlab[i]]==endin)) && (i=0;startrun++) if (orbits[k]==orbits[canoncenter]) { for (i=0; (j=ends[k][i])>=0; i++) { if (orbits[j]==orbits[canonend]) { edgelist[edgelistcounter][0]=k; edgelist[edgelistcounter][1]=j; orb[edgelistcounter]=edgelistcounter; edgelistcounter++; } } } compute_edgeorbits(edgelist,orb,edgelistcounter); i=search_edge(center,endvertex,edgelist,edgelistcounter); while(i!=orb[i]) i=orb[i]; j=search_edge(canoncenter,canonend,edgelist,edgelistcounter); while(j!=orb[j]) j=orb[j]; if (i==j) { return 1; } // same orbit as canonical edge else { return 0; } } int all_diff_colours(graph testset, int orbitid) // returns 1 if all elements have some different vertex invariant and 0 otherwise { int *colour; int i,buffer; RESETMARKS_COLOUR; colour=rememberorbits[orbitid]; FORALLELEMENTS(testset,i) { buffer = (tobedirected[i]<<12)+(indeg[i]<<9)+(outdeg[i]<<6)+(colour[i]<<3)+deg[i]; buffer &= 32767; if (ISMARKED_COLOUR(buffer)) return 0; MARK_COLOUR(buffer); } return 1; } void fill_edgelist_final(graph g[]) /* writes the edges in gg into the list in a lexicographic way and initializes is_gericht and positie*/ { int i,j,end; for (i=0, j=aantal_gerichte_bogen; i>1; // every edge was counted twice memcpy(g,workg,aantal_toppen*sizeof(graph)); EMPTYSET1(¬ready,1); // make a list of vertices according to the whole degree. Vertices with large degree give stronger restrictions for (i=0; i=aantal_gerichte_bogen) { for (beste=1;listlen[beste]==0; beste++); // zoek kleinste aanwezige nog toe te voegen graad (listlen[beste])--; /* zo wordt hij ook verwijderd */ start=list[beste][listlen[beste]]; for (i=0; g[start] != (graph)0; i++) /* alle buren opslaan*/ { end=buren[i]=FIRSTBIT(g[start]); DELELEMENT(g+start,end); } buren[i]= -1; for (i=0; (end=buren[i])>=0; i++) /* alle buren */ { edgelist_final[last_positie][0]=start; edgelist_final[last_positie][1]=end; last_positie--; DELELEMENT(g+end,start); /* de buur verhuizen: */ olddeg=bufferdeg[end]; (bufferdeg[end])--; newdeg=bufferdeg[end]; /* uit de oude lijst verwijderen */ if (listlen[olddeg]==1) listlen[olddeg]=0; else { (listlen[olddeg])--; buffer= list[olddeg][listlen[olddeg]]; list[olddeg][toppositie[end]]=buffer; toppositie[buffer]=toppositie[end]; } /* tot de nieuwe toevoegen */ if (newdeg) { list[newdeg][listlen[newdeg]]=end; toppositie[end]=listlen[newdeg]; (listlen[newdeg])++; } } } return; } /******************************CHOOSEORBIT***************************/ void chooseorbit(graph *touched, int best_orbit[], int orbitid) { int i,j,k, num_in_orbit[MAXN], dummy, inorbit[MAXN][MAXN], best, bestroot=0; // the group must be up to date at this point! for (i=0;i0)) ptn[orbitid][i-1]=0; for ( i++; i=0) { choose_triv_orbit(&touched, local_todolist, orbitid+1, d); directorbit(local_todolist,local_todolist,0,orbitid+1,iterationdepth,touched); return; } // nieuwe orbit berekenen: //if (!group_uptodate) // { number_of_generators=0; // since the orbit is complete, we don't NEED to distinguish between ready vertices or not // to mark double edges. Tests showed that it also doesn't help. memcpy(bufferlab,lab[orbitid],aantal_toppen*sizeof(int)); memcpy(bufferptn,ptn[orbitid],aantal_toppen*sizeof(int)); number_of_generators=0; nauty(workg,bufferlab,bufferptn,NULL,orbits,&options_directed,&stats,workspace,100*MAXN,1,aantal_toppen,NULL); //orbitchoose_nauty++; } end=1; // can we end the groupcomputations and switch to trivgroup? if (stats.numorbits=0; i++) if (NOT_READY(todo_list[i])) { local_todolist[local_left_to_do]=todo_list[i]; local_left_to_do++; } local_todolist[local_left_to_do]= -1; if (local_left_to_do==0) { if (touched==all) { MAYBEPROCESS; WRITEUP(); return; } else prepare_next_step(group_uptodate, orbitid, iterationdepth, touched); return; } // else -- there are still vertices in the orbit if (vertexorbit[local_left_to_do]== -1) first_in_orbit=1; else first_in_orbit=0; if (first_in_orbit) // new orbit to start with orbitblocklength[orbitid]=tobedirected[vertexorbit[0]]+4; localblocklength=blocklength=orbitblocklength[orbitid]; /* als een top al afgewerkt is kan de bloklengte voor de verschillende toppen in wat vroeger een orbit was verschillen. orbitblocklength[orbitid] is altijd een bovengrens */ construct_extensions(local_todolist,vertexorbit,touched, first_in_orbit, bit_orbit[orbitid]); if (number_operations==0) return; // in case of one element with tobedirected>1 the group is up to date if ((group_uptodate && (stats.numorbits==aantal_toppen)) || ((vertexorbit[1]== -1) && (tobedirected[vertexorbit[0]]==1))) doit=0; else doit=1; // doit means: really check the group if (doit) { dummy= all & (~touched); for (i=0; (k=local_todolist[i])>=0; i++) ADDELEMENT(&dummy,k); if (all_diff_colours(dummy,orbitid)) doit=0; else { if (!group_uptodate) { /* we need only the group -- no canonical numbering */ number_of_generators=0; if (double_allowed) { for (i=j=k=0; i=0; i++) if (orbits[k]!=k) doit=1; if (!doit) { dummy= all & (~touched); FORALLELEMENTS(dummy,j) if (orbits[j]!=j) doit=1; } } } if (doit) number_orbits=compute_orbits(); else number_orbits=number_operations; if (remember_size[iterationdepth]<(number_orbits*localblocklength)) { free(remember_operations[iterationdepth]); remember_size[iterationdepth] = 2*number_orbits*localblocklength; remember_operations[iterationdepth]=malloc((size_t)remember_size[iterationdepth]); if (remember_operations[iterationdepth]==NULL) { fprintf(stderr,"Can't allocate %d items to store orbits -- exiting.\n",remember_size[iterationdepth]); exit(3); } } if (doit) { for (i=j=0; i%d) ",start, end); else if (GETTYPE(op,i)==1) fprintf(stderr,"(%d<->%d) ",start, end); else fprintf(stderr,"(%d<-%d) ",start, end); } fprintf(stderr,"\n"); return; } void writegraph_edgeorb(graph *g, int aantal_toppen,int aantal_bogen, int aantal_gerichte_bogen) // for graphs as they are used in the edgeorbit routines { int i,j; fprintf(stderr,"---------------------------------------------------------\n"); fprintf(stderr,"Graph with %d vertices, %d edges, %d already directed\n",aantal_toppen,aantal_bogen, aantal_gerichte_bogen); for (i=0; i=0) { for (beste=1;listlen[beste]==0; beste++); (listlen[beste])--; /* zo wordt hij ook verwijderd */ start=list[beste][listlen[beste]]; for (i=0; i=aantal_gerichte_bogen) { for (beste=1;listlen[beste]==0; beste++); (listlen[beste])--; /* zo wordt hij ook verwijderd */ start=list[beste][listlen[beste]]; for (i=0; i=0) return; } else { if (edge_fixed) *completelyfixededge=i; } } } mark_components(graaf,adj,aantal_bogen,number); *specialexists=-1; } void mark_orbitnumbers(int number[], BOOG list_of_dir_edges[], int listlength) /* Computes the orbits of the DIRECTED edges in list_of_dir_edges. All edges in list_of_dir_edges[] are interpreted as going from ...[][0] to ...[][1]*/ { int i, j, pos2; BOOG boog; int positie[2*MAX_BOGEN][2*MAX_BOGEN]; int graaf[MAX_BOGEN][MAXN], adj[MAX_BOGEN]; for (i=0; i=0) { COPYBOOG(kleinste_orbit[0],edgelist[orbit_met_een]); *orbitsize=1; return; } /* else */ //for (i=0; i1) && (aantallen[i]max) max=aantallen[i]; } *biggest_orbit=max; /* it is possible that no orbit was found -- that is: in spite of the fact that the group is NOT trivial, it does act trivially on the set of still undirected edges: */ if (minorb== -1) { *orbitsize=0; return; } /* nu worden de bogen uit de gekozen orbit in de lijst kleinste_orbit geschreven */ for (i=j=0; i=0) { COPYBOOG(kleinste_orbit[0],edgelist[orbit_met_een]); *biggest_orbit=MAXPAR_ORBSIZE+1; *orbitsize=1; return; } /* else */ for (i=0; i1) || (orbits[edgelist[i][0]]==orbits[edgelist[i][1]]))) { min=aantallen[i]; minorb=i; } if ((aantallen[i])>max) max=aantallen[i]; } *biggest_orbit=max; /* it is possible that no orbit was found -- that is: in spite of the fact that the group is NOT trivial, it does act trivially on the set of still undirected edges: */ if (minorb == -1) { *orbitsize=0; return; } /* the group only permutes isolated vertices */ /* nu worden de bogen uit de gekozen orbit in de lijst kleinste_orbit geschreven */ for (i=j=0; i1) { if (!group_up_to_date) { number_of_generators=0; memcpy(bufferlab,lab[nextstep_depth],aantal_toppen*sizeof(int)); memcpy(bufferptn,ptn[nextstep_depth],aantal_toppen*sizeof(int)); nauty(workg,bufferlab,bufferptn,NULL,orbits,&options_directed_canon,&stats,\ workspace, 100*MAXN,1,aantal_toppen,canong); group_up_to_date=1; } mark_orbitnumbers(number,bufferlist,buffersize); for (i=n_ext=0; iy is canonical -- that is: choose one with minimal QUALITY and amongst them one with biggest (deg[end]+maxoutdeg-outdeg_free[end])<<6+indeg_free[start] the smallest among all lexicographic pairs canon_number(a),canon_number(b) with a->b or b-> a a directed edge in list[]. Then check whether a->b is in the same orbit as the edge of this smallest pair. The group must be up to date. */ { int i, minx=INT_MAX, miny=INT_MAX, a,b, which=INT_MAX; /* Some of the initializations are not necessary -- just to get rid of warnings if compiled with -Wall */ int canonnumber[MAXN],x,y; int number[MAX_BOGEN]; int candidate[MAX_BOGEN]; int referencesum, sum, gotacandidate, endquality, lq, eq, expensivequality; x=list[last_positie][0]; y=list[last_positie][1]; referencesum=QUALITY(x,y); endquality=((deg[y]+maxoutdeg-outdeg_free[y])<<6)-indeg_free[x]; expensivequality= -1; candidate[last_positie]=1; gotacandidate=0; for (i=0; iendquality) return 0; if (lq==endquality) { /* OK -- nog een poging */ if (expensivequality== -1) expensivequality=getexpensivequality(workg[x],workg[y]); eq=getexpensivequality(workg[a],workg[b]); if (eq0) && (outdeg_free[end]-numberout[end]>0)) // incoming possible { SETOP(op,positie,2); numberin[start]++; numberout[end]++; do_extensions_par(orbit,positie-1,numberin,numberout,numberdouble,op); numberin[start]--; numberout[end]--; } if (double_allowed && (indeg_free[start]-numberin[start]>0) && (indeg_free[end]-numberin[end]>0) && (outdeg_free[start]-numberout[start]>0) && (outdeg_free[end]-numberout[end]>0) && (double_free[start]-numberdouble[start]>0) && (double_free[end]-numberdouble[end]>0)) // double possible { SETOP(op,positie,1); numberin[start]++; numberin[end]++; numberout[start]++; numberout[end]++; numberdouble[start]++; numberdouble[end]++; do_extensions_par(orbit,positie-1,numberin,numberout,numberdouble,op); numberin[start]--; numberin[end]--; numberout[start]--; numberout[end]--; numberdouble[start]--; numberdouble[end]--; } if ((indeg_free[end]-numberin[end]>0) && (outdeg_free[start]-numberout[start]>0)) // outgoing possible { SETOP(op,positie,0); numberin[end]++; numberout[start]++; do_extensions_par(orbit,positie-1,numberin,numberout,numberdouble,op); numberin[end]--; numberout[start]--; } return; } /*************************COMPUTE_PAR_EXTENSIONS********************************/ int compute_par_extensions(BOOG orbit[], int orbitsize) // orbitsize must be at least 1 { int numberin[MAXN], numberout[MAXN], numberdouble[MAXN], i; unsigned int op; for (i=0; i1) && (orbitsize<=SWITCHPAR_ORBSIZE) && (maxorbit<=MAXPAR_ORBSIZE)) { parallel_orbit_labelling(kleinste_orbit, orbitsize); return; } compute_extensions(kleinste_orbit, orbitsize, extensionlist,&number_of_extensions); for (i=0; i0, so nauty was just called and the group is up to date */ { if (done_in_orbit+1 < orbitsize) nontrivlabels(kleinste_orbit, done_in_orbit+1, orbitsize,al_gericht,maxorbit); else trynextstep(); } outdeg_free[start]++; indeg_free[end]++; saturated[start]--; saturated[end]--; is_gericht[start][end]=is_gericht[end][start]=0; ADDELEMENT(workg+end,start); group_up_to_date=0; aantal_gerichte_bogen--; if (!virtual_gericht[start][end]) { virtual_outdeg[start]--; virtual_indeg[end]--; for (j=0; j= orbitsize-done_in_orbit) && (orbitsize>done_in_orbit)) { if (allemaal_doubles_mogelijk(kleinste_orbit,orbitsize,marklist,&marklistend)) { trynextstep(); } reset_doubles(kleinste_orbit,marklist,marklistend); } } int connected(graph g[], int aantal_toppen) { int i, list[MAXN], length, j; graph reached, dummy; reached=(graph)0; ADDELEMENT(&reached,0); list[0]=0; list[1]= -1; length=1; for (i=0; igrens) return 0; memcpy(g,globalg,n*sizeof(graph)); memcpy(deg,globaldeg,n*sizeof(int)); for (i=0; igrens) { return 0; } FORALLELEMENTS(g[top],j) { DELELEMENT(g+j,top); deg[j]--; /* topmaxgraphdeg) maxgraphdeg=deg[i]; aantal_bogen+=deg[i]; } aantal_bogen /= 2; /* als maxindeg==maxoutdeg is gegarandeerd dat voor elke graaf die gegenereerd word ook een manier bestaat om richtingen toe te kennen zonder de voorwaarden te schenden */ if (maxgraphdeg>2*mingerichtdeg) { if (test_possible(g,deg,n,aantal_bogen,mingerichtdeg)==0) return; } //if ((maxgraphdeg<=maxindeg) && (maxgraphdeg<=maxoutdeg)) nodegbound=1; else nodegbound=0; if (aantal_bogen==0) { addnumber=1; MAYBEPROCESS; WRITEUP(); return; } if (double_allowed) max_doubles=remaining_doubles=watermaxedges-aantal_bogen; else max_doubles=remaining_doubles=0; if (remaining_doubles) { /* deg[i] is altijd <= maxindeg+maxoutdeg */ for (i=0;imaxgraphdeg) maxgraphdeg=deg[i]; if (deg[i]!=deg[0]) regular=0; } if ((maxgraphdeg<=maxindeg) && (maxgraphdeg<=maxoutdeg)) nodegbound=1; else nodegbound=0; if (maxindeg<=maxoutdeg) { maxedges=aantal_toppen*maxindeg; minrestriction=maxindeg;} else { maxedges=aantal_toppen*maxoutdeg; minrestriction=maxoutdeg; } if (maxedgesmaxdeg) return; if (deg[i]((2*aantal_toppen)/3)))) { //waterclusteruse++; waterclusters (staticg, aantal_toppen); return; } //water_v_use++; memcpy(workg,staticg,sizeof(graph)*aantal_toppen); for (i=0; iE long long too short; This may cause problems with the hashing function for large degree -- exit().\n"); exit(1); } nauty_check(WORDSIZE,1,MAXN,NAUTYVERSIONID); /* BDM: Check compatible nauty is linked */ for (i=1; i=MAXN) { fprintf(stderr,"At most %d vertices possible -- exiting.\n",MAXN-1); exit(1); } if (multicode) decode_to_nauty(code,codelaenge,staticg,deg); else /* g6code */ init_for_g6(staticg,aantal_toppen,deg); // direct_edges(staticg, aantal_toppen); direct_edges(); // BDM last=aantal_gerichte_grafen; } fprintf(stderr,"Number of directed graphs: %lld \n",aantal_gerichte_grafen); #ifdef SUMMARY SUMMARY(); #endif //fprintf(stderr,"Used edge routines %u times.\n",waterclusteruse); //fprintf(stderr,"Used vertex routines %u times.\n",water_v_use); return 0; }