1 /************************************************************************************[SimpSolver.C]
2 MiniSat -- Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
4 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
5 associated documentation files (the "Software"), to deal in the Software without restriction,
6 including without limitation the rights to use, copy, modify, merge, publish, distribute,
7 sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
8 furnished to do so, subject to the following conditions:
10 The above copyright notice and this permission notice shall be included in all copies or
11 substantial portions of the Software.
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
14 NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
17 OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 **************************************************************************************************/
21 #include "SimpSolver.h"
24 //=================================================================================================
25 // Constructor/Destructor:
28 SimpSolver::SimpSolver() :
31 , redundancy_check (false)
34 , remembered_clauses (0)
36 , use_simplification (true)
37 , elim_heap (ElimLt(n_occ))
40 vec<Lit> dummy(1,lit_Undef);
41 bwdsub_tmpunit = Clause::Clause_new(dummy);
42 remove_satisfied = false;
46 SimpSolver::~SimpSolver()
50 // NOTE: elimtable.size() might be lower than nVars() at the moment
51 for (int i = 0; i < elimtable.size(); i++)
52 for (int j = 0; j < elimtable[i].eliminated.size(); j++)
53 free(elimtable[i].eliminated[j]);
57 Var SimpSolver::newVar(bool sign, bool dvar) {
58 Var v = Solver::newVar(sign, dvar);
60 if (use_simplification){
64 frozen .push((char)false);
73 bool SimpSolver::solve(const vec<Lit>& assumps, bool do_simp, bool turn_off_simp) {
74 vec<Var> extra_frozen;
77 do_simp &= use_simplification;
80 // Assumptions must be temporarily frozen to run variable elimination:
81 for (int i = 0; i < assumps.size(); i++){
82 Var v = var(assumps[i]);
84 // If an assumption has been eliminated, remember it.
94 result = eliminate(turn_off_simp);
98 result = Solver::solve(assumps);
108 // Unfreeze the assumptions that were frozen:
109 for (int i = 0; i < extra_frozen.size(); i++)
110 setFrozen(extra_frozen[i], false);
117 bool SimpSolver::addClause(vec<Lit>& ps)
119 for (int i = 0; i < ps.size(); i++)
120 if (isEliminated(var(ps[i])))
121 remember(var(ps[i]));
123 int nclauses = clauses.size();
125 if (redundancy_check && implied(ps))
128 if (!Solver::addClause(ps))
131 if (use_simplification && clauses.size() == nclauses + 1){
132 Clause& c = *clauses.last();
134 subsumption_queue.insert(&c);
136 for (int i = 0; i < c.size(); i++){
137 assert(occurs.size() > var(c[i]));
138 assert(!find(occurs[var(c[i])], &c));
140 occurs[var(c[i])].push(&c);
141 n_occ[toInt(c[i])]++;
142 touched[var(c[i])] = 1;
143 assert(elimtable[var(c[i])].order == 0);
144 if (elim_heap.inHeap(var(c[i])))
145 elim_heap.increase_(var(c[i]));
153 void SimpSolver::removeClause(Clause& c)
157 if (use_simplification)
158 for (int i = 0; i < c.size(); i++){
159 n_occ[toInt(c[i])]--;
160 updateElimHeap(var(c[i]));
168 bool SimpSolver::strengthenClause(Clause& c, Lit l)
170 assert(decisionLevel() == 0);
171 assert(c.mark() == 0);
173 assert(find(watches[toInt(~c[0])], &c));
174 assert(find(watches[toInt(~c[1])], &c));
176 // FIX: this is too inefficient but would be nice to have (properly implemented)
177 // if (!find(subsumption_queue, &c))
178 subsumption_queue.insert(&c);
180 // If l is watched, delete it from watcher list and watch a new literal
181 if (c[0] == l || c[1] == l){
182 Lit other = c[0] == l ? c[1] : c[0];
188 remove(watches[toInt(~l)], &c);
190 // Add a watch for the correct literal
191 watches[toInt(~(c[1] == other ? c[0] : c[1]))].push(&c);
193 // !! this version assumes that remove does not change the order !!
194 //watches[toInt(~c[1])].push(&c);
195 clauses_literals -= 1;
200 clauses_literals -= 1;
203 // if subsumption-indexing is active perform the necessary updates
204 if (use_simplification){
205 remove(occurs[var(l)], &c);
207 updateElimHeap(var(l));
210 return c.size() == 1 ? enqueue(c[0]) && propagate() == NULL : true;
214 // Returns FALSE if clause is always satisfied ('out_clause' should not be used).
215 bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, vec<Lit>& out_clause)
220 bool ps_smallest = _ps.size() < _qs.size();
221 const Clause& ps = ps_smallest ? _qs : _ps;
222 const Clause& qs = ps_smallest ? _ps : _qs;
224 for (int i = 0; i < qs.size(); i++){
225 if (var(qs[i]) != v){
226 for (int j = 0; j < ps.size(); j++)
227 if (var(ps[j]) == var(qs[i])) {
233 out_clause.push(qs[i]);
238 for (int i = 0; i < ps.size(); i++)
240 out_clause.push(ps[i]);
246 // Returns FALSE if clause is always satisfied.
247 bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v)
251 bool ps_smallest = _ps.size() < _qs.size();
252 const Clause& ps = ps_smallest ? _qs : _ps;
253 const Clause& qs = ps_smallest ? _ps : _qs;
254 const Lit* __ps = (const Lit*)ps;
255 const Lit* __qs = (const Lit*)qs;
257 for (int i = 0; i < qs.size(); i++){
258 if (var(__qs[i]) != v){
259 for (int j = 0; j < ps.size(); j++)
260 if (var(__ps[j]) == var(__qs[i])) {
261 if (__ps[j] == ~__qs[i])
274 void SimpSolver::gatherTouchedClauses()
276 //fprintf(stderr, "Gathering clauses for backwards subsumption\n");
278 for (int i = 0; i < touched.size(); i++)
280 const vec<Clause*>& cs = getOccurs(i);
282 for (int j = 0; j < cs.size(); j++)
283 if (cs[j]->mark() == 0){
284 subsumption_queue.insert(cs[j]);
290 //fprintf(stderr, "Touched variables %d of %d yields %d clauses to check\n", ntouched, touched.size(), clauses.size());
291 for (int i = 0; i < subsumption_queue.size(); i++)
292 subsumption_queue[i]->mark(0);
296 bool SimpSolver::implied(const vec<Lit>& c)
298 assert(decisionLevel() == 0);
300 trail_lim.push(trail.size());
301 for (int i = 0; i < c.size(); i++)
302 if (value(c[i]) == l_True){
305 }else if (value(c[i]) != l_False){
306 assert(value(c[i]) == l_Undef);
307 uncheckedEnqueue(~c[i]);
310 bool result = propagate() != NULL;
316 // Backward subsumption + backward subsumption resolution
317 bool SimpSolver::backwardSubsumptionCheck(bool verbose)
321 int deleted_literals = 0;
322 assert(decisionLevel() == 0);
324 while (subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()){
326 // Check top-level assignments by creating a dummy clause and placing it in the queue:
327 if (subsumption_queue.size() == 0 && bwdsub_assigns < trail.size()){
328 Lit l = trail[bwdsub_assigns++];
329 (*bwdsub_tmpunit)[0] = l;
330 bwdsub_tmpunit->calcAbstraction();
331 assert(bwdsub_tmpunit->mark() == 0);
332 subsumption_queue.insert(bwdsub_tmpunit); }
334 Clause& c = *subsumption_queue.peek(); subsumption_queue.pop();
336 if (c.mark()) continue;
338 if (verbose && verbosity >= 2 && cnt++ % 1000 == 0)
339 reportf("subsumption left: %10d (%10d subsumed, %10d deleted literals)\r", subsumption_queue.size(), subsumed, deleted_literals);
341 assert(c.size() > 1 || value(c[0]) == l_True); // Unit-clauses should have been propagated before this point.
343 // Find best variable to scan:
344 Var best = var(c[0]);
345 for (int i = 1; i < c.size(); i++)
346 if (occurs[var(c[i])].size() < occurs[best].size())
349 // Search all candidates:
350 vec<Clause*>& _cs = getOccurs(best);
351 Clause** cs = (Clause**)_cs;
353 for (int j = 0; j < _cs.size(); j++)
356 else if (!cs[j]->mark() && cs[j] != &c){
357 Lit l = c.subsumes(*cs[j]);
360 subsumed++, removeClause(*cs[j]);
361 else if (l != lit_Error){
364 if (!strengthenClause(*cs[j], ~l))
367 // Did current candidate get deleted from cs? Then check candidate at index j again:
378 bool SimpSolver::asymm(Var v, Clause& c)
380 assert(decisionLevel() == 0);
382 if (c.mark() || satisfied(c)) return true;
384 trail_lim.push(trail.size());
386 for (int i = 0; i < c.size(); i++)
387 if (var(c[i]) != v && value(c[i]) != l_False)
388 uncheckedEnqueue(~c[i]);
392 if (propagate() != NULL){
395 if (!strengthenClause(c, l))
404 bool SimpSolver::asymmVar(Var v)
407 assert(use_simplification);
409 vec<Clause*> pos, neg;
410 const vec<Clause*>& cls = getOccurs(v);
412 if (value(v) != l_Undef || cls.size() == 0)
415 for (int i = 0; i < cls.size(); i++)
416 if (!asymm(v, *cls[i]))
419 return backwardSubsumptionCheck();
423 void SimpSolver::verifyModel()
427 // NOTE: elimtable.size() might be lower than nVars() at the moment
428 for (int i = 0; i < elimtable.size(); i++)
429 if (elimtable[i].order > 0)
430 for (int j = 0; j < elimtable[i].eliminated.size(); j++){
432 Clause& c = *elimtable[i].eliminated[j];
433 for (int k = 0; k < c.size(); k++)
434 if (modelValue(c[k]) == l_True)
437 reportf("unsatisfied clause: ");
438 printClause(*elimtable[i].eliminated[j]);
445 reportf("Verified %d eliminated clauses.\n", cnt);
449 bool SimpSolver::eliminateVar(Var v, bool fail)
451 if (!fail && asymm_mode && !asymmVar(v)) return false;
453 const vec<Clause*>& cls = getOccurs(v);
455 // if (value(v) != l_Undef || cls.size() == 0) return true;
456 if (value(v) != l_Undef) return true;
458 // Split the occurrences into positive and negative:
459 vec<Clause*> pos, neg;
460 for (int i = 0; i < cls.size(); i++)
461 (find(*cls[i], Lit(v)) ? pos : neg).push(cls[i]);
463 // Check if number of clauses decreases:
465 for (int i = 0; i < pos.size(); i++)
466 for (int j = 0; j < neg.size(); j++)
467 if (merge(*pos[i], *neg[j], v) && ++cnt > cls.size() + grow)
470 // Delete and store old clauses:
471 setDecisionVar(v, false);
472 elimtable[v].order = elimorder++;
473 assert(elimtable[v].eliminated.size() == 0);
474 for (int i = 0; i < cls.size(); i++){
475 elimtable[v].eliminated.push(Clause::Clause_new(*cls[i]));
476 removeClause(*cls[i]); }
478 // Produce clauses in cross product:
479 int top = clauses.size();
481 for (int i = 0; i < pos.size(); i++)
482 for (int j = 0; j < neg.size(); j++)
483 if (merge(*pos[i], *neg[j], v, resolvent) && !addClause(resolvent))
486 // DEBUG: For checking that a clause set is saturated with respect to variable elimination.
487 // If the clause set is expected to be saturated at this point, this constitutes an
490 reportf("eliminated var %d, %d <= %d\n", v+1, cnt, cls.size());
491 reportf("previous clauses:\n");
492 for (int i = 0; i < cls.size(); i++){
493 printClause(*cls[i]); reportf("\n"); }
494 reportf("new clauses:\n");
495 for (int i = top; i < clauses.size(); i++){
496 printClause(*clauses[i]); reportf("\n"); }
499 return backwardSubsumptionCheck();
503 void SimpSolver::remember(Var v)
505 assert(decisionLevel() == 0);
506 assert(isEliminated(v));
510 // Re-activate variable:
511 elimtable[v].order = 0;
512 setDecisionVar(v, true); // Not good if the variable wasn't a decision variable before. Not sure how to fix this right now.
514 if (use_simplification)
517 // Reintroduce all old clauses which may implicitly remember other clauses:
518 for (int i = 0; i < elimtable[v].eliminated.size(); i++){
519 Clause& c = *elimtable[v].eliminated[i];
521 for (int j = 0; j < c.size(); j++)
524 remembered_clauses++;
525 check(addClause(clause));
529 elimtable[v].eliminated.clear();
533 void SimpSolver::extendModel()
537 // NOTE: elimtable.size() might be lower than nVars() at the moment
538 for (int v = 0; v < elimtable.size(); v++)
539 if (elimtable[v].order > 0)
542 sort(vs, ElimOrderLt(elimtable));
544 for (int i = 0; i < vs.size(); i++){
548 for (int j = 0; j < elimtable[v].eliminated.size(); j++){
549 Clause& c = *elimtable[v].eliminated[j];
551 for (int k = 0; k < c.size(); k++)
554 else if (modelValue(c[k]) != l_False)
557 assert(l != lit_Undef);
558 model[v] = lbool(!sign(l));
564 if (model[v] == l_Undef)
570 bool SimpSolver::eliminate(bool turn_off_elim)
572 if (!ok || !use_simplification)
575 // Main simplification loop:
576 //assert(subsumption_queue.size() == 0);
577 //gatherTouchedClauses();
578 while (subsumption_queue.size() > 0 || elim_heap.size() > 0){
580 //fprintf(stderr, "subsumption phase: (%d)\n", subsumption_queue.size());
581 if (!backwardSubsumptionCheck(true))
584 //fprintf(stderr, "elimination phase:\n (%d)", elim_heap.size());
585 for (int cnt = 0; !elim_heap.empty(); cnt++){
586 Var elim = elim_heap.removeMin();
588 if (verbosity >= 2 && cnt % 100 == 0)
589 reportf("elimination left: %10d\r", elim_heap.size());
591 if (!frozen[elim] && !eliminateVar(elim))
595 assert(subsumption_queue.size() == 0);
596 gatherTouchedClauses();
601 order_heap.filter(VarFilter(*this));
604 // Check that no more subsumption is possible:
605 reportf("Checking that no more subsumption is possible\n");
606 for (int i = 0; i < clauses.size(); i++){
608 reportf("left %10d\r", clauses.size() - i);
610 assert(clauses[i]->mark() == 0);
611 for (int j = 0; j < i; j++)
612 assert(clauses[i]->subsumes(*clauses[j]) == lit_Error);
616 // Check that no more elimination is possible:
617 reportf("Checking that no more elimination is possible\n");
618 for (int i = 0; i < nVars(); i++)
619 if (!frozen[i]) eliminateVar(i, true);
624 // If no more simplification is needed, free all simplification-related data structures:
626 use_simplification = false;
630 subsumption_queue.clear(true);
631 elim_heap.clear(true);
632 remove_satisfied = true;
640 void SimpSolver::cleanUpClauses()
644 for (i = 0; i < clauses.size(); i++)
645 if (clauses[i]->mark() == 1){
646 Clause& c = *clauses[i];
647 for (int k = 0; k < c.size(); k++)
648 if (!seen[var(c[k])]){
650 dirty.push(var(c[k]));
653 for (i = 0; i < dirty.size(); i++){
655 seen[dirty[i]] = 0; }
657 for (i = j = 0; i < clauses.size(); i++)
658 if (clauses[i]->mark() == 1)
661 clauses[j++] = clauses[i];
662 clauses.shrink(i - j);
666 //=================================================================================================
667 // Convert to DIMACS:
670 void SimpSolver::toDimacs(FILE* f, Clause& c)
672 if (satisfied(c)) return;
674 for (int i = 0; i < c.size(); i++)
675 if (value(c[i]) != l_False)
676 fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", var(c[i])+1);
681 void SimpSolver::toDimacs(const char* file)
683 assert(decisionLevel() == 0);
684 FILE* f = fopen(file, "wr");
687 // Cannot use removeClauses here because it is not safe
688 // to deallocate them at this point. Could be improved.
690 for (int i = 0; i < clauses.size(); i++)
691 if (!satisfied(*clauses[i]))
694 fprintf(f, "p cnf %d %d\n", nVars(), cnt);
696 for (int i = 0; i < clauses.size(); i++)
697 toDimacs(f, *clauses[i]);
699 fprintf(stderr, "Wrote %d clauses...\n", clauses.size());
701 fprintf(stderr, "could not open file %s\n", file);