#define __STDC_FORMAT_MACROS #define __STDC_LIMIT_MACROS /***************************************************************************************[Solver.cc] Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson Copyright (c) 2007-2010, Niklas Sorensson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **************************************************************************************************/ #include #include "Alg.h" #include "Sort.h" #include "System.h" #include "Solver.h" using namespace Minisat; //================================================================================================= // Options: static const char* _cat = "CORE"; static DoubleOption opt_var_decay (_cat, "var-decay", "The variable activity decay factor", 0.95, DoubleRange(0, false, 1, false)); static DoubleOption opt_clause_decay (_cat, "cla-decay", "The clause activity decay factor", 0.999, DoubleRange(0, false, 1, false)); static DoubleOption opt_random_var_freq (_cat, "rnd-freq", "The frequency with which the decision heuristic tries to choose a random variable", 0, DoubleRange(0, true, 1, true)); static DoubleOption opt_random_seed (_cat, "rnd-seed", "Used by the random variable selection", 91648253, DoubleRange(0, false, HUGE_VAL, false)); static IntOption opt_ccmin_mode (_cat, "ccmin-mode", "Controls conflict clause minimization (0=none, 1=basic, 2=deep)", 2, IntRange(0, 2)); static IntOption opt_phase_saving (_cat, "phase-saving", "Controls the level of phase saving (0=none, 1=limited, 2=full)", 2, IntRange(0, 2)); static BoolOption opt_rnd_init_act (_cat, "rnd-init", "Randomize the initial activity", false); static BoolOption opt_luby_restart (_cat, "luby", "Use the Luby restart sequence", true); static IntOption opt_restart_first (_cat, "rfirst", "The base restart interval", 100, IntRange(1, INT32_MAX)); static DoubleOption opt_restart_inc (_cat, "rinc", "Restart interval increase factor", 2, DoubleRange(1, false, HUGE_VAL, false)); static DoubleOption opt_garbage_frac (_cat, "gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered", 0.20, DoubleRange(0, false, HUGE_VAL, false)); static IntOption opt_min_learnts_lim (_cat, "min-learnts", "Minimum learnt clause limit", 0, IntRange(0, INT32_MAX)); //================================================================================================= // Constructor/Destructor: Solver::Solver() : // Parameters (user settable): // verbosity (0) , var_decay (opt_var_decay) , clause_decay (opt_clause_decay) , random_var_freq (opt_random_var_freq) , random_seed (opt_random_seed) , luby_restart (opt_luby_restart) , ccmin_mode (opt_ccmin_mode) , phase_saving (opt_phase_saving) , rnd_pol (false) , rnd_init_act (opt_rnd_init_act) , garbage_frac (opt_garbage_frac) , min_learnts_lim (opt_min_learnts_lim) , restart_first (opt_restart_first) , restart_inc (opt_restart_inc) // Parameters (the rest): // , learntsize_factor((double)1/(double)3), learntsize_inc(1.1) // Parameters (experimental): // , learntsize_adjust_start_confl (100) , learntsize_adjust_inc (1.5) // Statistics: (formerly in 'SolverStats') // , solves(0), starts(0), decisions(0), rnd_decisions(0), propagations(0), conflicts(0) , dec_vars(0), num_clauses(0), num_learnts(0), clauses_literals(0), learnts_literals(0), max_literals(0), tot_literals(0) , watches (WatcherDeleted(ca)) , order_heap (VarOrderLt(activity)) , ok (true) , cla_inc (1) , var_inc (1) , qhead (0) , simpDB_assigns (-1) , simpDB_props (0) , progress_estimate (0) , remove_satisfied (true) , next_var (0) // Resource constraints: // , conflict_budget (-1) , propagation_budget (-1) , asynch_interrupt (false) {} Solver::~Solver() { } //================================================================================================= // Minor methods: // Creates a new SAT variable in the solver. If 'decision' is cleared, variable will not be // used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result). // Var Solver::newVar(lbool upol, bool dvar) { Var v; if (free_vars.size() > 0){ v = free_vars.last(); free_vars.pop(); }else v = next_var++; watches .init(mkLit(v, false)); watches .init(mkLit(v, true )); assigns .insert(v, l_Undef); vardata .insert(v, mkVarData(CRef_Undef, 0)); activity .insert(v, rnd_init_act ? drand(random_seed) * 0.00001 : 0); seen .insert(v, 0); polarity .insert(v, true); user_pol .insert(v, upol); decision .reserve(v); trail .capacity(v+1); setDecisionVar(v, dvar); return v; } // Note: at the moment, only unassigned variable will be released (this is to avoid duplicate // releases of the same variable). void Solver::releaseVar(Lit l) { if (value(l) == l_Undef){ addClause(l); released_vars.push(var(l)); } } bool Solver::addClause_(vec& ps) { assert(decisionLevel() == 0); if (!ok) return false; // Check if clause is satisfied and remove false/duplicate literals: sort(ps); Lit p; int i, j; for (i = j = 0, p = lit_Undef; i < ps.size(); i++) if (value(ps[i]) == l_True || ps[i] == ~p) return true; else if (value(ps[i]) != l_False && ps[i] != p) ps[j++] = p = ps[i]; ps.shrink(i - j); if (ps.size() == 0) return ok = false; else if (ps.size() == 1){ uncheckedEnqueue(ps[0]); return ok = (propagate() == CRef_Undef); }else{ CRef cr = ca.alloc(ps, false); clauses.push(cr); attachClause(cr); } return true; } void Solver::attachClause(CRef cr){ const Clause& c = ca[cr]; assert(c.size() > 1); watches[~c[0]].push(Watcher(cr, c[1])); watches[~c[1]].push(Watcher(cr, c[0])); if (c.learnt()) num_learnts++, learnts_literals += c.size(); else num_clauses++, clauses_literals += c.size(); } void Solver::detachClause(CRef cr, bool strict){ const Clause& c = ca[cr]; assert(c.size() > 1); // Strict or lazy detaching: if (strict){ remove(watches[~c[0]], Watcher(cr, c[1])); remove(watches[~c[1]], Watcher(cr, c[0])); }else{ watches.smudge(~c[0]); watches.smudge(~c[1]); } if (c.learnt()) num_learnts--, learnts_literals -= c.size(); else num_clauses--, clauses_literals -= c.size(); } void Solver::removeClause(CRef cr) { Clause& c = ca[cr]; detachClause(cr); // Don't leave pointers to free'd memory! if (locked(c)) vardata[var(c[0])].reason = CRef_Undef; c.mark(1); ca.free(cr); } bool Solver::satisfied(const Clause& c) const { for (int i = 0; i < c.size(); i++) if (value(c[i]) == l_True) return true; return false; } // Revert to the state at given level (keeping all assignment at 'level' but not beyond). // void Solver::cancelUntil(int level) { if (decisionLevel() > level){ for (int c = trail.size()-1; c >= trail_lim[level]; c--){ Var x = var(trail[c]); assigns [x] = l_Undef; if (phase_saving > 1 || (phase_saving == 1 && c > trail_lim.last())) polarity[x] = sign(trail[c]); insertVarOrder(x); } qhead = trail_lim[level]; trail.shrink(trail.size() - trail_lim[level]); trail_lim.shrink(trail_lim.size() - level); } } //================================================================================================= // Major methods: Lit Solver::pickBranchLit() { Var next = var_Undef; // Random decision: if (drand(random_seed) < random_var_freq && !order_heap.empty()){ next = order_heap[irand(random_seed,order_heap.size())]; if (value(next) == l_Undef && decision[next]) rnd_decisions++; } // Activity based decision: while (next == var_Undef || value(next) != l_Undef || !decision[next]) if (order_heap.empty()){ next = var_Undef; break; }else next = order_heap.removeMin(); // Choose polarity based on different polarity modes (global or per-variable): if (next == var_Undef) return lit_Undef; else if (user_pol[next] != l_Undef) return mkLit(next, user_pol[next] == l_True); else if (rnd_pol) return mkLit(next, drand(random_seed) < 0.5); else return mkLit(next, polarity[next]); } /*_________________________________________________________________________________________________ | | analyze : (confl : Clause*) (out_learnt : vec&) (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'. | * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the | rest of literals. There may be others from the same level though. | |________________________________________________________________________________________________@*/ void Solver::analyze(CRef confl, vec& out_learnt, int& out_btlevel) { int pathC = 0; Lit p = lit_Undef; // Generate conflict clause: // out_learnt.push(); // (leave room for the asserting literal) int index = trail.size() - 1; do{ assert(confl != CRef_Undef); // (otherwise should be UIP) Clause& c = ca[confl]; 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(var(q)); seen[var(q)] = 1; if (level(var(q)) >= decisionLevel()) pathC++; else out_learnt.push(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; // Simplify conflict clause: // int i, j; out_learnt.copyTo(analyze_toclear); if (ccmin_mode == 2){ for (i = j = 1; i < out_learnt.size(); i++) if (reason(var(out_learnt[i])) == CRef_Undef || !litRedundant(out_learnt[i])) out_learnt[j++] = out_learnt[i]; }else if (ccmin_mode == 1){ for (i = j = 1; i < out_learnt.size(); i++){ Var x = var(out_learnt[i]); if (reason(x) == CRef_Undef) out_learnt[j++] = out_learnt[i]; else{ Clause& c = ca[reason(var(out_learnt[i]))]; 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; } } } }else i = j = out_learnt.size(); max_literals += out_learnt.size(); out_learnt.shrink(i - j); tot_literals += out_learnt.size(); // Find correct backtrack level: // if (out_learnt.size() == 1) out_btlevel = 0; else{ int max_i = 1; // Find the first literal assigned at the next-highest level: for (int i = 2; i < out_learnt.size(); i++) if (level(var(out_learnt[i])) > level(var(out_learnt[max_i]))) max_i = i; // Swap-in this literal at index 1: Lit p = out_learnt[max_i]; out_learnt[max_i] = out_learnt[1]; out_learnt[1] = p; out_btlevel = level(var(p)); } for (int j = 0; j < analyze_toclear.size(); j++) seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared) } // Check if 'p' can be removed from a conflict clause. bool Solver::litRedundant(Lit p) { enum { seen_undef = 0, seen_source = 1, seen_removable = 2, seen_failed = 3 }; assert(seen[var(p)] == seen_undef || seen[var(p)] == seen_source); assert(reason(var(p)) != CRef_Undef); Clause* c = &ca[reason(var(p))]; vec& stack = analyze_stack; stack.clear(); for (uint32_t i = 1; ; i++){ if (i < (uint32_t)c->size()){ // Checking 'p'-parents 'l': Lit l = (*c)[i]; // Variable at level 0 or previously removable: if (level(var(l)) == 0 || seen[var(l)] == seen_source || seen[var(l)] == seen_removable){ continue; } // Check variable can not be removed for some local reason: if (reason(var(l)) == CRef_Undef || seen[var(l)] == seen_failed){ stack.push(ShrinkStackElem(0, p)); for (int i = 0; i < stack.size(); i++) if (seen[var(stack[i].l)] == seen_undef){ seen[var(stack[i].l)] = seen_failed; analyze_toclear.push(stack[i].l); } return false; } // Recursively check 'l': stack.push(ShrinkStackElem(i, p)); i = 0; p = l; c = &ca[reason(var(p))]; }else{ // Finished with current element 'p' and reason 'c': if (seen[var(p)] == seen_undef){ seen[var(p)] = seen_removable; analyze_toclear.push(p); } // Terminate with success if stack is empty: if (stack.size() == 0) break; // Continue with top element on stack: i = stack.last().i; p = stack.last().l; c = &ca[reason(var(p))]; stack.pop(); } } return true; } /*_________________________________________________________________________________________________ | | analyzeFinal : (p : Lit) -> [void] | | Description: | Specialized analysis procedure to express the final conflict in terms of assumptions. | Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and | stores the result in 'out_conflict'. |________________________________________________________________________________________________@*/ void Solver::analyzeFinal(Lit p, LSet& out_conflict) { out_conflict.clear(); out_conflict.insert(p); if (decisionLevel() == 0) return; seen[var(p)] = 1; for (int i = trail.size()-1; i >= trail_lim[0]; i--){ Var x = var(trail[i]); if (seen[x]){ if (reason(x) == CRef_Undef){ assert(level(x) > 0); out_conflict.insert(~trail[i]); }else{ Clause& c = ca[reason(x)]; for (int j = 1; j < c.size(); j++) if (level(var(c[j])) > 0) seen[var(c[j])] = 1; } seen[x] = 0; } } seen[var(p)] = 0; } void Solver::uncheckedEnqueue(Lit p, CRef from) { assert(value(p) == l_Undef); assigns[var(p)] = lbool(!sign(p)); vardata[var(p)] = mkVarData(from, decisionLevel()); trail.push_(p); } /*_________________________________________________________________________________________________ | | propagate : [void] -> [Clause*] | | Description: | Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, | otherwise CRef_Undef. | | Post-conditions: | * the propagation queue is empty, even if there was a conflict. |________________________________________________________________________________________________@*/ CRef Solver::propagate() { CRef confl = CRef_Undef; int num_props = 0; while (qhead < trail.size()){ Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. vec& ws = watches.lookup(p); Watcher *i, *j, *end; num_props++; for (i = j = (Watcher*)ws, end = i + ws.size(); i != end;){ // Try to avoid inspecting the clause: Lit blocker = i->blocker; if (value(blocker) == l_True){ *j++ = *i++; continue; } // Make sure the false literal is data[1]: CRef cr = i->cref; Clause& c = ca[cr]; Lit false_lit = ~p; if (c[0] == false_lit) c[0] = c[1], c[1] = false_lit; assert(c[1] == false_lit); i++; // If 0th watch is true, then clause is already satisfied. Lit first = c[0]; Watcher w = Watcher(cr, first); if (first != blocker && value(first) == l_True){ *j++ = w; continue; } // 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[~c[1]].push(w); goto NextClause; } // Did not find watch -- clause is unit under assignment: *j++ = w; if (value(first) == l_False){ confl = cr; qhead = trail.size(); // Copy the remaining watches: while (i < end) *j++ = *i++; }else uncheckedEnqueue(first, cr); NextClause:; } ws.shrink(i - j); } propagations += num_props; simpDB_props -= num_props; return confl; } /*_________________________________________________________________________________________________ | | reduceDB : () -> [void] | | Description: | Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked | clauses are clauses that are reason to some assignment. Binary clauses are never removed. |________________________________________________________________________________________________@*/ struct reduceDB_lt { ClauseAllocator& ca; reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {} bool operator () (CRef x, CRef y) { return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } }; void Solver::reduceDB() { int i, j; double extra_lim = cla_inc / learnts.size(); // Remove any clause below this activity sort(learnts, reduceDB_lt(ca)); // Don't delete binary or locked clauses. From the rest, delete clauses from the first half // and clauses with activity smaller than 'extra_lim': for (i = j = 0; i < learnts.size(); i++){ Clause& c = ca[learnts[i]]; if (c.size() > 2 && !locked(c) && (i < learnts.size() / 2 || c.activity() < extra_lim)) removeClause(learnts[i]); else learnts[j++] = learnts[i]; } learnts.shrink(i - j); checkGarbage(); } void Solver::removeSatisfied(vec& cs) { int i, j; for (i = j = 0; i < cs.size(); i++){ Clause& c = ca[cs[i]]; if (satisfied(c)) removeClause(cs[i]); else{ // Trim clause: assert(value(c[0]) == l_Undef && value(c[1]) == l_Undef); for (int k = 2; k < c.size(); k++) if (value(c[k]) == l_False){ c[k--] = c[c.size()-1]; c.pop(); } cs[j++] = cs[i]; } } cs.shrink(i - j); } void Solver::rebuildOrderHeap() { vec vs; for (Var v = 0; v < nVars(); v++) if (decision[v] && value(v) == l_Undef) vs.push(v); order_heap.build(vs); } /*_________________________________________________________________________________________________ | | simplify : [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. |________________________________________________________________________________________________@*/ bool Solver::simplify() { assert(decisionLevel() == 0); if (!ok || propagate() != CRef_Undef) return ok = false; if (nAssigns() == simpDB_assigns || (simpDB_props > 0)) return true; // Remove satisfied clauses: removeSatisfied(learnts); if (remove_satisfied){ // Can be turned off. removeSatisfied(clauses); // TODO: what todo in if 'remove_satisfied' is false? // Remove all released variables from the trail: for (int i = 0; i < released_vars.size(); i++){ assert(seen[released_vars[i]] == 0); seen[released_vars[i]] = 1; } int i, j; for (i = j = 0; i < trail.size(); i++) if (seen[var(trail[i])] == 0) trail[j++] = trail[i]; trail.shrink(i - j); //printf("trail.size()= %d, qhead = %d\n", trail.size(), qhead); qhead = trail.size(); for (int i = 0; i < released_vars.size(); i++) seen[released_vars[i]] = 0; // Released variables are now ready to be reused: append(released_vars, free_vars); released_vars.clear(); } checkGarbage(); rebuildOrderHeap(); simpDB_assigns = nAssigns(); simpDB_props = clauses_literals + learnts_literals; // (shouldn't depend on stats really, but it will do for now) return true; } /*_________________________________________________________________________________________________ | | search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool] | | Description: | Search for a model the specified number of conflicts. | NOTE! Use negative value for 'nof_conflicts' indicate infinity. | | Output: | 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If | all variables are decision variables, this means that the clause set is satisfiable. 'l_False' | if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. |________________________________________________________________________________________________@*/ lbool Solver::search(int nof_conflicts) { assert(ok); int backtrack_level; int conflictC = 0; vec learnt_clause; starts++; for (;;){ CRef confl = propagate(); if (confl != CRef_Undef){ // CONFLICT conflicts++; conflictC++; if (decisionLevel() == 0) return l_False; learnt_clause.clear(); analyze(confl, learnt_clause, backtrack_level); cancelUntil(backtrack_level); if (learnt_clause.size() == 1){ uncheckedEnqueue(learnt_clause[0]); }else{ CRef cr = ca.alloc(learnt_clause, true); learnts.push(cr); attachClause(cr); claBumpActivity(ca[cr]); uncheckedEnqueue(learnt_clause[0], cr); } varDecayActivity(); claDecayActivity(); if (--learntsize_adjust_cnt == 0){ learntsize_adjust_confl *= learntsize_adjust_inc; learntsize_adjust_cnt = (int)learntsize_adjust_confl; max_learnts *= learntsize_inc; if (verbosity >= 1) printf("| %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n", (int)conflicts, (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int)clauses_literals, (int)max_learnts, nLearnts(), (double)learnts_literals/nLearnts(), progressEstimate()*100); } }else{ // NO CONFLICT if ((nof_conflicts >= 0 && conflictC >= nof_conflicts) || !withinBudget()){ // Reached bound on number of conflicts: progress_estimate = progressEstimate(); cancelUntil(0); return l_Undef; } // Simplify the set of problem clauses: if (decisionLevel() == 0 && !simplify()) return l_False; if (learnts.size()-nAssigns() >= max_learnts) // Reduce the set of learnt clauses: reduceDB(); Lit next = lit_Undef; while (decisionLevel() < assumptions.size()){ // Perform user provided assumption: Lit p = assumptions[decisionLevel()]; if (value(p) == l_True){ // Dummy decision level: newDecisionLevel(); }else if (value(p) == l_False){ analyzeFinal(~p, conflict); return l_False; }else{ next = p; break; } } if (next == lit_Undef){ // New variable decision: decisions++; next = pickBranchLit(); if (next == lit_Undef) // Model found: return l_True; } // Increase decision level and enqueue 'next' newDecisionLevel(); uncheckedEnqueue(next); } } } double Solver::progressEstimate() const { double progress = 0; double F = 1.0 / nVars(); for (int i = 0; i <= decisionLevel(); i++){ int beg = i == 0 ? 0 : trail_lim[i - 1]; int end = i == decisionLevel() ? trail.size() : trail_lim[i]; progress += pow(F, i) * (end - beg); } return progress / nVars(); } /* Finite subsequences of the Luby-sequence: 0: 1 1: 1 1 2 2: 1 1 2 1 1 2 4 3: 1 1 2 1 1 2 4 1 1 2 1 1 2 4 8 ... */ static double luby(double y, int x){ // Find the finite subsequence that contains index 'x', and the // size of that subsequence: int size, seq; for (size = 1, seq = 0; size < x+1; seq++, size = 2*size+1); while (size-1 != x){ size = (size-1)>>1; seq--; x = x % size; } return pow(y, seq); } // NOTE: assumptions passed in member-variable 'assumptions'. lbool Solver::solve_() { model.clear(); conflict.clear(); if (!ok) return l_False; solves++; max_learnts = nClauses() * learntsize_factor; if (max_learnts < min_learnts_lim) max_learnts = min_learnts_lim; learntsize_adjust_confl = learntsize_adjust_start_confl; learntsize_adjust_cnt = (int)learntsize_adjust_confl; lbool status = l_Undef; if (verbosity >= 1){ printf("============================[ Search Statistics ]==============================\n"); printf("| Conflicts | ORIGINAL | LEARNT | Progress |\n"); printf("| | Vars Clauses Literals | Limit Clauses Lit/Cl | |\n"); printf("===============================================================================\n"); } // Search: int curr_restarts = 0; while (status == l_Undef){ double rest_base = luby_restart ? luby(restart_inc, curr_restarts) : pow(restart_inc, curr_restarts); status = search(rest_base * restart_first); if (!withinBudget()) break; curr_restarts++; } if (verbosity >= 1) printf("===============================================================================\n"); if (status == l_True){ // Extend & copy model: model.growTo(nVars()); for (int i = 0; i < nVars(); i++) model[i] = value(i); }else if (status == l_False && conflict.size() == 0) ok = false; cancelUntil(0); return status; } bool Solver::implies(const vec& assumps, vec& out) { trail_lim.push(trail.size()); for (int i = 0; i < assumps.size(); i++){ Lit a = assumps[i]; if (value(a) == l_False){ cancelUntil(0); return false; }else if (value(a) == l_Undef) uncheckedEnqueue(a); } unsigned trail_before = trail.size(); bool ret = true; if (propagate() == CRef_Undef){ out.clear(); for (int j = trail_before; j < trail.size(); j++) out.push(trail[j]); }else ret = false; cancelUntil(0); return ret; } //================================================================================================= // Writing CNF to DIMACS: // // FIXME: this needs to be rewritten completely. static Var mapVar(Var x, vec& map, Var& max) { if (map.size() <= x || map[x] == -1){ map.growTo(x+1, -1); map[x] = max++; } return map[x]; } void Solver::toDimacs(FILE* f, Clause& c, vec& map, Var& max) { if (satisfied(c)) return; for (int i = 0; i < c.size(); i++) if (value(c[i]) != l_False) fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", mapVar(var(c[i]), map, max)+1); fprintf(f, "0\n"); } void Solver::toDimacs(const char *file, const vec& assumps) { FILE* f = fopen(file, "wr"); if (f == NULL) fprintf(stderr, "could not open file %s\n", file), exit(1); toDimacs(f, assumps); fclose(f); } void Solver::toDimacs(FILE* f, const vec& assumps) { // Handle case when solver is in contradictory state: if (!ok){ fprintf(f, "p cnf 1 2\n1 0\n-1 0\n"); return; } vec map; Var max = 0; // Cannot use removeClauses here because it is not safe // to deallocate them at this point. Could be improved. int cnt = 0; for (int i = 0; i < clauses.size(); i++) if (!satisfied(ca[clauses[i]])) cnt++; for (int i = 0; i < clauses.size(); i++) if (!satisfied(ca[clauses[i]])){ Clause& c = ca[clauses[i]]; for (int j = 0; j < c.size(); j++) if (value(c[j]) != l_False) mapVar(var(c[j]), map, max); } // Assumptions are added as unit clauses: cnt += assumps.size(); fprintf(f, "p cnf %d %d\n", max, cnt); for (int i = 0; i < assumps.size(); i++){ assert(value(assumps[i]) != l_False); fprintf(f, "%s%d 0\n", sign(assumps[i]) ? "-" : "", mapVar(var(assumps[i]), map, max)+1); } for (int i = 0; i < clauses.size(); i++) toDimacs(f, ca[clauses[i]], map, max); if (verbosity > 0) printf("Wrote DIMACS with %d variables and %d clauses.\n", max, cnt); } void Solver::printStats() const { double cpu_time = cpuTime(); double mem_used = memUsedPeak(); printf("restarts : %" PRIu64 "\n", starts); printf("conflicts : %-12" PRIu64 " (%.0f /sec)\n", conflicts , conflicts /cpu_time); printf("decisions : %-12" PRIu64 " (%4.2f %% random) (%.0f /sec)\n", decisions, (float)rnd_decisions*100 / (float)decisions, decisions /cpu_time); printf("propagations : %-12" PRIu64 " (%.0f /sec)\n", propagations, propagations/cpu_time); printf("conflict literals : %-12" PRIu64 " (%4.2f %% deleted)\n", tot_literals, (max_literals - tot_literals)*100 / (double)max_literals); if (mem_used != 0) printf("Memory used : %.2f MB\n", mem_used); printf("CPU time : %g s\n", cpu_time); } //================================================================================================= // Garbage Collection methods: void Solver::relocAll(ClauseAllocator& to) { // All watchers: // watches.cleanAll(); for (int v = 0; v < nVars(); v++) for (int s = 0; s < 2; s++){ Lit p = mkLit(v, s); vec& ws = watches[p]; for (int j = 0; j < ws.size(); j++) ca.reloc(ws[j].cref, to); } // All reasons: // for (int i = 0; i < trail.size(); i++){ Var v = var(trail[i]); // Note: it is not safe to call 'locked()' on a relocated clause. This is why we keep // 'dangling' reasons here. It is safe and does not hurt. if (reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))){ assert(!isRemoved(reason(v))); ca.reloc(vardata[v].reason, to); } } // All learnt: // int i, j; for (i = j = 0; i < learnts.size(); i++) if (!isRemoved(learnts[i])){ ca.reloc(learnts[i], to); learnts[j++] = learnts[i]; } learnts.shrink(i - j); // All original: // for (i = j = 0; i < clauses.size(); i++) if (!isRemoved(clauses[i])){ ca.reloc(clauses[i], to); clauses[j++] = clauses[i]; } clauses.shrink(i - j); } void Solver::garbageCollect() { // Initialize the next region to a size corresponding to the estimated utilization degree. This // is not precise but should avoid some unnecessary reallocations for the new region: ClauseAllocator to(ca.size() - ca.wasted()); relocAll(to); if (verbosity >= 2) printf("| Garbage collection: %12d bytes => %12d bytes |\n", ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); to.moveTo(ca); }