| #ifndef __STDC_FORMAT_MACROS |
| #define __STDC_FORMAT_MACROS |
| #endif |
| #ifndef __STDC_LIMIT_MACROS |
| #define __STDC_LIMIT_MACROS |
| #endif |
| /***************************************************************************************[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 <math.h> |
| |
| #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<Lit>& 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<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'. |
| | * 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<Lit>& 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<ShrinkStackElem>& 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<Watcher>& 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<CRef>& 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<Var> 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<Lit> 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<Lit>& assumps, vec<Lit>& 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<Var>& 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<Var>& 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<Lit>& 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<Lit>& 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<Var> 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<Watcher>& 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); |
| } |