// Written by Ari Trachtenberg 12/1998. /* This is a heavily optimized version of Ari's tiling.C. */ /* Looks for a full-rank tiling in 8 dimensions. Specifically, we need two sets V and A in F_2^8 with the following properties: 1. each set contains the zero vector 0^8 2. each set has 16 vectors 3. each set has full rank (i.e. it's linear combinations generate the entire vector space) 4. the sets V+V intersect A+A = {0} (i.e. V+V = {x | x = v_1 + v_2, v_1,v_2 in V}) */ /* COMMENTING STYLE: 1) #endif is usually followed by a comment explaining which #ifdef it is closing 2) procedures/functions are commented as follows: %R: items in this line are REQUIRED to be true before the procedure is called %M: items in this line are MODIFIED by the procedure %E: items in this line are the EFFECTS of the procedure */ /* COMPILER DIRECTIVES: ** ** Normal compilation includes: -DKEEP_INFORMED -DNAUTY -DTRAP_SIGNALS ** ** ...debugging flags ** DOUBLE_CHECK Double check many computations using alternative means ** DEBUG_ISOMORPHIC Includes code to debug graph isomorphism routines ** DEBUG_RECURSE_TILINGS Includes debugging code for the recurse_tilings procedure ** DEBUG_FILL_REST Includes debugging code for the fill_rest procedure (obsolete) ** DEBUG_INSERT Debug the "insert" procedure ** PROFILE Include code to stop program after a certain point for profiling reasons ** KEEP_INFORMED Includes extra info to keep the user informed ** TEST_BASES Includes code to test (and debug) the basis construction in array A ** FORK Includes for forking 16 processes to look for the tiling ** ** ...operational flags ** NAUTY Includes code to use the nauty procedure to check for isomorphisms ** SEED Seeds the A vector search with values from the SEEDS array ** TRAP_SIGNALS Outputs important memory to stdout upon a signal (e.g. interrupt) */ /* VARIOUS CONSTANTS: ** #define NAUTY_MAX 7 // maximum size of bases on which to use the NAUTY subroutine ** #define STAT_INDEX 5 // the index of ISOMORPH_MEM used to estimate remaining search time ** #ifdef SEED ** const int SEEDS[]={0,248,247,11,23,45,94,98,163} ** // the initial seed basis of A from which to start ** // the search; note that the first seed vector must ** // be zero, and that the subsequent vectors must ** // correspond to a basis that the program will ** // see sometime in its execution ** #endif */ #include #include #include #include #include #include #include #include #include #include #include "constants.h" #include "bits.h" #include "matrix.h" #include "permute.h" // defined constants #ifdef FORK FILE *fp; // for the kill file static int fork_id=-1; // uniquely identifies each fork, starting with 0 long tot_bases; // a variable whose contents are set in setup(*) #endif // FORK // global variables short HTMLIFY; // if I want to make this outputable to an HTML document at some later stage int A[16],V[16]; // the A and V sets, respectively #ifdef SEED int seed_index=0; // the SEEDS[] array is used to seed the search of bases "A"; this is the index of SEEDS that has already been seeded into A int init_bases=0; // used for SEED computation to insure correct statistics int init_isos=0; // used to insure correct statistics to report to the user #endif // SEED int subspace[256]; /* stores temporarily the current subspace being considered; ** each entry is blank if the vector is not in the subspace, and ** otherwise contains the index (of the iteration) at which the the vector ** was introduced into the subspace */ int equal_cols[9]; /* keeps track of equal columns so as to remove isomorphisms; ** the i'th entry of this array is an integer whose $1$ locations ** correspond to equal columns (least significant bit is first) ** ** in a sense, each entry is a partition of the columns ** this array is terminated by the constant UNDEFINED */ int APA[256], VPV[256];/* APA is the set A+A (in other words {a+a' | a,a' in A}. Similarly ** VPV is the set V+V. The i'th entry of the array is FALSE if i is not ** in the set and TRUE otherwise */ #ifdef MAINTAIN_SIZE int APA_size, VPV_size; /* number of non-zero entries in the APA and VPV arrays respectively */ #endif // MAINTAIN_SIZE #ifdef NAUTY graph **isomorphics[9];// isomorphics[ii] contains non-isomorphic bases found so far of size ii (including the all-zero vector) int iso_sizes[9]; // iso_sizes[ii] contains the current size of isomorphics[ii] #ifdef DOUBLE_CHECK matrix *isomorph_mat[9]; // same data as isomorphics, but in matrix form for use with double-checking #endif // DOUBLE_CHECK // ... global nauty-related variables nvector *lab,*ptn,*orbits; static DEFAULTOPTIONS(options); setword workspace[50]; statsblk(stats); #endif // NAUTY int no_list[16][2]={{224,31},{224,159},{224,223},{224,255},{224,28},{224,156},{224,220},{224,252}, {248,135},{248,199},{248,231},{248,247},{248,255},{248,228}, {254,225},{254,249}}; /* * initially seed with the first sixteen possible choices ** for A[1] and A[2]. This contains the ** * list of basis vectors that we risk counting twice in our basis search; ** once under the current set A[1] and A[2] and once when they appear ** as the first and second bases in A ** * only entries [0] through [14] are actually used, the last one is a ** place-holder */ double skipped_choices;// counts the number of choices skipped (roughly) long bases; // counts the number of bases seen so far int vects_seen; // keeps track of the maximum number vectors we have been able to fit into // (V,A) so far // ... for time-keeping time_t start_time; // when we started the program time_t last_update; // the last time we updated the user // AUXILIARY AND INLINED FUNCTIONS inline short int weightge3(int val) { // %E: returns TRUE iff val has at least three 1's in its binary representation (else FALSE) short int num_ones=0; if ((val & 1) != 0) num_ones++; if ((val & 2) != 0) num_ones++; if ((val & 4) != 0) num_ones++; if ((val & 8) != 0) num_ones++; if (num_ones>=3) return TRUE; if ((val & 16) != 0) num_ones++; if ((val & 32) != 0) num_ones++; if (num_ones>=3) return TRUE; if ((val & 64) != 0) num_ones++; if ((val & 128) != 0) num_ones++; if (num_ones>=3) return TRUE; else return FALSE; } // ... for debugging inline void print_array(int the_array[16], char *name) { // %E: prints out the information stored in an array of size 16 whose name is "name" int ii,jj; cout << name << ":" << endl; for (ii=0; ii<16; ii++) { cout << " " << setw(2) << ii << ": "; if (the_array[ii]==UNDEFINED) cout << "---" << endl; else { bits the_val(the_array[ii]); // pretty-printing for (jj=0; jj<(7-lg(the_val)); jj++) cout << "0"; the_val.printon(cout,2); cout << " [" << the_array[ii] << "]" << endl; } } cout << endl; } #ifdef NAUTY void print_graph(set the_array[16], char *name) { // %E: prints out the information stored in an array of size 16 whose name is "name" int ii,jj; cout << name << ":" << endl; for (ii=0; ii<16; ii++) { cout << " " << setw(2) << ii << ": "; if (the_array[ii]==UNDEFINED) cout << "---" << endl; else { bits the_val(the_array[ii]); // pretty-printing for (jj=0; jj<(7-lg(the_val)); jj++) cout << "0"; the_val.printon(cout,2); cout << " [" << the_array[ii] << "]" << endl; } } cout << endl; } #endif inline void print_space(int subspace[256],char *name) { // %E: prints out the non-zero values of the 256 integer array whose name will be "name" int ii, jj, count; count=0; cout << name << ":" << endl; for (ii=0; ii<256; ii++) if (subspace[ii]) { bits the_val(ii); // pretty-printing cout << " " << setw(2) << (count++) << ": "; for (jj=0; jj<(7-lg(the_val)); jj++) cout << "0"; the_val.printon(cout,2); cout << " [" << ii << "] introduced at index " << subspace[ii] << endl; } } void print_time(time_t elapsed) { // %E: pretty prints "elapsed" seconds onto cout int days, hours, mins, secs; //cout << "Time: " << elapsed << endl; days=(int) floor(elapsed/86400); //86400==24*3600 elapsed-=days*86400; hours=(int) floor(elapsed/3600); elapsed-=hours*3600; mins=(int) floor(elapsed/60); elapsed-=mins*60; secs=elapsed; if (days!=0) { cout << days << " days " << hours << " hrs " << mins << " min " << secs << " secs"; } else if (hours!=0) { cout << hours << " hours " << mins << " min " << secs << " secs"; } else if (mins!=0) { cout << mins << " minutes " << secs << " secs"; } else { cout << secs << " seconds"; } } inline void print_entries(int* the_array, char *name) { /* %R: the_array values are assumed to be 8-bit integers ** %E: prints out the (reversed) information stored in the array whose name is "name" until ** an UNDEFINED value is reached */ int ii,jj; // looping cout << name << ":" << endl; // i.e. the name for (ii=0; the_array[ii]!=UNDEFINED; ii++) { cout << " " << setw(2) << ii << ": "; // pretty-printing for (jj=7; jj>=0; jj--) if (the_array[ii]&TWO_POW[jj]) cout << "1"; else cout << "0"; cout << " [" << the_array[ii] << "]" << endl; } cout << endl; } inline int verify_V(int index) { /* %R: index<16 ** %M: VPV ** %E: Checks: ** * returns FALSE if V[index] is already among V[0 %E: returns true iff entries A[1..Alast] are linearly independent */ matrix the_mat=matrix(Alast,8,A+1); short response=the_mat.reduce(); the_mat.destroy(); return response; } short check_intersect(int Alast,int Vlast) { /* %R: Alast>0, Vlast>0 %E: returns true iff the sum of every two entries from A[0..Alast] is different from the sum of every two entries from V[0..Vlast] (except A[0]+A[0]=V[0]+V[0]=0) AND there are no repeated entries in A (respectively V) */ int ii,jj; int thisAPA[256],thisVPV[256]; // initialize for (ii=0; ii<256; ii++) thisAPA[ii]=thisVPV[ii]=0; // compute thisAPA and thisVPV for (ii=0; ii<=Alast; ii++) for (jj=ii+1; jj<=Alast; jj++) if (A[ii]==A[jj]) return FALSE; else thisAPA[ A[ii]^A[jj] ] = 1; for (ii=0; ii<=Vlast; ii++) for (jj=ii+1; jj<=Vlast; jj++) if (V[ii]==V[jj]) return FALSE; else thisVPV[ V[ii]^V[jj] ] = 1; // check for equality for (ii=0; ii<256; ii++) if (thisAPA[ii] && thisVPV[ii]) return FALSE; // found an instance where they are both 1 return TRUE; } #endif // DOUBLE_CHECK #ifdef NAUTY inline void insert(graph **new_graph_ptr, graph **the_isomorph, int isomorph_size,int compare(const void *, const void *)) /* %R: the_isomorph must contain isomorph_size-1 graphs (graph *'s) that are sorted ** by means of the "compare" function ** %M: the_isomorph ** %E: inserts *new_graph_ptr into the_isomorph while maintaining order (w/r to "compare"); ** this is a simple insertion into a sorted array using binary search */ { long int loc=0; // find the proper location // while (loc1) { half_way=(end+beg)/2; if (compare(new_graph_ptr,the_isomorph+half_way)==-1) end=half_way; else beg=half_way; } loc=end; // I need to replace the bigger index } // move the remaining elements memmove(the_isomorph+loc+1,the_isomorph+loc,(isomorph_size-loc-1)*sizeof(graph *)); // add the new graph the_isomorph[loc]=*new_graph_ptr; #ifdef DEBUG_INSERT for (loc=0; loc=height>0 %E: returns 1 if (*p1) is lexicographically later than (*p2), 0 if they are equal, and -1 if (*p1) is earlier; only considers the first "height" vectors in both graphs */ { int ii; for (ii=0; ii=3 // other variables int ii, jj, count; // for looping int temp_cols[9]; // temporary array for storing equal_cols entries // for nauty #ifdef NAUTY graph **canon_ptr; // the canonical graph for the sub-basis considered so far #ifdef DOUBLE_CHECK graph **canon_ptr2; // for double-checking work #endif graph **found_graph; matrix my_mat; graph *A_graph; #endif #ifdef DEBUG_RECURSE_TILINGS int sub_size=0; for (ii=0; ii<256; ii++) if (subspace[ii]) sub_size++; // print_array(A,"A"); // print_space(subspace,"Subspace"); // cout << "index is " << index << endl; // cout << "subspace size: " << sub_size << endl; if (sub_size!=1 && sub_size!=2 && sub_size!=4 && sub_size!=8 && sub_size!=16 && sub_size!=32 && sub_size!=64 && sub_size!=128 && sub_size!=256) { cout << "ERR: Subspace compromised: error!" << endl; exit(-1); } #endif #ifndef TEST_BASES #define MAX_INDEX 9 // the normal recursion limit #else #define MAX_INDEX 5 // the recursion limit for testing purposes #endif // TEST_BASES if (index==MAX_INDEX) { // RECURSION TERMINATING CONDITION: we have a valid set of full_rank vectors bases++; //print_array(A,"A:"); // ...update the user? #ifdef PROFILE if (bases>=25) { cout << "Found: " << bases << " bases." << endl; cout << "Elapsed time: "; print_time(time(NULL)-start_time); cout << endl; exit(-1); // exit and dump profiling data } #else // !PROFILE if (time(NULL)-last_update > USER_WAIT) { last_update=time(NULL); cout << "Current status "; #ifdef FORK cout << "FOR PROCESS #" << fork_id; #endif // FORK cout << " after " << (last_update-start_time) << " seconds: " << endl; print_array(A,"A"); cout << endl; double frac_comp=0.0; // fraction of the space completed // try to compute an approximation for the number of vectors we've gone through #ifndef NAUTY // use another heuristic for (ii=3; ii=3? if (!weightge3(A[index])) { #ifdef DOUBLE_CHECK CONFIRM(check_intersect(index,9),FALSE); #endif // DOUBLE_CHECK continue; // the weight was too small } // ... is A[index] of dist >3 from A[ 8) { cout << "ERR: Current index = " << curr_ind << " > 8 for equal_col computation, " << "which shouldn't happen!" << endl; exit(-1); } // check correctness of equal_cols // convert each column into an integer int cols[8]; for (ii=0; ii<8; ii++) { cols[ii]=0; for (jj=0; jj<=index; jj++) if (A[jj]&TWO_POW[ii]) cols[ii]|=TWO_POW[jj]; } // check the equal_cols values for (ii=0; equal_cols[ii]!=UNDEFINED; ii++) { int first_col=FALSE; for (jj=0; jj<8; jj++) if (equal_cols[ii]&TWO_POW[jj]) { if (!first_col) first_col=cols[jj]; else if (cols[jj]!=first_col) { cout << "ERR: equal_cols values not correct!:::" << endl; cout << " cols[" << jj << "] != " << first_col << endl; print_array(A,"A"); print_entries(equal_cols,"equal_cols"); exit(-1); } } else // make sure the two columns are indeed different if (first_col && (cols[jj]==first_col)) { cout << "ERR: equal_cols values are wrong!:::" << endl; cout << " cols[" << jj << "] == " << first_col << " in iteration " << ii << "." << endl; print_array(A,"A"); print_entries(equal_cols,"equal_cols"); print_entries(temp_cols,"temp_cols"); exit(-1); } } #endif } // THE MAIN RECURSIVE CALL // go on to the next tiling; only try values > A[index]+1 after this point recurse_tilings(index+1, A[index]+1); if (count!=8) // if count was 8, then we really didn't change anything // reset equal_cols from temp_cols; for (ii=0; ii<=count; ii++) equal_cols[ii]=temp_cols[ii]; /* return the subspace to what it used to be; all contributions from A[ii] must have been unique or else we had a linear dependency */ for (ii=0; ii<256; ii++) if (subspace[ii]==index) // i.e. was it introduced at the last level? subspace[ii]=0; // i.e. reset to 0 #ifdef DEBUG_RECURSE_TILINGS for (ii=0; ii<256; ii++) if (copy_test[ii]!=subspace[ii]) { cout << "Major error at " << ii << endl; cout << "copy_test: " << copy_test[ii] << " vs subspace: " << subspace[ii] << endl; print_array(A,"A"); print_space(subspace,"Subspace"); exit(-1); } // cout << "integrity test passed!" << endl; #endif } } } void setup(int first, int second) { /* %M: A ** %E: sets up A with first vector "first" and second vector "second" and recurses to ** attempt to find tilings */ // variables int ii, jj; // for looping static int next_proc=0; // when I should fork for the next process static int num_proc=0; // how many processes we have so far // constants known at compile time #define SMALLEST_POSS 7 // the smallest 8-bit vector (in counting order) of weight >=3 #ifdef FORK const long NUM_BASES[16]= {1798666,1928586,2204627,1647596,15944537,17993024,20881918,15543236, 5646385,6724950,10267097,12329605,4543995,41874798,8413365,15124583}; fork_id++; // increment for each call pid_t child_address; if (fork_id==next_proc) { // time to fork // resetart time counters start_time=time(NULL); last_update=start_time-USER_WAIT; // for initial display of information // figure out when next to fork if (++num_proc==FORK_PROC)// i.e. we have only one process left next_proc=17; // keep going until the end else next_proc+=16/FORK_PROC; if (child_address=fork()) // i.e. I am the parent { fprintf(fp,"kill %d\n",child_address); // kills the child proc when the kill file is processed sleep(USER_WAIT/(FORK_PROC+5)); // stagger the processes return; // ... and go on to the next setup } tot_bases=NUM_BASES[fork_id]; // record the number of bases left } else return; // let the child deal with it #endif // set A and subspace entries A[1]=first; A[2]=second; subspace[A[1]]=1; subspace[A[2]]=2; subspace[A[1]^A[2]]=2; // i.e. make in the subspace // set up the no_list for (ii=0; ii<15; ii++) if (A[1]==no_list[ii][0] && A[2]==no_list[ii][1]) { // found the item...make it unchecked int temp[1][2]; // for swapping no_list[ii] and no_list[15] temp[0][0]=no_list[15][0]; temp[0][1]=no_list[15][1]; no_list[15][0]=no_list[ii][0]; no_list[15][1]=no_list[ii][1]; no_list[ii][0]=temp[0][0]; no_list[ii][1]=temp[0][1]; } // ... set equal_cols entries equal_cols[0]=equal_cols[1]=equal_cols[2]=equal_cols[3]=0; // i.e. initialize to 0 // there are four possible columns now: 00, 01, 10, and 11; each will get an entry for (ii=0; ii<8; ii++) { // find all like columns int curr_top=MAKE_BIT(A[1]&TWO_POW[ii]); int curr_bot=MAKE_BIT(A[2]&TWO_POW[ii]); if (curr_top==0 && curr_bot==0) equal_cols[0]|=TWO_POW[ii]; else if (curr_top==0 && curr_bot==1) equal_cols[1]|=TWO_POW[ii]; else if (curr_top==1 && curr_bot==0) equal_cols[2]|=TWO_POW[ii]; else if (curr_top==1 && curr_bot==1) equal_cols[3]|=TWO_POW[ii]; } // get rid of redundant entries int max=4; // the maximum number of equal_col entries we can have for (ii=0; ii