// Disposes a clauses and removes it from watcher lists. NOTE! Low-level; does NOT change the 'clauses' and 'learnts' vector. // void Solver::remove(Clause* c, bool just_dealloc) { if (!just_dealloc){ if (c->size() == 2) removeWatch(watches[index(~(*c)[0])], GClause_new((*c)[1])), removeWatch(watches[index(~(*c)[1])], GClause_new((*c)[0])); else removeWatch(watches[index(~(*c)[0])], GClause_new(c)), removeWatch(watches[index(~(*c)[1])], GClause_new(c)); } if (c->learnt()) stats.learnts_literals -= c->size(); else stats.clauses_literals -= c->size(); xfree(c); }
/*_________________________________________________________________________________________________ | | simplifyDB : [void] -> [bool] | | Description: | Simplify the clause database according to the current top-level assigment. Currently, the only | thing done here is the removal of satisfied clauses, but more things can be put here. |________________________________________________________________________________________________@*/ void Solver::simplifyDB() { if (!ok) return; // GUARD (public method) assert(decisionLevel() == 0); if (propagate() != NULL){ ok = false; return; } if (nAssigns() == simpDB_assigns || simpDB_props > 0) // (nothing has changed or preformed a simplification too recently) return; // Clear watcher lists: for (int i = simpDB_assigns; i < nAssigns(); i++){ Lit p = trail[i]; vec<GClause>& ws = watches[index(~p)]; for (int j = 0; j < ws.size(); j++) if (ws[j].isLit()) if (removeWatch(watches[index(~ws[j].lit())], GClause_new(p))) // (remove binary GClause from "other" watcher list) n_bin_clauses--; watches[index( p)].clear(true); watches[index(~p)].clear(true); } // Remove satisfied clauses: for (int type = 0; type < 2; type++){ vec<Clause*>& cs = type ? learnts : clauses; int j = 0; for (int i = 0; i < cs.size(); i++){ if (!locked(cs[i]) && simplify(cs[i])) // (the test for 'locked()' is currently superfluous, but without it the reason-graph is not correctly maintained for decision level 0) remove(cs[i]); else cs[j++] = cs[i]; } cs.shrink(cs.size()-j); } simpDB_assigns = nAssigns(); simpDB_props = stats.clauses_literals + stats.learnts_literals; // (shouldn't depend on 'stats' really, but it will do for now) }
/*_________________________________________________________________________________________________ | | newClause : (ps : const vec<Lit>&) (learnt : bool) -> [void] | | Description: | Allocate and add a new clause to the SAT solvers clause database. If a conflict is detected, | the 'ok' flag is cleared and the solver is in an unusable state (must be disposed). | | Input: | ps - The new clause as a vector of literals. | learnt - Is the clause a learnt clause? For learnt clauses, 'ps[0]' is assumed to be the | asserting literal. An appropriate 'enqueue()' operation will be performed on this | literal. One of the watches will always be on this literal, the other will be set to | the literal with the highest decision level. | | Effect: | Activity heuristics are updated. |________________________________________________________________________________________________@*/ void Solver::newClause(const vec<Lit>& ps_, bool learnt) { if (!ok) return; vec<Lit> qs; if (!learnt){ assert(decisionLevel() == 0); ps_.copyTo(qs); // Make a copy of the input vector. // Remove duplicates: sortUnique(qs); // Check if clause is satisfied: for (int i = 0; i < qs.size()-1; i++){ if (qs[i] == ~qs[i+1]) return; } for (int i = 0; i < qs.size(); i++){ if (value(qs[i]) == l_True) return; } // Remove false literals: int i, j; for (i = j = 0; i < qs.size(); i++) if (value(qs[i]) != l_False) qs[j++] = qs[i]; qs.shrink(i - j); } const vec<Lit>& ps = learnt ? ps_ : qs; // 'ps' is now the (possibly) reduced vector of literals. if (ps.size() == 0){ ok = false; }else if (ps.size() == 1){ // NOTE: If enqueue takes place at root level, the assignment will be lost in incremental use (it doesn't seem to hurt much though). if (!enqueue(ps[0])) ok = false; }else if (ps.size() == 2){ // Create special binary clause watch: watches[index(~ps[0])].push(GClause_new(ps[1])); watches[index(~ps[1])].push(GClause_new(ps[0])); if (learnt){ check(enqueue(ps[0], GClause_new(~ps[1]))); stats.learnts_literals += ps.size(); }else stats.clauses_literals += ps.size(); n_bin_clauses++; }else{ // Allocate clause: Clause* c = Clause_new(learnt, ps); if (learnt){ // Put the second watch on the literal with highest decision level: int max_i = 1; int max = level[var(ps[1])]; for (int i = 2; i < ps.size(); i++) if (level[var(ps[i])] > max) max = level[var(ps[i])], max_i = i; (*c)[1] = ps[max_i]; (*c)[max_i] = ps[1]; // Bump, enqueue, store clause: claBumpActivity(c); // (newly learnt clauses should be considered active) check(enqueue((*c)[0], GClause_new(c))); learnts.push(c); stats.learnts_literals += c->size(); }else{ // Store clause: clauses.push(c); stats.clauses_literals += c->size(); } // Watch clause: watches[index(~(*c)[0])].push(GClause_new(c)); watches[index(~(*c)[1])].push(GClause_new(c)); } }
/*_________________________________________________________________________________________________ | | propagate : [void] -> [Clause*] | | Description: | Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, | otherwise NULL. | | Post-conditions: | * the propagation queue is empty, even if there was a conflict. |________________________________________________________________________________________________@*/ Clause* Solver::propagate() { Clause* confl = NULL; while (qhead < trail.size()){ stats.propagations++; simpDB_props--; Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. vec<GClause>& ws = watches[index(p)]; GClause* i,* j, *end; for (i = j = (GClause*)ws, end = i + ws.size(); i != end;){ if (i->isLit()){ if (!enqueue(i->lit(), GClause_new(p))){ if (decisionLevel() == 0) ok = false; confl = propagate_tmpbin; (*confl)[1] = ~p; (*confl)[0] = i->lit(); qhead = trail.size(); // Copy the remaining watches: while (i < end) *j++ = *i++; }else *j++ = *i++; }else{ Clause& c = *i->clause(); i++; assert(c.size() > 2); // Make sure the false literal is data[1]: Lit false_lit = ~p; if (c[0] == false_lit) c[0] = c[1], c[1] = false_lit; assert(c[1] == false_lit); // If 0th watch is true, then clause is already satisfied. Lit first = c[0]; lbool val = value(first); if (val == l_True){ *j++ = GClause_new(&c); }else{ // Look for new watch: for (int k = 2; k < c.size(); k++) if (value(c[k]) != l_False){ c[1] = c[k]; c[k] = false_lit; watches[index(~c[1])].push(GClause_new(&c)); goto FoundWatch; } // Did not find watch -- clause is unit under assignment: *j++ = GClause_new(&c); if (!enqueue(first, GClause_new(&c))){ if (decisionLevel() == 0) ok = false; confl = &c; qhead = trail.size(); // Copy the remaining watches: while (i < end) *j++ = *i++; } FoundWatch:; } } } ws.shrink(i - j); } return confl; }
/*_________________________________________________________________________________________________ | | analyze : (confl : Clause*) (out_learnt : vec<Lit>&) (out_btlevel : int&) -> [void] | | Description: | Analyze conflict and produce a reason clause. | | Pre-conditions: | * 'out_learnt' is assumed to be cleared. | * Current decision level must be greater than root level. | | Post-conditions: | * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. | | Effect: | Will undo part of the trail, upto but not beyond the assumption of the current decision level. |________________________________________________________________________________________________@*/ void Solver::analyze(Clause* _confl, vec<Lit>& out_learnt, int& out_btlevel) { GClause confl = GClause_new(_confl); vec<char>& seen = analyze_seen; int pathC = 0; Lit p = lit_Undef; // Generate conflict clause: // out_learnt.push(); // (leave room for the asserting literal) out_btlevel = 0; int index = trail.size()-1; do{ assert(confl != GClause_NULL); // (otherwise should be UIP) Clause& c = confl.isLit() ? ((*analyze_tmpbin)[1] = confl.lit(), *analyze_tmpbin) : *confl.clause(); if (c.learnt()) claBumpActivity(&c); for (int j = (p == lit_Undef) ? 0 : 1; j < c.size(); j++){ Lit q = c[j]; if (!seen[var(q)] && level[var(q)] > 0){ varBumpActivity(q); seen[var(q)] = 1; if (level[var(q)] == decisionLevel()) pathC++; else{ out_learnt.push(q); out_btlevel = max(out_btlevel, level[var(q)]); } } } // Select next clause to look at: while (!seen[var(trail[index--])]); p = trail[index+1]; confl = reason[var(p)]; seen[var(p)] = 0; pathC--; }while (pathC > 0); out_learnt[0] = ~p; int i, j; if (expensive_ccmin){ // Simplify conflict clause (a lot): // unsigned int min_level = 0; for (i = 1; i < out_learnt.size(); i++) min_level |= 1 << (level[var(out_learnt[i])] & 31); // (maintain an abstraction of levels involved in conflict) out_learnt.copyTo(analyze_toclear); for (i = j = 1; i < out_learnt.size(); i++) if (reason[var(out_learnt[i])] == GClause_NULL || !analyze_removable(out_learnt[i], min_level)) out_learnt[j++] = out_learnt[i]; }else{ // Simplify conflict clause (a little): // out_learnt.copyTo(analyze_toclear); for (i = j = 1; i < out_learnt.size(); i++){ GClause r = reason[var(out_learnt[i])]; if (r == GClause_NULL) out_learnt[j++] = out_learnt[i]; else if (r.isLit()){ Lit q = r.lit(); if (!seen[var(q)] && level[var(q)] != 0) out_learnt[j++] = out_learnt[i]; }else{ Clause& c = *r.clause(); for (int k = 1; k < c.size(); k++) if (!seen[var(c[k])] && level[var(c[k])] != 0){ out_learnt[j++] = out_learnt[i]; break; } } } } stats.max_literals += out_learnt.size(); out_learnt.shrink(i - j); stats.tot_literals += out_learnt.size(); for (int j = 0; j < analyze_toclear.size(); j++) seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared) }