plan9front/sys/src/libsat/satsolve.c

888 lines
22 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <u.h>
#include <libc.h>
#include <sat.h>
#include "impl.h"
/* the solver follows Algorithm C from Knuth's The Art of Computer Programming, Vol. 4, Fascicle 6 */
#define verbosestate 0
#define verboseforcing 0
#define verboseconflict 0
#define paranoia 0
#define sanity(s) if(paranoia) satsanity(s)
void
sataddtrail(SATSolve *s, int l)
{
s->trail[s->ntrail++] = l;
s->lit[l].val = 1;
s->lit[NOT(l)].val = 0;
s->var[VAR(l)].lvl = s->lvl;
s->agility -= s->agility >> 13;
if(((s->var[VAR(l)].flags ^ l) & 1) != 0)
s->agility += 1<<19;
if(verbosestate) satprintstate(s);
}
/* compute watchlists from scratch */
static void
rewatch(SATSolve *s)
{
SATLit *l;
SATClause *c;
int i, j, x;
for(l = s->lit; l < s->lit + 2*s->nvar; l++)
l->watch = nil;
for(c = s->cl; c != nil; c = c->next)
for(i = 0; i < 2; i++){
if(s->lit[c->l[i]].val == 0)
for(j = 2; j < c->n; j++)
if(s->lit[c->l[j]].val != 0){
x = c->l[i], c->l[i] = c->l[j], c->l[j] = x;
break;
}
c->watch[i] = s->lit[c->l[i]].watch;
s->lit[c->l[i]].watch = c;
}
}
/* jump back to decision level d */
void
satbackjump(SATSolve *s, int d)
{
int l;
SATVar *v;
if(s->lvl == d) return;
while(s->ntrail > s->decbd[d + 1]){
l = s->trail[--s->ntrail];
v = &s->var[VAR(l)];
if((v->flags & VARUSER) != 0){ /* don't delete user assignments */
s->ntrail++;
break;
}
s->lit[l].val = -1;
s->lit[NOT(l)].val = -1;
v->flags = v->flags & ~1 | l & 1;
v->lvl = -1;
v->reason = nil;
v->isbinreason = 0;
if(v->heaploc < 0)
satheapput(s, v);
}
s->lvl = d;
if(s->forptr > s->ntrail) s->forptr = s->ntrail;
if(s->binptr > s->ntrail) s->binptr = s->ntrail;
if(verbosestate) satprintstate(s);
}
static void
solvinit(SATSolve *s)
{
satdebuginit(s);
satheapreset(s);
s->decbd = satrealloc(s, s->decbd, (s->nvar + 1) * sizeof(int));
s->decbd[0] = 0;
s->trail = satrealloc(s, s->trail, sizeof(int) * s->nvar);
s->fullrlits = satrealloc(s, s->fullrlits, sizeof(int) * s->nvar);
s->lvlstamp = satrealloc(s, s->lvlstamp, sizeof(int) * s->nvar);
memset(s->lvlstamp, 0, sizeof(int) * s->nvar);
if(s->cflclalloc == 0){
s->cflcl = satrealloc(s, s->cflcl, CFLCLALLOC * sizeof(int));
s->cflclalloc = CFLCLALLOC;
}
rewatch(s);
s->conflicts = 0;
s->nextpurge = s->purgeΔ;
s->purgeival = s->purgeΔ;
s->nextflush = 1;
s->flushu = 1;
s->flushv = 1;
s->flushθ = s->flushψ;
s->agility = 0;
satbackjump(s, 0);
s->forptr = 0;
s->binptr = 0;
}
void
satcleanup(SATSolve *s, int all)
{
SATBlock *b, *bn;
if(all){
*s->lastp[0] = nil;
s->learncl = nil;
s->lastp[1] = &s->learncl;
s->ncl = s->ncl0;
}
for(b = s->bl[1].next; b != &s->bl[1]; b = bn){
bn = b->next;
if(b->last != nil && !all) continue;
b->next->prev = b->prev;
b->prev->next = b->next;
free(b);
}
s->lastbl = s->bl[1].prev;
free(s->fullrlits);
s->fullrlits = nil;
free(s->lvlstamp);
s->lvlstamp = nil;
free(s->cflcl);
s->cflcl = nil;
s->cflclalloc = 0;
}
static void
stampoverflow(SATSolve *s)
{
int i;
for(i = 0; i < s->nvar; i++){
s->var[i].stamp = 0;
s->lvlstamp[i] = 0;
}
s->stamp = -2;
}
/* "bump" the variable, i.e. increase its activity score. reduce all score when one exceeds MAXACTIVITY (1e100) */
static void
varbump(SATSolve *s, SATVar *v)
{
v->activity += s->Δactivity;
satreheap(s, v);
if(v->activity < MAXACTIVITY) return;
for(v = s->var; v < s->var + s->nvar; v++)
if(v->activity != 0){
v->activity /= MAXACTIVITY;
if(v->activity < ε)
v->activity = ε;
}
s->Δactivity /= MAXACTIVITY;
}
/* ditto for clauses */
static void
clausebump(SATSolve *s, SATClause *c)
{
c->activity += s->Δclactivity;
if(c->activity < MAXACTIVITY) return;
for(c = s->cl; c != nil; c = c->next)
if(c->activity != 0){
c->activity /= MAXACTIVITY;
if(c->activity < ε)
c->activity = ε;
}
s->Δclactivity /= MAXACTIVITY;
}
/* pick a literal. normally we pick the variable with highest activity from the heap. sometimes we goof and pick a random one. */
static void
decision(SATSolve *s)
{
SATVar *v;
s->decbd[++s->lvl] = s->ntrail;
if((uint)s->randfn(s->randaux) < s->goofprob){
v = s->heap[satnrand(s, s->nheap)];
if(v->lvl < 0)
goto gotv;
}
do
v = satheaptake(s);
while(v->lvl >= 0);
gotv:
sataddtrail(s, 2 * (v - s->var) + (v->flags & VARPHASE));
}
/* go through the watchlist of a literal that just turned out false. */
/* full == 1 records the first conflict and goes on rather than aborting immediately */
static SATClause *
forcing(SATSolve *s, int l, int full)
{
SATClause **cp, *rc, *c, *xp;
int v0;
int x, j;
cp = &s->lit[l].watch;
rc = nil;
if(verboseforcing) print("forcing literal %d\n", signf(l));
while(c = *cp, c != nil){
if(l == c->l[0]){
/* this swap implies that the reason r for a literal l always has r->l[0]==l */
x = c->l[1], c->l[1] = c->l[0], c->l[0] = x;
xp = c->watch[1], c->watch[1] = c->watch[0], c->watch[0] = xp;
}
assert(c->l[1] == l);
v0 = s->lit[c->l[0]].val;
if(v0 > 0) /* the clause is true anyway */
goto next;
for(j = 2; j < c->n; j++)
if(s->lit[c->l[j]].val != 0){
/* found another literal to watch for this clause */
if(verboseforcing) print("moving clause %+Γ onto watchlist %d\n", c, signf(c->l[j]));
*cp = c->watch[1];
x = c->l[j], c->l[j] = c->l[1], c->l[1] = x;
c->watch[1] = s->lit[x].watch;
s->lit[x].watch = c;
goto cont;
}
if(v0 == 0){
/* conflict */
if(!full) return c;
if(rc == nil) rc = c;
goto next;
}
if(verboseforcing) print("inferring %d using clause %+Γ\n", signf(c->l[0]), c);
sataddtrail(s, c->l[0]);
s->var[VAR(c->l[0])].reason = c;
next:
cp = &c->watch[1];
cont: ;
}
return rc;
}
/* forcing() for binary implications */
static uvlong
binforcing(SATSolve *s, int l, int full)
{
SATLit *lp;
int i, m;
uvlong rc;
lp = &s->lit[l];
rc = 0;
if(verboseforcing && lp->nbimp > 0) print("forcing literal %d (binary)\n", signf(l));
for(i = 0; i < lp->nbimp; i++){
m = lp->bimp[i];
switch(s->lit[m].val){
case -1:
if(verboseforcing) print("inferring %d using binary clause (%d) %d\n", signf(m), -signf(l), signf(m));
sataddtrail(s, m);
s->var[VAR(m)].binreason = NOT(l);
s->var[VAR(m)].isbinreason = 1;
break;
case 0:
if(verboseforcing) print("conflict (%d) (%d)\n", -signf(l), signf(m));
if(rc == 0) rc = (uvlong)NOT(l) << 32 | (uint)m;
if(!full) return rc;
break;
}
}
return rc;
}
/* check if we can discard the previously learned clause because the current one subsumes it */
static int
checkdiscard(SATSolve *s)
{
SATClause *c;
SATVar *v;
int q, j;
if(s->lastp[1] == &s->learncl) return 0;
c = (SATClause*) ((uchar*) s->lastp[1] - (uchar*) &((SATClause*)0)->next);
if(s->lit[c->l[0]].val >= 0) return 0; /* clause is a reason, hands off */
q = s->ncflcl;
for(j = c->n - 1; q > 0 && j >= q; j--){
v = &s->var[VAR(c->l[j])];
/* check if literal is in the current clause */
if(c->l[j] == s->cflcl[0] || (uint)v->lvl <= s->cfllvl && v->stamp == s->stamp)
q--;
}
return q == 0;
}
/* add the clause we just learned to our collection */
static SATClause *
learn(SATSolve *s, int notriv)
{
SATClause *r;
int i, l, triv;
/* clauses that are too complicated are not worth it. learn the trivial clause (all decisions negated) instead */
if(triv = !notriv && s->ncflcl > s->lvl + s->trivlim){
assert(s->lvl + 1 <= s->cflclalloc);
for(i = 1; i <= s->lvl; i++)
s->cflcl[i] = NOT(s->trail[s->decbd[s->lvl + 1 - i]]);
s->ncflcl = s->lvl + 1;
}
if(s->ncflcl == 1) /* unit clauses are handled by putting them on the trail in conflict() */
return nil;
if(!triv && checkdiscard(s))
r = satreplclause(s, s->ncflcl);
else
r = satnewclause(s, s->ncflcl, 1);
r->n = s->ncflcl;
memcpy(r->l, s->cflcl, s->ncflcl * sizeof(int));
for(i = 0; i < 2; i++){
l = r->l[i];
r->watch[i] = s->lit[l].watch;
s->lit[l].watch = r;
}
return r;
}
/* recursive procedure to determine if a literal is redundant.
* to avoid repeated work, each known redundant literal is stamped with stamp+1
* and each known nonredundant literal is stamped with stamp+2.
*/
static int
redundant(SATSolve *s, int l)
{
SATVar *v, *w;
SATClause *c;
int i, r;
v = &s->var[VAR(l)];
if(v->isbinreason){
/* stupid special case code */
r = v->binreason;
w = &s->var[VAR(r)];
if(w->lvl != 0){
if(w->stamp == s->stamp + 2)
return 0;
if(w->stamp < s->stamp && (s->lvlstamp[w->lvl] < s->stamp || !redundant(s, r))){
w->stamp = s->stamp + 2;
return 0;
}
}
v->stamp = s->stamp + 1;
return 1;
}
if(v->reason == nil) return 0; /* decision literals are never redundant */
c = v->reason;
for(i = 0; i < c->n; i++){
if(c->l[i] == NOT(l)) continue;
w = &s->var[VAR(c->l[i])];
if(w->lvl == 0)
continue; /* literals at level 0 are redundant */
if(w->stamp == s->stamp + 2)
return 0;
/* if the literal is not in the clause or known redundant, check if it is redundant */
/* we can skip the check if the level is not stamped: */
/* if there are no literals at the same level in the clause, it must be nonredundant */
if(w->stamp < s->stamp && (s->lvlstamp[w->lvl] < s->stamp || !redundant(s, c->l[i]))){
w->stamp = s->stamp + 2;
return 0;
}
}
v->stamp = s->stamp + 1;
return 1;
}
/* "blitting" a literal means to either add it to the conflict clause
* (if v->lvl < s->lvl) or to increment the counter of literals to be
* resolved, plus some bookkeeping. */
static void
blit(SATSolve *s, int l)
{
SATVar *v;
int p;
v = &s->var[VAR(l)];
if(v->stamp == s->stamp) return;
v->stamp = s->stamp;
p = v->lvl;
if(p == 0) return;
if(verboseconflict) print("stamp %d %s (ctr=%d)\n", signf(l), p == s->lvl ? "and increment" : "and collect", s->cflctr);
varbump(s, v);
if(p == s->lvl){
s->cflctr++;
return;
}
if(s->ncflcl >= s->cflclalloc){
s->cflcl = satrealloc(s, s->cflcl, (s->cflclalloc + CFLCLALLOC) * sizeof(int));
s->cflclalloc += CFLCLALLOC;
}
s->cflcl[s->ncflcl++] = l;
if(p > s->cfllvl) s->cfllvl = p;
/* lvlstamp[p] == stamp if there is exactly one literal and ==stamp+1 if there is more than one literal on level p */
if(s->lvlstamp[p] <= s->stamp)
s->lvlstamp[p] = s->stamp + (s->lvlstamp[p] == s->stamp);
}
/* to resolve a conflict, we start with the conflict clause and use
* resolution (a b and ¬a c imply b c) with the reasons for the
* literals to remove all but one literal at the current level. this
* gives a new "learned" clause with all literals false and we jump back
* to the second-highest level in it. at this point, the clause implies
* the one remaining literal and we can continue.
* to do this quickly, rather than explicitly apply resolution, we keep a
* counter of literals at the highest level (unresolved literals) and an
* array with all other literals (which will become the learned clause). */
static void
conflict(SATSolve *s, SATClause *c, uvlong bin, int full)
{
int i, j, l, p, *nl, found;
SATVar *v;
SATClause *r;
if(verboseconflict) satprintstate(s);
/* choose a new unique stamp value */
if(s->stamp >= (uint)-3)
stampoverflow(s);
s->stamp += 3;
s->ncflcl = 1;
s->cflctr = 0;
s->cfllvl = 0;
/* we start by blitting each literal in the conflict clause */
if(c != nil){
clausebump(s, c);
for(i = 0; i < c->n; i++)
blit(s, c->l[i]);
/* if there is only one literal l at the current level, we should have inferred ¬l at a lower level (bug). */
if(s->cflctr <= 1){
satprintstate(s);
print("conflict clause %+Γ\n", c);
assert(s->cflctr > 1);
}
}else{
blit(s, bin);
blit(s, bin>>32);
if(s->cflctr <= 1){
satprintstate(s);
print("binary conflict clause %d %d\n", (int)(bin>>32), (int)bin);
assert(s->cflctr > 1);
}
}
/* now we go backwards through the trail, decrementing the unresolved literal counter at each stamped literal */
/* and blitting the literals in their reason */
for(i = s->ntrail; --i >= 0; ){
v = &s->var[VAR(s->trail[i])];
if(v->stamp != s->stamp) continue;
if(verboseconflict) print("trail literal %d\n", signf(s->trail[i]));
if(--s->cflctr == 0) break;
if(v->isbinreason)
blit(s, v->binreason);
else if((r = v->reason) != nil){
clausebump(s, r);
for(j = 0; j < r->n; j++)
blit(s, r->l[j]);
}
}
/* i should point to the one remaining literal at the current level */
assert(i >= 0);
nl = s->cflcl;
nl[0] = NOT(s->trail[i]);
found = 0;
/* delete redundant literals. note we must watch a literal at cfllvl, so put it in l[1]. */
for(i = 1, j = 1; i < s->ncflcl; i++){
l = nl[i];
p = s->var[VAR(nl[i])].lvl;
/* lvlstamp[p] != s->stamp + 1 => only one literal at level p => must be nonredundant */
if(s->lvlstamp[p] != s->stamp + 1 || !redundant(s, l))
if(found || p < s->cfllvl)
nl[j++] = nl[i];
else{
/* watch this literal */
l = nl[i], nl[j++] = nl[1], nl[1] = l;
found = 1;
}
}
s->ncflcl = j;
if(!full){
/* normal mode: jump back and add to trail right away */
satbackjump(s, s->cfllvl);
sataddtrail(s, nl[0]);
}else{
/* purging: record minimum cfllvl and literals at that level */
if(s->cfllvl < s->fullrlvl){
s->fullrlvl = s->cfllvl;
s->nfullrlits = 0;
}
s->fullrlits[s->nfullrlits++] = nl[0];
}
r = learn(s, full);
if(!full && r != nil)
s->var[VAR(nl[0])].reason = r;
if(verboseconflict)
if(r != nil)
print("learned %+Γ\n", r);
else
print("learned %d\n", signf(nl[0]));
s->Δactivity *= s->varρ;
s->Δclactivity *= s->clauseρ;
s->conflicts++;
}
/* to purge, we need a fullrun that assigns values to all variables.
* during this we record the first conflict at each level, to be resolved
* later. otherwise this is just a copy of the main loop which never
* purges or flushes. */
static int
fullrun(SATSolve *s)
{
int l;
uvlong b;
SATClause *c;
while(s->ntrail < s->nvar){
decision(s);
re:
while(s->binptr < s->ntrail){
l = s->trail[s->binptr++];
b = binforcing(s, l, 1);
if(b != 0){
if(s->lvl == 0){
s->unsat = 1;
return -1;
}
if(s->nfullrcfl == 0 || s->lvl > CFLLVL(s->fullrcfl[s->nfullrcfl-1])){
s->fullrcfl = satrealloc(s, s->fullrcfl, sizeof(SATConflict) * (s->nfullrcfl + 1));
s->fullrcfl[s->nfullrcfl].lvl = 1<<31 | s->lvl;
s->fullrcfl[s->nfullrcfl++].b = b;
}
}
sanity(s);
}
while(s->forptr < s->ntrail){
l = s->trail[s->forptr++];
c = forcing(s, NOT(l), 1);
if(c != nil){
if(s->lvl == 0){
s->unsat = 1;
return -1;
}
if(s->nfullrcfl == 0 || s->lvl > CFLLVL(s->fullrcfl[s->nfullrcfl-1])){
s->fullrcfl = satrealloc(s, s->fullrcfl, sizeof(SATConflict) * (s->nfullrcfl + 1));
s->fullrcfl[s->nfullrcfl].lvl = s->lvl;
s->fullrcfl[s->nfullrcfl++].c = c;
}
}
}
if(s->binptr < s->ntrail) goto re;
}
return 0;
}
/* assign range scores to all clauses.
* p == number of levels that have positive literals in the clause.
* r == number of levels that have literals in the clause.
* range == min(floor(16 * (p + α (r - p))), 255) with magic constant α. */
static void
ranges(SATSolve *s)
{
SATClause *c;
int p, r, k, l, v;
uint ci;
ci = 2;
memset(s->lvlstamp, 0, sizeof(int) * s->nvar);
memset(s->rangecnt, 0, sizeof(s->rangecnt));
for(c = s->learncl; c != nil; c = c->next, ci += 2){
if(!s->var[VAR(c->l[0])].binreason && s->var[VAR(c->l[0])].reason == c){
c->range = 0;
s->rangecnt[0]++;
continue;
}
p = 0;
r = 0;
for(k = 0; k < c->n; k++){
l = c->l[k];
v = s->var[VAR(l)].lvl;
if(v == 0){
if(s->lit[l].val == 1){
c->range = 256;
goto next;
}
}else{
if(s->lvlstamp[v] < ci){
s->lvlstamp[v] = ci;
r++;
}
if(s->lvlstamp[v] == ci && s->lit[l].val == 1){
s->lvlstamp[v] = ci + 1;
p++;
}
}
}
r = 16 * (p + s->purgeα * (r - p));
if(r > 255) r = 255;
c->range = r;
s->rangecnt[r]++;
next: ;
}
}
/* resolve conflicts found during fullrun() */
static void
fullrconflicts(SATSolve *s)
{
SATConflict *cfl;
int i;
s->fullrlvl = s->lvl;
s->nfullrlits = 0;
for(cfl = &s->fullrcfl[s->nfullrcfl - 1]; cfl >= s->fullrcfl; cfl--){
satbackjump(s, CFLLVL(*cfl));
if(cfl->lvl < 0)
conflict(s, nil, cfl->b, 1);
else
conflict(s, cfl->c, 0, 1);
}
satbackjump(s, 0);
if(s->fullrlvl == 0)
for(i = 0; i < s->nfullrlits; i++)
sataddtrail(s, s->fullrlits[i]);
free(s->fullrcfl);
s->fullrcfl = nil;
}
/* note that nil > *, this simplifies the algorithm by having nil "bubble" to the top */
static int
actgt(SATClause *a, SATClause *b)
{
if(b == nil) return 0;
if(a == nil) return 1;
return a->activity > b->activity || a->activity == b->activity && a > b;
}
/* select n clauses to keep
* first we find the upper limit j on the range score
* to get the exact number, we move htot clauses from j to j+1
* to this end, we put them in a max-heap of size htot, sorted by activity,
* continually replacing the largest element if we find a less active clause.
* the heap starts out filled with nil and the nil are replaced during the first
* htot iterations. */
#define LEFT(i) (2*(i)+1)
#define RIGHT(i) (2*(i)+2)
static int
judgement(SATSolve *s, int n)
{
int cnt, i, j, htot, m;
SATClause *c, **h, *z;
cnt = 0;
for(j = 0; j < 256; j++){
cnt += s->rangecnt[j];
if(cnt >= n) break;
}
if(j == 256) return j;
if(cnt > n){
htot = cnt - n;
h = satrealloc(s, nil, sizeof(SATClause *) * htot);
memset(h, 0, sizeof(SATClause *) * htot);
for(c = s->learncl; c != nil; c = c->next){
if(c->range != j || actgt(c, h[0])) continue;
h[0] = c;
m = 0;
for(;;){
i = m;
if(LEFT(i) < htot && actgt(h[LEFT(i)], h[m])) m = LEFT(i);
if(RIGHT(i) < htot && actgt(h[RIGHT(i)], h[m])) m = RIGHT(i);
if(i == m) break;
z = h[i], h[i] = h[m], h[m] = z;
}
}
for(i = 0; i < htot; i++)
if(h[i] != nil)
h[i]->range = j + 1;
free(h);
}
return j;
}
/* during purging we remove permanently false literals from learned clauses.
* returns 1 if the clause can be deleted entirely. */
static int
cleanupclause(SATSolve *s, SATClause *c)
{
int i, k;
for(i = 0; i < c->n; i++)
if(s->lit[c->l[i]].val == 0)
break;
if(i == c->n) return 0;
for(k = i; i < c->n; i++)
if(s->lit[c->l[i]].val != 0)
c->l[k++] = c->l[i];
c->n = k;
if(k > 1) return 0;
if(k == 0)
s->unsat = 1;
else if(s->lit[c->l[0]].val < 0)
sataddtrail(s, c->l[0]);
return 1;
}
/* delete clauses by overwriting them. don't delete empty blocks; we're going to fill them up soon enough again. */
static void
execution(SATSolve *s, int j)
{
SATClause *c, *n, **cp, *p;
SATBlock *b;
SATVar *v0;
int f, sz;
b = s->bl[1].next;
p = (SATClause*) b->data;
s->ncl = s->ncl0;
cp = &s->learncl;
for(c = p; c != nil; c = n){
n = c->next;
if(c->range > j || cleanupclause(s, c))
continue;
sz = sizeof(SATClause) + (c->n - 1) * sizeof(int);
f = (uchar*)b + SATBLOCKSZ - (uchar*)p;
if(f < sz){
memset(p, 0, f);
b = b->next;
assert(b != &s->bl[1]);
p = (SATClause *) b->data;
}
b->last = p;
/* update reason field of the first variable (if applicable) */
v0 = &s->var[VAR(c->l[0])];
if(!v0->isbinreason && v0->reason == c)
v0->reason = p;
memmove(p, c, sz);
*cp = p;
cp = &p->next;
p = (void*)((uintptr)p + sz + CLAUSEALIGN - 1 & -CLAUSEALIGN);
b->end = p;
s->ncl++;
}
*cp = nil;
*s->lastp[0] = s->learncl;
s->lastp[1] = cp;
s->lastbl = b;
f = (uchar*)b + SATBLOCKSZ - (uchar*)p;
memset(p, 0, f);
for(b = b->next; b != &s->bl[1]; b = b->next){
b->last = nil;
b->end = b->data;
}
}
static void
thepurge(SATSolve *s)
{
int nkeep, i, j;
SATVar *v;
s->purgeival += s->purgeδ;
s->nextpurge = s->conflicts + s->purgeival;
nkeep = (s->ncl - s->ncl0) / 2;
for(i = 0; i < s->ntrail; i++){
v = &s->var[VAR(s->trail[i])];
if(!v->isbinreason && v->reason != nil)
nkeep++;
}
if(nkeep <= 0) return; /* shouldn't happen */
s->nfullrcfl = 0;
if(fullrun(s) < 0){ /* accidentally determined UNSAT during fullrun() */
free(s->fullrcfl);
s->fullrcfl = nil;
return;
}
ranges(s);
fullrconflicts(s);
j = judgement(s, nkeep);
execution(s, j);
rewatch(s);
}
/* to avoid getting stuck, flushing backs up the trail to remove low activity variables.
* don't worry about throwing out high activity ones, they'll get readded quickly. */
static void
theflush(SATSolve *s)
{
double actk;
int dd, l;
/* "reluctant doubling" wizardry to determine when to flush */
if((s->flushu & -s->flushu) == s->flushv){
s->flushu++;
s->flushv = 1;
s->flushθ = s->flushψ;
}else{
s->flushv *= 2;
s->flushθ += s->flushθ >> 4;
}
s->nextflush = s->conflicts + s->flushv;
if(s->agility > s->flushθ) return; /* don't flush when we're too busy */
/* clean up the heap so that a free variable is at the top */
while(s->nheap > 0 && s->heap[0]->lvl >= 0)
satheaptake(s);
if(s->nheap == 0) return; /* shouldn't happen */
actk = s->heap[0]->activity;
for(dd = 0; dd < s->lvl; dd++){
l = s->trail[s->decbd[dd+1]];
if(s->var[VAR(l)].activity < actk)
break;
}
satbackjump(s, dd);
}
int
satsolve(SATSolve *s)
{
int l;
SATClause *c;
uvlong b;
if(s == nil) return 1;
if(s->scratched) return -1;
if(s->nvar == 0) return 1;
solvinit(s);
while(!s->unsat){
re:
while(s->binptr < s->ntrail){
l = s->trail[s->binptr++];
b = binforcing(s, l, 0);
sanity(s);
if(b != 0){
if(s->lvl == 0) goto unsat;
conflict(s, nil, b, 0);
sanity(s);
}
}
while(s->forptr < s->ntrail){
l = s->trail[s->forptr++];
c = forcing(s, NOT(l), 0);
sanity(s);
if(c != nil){
if(s->lvl == 0) goto unsat;
conflict(s, c, 0, 0);
sanity(s);
}
}
if(s->binptr < s->ntrail) goto re;
if(s->ntrail == s->nvar) goto out;
if(s->conflicts >= s->nextpurge)
thepurge(s);
else if(s->conflicts >= s->nextflush)
theflush(s);
else
decision(s);
}
unsat:
s->unsat = 1;
out:
satcleanup(s, 0);
return !s->unsat;
}
void
satreset(SATSolve *s)
{
int i;
if(s == nil || s->decbd == nil) return;
satbackjump(s, -1);
s->lvl = 0;
for(i = 0; i < s->nvar; i++){
s->var[i].activity = 0;
s->var[i].flags |= VARPHASE;
}
satcleanup(s, 1);
s->Δactivity = 1;
s->Δclactivity = 1;
}