1 #ifndef __STDC_FORMAT_MACROS
2 #define __STDC_FORMAT_MACROS
4 #ifndef __STDC_LIMIT_MACROS
5 #define __STDC_LIMIT_MACROS
7 /***********************************************************************************[SimpSolver.cc]
8 Copyright (c) 2006, Niklas Een, Niklas Sorensson
9 Copyright (c) 2007-2010, Niklas Sorensson
11 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
12 associated documentation files (the "Software"), to deal in the Software without restriction,
13 including without limitation the rights to use, copy, modify, merge, publish, distribute,
14 sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
15 furnished to do so, subject to the following conditions:
17 The above copyright notice and this permission notice shall be included in all copies or
18 substantial portions of the Software.
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
21 NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
23 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
24 OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 **************************************************************************************************/
28 #include "SimpSolver.h"
31 using namespace Minisat
;
33 //=================================================================================================
37 static const char* _cat
= "SIMP";
39 static BoolOption
opt_use_asymm (_cat
, "asymm", "Shrink clauses by asymmetric branching.", false);
40 static BoolOption
opt_use_rcheck (_cat
, "rcheck", "Check if a clause is already implied. (costly)", false);
41 static BoolOption
opt_use_elim (_cat
, "elim", "Perform variable elimination.", true);
42 static IntOption
opt_grow (_cat
, "grow", "Allow a variable elimination step to grow by a number of clauses.", 0);
43 static IntOption
opt_clause_lim (_cat
, "cl-lim", "Variables are not eliminated if it produces a resolvent with a length above this limit. -1 means no limit", 20, IntRange(-1, INT32_MAX
));
44 static IntOption
opt_subsumption_lim (_cat
, "sub-lim", "Do not check if subsumption against a clause larger than this. -1 means no limit.", 1000, IntRange(-1, INT32_MAX
));
45 static DoubleOption
opt_simp_garbage_frac(_cat
, "simp-gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered during simplification.", 0.5, DoubleRange(0, false, HUGE_VAL
, false));
48 //=================================================================================================
49 // Constructor/Destructor:
52 SimpSolver::SimpSolver() :
54 , clause_lim (opt_clause_lim
)
55 , subsumption_lim (opt_subsumption_lim
)
56 , simp_garbage_frac (opt_simp_garbage_frac
)
57 , use_asymm (opt_use_asymm
)
58 , use_rcheck (opt_use_rcheck
)
59 , use_elim (opt_use_elim
)
65 , use_simplification (true)
66 , occurs (ClauseDeleted(ca
))
67 , elim_heap (ElimLt(n_occ
))
71 vec
<Lit
> dummy(1,lit_Undef
);
72 ca
.extra_clause_field
= true; // NOTE: must happen before allocating the dummy clause below.
73 bwdsub_tmpunit
= ca
.alloc(dummy
);
74 remove_satisfied
= false;
78 SimpSolver::~SimpSolver()
83 Var
SimpSolver::newVar(lbool upol
, bool dvar
) {
84 Var v
= Solver::newVar(upol
, dvar
);
86 frozen
.insert(v
, (char)false);
87 eliminated
.insert(v
, (char)false);
89 if (use_simplification
){
90 n_occ
.insert( mkLit(v
), 0);
91 n_occ
.insert(~mkLit(v
), 0);
93 touched
.insert(v
, 0);
99 void SimpSolver::releaseVar(Lit l
)
101 assert(!isEliminated(var(l
)));
102 if (!use_simplification
&& var(l
) >= max_simp_var
)
103 // Note: Guarantees that no references to this variable is
104 // left in model extension datastructure. Could be improved!
105 Solver::releaseVar(l
);
107 // Otherwise, don't allow variable to be reused.
108 Solver::addClause(l
);
112 lbool
SimpSolver::solve_(bool do_simp
, bool turn_off_simp
)
114 vec
<Var
> extra_frozen
;
115 lbool result
= l_True
;
117 do_simp
&= use_simplification
;
120 // Assumptions must be temporarily frozen to run variable elimination:
121 for (int i
= 0; i
< assumptions
.size(); i
++){
122 Var v
= var(assumptions
[i
]);
124 // If an assumption has been eliminated, remember it.
125 assert(!isEliminated(v
));
130 extra_frozen
.push(v
);
133 result
= lbool(eliminate(turn_off_simp
));
136 if (result
== l_True
)
137 result
= Solver::solve_();
138 else if (verbosity
>= 1)
139 printf("===============================================================================\n");
141 if (result
== l_True
&& extend_model
)
145 // Unfreeze the assumptions that were frozen:
146 for (int i
= 0; i
< extra_frozen
.size(); i
++)
147 setFrozen(extra_frozen
[i
], false);
154 bool SimpSolver::addClause_(vec
<Lit
>& ps
)
157 for (int i
= 0; i
< ps
.size(); i
++)
158 assert(!isEliminated(var(ps
[i
])));
161 int nclauses
= clauses
.size();
163 if (use_rcheck
&& implied(ps
))
166 if (!Solver::addClause_(ps
))
169 if (use_simplification
&& clauses
.size() == nclauses
+ 1){
170 CRef cr
= clauses
.last();
171 const Clause
& c
= ca
[cr
];
173 // NOTE: the clause is added to the queue immediately and then
174 // again during 'gatherTouchedClauses()'. If nothing happens
175 // in between, it will only be checked once. Otherwise, it may
176 // be checked twice unnecessarily. This is an unfortunate
177 // consequence of how backward subsumption is used to mimic
178 // forward subsumption.
179 subsumption_queue
.insert(cr
);
180 for (int i
= 0; i
< c
.size(); i
++){
181 occurs
[var(c
[i
])].push(cr
);
183 touched
[var(c
[i
])] = 1;
185 if (elim_heap
.inHeap(var(c
[i
])))
186 elim_heap
.increase(var(c
[i
]));
194 void SimpSolver::removeClause(CRef cr
)
196 const Clause
& c
= ca
[cr
];
198 if (use_simplification
)
199 for (int i
= 0; i
< c
.size(); i
++){
201 updateElimHeap(var(c
[i
]));
202 occurs
.smudge(var(c
[i
]));
205 Solver::removeClause(cr
);
209 bool SimpSolver::strengthenClause(CRef cr
, Lit l
)
212 assert(decisionLevel() == 0);
213 assert(use_simplification
);
215 // FIX: this is too inefficient but would be nice to have (properly implemented)
216 // if (!find(subsumption_queue, &c))
217 subsumption_queue
.insert(cr
);
223 detachClause(cr
, true);
226 remove(occurs
[var(l
)], cr
);
228 updateElimHeap(var(l
));
231 return c
.size() == 1 ? enqueue(c
[0]) && propagate() == CRef_Undef
: true;
235 // Returns FALSE if clause is always satisfied ('out_clause' should not be used).
236 bool SimpSolver::merge(const Clause
& _ps
, const Clause
& _qs
, Var v
, vec
<Lit
>& out_clause
)
241 bool ps_smallest
= _ps
.size() < _qs
.size();
242 const Clause
& ps
= ps_smallest
? _qs
: _ps
;
243 const Clause
& qs
= ps_smallest
? _ps
: _qs
;
245 for (int i
= 0; i
< qs
.size(); i
++){
246 if (var(qs
[i
]) != v
){
247 for (int j
= 0; j
< ps
.size(); j
++)
248 if (var(ps
[j
]) == var(qs
[i
])){
254 out_clause
.push(qs
[i
]);
259 for (int i
= 0; i
< ps
.size(); i
++)
261 out_clause
.push(ps
[i
]);
267 // Returns FALSE if clause is always satisfied.
268 bool SimpSolver::merge(const Clause
& _ps
, const Clause
& _qs
, Var v
, int& size
)
272 bool ps_smallest
= _ps
.size() < _qs
.size();
273 const Clause
& ps
= ps_smallest
? _qs
: _ps
;
274 const Clause
& qs
= ps_smallest
? _ps
: _qs
;
275 const Lit
* __ps
= (const Lit
*)ps
;
276 const Lit
* __qs
= (const Lit
*)qs
;
280 for (int i
= 0; i
< qs
.size(); i
++){
281 if (var(__qs
[i
]) != v
){
282 for (int j
= 0; j
< ps
.size(); j
++)
283 if (var(__ps
[j
]) == var(__qs
[i
])){
284 if (__ps
[j
] == ~__qs
[i
])
298 void SimpSolver::gatherTouchedClauses()
300 if (n_touched
== 0) return;
303 for (i
= j
= 0; i
< subsumption_queue
.size(); i
++)
304 if (ca
[subsumption_queue
[i
]].mark() == 0)
305 ca
[subsumption_queue
[i
]].mark(2);
307 for (i
= 0; i
< nVars(); i
++)
309 const vec
<CRef
>& cs
= occurs
.lookup(i
);
310 for (j
= 0; j
< cs
.size(); j
++)
311 if (ca
[cs
[j
]].mark() == 0){
312 subsumption_queue
.insert(cs
[j
]);
318 for (i
= 0; i
< subsumption_queue
.size(); i
++)
319 if (ca
[subsumption_queue
[i
]].mark() == 2)
320 ca
[subsumption_queue
[i
]].mark(0);
326 bool SimpSolver::implied(const vec
<Lit
>& c
)
328 assert(decisionLevel() == 0);
330 trail_lim
.push(trail
.size());
331 for (int i
= 0; i
< c
.size(); i
++)
332 if (value(c
[i
]) == l_True
){
335 }else if (value(c
[i
]) != l_False
){
336 assert(value(c
[i
]) == l_Undef
);
337 uncheckedEnqueue(~c
[i
]);
340 bool result
= propagate() != CRef_Undef
;
346 // Backward subsumption + backward subsumption resolution
347 bool SimpSolver::backwardSubsumptionCheck(bool verbose
)
351 int deleted_literals
= 0;
352 assert(decisionLevel() == 0);
354 while (subsumption_queue
.size() > 0 || bwdsub_assigns
< trail
.size()){
356 // Empty subsumption queue and return immediately on user-interrupt:
357 if (asynch_interrupt
){
358 subsumption_queue
.clear();
359 bwdsub_assigns
= trail
.size();
362 // Check top-level assignments by creating a dummy clause and placing it in the queue:
363 if (subsumption_queue
.size() == 0 && bwdsub_assigns
< trail
.size()){
364 Lit l
= trail
[bwdsub_assigns
++];
365 ca
[bwdsub_tmpunit
][0] = l
;
366 ca
[bwdsub_tmpunit
].calcAbstraction();
367 subsumption_queue
.insert(bwdsub_tmpunit
); }
369 CRef cr
= subsumption_queue
.peek(); subsumption_queue
.pop();
372 if (c
.mark()) continue;
374 if (verbose
&& verbosity
>= 2 && cnt
++ % 1000 == 0)
375 printf("subsumption left: %10d (%10d subsumed, %10d deleted literals)\r", subsumption_queue
.size(), subsumed
, deleted_literals
);
377 assert(c
.size() > 1 || value(c
[0]) == l_True
); // Unit-clauses should have been propagated before this point.
379 // Find best variable to scan:
380 Var best
= var(c
[0]);
381 for (int i
= 1; i
< c
.size(); i
++)
382 if (occurs
[var(c
[i
])].size() < occurs
[best
].size())
385 // Search all candidates:
386 vec
<CRef
>& _cs
= occurs
.lookup(best
);
387 CRef
* cs
= (CRef
*)_cs
;
389 for (int j
= 0; j
< _cs
.size(); j
++)
392 else if (!ca
[cs
[j
]].mark() && cs
[j
] != cr
&& (subsumption_lim
== -1 || ca
[cs
[j
]].size() < subsumption_lim
)){
393 Lit l
= c
.subsumes(ca
[cs
[j
]]);
396 subsumed
++, removeClause(cs
[j
]);
397 else if (l
!= lit_Error
){
400 if (!strengthenClause(cs
[j
], ~l
))
403 // Did current candidate get deleted from cs? Then check candidate at index j again:
414 bool SimpSolver::asymm(Var v
, CRef cr
)
417 assert(decisionLevel() == 0);
419 if (c
.mark() || satisfied(c
)) return true;
421 trail_lim
.push(trail
.size());
423 for (int i
= 0; i
< c
.size(); i
++)
424 if (var(c
[i
]) != v
&& value(c
[i
]) != l_False
)
425 uncheckedEnqueue(~c
[i
]);
429 if (propagate() != CRef_Undef
){
432 if (!strengthenClause(cr
, l
))
441 bool SimpSolver::asymmVar(Var v
)
443 assert(use_simplification
);
445 const vec
<CRef
>& cls
= occurs
.lookup(v
);
447 if (value(v
) != l_Undef
|| cls
.size() == 0)
450 for (int i
= 0; i
< cls
.size(); i
++)
451 if (!asymm(v
, cls
[i
]))
454 return backwardSubsumptionCheck();
458 static void mkElimClause(vec
<uint32_t>& elimclauses
, Lit x
)
460 elimclauses
.push(toInt(x
));
465 static void mkElimClause(vec
<uint32_t>& elimclauses
, Var v
, Clause
& c
)
467 int first
= elimclauses
.size();
470 // Copy clause to elimclauses-vector. Remember position where the
471 // variable 'v' occurs:
472 for (int i
= 0; i
< c
.size(); i
++){
473 elimclauses
.push(toInt(c
[i
]));
479 // Swap the first literal with the 'v' literal, so that the literal
480 // containing 'v' will occur first in the clause:
481 uint32_t tmp
= elimclauses
[v_pos
];
482 elimclauses
[v_pos
] = elimclauses
[first
];
483 elimclauses
[first
] = tmp
;
485 // Store the length of the clause last:
486 elimclauses
.push(c
.size());
491 bool SimpSolver::eliminateVar(Var v
)
494 assert(!isEliminated(v
));
495 assert(value(v
) == l_Undef
);
497 // Split the occurrences into positive and negative:
499 const vec
<CRef
>& cls
= occurs
.lookup(v
);
501 for (int i
= 0; i
< cls
.size(); i
++)
502 (find(ca
[cls
[i
]], mkLit(v
)) ? pos
: neg
).push(cls
[i
]);
504 // Check wether the increase in number of clauses stays within the allowed ('grow'). Moreover, no
505 // clause must exceed the limit on the maximal clause size (if it is set):
510 for (int i
= 0; i
< pos
.size(); i
++)
511 for (int j
= 0; j
< neg
.size(); j
++)
512 if (merge(ca
[pos
[i
]], ca
[neg
[j
]], v
, clause_size
) &&
513 (++cnt
> cls
.size() + grow
|| (clause_lim
!= -1 && clause_size
> clause_lim
)))
516 // Delete and store old clauses:
517 eliminated
[v
] = true;
518 setDecisionVar(v
, false);
521 if (pos
.size() > neg
.size()){
522 for (int i
= 0; i
< neg
.size(); i
++)
523 mkElimClause(elimclauses
, v
, ca
[neg
[i
]]);
524 mkElimClause(elimclauses
, mkLit(v
));
526 for (int i
= 0; i
< pos
.size(); i
++)
527 mkElimClause(elimclauses
, v
, ca
[pos
[i
]]);
528 mkElimClause(elimclauses
, ~mkLit(v
));
531 for (int i
= 0; i
< cls
.size(); i
++)
532 removeClause(cls
[i
]);
534 // Produce clauses in cross product:
535 vec
<Lit
>& resolvent
= add_tmp
;
536 for (int i
= 0; i
< pos
.size(); i
++)
537 for (int j
= 0; j
< neg
.size(); j
++)
538 if (merge(ca
[pos
[i
]], ca
[neg
[j
]], v
, resolvent
) && !addClause_(resolvent
))
541 // Free occurs list for this variable:
542 occurs
[v
].clear(true);
544 // Free watchers lists for this variable, if possible:
545 if (watches
[ mkLit(v
)].size() == 0) watches
[ mkLit(v
)].clear(true);
546 if (watches
[~mkLit(v
)].size() == 0) watches
[~mkLit(v
)].clear(true);
548 return backwardSubsumptionCheck();
552 bool SimpSolver::substitute(Var v
, Lit x
)
555 assert(!isEliminated(v
));
556 assert(value(v
) == l_Undef
);
558 if (!ok
) return false;
560 eliminated
[v
] = true;
561 setDecisionVar(v
, false);
562 const vec
<CRef
>& cls
= occurs
.lookup(v
);
564 vec
<Lit
>& subst_clause
= add_tmp
;
565 for (int i
= 0; i
< cls
.size(); i
++){
566 Clause
& c
= ca
[cls
[i
]];
568 subst_clause
.clear();
569 for (int j
= 0; j
< c
.size(); j
++){
571 subst_clause
.push(var(p
) == v
? x
^ sign(p
) : p
);
574 removeClause(cls
[i
]);
576 if (!addClause_(subst_clause
))
584 void SimpSolver::extendModel()
589 for (i
= elimclauses
.size()-1; i
> 0; i
-= j
){
590 for (j
= elimclauses
[i
--]; j
> 1; j
--, i
--)
591 if (modelValue(toLit(elimclauses
[i
])) != l_False
)
594 x
= toLit(elimclauses
[i
]);
595 model
[var(x
)] = lbool(!sign(x
));
601 bool SimpSolver::eliminate(bool turn_off_elim
)
605 else if (!use_simplification
)
608 // Main simplification loop:
610 while (n_touched
> 0 || bwdsub_assigns
< trail
.size() || elim_heap
.size() > 0){
612 gatherTouchedClauses();
613 // printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", cpuTime(), subsumption_queue.size(), trail.size() - bwdsub_assigns);
614 if ((subsumption_queue
.size() > 0 || bwdsub_assigns
< trail
.size()) &&
615 !backwardSubsumptionCheck(true)){
616 ok
= false; goto cleanup
; }
618 // Empty elim_heap and return immediately on user-interrupt:
619 if (asynch_interrupt
){
620 assert(bwdsub_assigns
== trail
.size());
621 assert(subsumption_queue
.size() == 0);
622 assert(n_touched
== 0);
626 // printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), elim_heap.size());
627 for (int cnt
= 0; !elim_heap
.empty(); cnt
++){
628 Var elim
= elim_heap
.removeMin();
630 if (asynch_interrupt
) break;
632 if (isEliminated(elim
) || value(elim
) != l_Undef
) continue;
634 if (verbosity
>= 2 && cnt
% 100 == 0)
635 printf("elimination left: %10d\r", elim_heap
.size());
638 // Temporarily freeze variable. Otherwise, it would immediately end up on the queue again:
639 bool was_frozen
= frozen
[elim
];
641 if (!asymmVar(elim
)){
642 ok
= false; goto cleanup
; }
643 frozen
[elim
] = was_frozen
; }
645 // At this point, the variable may have been set by assymetric branching, so check it
646 // again. Also, don't eliminate frozen variables:
647 if (use_elim
&& value(elim
) == l_Undef
&& !frozen
[elim
] && !eliminateVar(elim
)){
648 ok
= false; goto cleanup
; }
650 checkGarbage(simp_garbage_frac
);
653 assert(subsumption_queue
.size() == 0);
657 // If no more simplification is needed, free all simplification-related data structures:
659 touched
.clear(true);
662 elim_heap
.clear(true);
663 subsumption_queue
.clear(true);
665 use_simplification
= false;
666 remove_satisfied
= true;
667 ca
.extra_clause_field
= false;
668 max_simp_var
= nVars();
670 // Force full cleanup (this is safe and desirable since it only happens once):
678 if (verbosity
>= 1 && elimclauses
.size() > 0)
679 printf("| Eliminated clauses: %10.2f Mb |\n",
680 double(elimclauses
.size() * sizeof(uint32_t)) / (1024*1024));
686 //=================================================================================================
687 // Garbage Collection methods:
690 void SimpSolver::relocAll(ClauseAllocator
& to
)
692 if (!use_simplification
) return;
696 for (int i
= 0; i
< nVars(); i
++){
698 vec
<CRef
>& cs
= occurs
[i
];
699 for (int j
= 0; j
< cs
.size(); j
++)
703 // Subsumption queue:
705 for (int i
= subsumption_queue
.size(); i
> 0; i
--){
706 CRef cr
= subsumption_queue
.peek(); subsumption_queue
.pop();
707 if (ca
[cr
].mark()) continue;
709 subsumption_queue
.insert(cr
);
714 ca
.reloc(bwdsub_tmpunit
, to
);
718 void SimpSolver::garbageCollect()
720 // Initialize the next region to a size corresponding to the estimated utilization degree. This
721 // is not precise but should avoid some unnecessary reallocations for the new region:
722 ClauseAllocator
to(ca
.size() - ca
.wasted());
724 to
.extra_clause_field
= ca
.extra_clause_field
; // NOTE: this is important to keep (or lose) the extra fields.
726 Solver::relocAll(to
);
728 printf("| Garbage collection: %12d bytes => %12d bytes |\n",
729 ca
.size()*ClauseAllocator::Unit_Size
, to
.size()*ClauseAllocator::Unit_Size
);