/* Analyzer for automorphisms, vertex equivalence classes, Also includes code to produce input to AT&T graph formatters */ /* Bisection width calculation is based on dynamic programming technique in J.A. Lukes, Combinatorial Solution to the Partitioning of General Graphs, IBM J. on R & D, Vol 19, March 1975. Coded recursively to keep storage management simple */ /* Bisection width calculation performed by METIS */ /* Graph formatter inputs are written to separate files */ /* Adjacency lists replace adjacency matrices */ #include /* va_start, va_arg, va_end */ #include /* EXIT_FAILURE, malloc, qsort */ #include #include #include #include #define MAXN 32675 #include "../NAUTY/nauty.h" #include "../metis-3.0/Lib/defs.h" #include "../metis-3.0/Lib/struct.h" /* #include "../metis-3.0/Lib/macros.h" not needed, conflicts w/ nauty.h*/ #include "../metis-3.0/Lib/rename.h" #include "../metis-3.0/Lib/proto.h" /* FORMATTING is to indicate that an execution is to produce input to neato (undirected) or dot (directed). INCLUDELABELS indicates that labels are desired in nodes ANALYZING indicates that analysis information is to be written to stdout. APPROXBISECTION indicates the largest graph size (in vertices) for which Lukes's algorithm should be used. If larger than this, METIS is used. EDGESGUESS is a guess at the number of edges per vertex. DISCARDSELFLOOPS indicates whether reflexive (i,i) edges are removed. */ #define FORMATTING TRUE #define INCLUDELABELS FALSE #define ANALYZING TRUE #define APPROXBISECTION 40 #define EDGESGUESS 20 #define DISCARDSELFLOOPS FALSE /* For NAUTY */ graph g[MAXN*MAXM]; nvector lab[MAXN],ptn[MAXN],orbits[MAXN]; static DEFAULTOPTIONS(options); statsblk(stats); setword workspace[50*MAXM]; int numWords,v; set *gv; /*****************/ FILE *scriptDescript; /* script for running formatters */ float nautyCPU=0.0,metisCPU=0.0,memoryCPU=0.0,hashCPU=0.0,LukesCPU=0.0, permutationCPU=0.0,sortCPU; int numVertices,numEdges,vertexCount; struct edge { int tail,head; }; typedef struct edge edgeType; edgeType edgeTab[EDGESGUESS*MAXN]; int *tailHeader,*headTab,*headHeader,*tailTab; char **vertexLabel; int *hashLabel; char graphName[60]; /* For Lukes's bisection width computation */ int W; int **conn,**adjacency; int *workPartition; int partitionCount,*bestPartition,bestValue; void memoryDie() { printf("Out of memory\n"); exit(0); } float CPUseconds() { struct rusage rusage; getrusage(RUSAGE_SELF,&rusage); return rusage.ru_utime.tv_sec+rusage.ru_utime.tv_usec/1000000.0; } int twoPower(k) /* Computes powers of two as int */ int k; { return 1<tail - y->tail; if (result!=0) return result; else return x->head - y->head; } int headThenTail(const void* xin, const void* yin) { int result; edgeType *x,*y; x=(edgeType*) xin; y=(edgeType*) yin; result=x->head - y->head; if (result!=0) return result; else return x->tail - y->tail; } void buildAdjacencies() { int duplicates=0,i,j,k; float startCPU; tailHeader=(int*) malloc((numVertices+1)*sizeof(int)); headTab=(int*) malloc(numEdges*sizeof(int)); headHeader=(int*) malloc((numVertices+1)*sizeof(int)); tailTab=(int*) malloc(numEdges*sizeof(int)); if (!tailHeader || !headTab || !headHeader || !tailTab) memoryDie(); startCPU=CPUseconds(); qsort(edgeTab,numEdges,sizeof(edgeType),tailThenHead); sortCPU+=CPUseconds()-startCPU; j=k=0; for (i=0;i0 && edgeTab[j].tail==edgeTab[j-1].tail && edgeTab[j].head==edgeTab[j-1].head) { duplicates++; j++; continue; } headTab[k++]=edgeTab[j++].head; } } tailHeader[numVertices]=k; if (duplicates>0) printf("%d duplicates found & removed\n",duplicates); startCPU=CPUseconds(); qsort(edgeTab,numEdges,sizeof(edgeType),headThenTail); sortCPU+=CPUseconds()-startCPU; j=k=0; for (i=0;i0 && edgeTab[j].tail==edgeTab[j-1].tail && edgeTab[j].head==edgeTab[j-1].head) { j++; continue; } tailTab[k++]=edgeTab[j++].tail; } } headHeader[numVertices]=k; numEdges-=duplicates; } void connectedSet() { /* See p. 172 of Lukes' paper for definition */ /* Warshall's algorithm has been adapted */ int i,j,k,**work; /* work is the number of vertices on the path */ work=(int**) malloc(numVertices*sizeof(int*)); if (!work) memoryDie(); for (i=0;i=0;j--) { for (i=0;ibestValue) { /**/ printf("bisection improved from %d to %d\n",bestValue,inputValue); /**/ bestValue=inputValue; for (i=0;iAPPROXBISECTION || bisectionWidth==(-1)) /* METIS code */ { startCPU=CPUseconds(); METIS_PartGraphRecursive(&numVertices, tailHeader,headTab, NULL,NULL, &wgtflag, &Cstyle, &nparts, &defaultOptions, &bisectionWidth, bestPartition); metisCPU+=CPUseconds()-startCPU; partitionSize[0]=partitionSize[1]=0; for (i=0;i1 && ANALYZING) printf("METIS returned unbalanced partitions %d %d!!!\n", partitionSize[0],partitionSize[1]); queue=(int*) malloc(numVertices*sizeof(int)); mark=(int*) malloc(numVertices*sizeof(int)); if (!queue || !mark) memoryDie(); for (i=0;i1) { /* Vertex display properties based on vecs */ for (i=0;i\"%s\";\n",vertexLabel[i],vertexLabel[headTab[j]]); } else { /* Color the partitions as blue and green, but the bisecting edges are colored red */ for (i=0;ioutdegree) outdegree=k; k=headHeader[i+1]-headHeader[i]; if (k>indegree) indegree=k; } /* Run breadth-first search once for each vec */ queue=(int*) malloc(numVertices*sizeof(int)); dist=(int*) malloc(numVertices*sizeof(int)); if (!queue || !dist) memoryDie(); diameter=0; for (i=0;idiameter) diameter=dist[j]; for (k=tailHeader[j];k>i)&1==1;i++) ; if (i==0) i=1; return radixLeaf-bits+i+(twoPower(bits)-1-radixLeaf)/2; } int vecCount(int bits,int radixLeaf) { /* Computes the number of vertex equivalence classes for a binary heap tree */ int work; int i; for (i=0;(radixLeaf>>i)&1==1;i++) ; work=i+1+bits*(bits+1)/2-i*(i+1)/2; for (;i>i)&1==1) work++; return work; } void tree(n) int n; { int i,j,bits,leafNumber; double autos; int vec,indegree,outdegree,diameter; char string1[20],string2[20]; sprintf(graphName,"tree(%d)",n); numVertices=n; allocate(); for (i=2;i<=n;i++) { j=i/2; sprintf(string1,"%d",i); sprintf(string2,"%d",j); connectVertices(string1,string2); connectVertices(string2,string1); } bits=0; leafNumber=n-1; while (leafNumber>=twoPower(bits)) { leafNumber-=twoPower(bits); bits++; } if (n==1) { autos=1.0; vec=1; indegree=0; outdegree=0; } else if (n==2) { autos=2.0; vec=1; indegree=1; outdegree=1; } else if (n==3 || n==4) { autos=2.0; vec=2; indegree=2; outdegree=2; } else { autos=twoPowerBig(powerCount(bits,leafNumber)); vec=vecCount(bits,leafNumber); indegree=3; outdegree=3; } diameter=0; for (i=2;i<=n;i*=2) diameter++; i/=2; diameter*=2; if (n1) for (j=1;j<=leavesInOneTree;j++) { sprintf(string1,"tree%dleaf%d",i,j); sprintf(string2,"tree%dinternal%d",i,(internalNodesInOneTree+j)/2); connectVertices(string1,string2); connectVertices(string2,string1); } } for (i=1;icolTreeHeight) abort(); sprintf(graphName,"meshCBT(%d,%d)",rowTreeHeight,colTreeHeight); nodesInOneRowTree=(twoPower(rowTreeHeight+1))-1; leavesInOneRowTree=twoPower(rowTreeHeight); internalNodesInOneRowTree=nodesInOneRowTree-leavesInOneRowTree; nodesInOneColTree=(twoPower(colTreeHeight+1))-1; leavesInOneColTree=twoPower(colTreeHeight); internalNodesInOneColTree=nodesInOneColTree-leavesInOneColTree; nodesInAllRowTrees=leavesInOneColTree*nodesInOneRowTree; numVertices=nodesInAllRowTrees +leavesInOneRowTree*internalNodesInOneColTree; allocate(); /* Row tree edges */ for (i=1;i<=leavesInOneColTree;i++) { for (j=2;j<=internalNodesInOneRowTree;j++) { sprintf(string1,"row%dinternal%d",i,j); sprintf(string2,"row%dinternal%d",i,j/2); connectVertices(string1,string2); connectVertices(string2,string1); } if (leavesInOneRowTree>1) for (j=1;j<=leavesInOneRowTree;j++) { sprintf(string1,"row%dcol%d",i,j); sprintf(string2,"row%dinternal%d",i,(internalNodesInOneRowTree +j)/2); connectVertices(string1,string2); connectVertices(string2,string1); } } /* Column tree edges */ for (i=1;i<=leavesInOneRowTree;i++) { for (j=2;j<=internalNodesInOneColTree;j++) { sprintf(string1,"col%dinternal%d",i,j); sprintf(string2,"col%dinternal%d",i,j/2); connectVertices(string1,string2); connectVertices(string2,string1); } if (leavesInOneColTree>1) for (j=1;j<=leavesInOneColTree;j++) { sprintf(string1,"row%dcol%d",j,i); sprintf(string2,"col%dinternal%d",i,(internalNodesInOneColTree +j)/2); connectVertices(string1,string2); connectVertices(string2,string1); } } if (rowTreeHeight==0) { autos=twoPowerBig(twoPower(colTreeHeight)-1); vecs=colTreeHeight+1; if (colTreeHeight<2) indegree=outdegree=2; else indegree=outdegree=3; } else if (rowTreeHeight==colTreeHeight) if (rowTreeHeight==1) { autos=16.0; vecs=1; indegree=outdegree=2; } else { autos=twoPowerBig(twoPower(rowTreeHeight+1)-1); vecs=rowTreeHeight+1; indegree=outdegree=3; } else { autos=twoPowerBig(twoPower(rowTreeHeight)+twoPower(colTreeHeight)-2); vecs=rowTreeHeight+colTreeHeight+1; indegree=outdegree=3; } diameter=2*(rowTreeHeight+colTreeHeight); bisectionWidth=twoPower(rowTreeHeight); nautyVerify(graphName,autos,0,vecs,diameter,indegree,outdegree, bisectionWidth); deallocate(); } void CBTmesh(k) int k; { int level,i,j,rows,columns; int treeNode; char string1[20],string2[20]; sprintf(graphName,"CBTmesh(%d)",k); if (k==0) return; numVertices=(2*k+1)*twoPower(k)*twoPower(k); allocate(); /* Build mesh for each tree node */ rows=columns=twoPower(k); for (level=0;level<2*k;level++) { for (treeNode=twoPower(level);treeNode0) { sprintf(string2,"n%dr%dc%d",treeNode,i-1,j); connectVertices(string1,string2); } if (i0) { sprintf(string2,"n%dr%dc%d",treeNode,i,j-1); connectVertices(string1,string2); } if (j0) { sprintf(string2,"%d",i-1); connectVertices(string1,string2); } sprintf(string2,"%d",i+1); connectVertices(string1,string2); sprintf(string2,"%d",2*i); connectVertices(string1,string2); } for (i=numVertices/2;i=0) { sprintf(string2,"row%dcol%d",i-1,j); connectVertices(string1,string2); } if (i+1=0) { sprintf(string2,"row%dcol%d",i,j-1); connectVertices(string1,string2); } if (j+1n) abort(); sprintf(graphName,"twoDmesh2(%d,%d)",m,n); numVertices=m*n; allocate(); for (i=0;i=0) { sprintf(string2,"row%dcol%d",i-1,j); connectVertices(string1,string2); } if (i+1=0) { sprintf(string2,"row%dcol%d",i,j-1); connectVertices(string1,string2); } if (j+1n) abort(); sprintf(graphName,"twoDtorus2(%d,%d)",m,n); numVertices=m*n; allocate(); for (i=0;i=0;i--) columnValue[i]=columnValue[i+1]*(n-i-1); card=n*columnValue[0]; /* printf("column values:\n"); for (i=0;in) abort(); sprintf(graphName,"twoDtorusGroup(%d,%d)",m,n); generator=(int**) malloc(4*sizeof(int*)); if (!generator) memoryDie(); for (i=0;i<4;i++) generator[i]=(int*) malloc((m+n)*sizeof(int*)); for (i=0;i<4;i++) for (j=0;jn || n>p) abort(); sprintf(graphName,"threeDtorusGroup(%d,%d,%d)",m,n,p); generator=(int**) malloc(6*sizeof(int*)); if (!generator) memoryDie(); for (i=0;i<6;i++) generator[i]=(int*) malloc((m+n+p)*sizeof(int*)); for (i=0;i<6;i++) for (j=0;j