/* 2012 (C) Jussi Rintanen, jrintanen.jr@gmail.com */ #include #include #include "interface.h" #include "clausedb.h" #include "printplan.h" #include "main.h" #include "asyntax.h" #include "ordintsets.h" #include "operators.h" #include "translate2sat.h" #include #ifdef __LP64__ #include #endif #define noUCC #define ASSERTS #ifdef ASSERTS #define ASSERT(a) (assert(a)) #else #define ASSERT(a) (1) #endif #include "varvals.c" #define unlikely(expr) (__builtin_expect((expr),0)) #define likely(expr) (__builtin_expect((expr),1)) //#define unlikely(expr) (expr) //#define likely(expr) (expr) #define CACHEALIGN #ifdef CACHEALIGN #define CACHELINESIZE 64 #define CACHELINESIZEi 16 PTRINT cachelineboundary(PTRINT addr) { if(addr&63) { return addr+(64-(addr&63)); } else return addr; } PTRINT cachelineboundaryi(PTRINT addr) { if(addr&63) { return addr+(16-(addr&15)); } else return addr; } #else #define CACHELINESIZE 0 #define CACHELINESIZEi 0 PTRINT cachelineboundary(PTRINT addr) { return addr; } PTRINT cachelineboundaryi(PTRINT addr) { return addr; } #endif /* Interface to malloc */ void *ownalloc(int code,int i) { void *ptr = malloc(i); ASSERT(ptr); allocatedbyCL += i; check_malloc_success(ptr,1000+code); return (PTRINT)ptr; } #define min(a,b) (((a)<(b))?(a):(b)) #define max(a,b) (((a)>(b))?(a):(b)) /* Heap data structure */ #ifdef VSIDS heap heap_create(int); void freeheap(heap); int heap_emptyp(satinstance); int heap_taketop(satinstance); void heap_delete(satinstance,int); void heap_insert(satinstance,int,int); void heap_increment(satinstance,int); void heap_increment_by(satinstance,int,int); void heap_decay(satinstance); void checkheapconsistency(satinstance); #endif /* Macros for accessing - the value of a propositional variable, - the reason for the value of a propositional variable, - the decision level of the propositional variable, - the status (mainly the phase) of a propositional variable. */ #if defined(__LP64__) #define REASON_INPUT -2L #define REASON_DECISION -1L #else #define REASON_INPUT -2 #define REASON_DECISION -1 #endif int VALUE(int l) { return (l&1)^1; } int NEG(int l) { return l^1; } int VAR(int l) { return l>>1; } int PLIT(int v) { return v << 1; } int NLIT(int v) { return 1+(PLIT(v)); } int LIT(int v) { return PLIT(v); } /* Macros for accessing - the score of a literal, - the clauses in which the literal is watched, - two literal clauses -l V l' for a literal l. */ #define LITSCORE(l) (sati->lits[(l)].score) /* Macros for accessing the heap. */ #define HINDEX(v) (sati->hindex[(v)]) void report_malloc(char *arrayname,int size) { #ifdef DEBUG printf("%s: %.2f MB\n",arrayname,((float)size)/1024.0/1024.0); #endif } /****** INCLUDED SOURCE FILES: ********************************************/ #include "heuristics2.c" /**************************************************************************/ #define noDEBUG #define noHARDDEBUG /* Initialize data structures used for all problem instances. */ int adjustheap; #ifndef MULTICORE int cround; /* Counter which is incremented for new conflict clauses */ int maxwasseen; int *wasseen; /* Counter when literal was last encountered */ #endif /* Functions for marking literals for different purposes. */ void nextround(satinstance sati) { #ifdef MULTICORE threads[sati->thread].cround += 1; #else cround += 1; #endif } int seenp(satinstance sati,int l) { /* Was literal already seen on the current round? */ int tmp; #ifdef MULTICORE ASSERT(l < threads[sati->thread].maxwasseen); #else ASSERT(l < maxwasseen); #endif #ifdef MULTICORE tmp = (threads[sati->thread].wasseen[l] == threads[sati->thread].cround); threads[sati->thread].wasseen[l] = threads[sati->thread].cround; /* Mark the literal as seen on the current round. */ #else tmp = (wasseen[l] == cround); wasseen[l] = cround; /* Mark the literal as seen on the current round. */ #endif return tmp; } /* This is called from outside after all clauses are there. */ float memoryused() { float MB; MB = (allocatedbyFE+allocatedbyCDB+allocatedbyCL)/1024.0/1024.0; return MB; } void init_clausesets(int varspertime) { int i; allocatedbyCL = 0.0; #ifdef MULTICORE threads = (thread *)malloc(sizeof(thread) * nOfThreads); for(j=0;j= sati->nOfVars) return 0; return 1; } #endif nintlist *nintcons(int i,nintlist *l) { nintlist *l2; l2 = (nintlist *)malloc(sizeof(nintlist)); l2->hd = i; l2->tl = l; return l2; } /*************************************************************************/ /** The size of an instance **********************************************/ /*************************************************************************/ /* Return an estimated size in MB of an instance data structure */ float estimateinstancesize(int tpoints,int varsPT,int actions) { float size; size = ((float)(tpoints*varsPT)) * ((float) (sizeof(int) + /* unitstack, int */ 2*sizeof(literal) + /* literal, literal */ sizeof(short) + /* variableval, 2*char */ #ifdef VSIDS sizeof(STATUSTYPE) + /* variablestatus, STATUSTYPE */ #endif sizeof(PTRINT) + /* variablereason, PTRINT */ sizeof(int) + /* variabledlevel, int */ sizeof(int) + /* declevels, int */ #ifdef COSTS sizeof(int) + /* declevelscost, int */ #endif sizeof(int))) + /* wasseen, int */ (float)(sizeof(int) * actions * 5); /* Frame axioms, very bad estimate */ return size / 1024.0 / 1024.0; } /*************************************************************************/ /** Initialize an instance **/ /*************************************************************************/ satinstance newinstance(int id,int times,int varsPT,int svars,int actions) { int i; satinstance sati = (satinstance)ownalloc(701,sizeof(struct _satinstance)); sati->value = -1; /* Truth-value unknown */ sati->id = id; sati->thread = -1; sati->nOfSVars = svars; sati->nOfActions = actions; sati->nOfVarsPerTime = varsPT; sati->nOfTPoints = times; /* Last time point has only state variables, no actions or auxiliaries. */ /* Is this correct? What happens with complex goals that require Tseitin? */ sati->nOfVars = (times-1)*varsPT+sati->nOfSVars; #ifdef DEBUG printf("\n==================================================\n"); printf("nOfSVars = %i\n",sati->nOfSVars); printf("nOfActions = %i\n",sati->nOfActions); printf("nOfVars = %i\n",sati->nOfVars); printf("nOfVarsPerTime = %i\n",sati->nOfVarsPerTime); printf("nOfTPoints = %i\n",sati->nOfTPoints); #endif sati->unitstack = (int *)ownalloc(702,sizeof(int) * sati->nOfVars); report_malloc("unitstack",sizeof(int) * sati->nOfVars); sati->endunitstack = -1; sati->startunitstack = 0; /* This does not take into account the initial unit clauses coming from the goal formula (which might also involve Tseitin!). */ sati->initialunits = 0; sati->maxinitialunits = sati->nOfSVars+200; sati->initialunittable = (int *)ownalloc(703,sati->maxinitialunits * sizeof(int)); report_malloc("initialunittable",sati->maxinitialunits * sizeof(int)); sati->notcalledbefore = 1; sati->complete = 0; if(!noT2clauses) { sati->l2its = (nintlist **)ownalloc(704,sizeof(nintlist *) * sati->nOfVarsPerTime * 2); report_malloc("l2its",sizeof(nintlist *) * sati->nOfVarsPerTime * 2); for(i=0;inOfVarsPerTime*2;i++) sati->l2its[i] = NULL; } sati->lits = (literal *)ownalloc(706,sizeof(literal) * sati->nOfVars * 2); report_malloc("lits",sizeof(literal) * sati->nOfVars * 2); /* variableval according to the chosen literal value representation */ #ifdef REPRONE sati->variableval = (VALTYPE *)ownalloc(707,sizeof(VALTYPE) * sati->nOfVars); #endif #ifdef REPRTWO sati->variableval = (VALTYPE *)ownalloc(708,sizeof(VALTYPE) * (1 + sati->nOfVars / 8)); #endif #ifdef REPRTHREE sati->variableval = (VALTYPE *)ownalloc(708,sizeof(VALTYPE) * 2 * sati->nOfVars); #endif #ifdef REPRFOUR sati->variableval = (VALTYPE *)ownalloc(708,sizeof(VALTYPE) * (1 + sati->nOfVars / 8)); #endif #ifdef VSIDS sati->variablestatus = (STATUSTYPE *)ownalloc(709,sizeof(STATUSTYPE) * sati->nOfVars); report_malloc("variablestatus",sizeof(STATUSTYPE) * sati->nOfVars); #endif #ifdef REASONDLEVEL sati->variablerd = (RD *)ownalloc(710,sizeof(RD) * sati->nOfVars); report_malloc("variablerd",sizeof(RD) * sati->nOfVars); #else sati->variablereason = (PTRINT *)ownalloc(710,sizeof(PTRINT) * sati->nOfVars); sati->variabledlevel = (int *)ownalloc(711,sizeof(int) * sati->nOfVars); report_malloc("variablereason",sizeof(PTRINT) * sati->nOfVars); report_malloc("variabledlevel",sizeof(int) * sati->nOfVars); #endif report_malloc("variableval",sizeof(VALTYPE) * sati->nOfVars); sati->declevels = (int *)ownalloc(800,sizeof(int) * sati->nOfVars); #ifdef COSTS sati->declevelscost = (int *)ownalloc(801,sizeof(int) * sati->nOfVars); #endif /* Resize tag arrays if too small. */ #ifdef MULTICORE if(2*sati->nOfVars > threads[0].maxwasseen) { int i,j; for(j=0;jnOfVars*3; threads[j].wasseen = (int *)realloc(threads[j].wasseen,threads[j].maxwasseen * sizeof(int)); for(i=0;inOfVars > maxwasseen) { int i; cround = 1; maxwasseen = sati->nOfVars*3; wasseen = (int *)realloc(wasseen,maxwasseen * sizeof(int)); for(i=0;idecisions = 0; sati->conflicts = 0; sati->decaysteps = 0; sati->pheuristic = 0; sati->heuristic = 0; // sati->nlevel = -1; #ifdef VSIDS /* Heap for maintaining a priority queue of variables. */ /* This is the location of a variable in the heap array. */ sati->hindex = (int *)ownalloc(712,sizeof(int) * (sati->nOfVars+2)); report_malloc("hindex",sizeof(int) * sati->nOfVars); sati->scoreheap = heap_create(sati->nOfVars); /* For efficiency reasons, we don't use heap_insert, and won't order the heap before all variable weights have been calculated. */ /* Initially each variable i is in the heap in position i. */ for(i=0;inOfVars;i++) { HINDEX(i) = i; sati->scoreheap->array[i].k = i; sati->scoreheap->array[i].v = 0; } sati->scoreheap->els = sati->nOfVars; adjustheap = 0; /* Don't adjust the heap before instance complete. */ #endif for(i=0;inOfVars*2;i++) { /* Initialize literals. */ LITWATCHES(i) = NULL; #ifdef WEIGHTS LITSCORE(i) = 0; #endif } for(i=0;inOfVars;i++) { /* Initialize variables. */ varunset(sati,i); #ifdef VSIDS VARSTATUS(i) = 0; #endif } #ifdef LBD sati->LBDflag = (int *)ownalloc(718,sati->nOfVars * sizeof(int)); for(i=0;inOfVars;i++) sati->LBDflag[i]=0; sati->LBDcounter = 1; #endif /* Cost vector used when finding assignments with low costs */ #ifdef COSTS sati->costs = (int *)ownalloc(719,sizeof(int) * sati->nOfVarsPerTime); for(i=0;inOfVarsPerTime;i++) sati->costs[i] = 0; sati->currentcost = 0; sati->costbound = 0x7fffffff; #endif #ifdef DEBUG printf("================================\n"); printf("Initialized instance:\n"); printf("total vars: %i\n",sati->nOfVars); printf("state vars: %i\n",sati->nOfSVars); printf("vars per time: %i\n",sati->nOfVarsPerTime); printf("time points: %i\n",sati->nOfTPoints); printf("================================\n"); #endif return sati; } void setheuristic(satinstance sati,int h) { sati->heuristic = h; } void setpheuristic(satinstance sati,int h) { sati->pheuristic = h; } /**************************************************************************/ /******* Keeping track of the heuristic ordering of literals *******/ /**************************************************************************/ #ifdef WEIGHTS int scoreof(satinstance sati,int var) { return LITSCORE(PLIT(var)) + LITSCORE(NLIT(var)); } void decay_score(satinstance sati) { int i; sati->decaysteps += 1; if((sati->decaysteps & 31) == 0) { /* Discount scores. */ #ifdef VSIDS heap_decay(sati); #endif for(i=0;i<2*sati->nOfVars;i++) LITSCORE(i) = (LITSCORE(i) >> 1); } } /* Increase score and update the heap. */ void increase_score(satinstance sati,int lit) { ASSERT(VAR(lit) < sati->nOfVars); LITSCORE(lit) += 1; #ifdef VSIDS if(adjustheap && HINDEX(VAR(lit)) != -1) { /* Update heap. */ heap_increment(sati,HINDEX(VAR(lit))); } #endif } void increase_score_by(satinstance sati,int lit,int n) { LITSCORE(lit) += n; #ifdef VSIDS if(adjustheap && HINDEX(VAR(lit)) != -1) { /* Update heap. */ heap_increment_by(sati,HINDEX(VAR(lit)),n); } #endif } #ifdef VSIDS int getbest(satinstance sati) { int var; if(!heap_emptyp(sati)) { do { var = heap_taketop(sati); } while((!varunsetp(sati,var)) && (!heap_emptyp(sati))); if(!varunsetp(sati,var)) return -1; /* Next, decide whether to have the literal POSITIVE or NEGATIVE. */ /* Use PHASE and SCORE. Break ties randomly. */ switch(VARSTATUS(var) & 3) { /* Check phase. */ case 2: return NLIT(var); case 3: return PLIT(var); default: if(LITSCORE(PLIT(var)) > LITSCORE(NLIT(var))) return PLIT(var); if(LITSCORE(PLIT(var)) < LITSCORE(NLIT(var))) return NLIT(var); if(2&(random())) return NLIT(var); else return PLIT(var); } } return -1; } int NEWgetbest(satinstance sati) { int var; while(!heap_emptyp(sati)) { var = heap_taketop(sati); if(varunsetp(sati,var)) { switch(VARSTATUS(var) & 3) { /* Check phase. */ case 2: return NLIT(var); case 3: return PLIT(var); default: /* Otherwise use SCORE to decide polarity. Break ties randomly. */ if(LITSCORE(PLIT(var)) > LITSCORE(NLIT(var))) return PLIT(var); if(LITSCORE(PLIT(var)) < LITSCORE(NLIT(var))) return NLIT(var); if(2&(random())) return NLIT(var); else return PLIT(var); } } } return -1; } #endif #endif /**************************************************************************/ /******* Construction and extension of clause sets *******/ /**************************************************************************/ /* As explained in Biere's PicoSAT implementation paper. For each literal, the list of clauses in which it is watched, is embedded in the clause data structure. From each literal there is a pointer to the first clause in which the literal is watched. The clause data includes a pointer to the next (and consequent) clauses in which the literal occurs. IMPLEMENTATION: table WATCHEDIN[literal] two additional pointers in each clause When new clause C is added for a literal, WATCHEDIN[literal] := C, and C has pointers to the old WATCHEDIN[literal] When watched literal changes, one literal gets a new clause (added in front of the list), and the other loses one clause (removed from the middle of the list by changing the pointer in the preceding clause). When clauses have been deleted (garbage collection), we must traverse WATCHEDIN[literal] for all literals, to skip the deleted clauses. */ /* Add clause c to literal's watched clause list. */ void setwatch(satinstance sati,int lit,int *c,int index) { if(index==0) ASSIGN_WATCHA = LITWATCHES(lit); else ASSIGN_WATCHB = LITWATCHES(lit); LITWATCHES(lit) = c; } /* Create a clause */ int from,to,bias,currentlen,minvar,maxvar; int *currentClause = NULL; int maxlen = 0; /* Insert an input clause (permanent) to the clause database. */ void addnewclause(satinstance sati,int len,clausetype ct) { ASSERT(len >= 2); if(currentClause == NULL) { maxlen = 10000; currentClause = (int *)malloc(sizeof(int) * maxlen); } currentlen = len; if(len > maxlen) { maxlen = len * 3/2; currentClause = (int *)realloc(currentClause,sizeof(int) * maxlen); } switch(ct) { case InitC: from=0; to=0; break; case FinalC: from=sati->nOfTPoints-1; to=from; break; case TransC: from=0; to=sati->nOfTPoints-2; break; } minvar = 1000000; maxvar = -1000000; bias = from*sati->nOfVarsPerTime; } void addliteral(satinstance sati,int c,int l) { int index = l+bias*2; currentClause[c] = index; minvar = min(VAR(index),minvar); maxvar = max(VAR(index),maxvar); } /* Finish the clause(s) and return the index of the last clause that was generated. */ void finishclause(satinstance sati) { int i,j,bias2,l,len; int *c; for(i=from;i<=to;i++) { len = currentlen; bias2 = i*sati->nOfVarsPerTime; if(maxvar-bias+bias2 >= sati->nOfVars) goto notthisclause; if(minvar-bias+bias2 < 0) goto notthisclause; c = allocpermclause(sati->id,len); ASSERT(c != 0); for(j=0;jnOfTPoints-1; to=from; break; case TransC: from=0; to=sati->nOfTPoints-2; break; default: assert(1==2); break; } for(i=from;i<=to;i++) { index = l+2*i*sati->nOfVarsPerTime; if(sati->initialunits+5 > sati->maxinitialunits) { sati->maxinitialunits = sati->maxinitialunits*2; sati->initialunittable = (int *)realloc(sati->initialunittable,sati->maxinitialunits*sizeof(int)); } sati->initialunittable[sati->initialunits++] = index; #ifdef WEIGHTS increase_score(sati,index); #endif } return 0; } /* Add a 2-literal clause to the clause base. The input is clauses without an absolute time. The relative time limits to InitC: initial state (first time point) only FinalC: goal state (last time point) only TransC: transition relation, with current and next state The 2-literal clause representation used by the solver associates with each clause an array of literals that follow from it. As an intermediate stage, we here construct linked lists that will be later mapped to those arrays after all input clauses are there. */ void add2clauseRAW(satinstance sati,int l1,int l2,clausetype ct) { int t; #ifdef WEIGHTS int i,i1,i2; #endif #ifdef asasASSERTS assert(l1 >= 0); assert(l1 < sati->nOfVarsPerTime*4); assert(l2 >= 0); assert(l2 < sati->nOfVarsPerTime*4); #endif if(ct == TransC) { if(!noT2clauses) { int l1time = VAR(l1) / sati->nOfVarsPerTime; int l2time = VAR(l2) / sati->nOfVarsPerTime; int nl1 = l1 - 2*l1time*sati->nOfVarsPerTime; int nl2 = l2 - 2*l2time*sati->nOfVarsPerTime; int tdiff = l2time-l1time; int bias = 2*tdiff*sati->nOfVarsPerTime; // printf("ADDING 2-LIT "); printTlit(sati,nl1); printf(" "); // printTlit(sati,nl2); printf(" with tdiff %i\n",tdiff); sati->l2its[NEG(nl1)] = nintcons(nl2+bias,sati->l2its[NEG(nl1)]); sati->l2its[NEG(nl2)] = nintcons(nl1-bias,sati->l2its[NEG(nl2)]); } #ifdef WEIGHTS /* Scores of the 2-literal clauses for all literals */ for(i=0;inOfTPoints;i++) { i1 = l1+2*i*sati->nOfVarsPerTime; i2 = l2+2*i*sati->nOfVarsPerTime; if((i1 >= 0) && (i2 >= 0) && (i1 < 2*sati->nOfVars) && (i2 < 2*sati->nOfVars)) { increase_score(sati,i1); increase_score(sati,i2); } } #endif } else { if(ct == InitC) t = 0; /* First time point */ else t=sati->nOfTPoints-1; /* Last time point */ // These are not needed if the increase_score below is their only use!!!! l1 += 2*t*sati->nOfVarsPerTime; l2 += 2*t*sati->nOfVarsPerTime; #ifdef WEIGHTS increase_score(sati,l1); increase_score(sati,l2); #endif // LIT2LITS(NEG(l1)) = nintcons(l2,LIT2LITS(NEG(l1))); // LIT2LITS(NEG(l2)) = nintcons(l1,LIT2LITS(NEG(l2))); } } void add2clause(satinstance sati,int l1,int l2,clausetype ct) { if(ct == TransC) add2clauseRAW(sati,l1,l2,ct); else { /* non-recurring 2-lit clauses handled as long clauses. */ // printf("@"); addnewclause(sati,2,ct); addliteral(sati,0,l1); addliteral(sati,1,l2); finishclause(sati); } } #ifdef COSTS void addtimedvarcost(satinstance sati,int v,int cost) { sati->costs[v] = cost; } #endif int nintlistlen(nintlist *ls) { int c; c = 0; while(ls != NULL) { c += 1; ls = ls->tl; } return c; } float megab(int i) { return ((float)i)/(1024.0*1024.0); } #ifdef DEBUG void showinstancesize(satinstance sati) { int cnt; int i; int longclauses; nintlist *ls; cnt = 0; for(i=0;i<2*sati->nOfVarsPerTime;i++) cnt += nintlistlen(sati->l2its[i]); longclauses = 0; for(i=0;i<2*sati->nOfVars;i++) { ls = LITWATCHES(i); while(ls != NULL) { longclauses += ((clauselen((int *)(ls->hd))+5)*4); ls = ls->tl; } } printf("===========================\n"); printf("l2its is %.1f MB\n",megab((sati->nOfSVars+2*cnt)*4)); printf("al2its is %.1f MB\n",megab((sati->nOfSVars+cnt)*4)); printf("lits is %.1f MB\n",megab(3*4*2*sati->nOfVars+longclauses)); printf("vars is %.1f MB\n",megab(4*4*sati->nOfVars)); printf("unitstack is %.1f MB\n",megab(4*sati->nOfVars)); #ifdef VSIDS printf("scoreheap is %.1f MB\n",megab(sati->nOfVars*8)); #endif #ifdef UCC printf("UCC is %.1f MB\n",megab(10000*4)); #endif printf("===========================\n"); } #endif /* Run this when all binary clauses are known. Main function: translated the binary clause linked lists to arrays for faster access. */ /* An ordering on lits, based on the variable indices and ignoring polarity */ int litCmp(int *a,int *b) { if(((*a) >> 1) < ((*b) >> 1)) return 1; else return 0; } int litCmp2(int *a,int *b) { if(((*a) >> 1) > ((*b) >> 1)) return 1; else return 0; } void instancecomplete(satinstance sati) { int i,c,j; nintlist *ls; sati->complete = 1; /* Update the heap. */ #ifdef VSIDS for(i=0;inOfVars;i++) heap_increment_by(sati,HINDEX(i),LITSCORE(PLIT(i))+LITSCORE(NLIT(i))); #endif adjustheap = 1; #ifdef VSIDS #ifdef ASSERTS checkheapconsistency(sati); #endif #endif if(!noT2clauses) { /* First SAT instance: map linked lists to arrays. */ int arraysize,*a2litarray,*ptr; /* Put the binary clauses' linked lists into arrays. Create one huge array, and then point into the middle. */ arraysize = 1; for(i=0;inOfVarsPerTime*2;i++) { arraysize = cachelineboundaryi(arraysize); arraysize += (1+nintlistlen(sati->l2its[i])); } arraysize += (CACHELINESIZEi-1); a2litarray = (int *)ownalloc(713,sizeof(int) * arraysize); report_malloc("a2litarray",sizeof(int) * arraysize); a2litarray[0] = 0; /* There is only one copy of the empty array, here. */ ptr = a2litarray+1; sati->al2its = (int **)ownalloc(714,sizeof(int *) * sati->nOfVarsPerTime * 2); for(i=0;inOfVarsPerTime*2;i++) { c = nintlistlen(sati->l2its[i]); if(c == 0) sati->al2its[i] = a2litarray; else { // printf("{%u,",(PTRINT)ptr); ptr = cachelineboundary((PTRINT)ptr); // printf("%u}",(PTRINT)ptr); sati->al2its[i] = ptr; ptr[0] = c; /* First element of the array is the # of elements. */ ls = sati->l2its[i]; for(j=0;jhd; ls = ls->tl; } /* Sort the array to reduce cache misses. */ qsort(sati->al2its[i]+1,c,sizeof(int),litCmp2); ptr += (c+1); #define noSHOW #ifdef SHOW printTlit(sati,i); printf(": "); for(j=0;jal2its[i][j+1]+2*sati->nOfVarsPerTime) % sati->nOfVarsPerTime); printTlit(sati,sati->al2its[i][j+1]+2*sati->nOfVarsPerTime); } printf("\n"); #endif } } /* Completed the move to arrays. */ } } /**************************************************************************/ /******* Functions for unit propagation *******/ /**************************************************************************/ /* Set literals in unitstack true and unit propagate. The reason parameter denotes the cause of the literal being set. -1 means that the literal is a decision literal -2 means that the literal is one of the unit clauses in the input CNF if the 2 lsbs are 0, the reason is a pointer to a clause where the literal was a unit (HERE WE ASSUME THAT ADDRESSES OF CLAUSES ALWAYS HAVE THEIR 2 LSBs 0.) otherwise the reason is (l << 1)|1 for another literal l If contradiction is reached, return the index of a falsified clause. */ inline int addtoqueue(satinstance sati,int l,PTRINT reason,int level) { if((litunsetp(sati,l))) { /* Literal has no value yet. */ sati->unitstack[++(sati->endunitstack)] = l; /* Push it into the stack. */ litsettrue(sati,l); /* Assign */ #ifdef VSIDS VARSTATUS(VAR(l)) = 2|(VALUE(l)); /* Record phase */ #endif LITREASON(l) = reason; LITDLEVEL(l) = level; #ifdef HARDDEBUG printf("Inferred "); printTlit(sati,l); printf(".\n"); printf(" REASON: "); fflush(stdout); switch(reason) { case REASON_DECISION: printf("DECISION\n"); break; case REASON_INPUT: printf("INPUT\n"); break; default: if((reason&1) == 0) { printf("Clause %i of length %i:",reason,clauselen((int *)reason)); printclause(sati,reason); printf("\n"); } else { printTlit(sati,reason >> 1); printf("\n"); } break; } #endif ASSERT(sati->endunitstack < sati->nOfVars); #ifdef COSTS if((l&1)==0) { /* Variable is true: observe cost. */ sati->currentcost = sati->currentcost + sati->costs[untimevar(sati,VAR(l))]; #ifdef HARDDEBUG printTlit(sati,l); printf("Adding %i to currentcost.\n",sati->costs[untimevar(sati,VAR(l))]); printf("Currentcost = %i.\n",sati->currentcost); #endif } #endif return 0; } /* Variable had a value already. */ if(unlikely(litfalsep(sati,l))) return 1; /* Literal is false. */ return 0; /* Literal was true. */ } /* Same as addtoqueue, except that we KNOW that the variable is unassigned. */ void simpleaddtoqueue(satinstance sati,int l,PTRINT reason,int level) { litsettrue(sati,l); /* Assign */ #ifdef VSIDS VARSTATUS(VAR(l)) = 2|(VALUE(l)); /* Record phase */ #endif LITREASON(l) = reason; LITDLEVEL(l) = level; sati->unitstack[++(sati->endunitstack)] = l; /* Push it into the stack. */ ASSERT(sati->endunitstack < sati->nOfVars); #ifdef COSTS if((l&1)==0) { /* Variable is true: observe cost. */ sati->currentcost += sati->costs[untimevar(VAR(l))]; #ifdef HARDDEBUG printTlit(sati,l); printf("Adding %i to currentcost for %i/%i.\n",sati->costs[untimevar(sati,VAR(l))],VAR(l),untimevar(sat,VAR(l))); printf("Currentcost = %i.\n",sati->currentcost); #endif } #endif } /* Propagation with 2-literal clauses. */ /* These variables are the interface from propaga{2lit,long} to learn(). */ inline int propagate2lit(satinstance sati,int lit,int level) { int glit,bias2; PTRINT reason; int j; int nofv2; int *AAA; reason = (lit << 1)|1; /* Reason for the literals to be set. */ /* Propagate the 2-literal clauses for all time points. */ /* Compact binary clause representation */ glit = lit%(2*sati->nOfVarsPerTime); bias2 = lit-glit; ASSERT(glit >= 0); ASSERT(glit < sati->nOfVarsPerTime*2); AAA = sati->al2its[glit]; nofv2 = 2*sati->nOfVars; for(j=1;j<=AAA[0];j++) { if(likely(AAA[j]+bias2 < nofv2) && likely(AAA[j]+bias2 >= 0) && unlikely(addtoqueue(sati,AAA[j]+bias2,reason,level))) { sati->conflicttype2lit = 1; sati->conflictl1 = NEG(AAA[j]+bias2); sati->conflictl2 = lit; return 1; } } return 0; } /* Propagation with long clauses (> 2 literals): Go through clauses in which literal is watched. If only one unassigned literal left, put into queue. */ inline int propagatelong(satinstance sati,int l,int level) { int *c,*c2; int *nextclause; PTRINT *prev; int tmp; PTRINT *ptmp; // printf("#"); prev = &(LITWATCHES(NEG(l))); nextclause = LITWATCHES(NEG(l)); /* Clauses where -l is watched */ while(nextclause != NULL) { /* Visit clauses where -l is watched. */ c = nextclause; /* The watched literals are always the first two in a clause. */ /* Make the current literal the first in the clause. */ if(c[0] != NEG(l)) { tmp = c[0]; c[0] = c[1]; c[1] = tmp; ptmp = ACCESS_WATCHA; ASSIGN_WATCHA = ACCESS_WATCHB; ASSIGN_WATCHB = ptmp; } // __builtin_prefetch(ACCESS_WATCHA); /* If the second watched literal is true, don't do anything. */ if(littruep(sati,c[1])) { prev = ADDRESS_WATCHA; nextclause = ACCESS_WATCHA; continue; } ASSERT(isliteral(sati,c[0])); ASSERT(isliteral(sati,c[1])); c2 = c+2; /* Find a non-false literal. */ while(*c2 != -1) { ASSERT(isliteral(sati,*c2)); if(!litfalsep(sati,*c2)) goto nonfalse; c2 += 1; } /* 2nd watched literal is a new unit clause. */ // updateactivity(c,sati->conflicts); c[PREFIX_ACTIVITY] = sati->conflicts; if(addtoqueue(sati,c[1],(PTRINT)c,level)) { // printf("C"); sati->conflicttype2lit = 0; sati->conflictclause = c; return 1; /* You might exit here before fixing all the clauses with the literal watched. Is this correct? */ } else { // printf("U%i",*(c+1)); } prev = ADDRESS_WATCHA; nextclause = ACCESS_WATCHA; continue; nonfalse: /* Swap the old watched literal (at *c) and the new (at *c2). */ tmp = *c2; *c2 = *c; *c = tmp; /* Remove the clause from the old literal's watched clause list. */ *prev = ACCESS_WATCHA; nextclause = ACCESS_WATCHA; /* Go to the next clause. */ /* Add the clause to the new literal's watched clause list. */ ASSIGN_WATCHA = LITWATCHES(*c); LITWATCHES(*c) = c; } return 0; } void setUPstacktop(satinstance sati,int top) { sati->endunitstack = top; sati->startunitstack = top+1; } /* Main function for propagation */ int propagate(satinstance sati) { int l; int startlong; startlong = sati->startunitstack; while(startlong <= sati->endunitstack) { while(sati->startunitstack <= sati->endunitstack) { l = sati->unitstack[sati->startunitstack++]; /* Propagate with 2-literal clauses. */ if(propagate2lit(sati,l,sati->dlevel)) return 1; } /* Propagate with long clauses. */ l = sati->unitstack[startlong++]; if(propagatelong(sati,l,sati->dlevel)) return 1; } return 0; /* No contradiction */ } /*************************************************************************/ /** Computation of learned clauses **/ /*************************************************************************/ #ifdef UCC /* The literals that have been added as unit clauses. They have to be remembered until decision level 0 is visited again, because their assignments will be undone for example during the standard CDCL main loop run on levels > 0. */ void addUCC(satinstance sati,int l) { sati->UCC[sati->NofUCClauses++] = l; ASSERT(sati->NofUCClauses < MAXUCC); #ifdef HARDDEBUG printf("New unit clause "); printTlit(sati,l); printf("\n"); printf("lvl: %i start: %i end: %i\n",-999,sati->endunitstack,sati->startunitstack); #endif } int returnUCC(satinstance sati) { int i; for(i=0;iNofUCClauses;i++) { // The return 1 below should never happen!!!!! if(addtoqueue(sati,sati->UCC[i],REASON_INPUT,0) || propagate(sati)) return 1; } if(sati->dlevel == 0) sati->NofUCClauses = 0; #ifdef HARDDEBUG printf("lvl: %i start: %i end: %i\n",sati->dlevel,sati->endunitstack,sati->startunitstack); #endif return 0; } #endif UCC /* Compute a 1-UIP or Last-UIP clause. 1. Go through the literals in the current clause. 2. If literal for non-decision level, store it. 3. If non-decision literal for the top level, traverse its reasons. 4. If the reason is -2, ignore the literal. */ satinstance dlcsati; int dlevelCmp(int *l1,int *l2) { #ifdef REASONDLEVEL if(dlcsati->variablerd[VAR(*l1)].dlevel > dlcsati->variablerd[VAR(*l2)].dlevel) { #else if(dlcsati->variabledlevel[VAR(*l1)] > dlcsati->variabledlevel[VAR(*l2)]) { #endif return 1; } return 0; } #define noFIRSTUIP 1 #ifdef FUIP #include "learn1UIP.c" #else //#include "learn1.c" #include "learn2.c" //#include "learn3.c" #endif /**************************************************************************/ /******* The Conflict-Driven Clause Learning algorithm *******/ /**************************************************************************/ int chooseliteral(satinstance sati) { switch(PLANheuristic) { case 0: break; default: return do_cpath_heuristic(sati); } /* If planning heuristic was not applicable, use the SAT heuristic. */ #ifdef VSIDS /* Choose unassigned literal with highest score. */ return getbest(sati); #else fprintf(stderr,"No decision literal chosen.\n"); exit(1); #endif } #ifdef HARDDEBUG void showstack(satinstance sati) { int i,l; printf("============================\n"); for(i=sati->endunitstack;i>max(0,sati->endunitstack-20);i--) { l = sati->unitstack[i]; printf("%2i: %2i ",i,LITDLEVEL(l)); printTlit(sati,l); printf("\n"); } printf("----------------------------\n"); printf("levels:"); for(i=max(0,sati->dlevel-8);i<=sati->dlevel;i++) { printf(" %i@%i",i,sati->declevels[i-1]); } printf("\n"); printf("============================\n"); } #endif void emptystack(satinstance sati) { int i; for(i=0;i<=sati->endunitstack;i++) litunset(sati,sati->unitstack[i]); setUPstacktop(sati,-1); } /* Make btlevel the new top decision level by - undoing all assignments on levels higher than btlevel - setting current dlevel to btlevel */ void undo_assignments_until_level(satinstance sati,int btlevel) { int i,l; // printf("FROM %i TO %i AT BTLEVEL %i\n",sati->declevels[btlevel],sati->endunitstack,btlevel); for(i=sati->declevels[btlevel];i<=sati->endunitstack;i++) { l = sati->unitstack[i]; litunset(sati,l); #ifdef HARDDEBUG printf("UNSET "); printTlit(sati,l); printf("\n"); #endif #ifdef VSIDS if(HINDEX(VAR(l)) == -1) heap_insert(sati,VAR(l),scoreof(sati,VAR(l))); #endif } #ifdef COSTS sati->currentcost = sati->declevelscost[btlevel]; #ifdef HARDDEBUG printf("Reverted currentcost = %i.\n",sati->currentcost); #endif #endif sati->dlevel = btlevel; setUPstacktop(sati,sati->declevels[btlevel]-1); } void undo_assignments_until_level_NOHEAP(satinstance sati,int btlevel) { int i,l; // printf("FROM %i TO %i AT BTLEVEL %i\n",sati->declevels[btlevel],sati->endunitstack,btlevel); for(i=sati->declevels[btlevel];i<=sati->endunitstack;i++) { l = sati->unitstack[i]; litunset(sati,l); // printf("UNSET "); printTlit(sati,l); printf("\n"); } #ifdef COSTS sati->currentcost = sati->declevelscost[btlevel]; #ifdef HARDDEBUG printf("Reverted currentcost = %i.\n",sati->currentcost); #endif #endif setUPstacktop(sati,sati->declevels[btlevel]-1); } /* The standard CDCL Conflict Driven Clause Learning algorithm */ int solve0(satinstance sati,int conflictstogo,int restart) { int l,p,btlevel; int q; PTRINT CCreason; if(sati->notcalledbefore) { sati->notcalledbefore = 0; for(p=0;pinitialunits;p++) { if(addtoqueue(sati,sati->initialunittable[p],REASON_INPUT,0)) goto UNSAT; } if(propagate(sati)) goto UNSAT; #ifdef UCC sati->NofUCClauses = 0; #endif setUPstacktop(sati,-1); /* Drop the input unit clauses from the stack. */ sati->dlevel = 0; if(sati->pheuristic >= 1) sati->heuristic_mode = 0; } q = 0; do { if(q || propagate(sati)) { /* Got a conflict? */ if(sati->dlevel == 0) goto UNSAT; #ifdef WEIGHTS decay_score(sati); #endif sati->heuristic_mode = 0; sati->conflicts += 1; conflictstogo -= 1; learn(sati,sati->dlevel,&p,&CCreason,&btlevel); /* When exiting learn(), CCreason points to the newly learned clause, and btlevel is the decision level of the second highest literal in the clause. */ ASSERT(btlevel >= 0); ASSERT(btlevel < sati->dlevel); ASSERT(LITDLEVEL(p) == sati->dlevel); #ifdef HARDDEBUG printf("Conflict level %i literal ",sati->dlevel); printTlit(sati,p); printf(", next level is %i.\n",btlevel); showstack(sati); #endif undo_assignments_until_level(sati,btlevel); ASSERT(litunsetp(sati,p)); #ifdef UCC if(returnUCC(sati)) goto UNSAT; #endif #ifdef HARDDEBUG printf("Setting "); printTlit(sati,p); printf(" true (flipping).\n"); fflush(stdout); #endif /* CCreason refers to the conflict clause. */ q = addtoqueue(sati,p,CCreason,sati->dlevel); ASSERT(littruep(sati,p)); // IF THE NEXT LINE IS MISSING, LEARNING A CLAUSE RIGHT BEFORE // A RESTART SOMTIMES LEADS TO UNASSIGNING DEC LEVEL 0 LITERAL p // AND IF IT IS THERE, heuristic2.c SOMTIMES CRASHES BECAUSE NO // BEST VARIABLE IS FOUND. REASON UNCLEAR!!!!!!!! // sati->declevels[sati->dlevel] = sati->endunitstack+1; } else { /* Extend the assignment. */ if(conflictstogo <= 0) { sati->declevels[sati->dlevel] = sati->endunitstack+1; break; } l = chooseliteral(sati); if(l == -1) goto SAT; /* All variables assigned already. */ ASSERT(VAR(l) < sati->nOfVars); ASSERT(litunsetp(sati,l)); #ifdef DEBUG printf("DECISION %i: ",sati->dlevel); printTlit(sati,l); printf("\n"); #endif sati->declevels[sati->dlevel] = sati->endunitstack+1; #ifdef COSTS sati->declevelscost[sati->dlevel] = sati->currentcost; #endif sati->dlevel += 1; sati->decisions += 1; simpleaddtoqueue(sati,l,REASON_DECISION,sati->dlevel); q=0; } // } while(conflictstogo); } while(1==1); /* Satisfiability status not determined. */ if(restart) { // PROBLEM: if decision level 0 learned clause is added right before // a restart, it will never get propagated. KESKEN #ifdef HARDDEBUG printf("DOING A RESTART\n"); #endif undo_assignments_until_level(sati,0); // if(propagate(sati)) goto UNSAT; #ifdef UCC if(returnUCC(sati)) goto UNSAT; #endif setUPstacktop(sati,-1); } return -1; UNSAT: sati->value = 0; return 0; SAT: printf("SAT (%i ID %i decisions %i conflicts %i variables)\n",sati->id,sati->decisions,sati->conflicts,sati->nOfVars); #ifdef COSTS printf("FINAL COST %i.\n",sati->currentcost); sati->costbound = sati->currentcost; #endif sati->value = 1; return 1; } int solve(satinstance sati) { int result; int interval; interval = 32; do { result = solve0(sati,interval,1); interval = interval + 5; } while(result == -1); return result; } void freeinstance(satinstance sati) { /* sati->lits is needed when garbage collecting the first time after the instance has become useless. */ // if(sati->lits) { free(sati->lits); sati->lits = NULL; } if(sati->variableval) { free(sati->variableval); sati->variableval = NULL; } #ifdef VSIDS if(sati->variablestatus) { free(sati->variablestatus); sati->variablestatus = NULL; } #endif #ifdef REASONDLEVEL if(sati->variablerd) { free(sati->variablerd); sati->variablerd = NULL; } #else if(sati->variablereason) { free(sati->variablereason); sati->variablereason = NULL; } if(sati->variabledlevel) { free(sati->variabledlevel); sati->variabledlevel = NULL; } #endif if(sati->initialunittable) { free(sati->initialunittable); sati->initialunittable = NULL; } if(sati->unitstack) { free(sati->unitstack); sati->unitstack = NULL; } #ifdef VSIDS if(sati->hindex) { free(sati->hindex); sati->hindex = NULL; } if(sati->scoreheap) { freeheap(sati->scoreheap); sati->scoreheap = NULL; } #endif } void showvaluation(satinstance sati) { int i; printf("Valuation:"); for(i=0;inOfVars;i++) if(vartruep(sati,i)) { printf(" "); printTlit(sati,PLIT(i)); } printf("\n"); } /*************************************************************************/ /** Heap for keeping track of highest score variables **/ /*************************************************************************/ #ifdef VSIDS /* Calculate the index of the parent of an index. */ inline int parent(int i) { return (i-1) >> 1; } /* Calculate the index of the 1st child of an index. */ inline int child1(int i) { return i*2+1; } /* Calculate the index of the 2nd child of an index. */ inline int child2(int i) { return i*2+2; } /* Test whether the heap is empty. */ int heap_emptyp(satinstance sati) { return (sati->scoreheap->els == 0); } /* Return true if index is a leaf. */ int leafp(heap h,int index) { return (child1(index) >= h->els); } heap heap_create(int elements) { heap h; h = (heap)ownalloc(716,sizeof(struct _heap)); h->els = 0; h->maxels = elements; h->array = (pair *)ownalloc(717,sizeof(pair) * elements); return h; } void freeheap(heap h) { free(h->array); free(h); } /* After moving a non-top element to the top, restore the heap property. */ inline int heap_property_down(satinstance sati,int index) { int c1index,c2index; int swap; int key,val; heap h; h = sati->scoreheap; ASSERT(index < h->els && index >= 0); key = h->array[index].k; val = h->array[index].v; c1index = child1(index); c2index = child2(index); /* Should one child be higher than the parent? */ while(((c1index < h->els && h->array[c1index].v > val) || (c2index < h->els && h->array[c2index].v > val))) { /* Not a leaf */ if(c2index < h->els) { if(h->array[c1index].v > h->array[c2index].v) swap = c1index; else swap = c2index; } else swap = c1index; ASSERT(index < h->els && index >= 0); /* Move child to the parent's node. */ h->array[index].k = h->array[swap].k; h->array[index].v = h->array[swap].v; HINDEX(h->array[index].k) = index; /* Continue with the child's node. */ index = swap; c1index = child1(index); c2index = child2(index); } /* Leave the node here when low enough. */ h->array[index].k = key; h->array[index].v = val; HINDEX(h->array[index].k) = index; return index; } void printheap(satinstance sati) { heap h = sati->scoreheap; int node; for(node=0;nodeels;node++) { printf("(%i,%i) ",h->array[node].k,h->array[node].v); } printf("\n"); } /* Get the top element of the heap (with the highest value). */ int heap_taketop(satinstance sati) { int key; heap h = sati->scoreheap; ASSERT(h->els); key = h->array[0].k; if(h->els > 1) { /* Move last element to top and then push down. */ h->array[0].k = h->array[h->els-1].k; h->array[0].v = h->array[h->els-1].v; HINDEX(h->array[0].k) = 0; } HINDEX(key) = -1; h->els -= 1; if(h->els) heap_property_down(sati,0); return key; } /* Move a new element to an appropriate place in the heap. */ void heap_property_up(satinstance sati,int index) { int pindex; int key,val; heap h = sati->scoreheap; ASSERT(index >= 0 && index < h->els); key = h->array[index].k; val = h->array[index].v; pindex = parent(index); while(index > 0 && val > h->array[pindex].v) { /* Move parent down. */ h->array[index].k = h->array[pindex].k; h->array[index].v = h->array[pindex].v; HINDEX(h->array[index].k) = index; /* Continue with the parent. */ index = pindex; pindex = parent(index); } /* When all parents are OK, leave the new element here. */ h->array[index].k = key; h->array[index].v = val; HINDEX(h->array[index].k) = index; } /* Add new element to the heap. */ void heap_insert(satinstance sati,int key,int val) { heap h = sati->scoreheap; /* You could speed this a bit up by not writing key and val to memory yet. */ /* But this would be for heap_insert only. */ h->array[h->els].k = key; h->array[h->els].v = val; h->els += 1; ASSERT(h->els <= h->maxels); heap_property_up(sati,h->els-1); } /* Increment the value of a heap element. */ void heap_increment(satinstance sati,int index) { heap h = sati->scoreheap; h->array[index].v += 1; heap_property_up(sati,index); } void heap_increment_by(satinstance sati,int index,int n) { heap h = sati->scoreheap; h->array[index].v += n; heap_property_up(sati,index); } /* Perform the decay operation, i.e. halve the value of each element. */ void heap_decay(satinstance sati) { int i; heap h = sati->scoreheap; for(i=0;iels;i++) h->array[i].v = (h->array[i].v) >> 1; } /* Delete an arbitrary element from the heap. */ void heap_delete(satinstance sati,int index) { int loc; heap h = sati->scoreheap; ASSERT((h->els > index) && (index >= 0)); h->array[index].v = -1000; loc = heap_property_down(sati,index); ASSERT(leafp(h,loc)); h->array[loc].k = h->array[h->els-1].k; h->array[loc].v = h->array[h->els-1].v; HINDEX(h->array[loc].k) = loc; h->els -= 1; if(loc < h->els) heap_property_up(sati,loc); } #ifdef ASSERTS void checkheapproperty(heap h) { int i; for(i=1;iels;i++) { assert(h->array[parent(i)].v >= h->array[i].v); } } void checkheapconsistency(satinstance sati) { int i; heap h = sati->scoreheap; for(i=0;iels;i++) { assert(HINDEX(h->array[i].k) == i); } checkheapproperty(h); } #endif #endif /**************************** INCLUDED SOURCE FILES ***********************/ #include "shortcuts.c" /**************************************************************************/