1 |
|
/***********************************************************************************[SimpSolver.cc] |
2 |
|
Copyright (c) 2006, Niklas Een, Niklas Sorensson |
3 |
|
Copyright (c) 2007-2010, Niklas Sorensson |
4 |
|
|
5 |
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and |
6 |
|
associated documentation files (the "Software"), to deal in the Software without restriction, |
7 |
|
including without limitation the rights to use, copy, modify, merge, publish, distribute, |
8 |
|
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is |
9 |
|
furnished to do so, subject to the following conditions: |
10 |
|
|
11 |
|
The above copyright notice and this permission notice shall be included in all copies or |
12 |
|
substantial portions of the Software. |
13 |
|
|
14 |
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT |
15 |
|
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
16 |
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
17 |
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT |
18 |
|
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
19 |
|
**************************************************************************************************/ |
20 |
|
|
21 |
|
#include "prop/bvminisat/simp/SimpSolver.h" |
22 |
|
|
23 |
|
#include "base/check.h" |
24 |
|
#include "options/bv_options.h" |
25 |
|
#include "options/smt_options.h" |
26 |
|
#include "proof/clause_id.h" |
27 |
|
#include "prop/bvminisat/mtl/Sort.h" |
28 |
|
#include "prop/bvminisat/utils/System.h" |
29 |
|
|
30 |
|
namespace cvc5 { |
31 |
|
namespace BVMinisat { |
32 |
|
|
33 |
|
//================================================================================================= |
34 |
|
// Options: |
35 |
|
|
36 |
|
|
37 |
|
static const char* _cat = "SIMP"; |
38 |
|
|
39 |
9835 |
static BoolOption opt_use_asymm (_cat, "asymm", "Shrink clauses by asymmetric branching.", false); |
40 |
9835 |
static BoolOption opt_use_rcheck (_cat, "rcheck", "Check if a clause is already implied. (costly)", false); |
41 |
9835 |
static BoolOption opt_use_elim (_cat, "elim", "Perform variable elimination.", true); |
42 |
9835 |
static IntOption opt_grow (_cat, "grow", "Allow a variable elimination step to grow by a number of clauses.", 0); |
43 |
9835 |
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 |
9835 |
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 |
9835 |
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)); |
46 |
|
|
47 |
|
|
48 |
|
//================================================================================================= |
49 |
|
// Constructor/Destructor: |
50 |
|
|
51 |
2 |
SimpSolver::SimpSolver(cvc5::context::Context* context) |
52 |
|
: Solver(context), |
53 |
|
grow(opt_grow), |
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 |
60 |
2 |
&& cvc5::options::bitblastMode() |
61 |
2 |
== cvc5::options::BitblastMode::EAGER |
62 |
4 |
&& !cvc5::options::produceModels()), |
63 |
|
merges(0), |
64 |
|
asymm_lits(0), |
65 |
|
eliminated_vars(0), |
66 |
|
elimorder(1), |
67 |
|
use_simplification(true), |
68 |
4 |
occurs(ClauseDeleted(ca)), |
69 |
4 |
elim_heap(ElimLt(n_occ)), |
70 |
|
bwdsub_assigns(0), |
71 |
12 |
n_touched(0) |
72 |
|
{ |
73 |
|
|
74 |
4 |
vec<Lit> dummy(1,lit_Undef); |
75 |
2 |
ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. |
76 |
2 |
bwdsub_tmpunit = ca.alloc(dummy); |
77 |
2 |
remove_satisfied = false; |
78 |
|
|
79 |
|
// add the initialization for all the internal variables |
80 |
6 |
for (int i = frozen.size(); i < vardata.size(); ++ i) { |
81 |
4 |
frozen .push(1); |
82 |
4 |
eliminated.push(0); |
83 |
4 |
if (use_simplification){ |
84 |
4 |
n_occ .push(0); |
85 |
4 |
n_occ .push(0); |
86 |
4 |
occurs .init(i); |
87 |
4 |
touched .push(0); |
88 |
4 |
elim_heap .insert(i); |
89 |
|
} |
90 |
|
} |
91 |
|
|
92 |
2 |
} |
93 |
|
|
94 |
|
|
95 |
4 |
SimpSolver::~SimpSolver() |
96 |
|
{ |
97 |
|
// cvc5::StatisticsRegistry::unregisterStat(&total_eliminate_time); |
98 |
4 |
} |
99 |
|
|
100 |
|
|
101 |
282 |
Var SimpSolver::newVar(bool sign, bool dvar, bool freeze) { |
102 |
282 |
Var v = Solver::newVar(sign, dvar); |
103 |
|
|
104 |
282 |
frozen .push((char)false); |
105 |
282 |
eliminated.push((char)false); |
106 |
|
|
107 |
282 |
if (use_simplification){ |
108 |
282 |
n_occ .push(0); |
109 |
282 |
n_occ .push(0); |
110 |
282 |
occurs .init(v); |
111 |
282 |
touched .push(0); |
112 |
282 |
elim_heap .insert(v); |
113 |
282 |
if (freeze) { |
114 |
68 |
setFrozen(v, true); |
115 |
|
} |
116 |
|
} |
117 |
282 |
return v; |
118 |
|
} |
119 |
|
|
120 |
|
|
121 |
|
|
122 |
2 |
lbool SimpSolver::solve_(bool do_simp, bool turn_off_simp) |
123 |
|
{ |
124 |
2 |
only_bcp = false; |
125 |
|
|
126 |
4 |
vec<Var> extra_frozen; |
127 |
2 |
lbool result = l_True; |
128 |
|
|
129 |
2 |
do_simp &= use_simplification; |
130 |
|
|
131 |
2 |
if (do_simp) { |
132 |
|
// Assumptions must be temporarily frozen to run variable elimination: |
133 |
2 |
for (int i = 0; i < assumptions.size(); i++){ |
134 |
|
Var v = var(assumptions[i]); |
135 |
|
|
136 |
|
// If an assumption has been eliminated, remember it. |
137 |
|
Assert(!isEliminated(v)); |
138 |
|
|
139 |
|
if (!frozen[v]){ |
140 |
|
// Freeze and store. |
141 |
|
setFrozen(v, true); |
142 |
|
extra_frozen.push(v); |
143 |
|
} } |
144 |
|
|
145 |
2 |
if (do_simp && clause_added) { |
146 |
2 |
cancelUntil(0); |
147 |
2 |
result = lbool(eliminate(turn_off_simp)); |
148 |
2 |
clause_added = false; |
149 |
|
} |
150 |
|
} |
151 |
|
|
152 |
2 |
if (result == l_True) |
153 |
2 |
result = Solver::solve_(); |
154 |
|
else if (verbosity >= 1) |
155 |
|
printf("===============================================================================\n"); |
156 |
|
|
157 |
2 |
if (do_simp) |
158 |
|
// Unfreeze the assumptions that were frozen: |
159 |
2 |
for (int i = 0; i < extra_frozen.size(); i++) |
160 |
|
setFrozen(extra_frozen[i], false); |
161 |
|
|
162 |
4 |
return result; |
163 |
|
} |
164 |
|
|
165 |
|
|
166 |
|
|
167 |
1238 |
bool SimpSolver::addClause_(vec<Lit>& ps, ClauseId& id) |
168 |
|
{ |
169 |
|
#ifdef CVC5_ASSERTIONS |
170 |
1238 |
for (int i = 0; i < ps.size(); i++) Assert(!isEliminated(var(ps[i]))); |
171 |
|
#endif |
172 |
|
|
173 |
1238 |
int nclauses = clauses.size(); |
174 |
|
|
175 |
1238 |
if (use_rcheck && implied(ps)) |
176 |
|
return true; |
177 |
|
|
178 |
1238 |
if (!Solver::addClause_(ps, id)) |
179 |
|
return false; |
180 |
|
|
181 |
1238 |
if (use_simplification && clauses.size() == nclauses + 1){ |
182 |
1234 |
CRef cr = clauses.last(); |
183 |
1234 |
const Clause& clause = ca[cr]; |
184 |
|
|
185 |
|
// NOTE: the clause is added to the queue immediately and then |
186 |
|
// again during 'gatherTouchedClauses()'. If nothing happens |
187 |
|
// in between, it will only be checked once. Otherwise, it may |
188 |
|
// be checked twice unnecessarily. This is an unfortunate |
189 |
|
// consequence of how backward subsumption is used to mimic |
190 |
|
// forward subsumption. |
191 |
1234 |
subsumption_queue.insert(cr); |
192 |
5164 |
for (int i = 0; i < clause.size(); i++) |
193 |
|
{ |
194 |
3930 |
occurs[var(clause[i])].push(cr); |
195 |
3930 |
n_occ[toInt(clause[i])]++; |
196 |
3930 |
touched[var(clause[i])] = 1; |
197 |
3930 |
n_touched++; |
198 |
3930 |
if (elim_heap.inHeap(var(clause[i]))) |
199 |
3930 |
elim_heap.increase(var(clause[i])); |
200 |
|
} |
201 |
|
} |
202 |
|
|
203 |
1238 |
return true; |
204 |
|
} |
205 |
|
|
206 |
|
|
207 |
704 |
void SimpSolver::removeClause(CRef cr) |
208 |
|
{ |
209 |
704 |
const Clause& clause = ca[cr]; |
210 |
|
|
211 |
704 |
if (use_simplification) |
212 |
|
{ |
213 |
2718 |
for (int i = 0; i < clause.size(); i++) |
214 |
|
{ |
215 |
2014 |
n_occ[toInt(clause[i])]--; |
216 |
2014 |
updateElimHeap(var(clause[i])); |
217 |
2014 |
occurs.smudge(var(clause[i])); |
218 |
|
} |
219 |
|
} |
220 |
704 |
Solver::removeClause(cr); |
221 |
704 |
} |
222 |
|
|
223 |
|
|
224 |
114 |
bool SimpSolver::strengthenClause(CRef cr, Lit l) |
225 |
|
{ |
226 |
114 |
Clause& clause = ca[cr]; |
227 |
114 |
Assert(decisionLevel() == 0); |
228 |
114 |
Assert(use_simplification); |
229 |
|
|
230 |
|
// FIX: this is too inefficient but would be nice to have (properly |
231 |
|
// implemented) if (!find(subsumption_queue, &clause)) |
232 |
114 |
subsumption_queue.insert(cr); |
233 |
|
|
234 |
114 |
if (clause.size() == 2) |
235 |
|
{ |
236 |
36 |
removeClause(cr); |
237 |
36 |
clause.strengthen(l); |
238 |
|
} |
239 |
|
else |
240 |
|
{ |
241 |
78 |
detachClause(cr, true); |
242 |
78 |
clause.strengthen(l); |
243 |
78 |
attachClause(cr); |
244 |
78 |
remove(occurs[var(l)], cr); |
245 |
78 |
n_occ[toInt(l)]--; |
246 |
78 |
updateElimHeap(var(l)); |
247 |
|
} |
248 |
|
|
249 |
114 |
return clause.size() == 1 ? enqueue(clause[0]) && propagate() == CRef_Undef |
250 |
114 |
: true; |
251 |
|
} |
252 |
|
|
253 |
|
|
254 |
|
// Returns FALSE if clause is always satisfied ('out_clause' should not be used). |
255 |
788 |
bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, vec<Lit>& out_clause) |
256 |
|
{ |
257 |
788 |
merges++; |
258 |
788 |
out_clause.clear(); |
259 |
|
|
260 |
788 |
bool ps_smallest = _ps.size() < _qs.size(); |
261 |
788 |
const Clause& ps = ps_smallest ? _qs : _ps; |
262 |
788 |
const Clause& qs = ps_smallest ? _ps : _qs; |
263 |
|
|
264 |
1800 |
for (int i = 0; i < qs.size(); i++) |
265 |
|
{ |
266 |
1398 |
if (var(qs[i]) != v) |
267 |
|
{ |
268 |
3240 |
for (int j = 0; j < ps.size(); j++) |
269 |
|
{ |
270 |
2800 |
if (var(ps[j]) == var(qs[i])) |
271 |
|
{ |
272 |
468 |
if (ps[j] == ~qs[i]) |
273 |
386 |
return false; |
274 |
|
else |
275 |
82 |
goto next; |
276 |
|
} |
277 |
|
} |
278 |
440 |
out_clause.push(qs[i]); |
279 |
|
} |
280 |
1502 |
next:; |
281 |
|
} |
282 |
|
|
283 |
1986 |
for (int i = 0; i < ps.size(); i++) |
284 |
1584 |
if (var(ps[i]) != v) |
285 |
1182 |
out_clause.push(ps[i]); |
286 |
|
|
287 |
402 |
return true; |
288 |
|
} |
289 |
|
|
290 |
|
|
291 |
|
// Returns FALSE if clause is always satisfied. |
292 |
2630 |
bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, int& size) |
293 |
|
{ |
294 |
2630 |
merges++; |
295 |
|
|
296 |
2630 |
bool ps_smallest = _ps.size() < _qs.size(); |
297 |
2630 |
const Clause& ps = ps_smallest ? _qs : _ps; |
298 |
2630 |
const Clause& qs = ps_smallest ? _ps : _qs; |
299 |
2630 |
const Lit* __ps = (const Lit*)ps; |
300 |
2630 |
const Lit* __qs = (const Lit*)qs; |
301 |
|
|
302 |
2630 |
size = ps.size()-1; |
303 |
|
|
304 |
7354 |
for (int i = 0; i < qs.size(); i++) |
305 |
|
{ |
306 |
5852 |
if (var(__qs[i]) != v) |
307 |
|
{ |
308 |
23712 |
for (int j = 0; j < ps.size(); j++) |
309 |
|
{ |
310 |
21106 |
if (var(__ps[j]) == var(__qs[i])) |
311 |
|
{ |
312 |
1554 |
if (__ps[j] == ~__qs[i]) |
313 |
1128 |
return false; |
314 |
|
else |
315 |
426 |
goto next; |
316 |
|
} |
317 |
|
} |
318 |
2606 |
size++; |
319 |
|
} |
320 |
6416 |
next:; |
321 |
|
} |
322 |
|
|
323 |
1502 |
return true; |
324 |
|
} |
325 |
|
|
326 |
|
|
327 |
4 |
void SimpSolver::gatherTouchedClauses() |
328 |
|
{ |
329 |
4 |
if (n_touched == 0) return; |
330 |
|
|
331 |
|
int i,j; |
332 |
836 |
for (i = j = 0; i < subsumption_queue.size(); i++) |
333 |
832 |
if (ca[subsumption_queue[i]].mark() == 0) |
334 |
832 |
ca[subsumption_queue[i]].mark(2); |
335 |
|
|
336 |
576 |
for (i = 0; i < touched.size(); i++) |
337 |
572 |
if (touched[i]){ |
338 |
464 |
const vec<CRef>& cs = occurs.lookup(i); |
339 |
4616 |
for (j = 0; j < cs.size(); j++) |
340 |
4152 |
if (ca[cs[j]].mark() == 0){ |
341 |
532 |
subsumption_queue.insert(cs[j]); |
342 |
532 |
ca[cs[j]].mark(2); |
343 |
|
} |
344 |
464 |
touched[i] = 0; |
345 |
|
} |
346 |
|
|
347 |
1368 |
for (i = 0; i < subsumption_queue.size(); i++) |
348 |
1364 |
if (ca[subsumption_queue[i]].mark() == 2) |
349 |
1364 |
ca[subsumption_queue[i]].mark(0); |
350 |
|
|
351 |
4 |
n_touched = 0; |
352 |
|
} |
353 |
|
|
354 |
|
bool SimpSolver::implied(const vec<Lit>& clause) |
355 |
|
{ |
356 |
|
Assert(decisionLevel() == 0); |
357 |
|
|
358 |
|
trail_lim.push(trail.size()); |
359 |
|
for (int i = 0; i < clause.size(); i++) |
360 |
|
{ |
361 |
|
if (value(clause[i]) == l_True) |
362 |
|
{ |
363 |
|
cancelUntil(0); |
364 |
|
return false; |
365 |
|
} |
366 |
|
else if (value(clause[i]) != l_False) |
367 |
|
{ |
368 |
|
Assert(value(clause[i]) == l_Undef); |
369 |
|
uncheckedEnqueue(~clause[i]); |
370 |
|
} |
371 |
|
} |
372 |
|
|
373 |
|
bool result = propagate() != CRef_Undef; |
374 |
|
cancelUntil(0); |
375 |
|
return result; |
376 |
|
} |
377 |
|
|
378 |
|
|
379 |
|
// Backward subsumption + backward subsumption resolution |
380 |
100 |
bool SimpSolver::backwardSubsumptionCheck(bool verbose) |
381 |
|
{ |
382 |
100 |
int cnt = 0; |
383 |
100 |
int subsumed = 0; |
384 |
100 |
int deleted_literals = 0; |
385 |
100 |
Assert(decisionLevel() == 0); |
386 |
|
|
387 |
3948 |
while (subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()){ |
388 |
|
|
389 |
|
// Empty subsumption queue and return immediately on user-interrupt: |
390 |
1924 |
if (asynch_interrupt){ |
391 |
|
subsumption_queue.clear(); |
392 |
|
bwdsub_assigns = trail.size(); |
393 |
|
break; } |
394 |
|
|
395 |
|
// Check top-level assignments by creating a dummy clause and placing it in the queue: |
396 |
1924 |
if (subsumption_queue.size() == 0 && bwdsub_assigns < trail.size()){ |
397 |
44 |
Lit l = trail[bwdsub_assigns++]; |
398 |
44 |
ca[bwdsub_tmpunit][0] = l; |
399 |
44 |
ca[bwdsub_tmpunit].calcAbstraction(); |
400 |
44 |
subsumption_queue.insert(bwdsub_tmpunit); } |
401 |
|
|
402 |
1924 |
CRef cr = subsumption_queue.peek(); subsumption_queue.pop(); |
403 |
1924 |
Clause& clause = ca[cr]; |
404 |
|
|
405 |
1924 |
if (clause.mark()) continue; |
406 |
|
|
407 |
1880 |
if (verbose && verbosity >= 2 && cnt++ % 1000 == 0) |
408 |
|
printf("subsumption left: %10d (%10d subsumed, %10d deleted literals)\r", subsumption_queue.size(), subsumed, deleted_literals); |
409 |
|
|
410 |
1880 |
Assert(clause.size() > 1 |
411 |
|
|| value(clause[0]) == l_True); // Unit-clauses should have been |
412 |
|
// propagated before this point. |
413 |
|
|
414 |
|
// Find best variable to scan: |
415 |
1880 |
Var best = var(clause[0]); |
416 |
5980 |
for (int i = 1; i < clause.size(); i++) |
417 |
4100 |
if (occurs[var(clause[i])].size() < occurs[best].size()) |
418 |
1928 |
best = var(clause[i]); |
419 |
|
|
420 |
|
// Search all candidates: |
421 |
1880 |
vec<CRef>& _cs = occurs.lookup(best); |
422 |
1880 |
CRef* cs = (CRef*)_cs; |
423 |
|
|
424 |
17794 |
for (int j = 0; j < _cs.size(); j++) |
425 |
15914 |
if (clause.mark()) |
426 |
|
break; |
427 |
47706 |
else if (!ca[cs[j]].mark() && cs[j] != cr |
428 |
43998 |
&& (subsumption_lim == -1 |
429 |
14042 |
|| ca[cs[j]].size() < subsumption_lim)) |
430 |
|
{ |
431 |
14042 |
Lit l = clause.subsumes(ca[cs[j]]); |
432 |
|
|
433 |
14042 |
if (l == lit_Undef) |
434 |
116 |
subsumed++, removeClause(cs[j]); |
435 |
13926 |
else if (l != lit_Error) |
436 |
|
{ |
437 |
114 |
deleted_literals++; |
438 |
|
|
439 |
114 |
if (!strengthenClause(cs[j], ~l)) return false; |
440 |
|
|
441 |
|
// Did current candidate get deleted from cs? Then check candidate |
442 |
|
// at index j again: |
443 |
114 |
if (var(l) == best) j--; |
444 |
|
} |
445 |
|
} |
446 |
|
} |
447 |
|
|
448 |
100 |
return true; |
449 |
|
} |
450 |
|
|
451 |
|
|
452 |
|
bool SimpSolver::asymm(Var v, CRef cr) |
453 |
|
{ |
454 |
|
Clause& clause = ca[cr]; |
455 |
|
Assert(decisionLevel() == 0); |
456 |
|
|
457 |
|
if (clause.mark() || satisfied(clause)) return true; |
458 |
|
|
459 |
|
trail_lim.push(trail.size()); |
460 |
|
Lit l = lit_Undef; |
461 |
|
for (int i = 0; i < clause.size(); i++) |
462 |
|
if (var(clause[i]) != v && value(clause[i]) != l_False) |
463 |
|
uncheckedEnqueue(~clause[i]); |
464 |
|
else |
465 |
|
l = clause[i]; |
466 |
|
|
467 |
|
if (propagate() != CRef_Undef) |
468 |
|
{ |
469 |
|
cancelUntil(0); |
470 |
|
asymm_lits++; |
471 |
|
if (!strengthenClause(cr, l)) return false; |
472 |
|
} |
473 |
|
else |
474 |
|
cancelUntil(0); |
475 |
|
|
476 |
|
return true; |
477 |
|
} |
478 |
|
|
479 |
|
|
480 |
|
bool SimpSolver::asymmVar(Var v) |
481 |
|
{ |
482 |
|
Assert(use_simplification); |
483 |
|
|
484 |
|
const vec<CRef>& cls = occurs.lookup(v); |
485 |
|
|
486 |
|
if (value(v) != l_Undef || cls.size() == 0) return true; |
487 |
|
|
488 |
|
for (int i = 0; i < cls.size(); i++) |
489 |
|
if (!asymm(v, cls[i])) return false; |
490 |
|
|
491 |
|
return backwardSubsumptionCheck(); |
492 |
|
} |
493 |
|
|
494 |
|
|
495 |
96 |
static void mkElimClause(vec<uint32_t>& elimclauses, Lit x) |
496 |
|
{ |
497 |
96 |
elimclauses.push(toInt(x)); |
498 |
96 |
elimclauses.push(1); |
499 |
96 |
} |
500 |
|
|
501 |
252 |
static void mkElimClause(vec<uint32_t>& elimclauses, Var v, Clause& clause) |
502 |
|
{ |
503 |
252 |
int first = elimclauses.size(); |
504 |
252 |
int v_pos = -1; |
505 |
|
|
506 |
|
// Copy clause to elimclauses-vector. Remember position where the |
507 |
|
// variable 'v' occurs: |
508 |
1002 |
for (int i = 0; i < clause.size(); i++) |
509 |
|
{ |
510 |
750 |
elimclauses.push(toInt(clause[i])); |
511 |
750 |
if (var(clause[i]) == v) v_pos = i + first; |
512 |
|
} |
513 |
252 |
Assert(v_pos != -1); |
514 |
|
|
515 |
|
// Swap the first literal with the 'v' literal, so that the literal |
516 |
|
// containing 'v' will occur first in the clause: |
517 |
252 |
uint32_t tmp = elimclauses[v_pos]; |
518 |
252 |
elimclauses[v_pos] = elimclauses[first]; |
519 |
252 |
elimclauses[first] = tmp; |
520 |
|
|
521 |
|
// Store the length of the clause last: |
522 |
252 |
elimclauses.push(clause.size()); |
523 |
252 |
} |
524 |
|
|
525 |
|
|
526 |
|
|
527 |
182 |
bool SimpSolver::eliminateVar(Var v) |
528 |
|
{ |
529 |
182 |
Assert(!frozen[v]); |
530 |
182 |
Assert(!isEliminated(v)); |
531 |
182 |
Assert(value(v) == l_Undef); |
532 |
|
|
533 |
|
// Split the occurrences into positive and negative: |
534 |
|
// |
535 |
182 |
const vec<CRef>& cls = occurs.lookup(v); |
536 |
364 |
vec<CRef> pos, neg; |
537 |
1748 |
for (int i = 0; i < cls.size(); i++) |
538 |
1566 |
(find(ca[cls[i]], mkLit(v)) ? pos : neg).push(cls[i]); |
539 |
|
|
540 |
|
// Check whether the increase in number of clauses stays within the allowed |
541 |
|
// ('grow'). Moreover, no clause must exceed the limit on the maximal clause |
542 |
|
// size (if it is set): |
543 |
|
// |
544 |
182 |
int cnt = 0; |
545 |
182 |
int clause_size = 0; |
546 |
|
|
547 |
668 |
for (int i = 0; i < pos.size(); i++) |
548 |
3116 |
for (int j = 0; j < neg.size(); j++) |
549 |
5260 |
if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) |
550 |
2716 |
&& (++cnt > cls.size() + grow |
551 |
1416 |
|| (clause_lim != -1 && clause_size > clause_lim))) |
552 |
86 |
return true; |
553 |
|
|
554 |
|
// Delete and store old clauses: |
555 |
96 |
eliminated[v] = true; |
556 |
96 |
setDecisionVar(v, false); |
557 |
96 |
eliminated_vars++; |
558 |
|
|
559 |
96 |
if (pos.size() > neg.size()) |
560 |
|
{ |
561 |
4 |
for (int i = 0; i < neg.size(); i++) |
562 |
2 |
mkElimClause(elimclauses, v, ca[neg[i]]); |
563 |
2 |
mkElimClause(elimclauses, mkLit(v)); |
564 |
|
} |
565 |
|
else |
566 |
|
{ |
567 |
344 |
for (int i = 0; i < pos.size(); i++) |
568 |
250 |
mkElimClause(elimclauses, v, ca[pos[i]]); |
569 |
94 |
mkElimClause(elimclauses, ~mkLit(v)); |
570 |
|
} |
571 |
|
|
572 |
96 |
for (int i = 0; i < cls.size(); i++) removeClause(cls[i]); |
573 |
|
|
574 |
|
// Produce clauses in cross product: |
575 |
96 |
vec<Lit>& resolvent = add_tmp; |
576 |
350 |
for (int i = 0; i < pos.size(); i++) |
577 |
1042 |
for (int j = 0; j < neg.size(); j++) { |
578 |
788 |
ClauseId id = -1; |
579 |
1190 |
if (merge(ca[pos[i]], ca[neg[j]], v, resolvent) && |
580 |
402 |
!addClause_(resolvent, id)) |
581 |
|
return false; |
582 |
|
} |
583 |
|
|
584 |
|
// Free occurs list for this variable: |
585 |
96 |
occurs[v].clear(true); |
586 |
|
|
587 |
|
// Free watchers lists for this variable, if possible: |
588 |
96 |
if (watches[ mkLit(v)].size() == 0) watches[ mkLit(v)].clear(true); |
589 |
96 |
if (watches[~mkLit(v)].size() == 0) watches[~mkLit(v)].clear(true); |
590 |
|
|
591 |
96 |
return backwardSubsumptionCheck(); |
592 |
|
} |
593 |
|
|
594 |
|
|
595 |
|
bool SimpSolver::substitute(Var v, Lit x) |
596 |
|
{ |
597 |
|
Assert(!frozen[v]); |
598 |
|
Assert(!isEliminated(v)); |
599 |
|
Assert(value(v) == l_Undef); |
600 |
|
|
601 |
|
if (!ok) return false; |
602 |
|
|
603 |
|
eliminated[v] = true; |
604 |
|
setDecisionVar(v, false); |
605 |
|
const vec<CRef>& cls = occurs.lookup(v); |
606 |
|
|
607 |
|
vec<Lit>& subst_clause = add_tmp; |
608 |
|
for (int i = 0; i < cls.size(); i++) |
609 |
|
{ |
610 |
|
Clause& clause = ca[cls[i]]; |
611 |
|
|
612 |
|
subst_clause.clear(); |
613 |
|
for (int j = 0; j < clause.size(); j++) |
614 |
|
{ |
615 |
|
Lit p = clause[j]; |
616 |
|
subst_clause.push(var(p) == v ? x ^ sign(p) : p); |
617 |
|
} |
618 |
|
|
619 |
|
removeClause(cls[i]); |
620 |
|
ClauseId id; |
621 |
|
if (!addClause_(subst_clause, id)) return ok = false; |
622 |
|
} |
623 |
|
|
624 |
|
return true; |
625 |
|
} |
626 |
|
|
627 |
|
|
628 |
|
void SimpSolver::extendModel() |
629 |
|
{ |
630 |
|
int i, j; |
631 |
|
Lit x; |
632 |
|
|
633 |
|
for (i = elimclauses.size()-1; i > 0; i -= j){ |
634 |
|
for (j = elimclauses[i--]; j > 1; j--, i--) |
635 |
|
if (modelValue(toLit(elimclauses[i])) != l_False) |
636 |
|
goto next; |
637 |
|
|
638 |
|
x = toLit(elimclauses[i]); |
639 |
|
model[var(x)] = lbool(!sign(x)); |
640 |
|
next:; |
641 |
|
} |
642 |
|
} |
643 |
|
|
644 |
|
|
645 |
2 |
bool SimpSolver::eliminate(bool turn_off_elim) |
646 |
|
{ |
647 |
|
// cvc5::TimerStat::CodeTimer codeTimer(total_eliminate_time); |
648 |
|
|
649 |
2 |
if (!simplify()) |
650 |
|
return false; |
651 |
2 |
else if (!use_simplification) |
652 |
|
return true; |
653 |
|
|
654 |
|
// Main simplification loop: |
655 |
|
// |
656 |
10 |
while (n_touched > 0 || bwdsub_assigns < trail.size() || elim_heap.size() > 0) |
657 |
|
{ |
658 |
4 |
gatherTouchedClauses(); |
659 |
|
// printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", |
660 |
|
// cpuTime(), subsumption_queue.size(), trail.size() - bwdsub_assigns); |
661 |
8 |
if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) |
662 |
8 |
&& !backwardSubsumptionCheck(true)) |
663 |
|
{ |
664 |
|
ok = false; |
665 |
|
goto cleanup; |
666 |
|
} |
667 |
|
|
668 |
|
// Empty elim_heap and return immediately on user-interrupt: |
669 |
4 |
if (asynch_interrupt) |
670 |
|
{ |
671 |
|
Assert(bwdsub_assigns == trail.size()); |
672 |
|
Assert(subsumption_queue.size() == 0); |
673 |
|
Assert(n_touched == 0); |
674 |
|
elim_heap.clear(); |
675 |
|
goto cleanup; |
676 |
|
} |
677 |
|
|
678 |
|
// printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), |
679 |
|
// elim_heap.size()); |
680 |
294 |
for (int cnt = 0; !elim_heap.empty(); cnt++) |
681 |
|
{ |
682 |
290 |
Var elim = elim_heap.removeMin(); |
683 |
|
|
684 |
290 |
if (asynch_interrupt) break; |
685 |
|
|
686 |
290 |
if (isEliminated(elim) || value(elim) != l_Undef) continue; |
687 |
|
|
688 |
246 |
if (verbosity >= 2 && cnt % 100 == 0) |
689 |
|
printf("elimination left: %10d\r", elim_heap.size()); |
690 |
|
|
691 |
246 |
if (use_asymm) |
692 |
|
{ |
693 |
|
// Temporarily freeze variable. Otherwise, it would immediately end up |
694 |
|
// on the queue again: |
695 |
|
bool was_frozen = frozen[elim]; |
696 |
|
frozen[elim] = true; |
697 |
|
if (!asymmVar(elim)) |
698 |
|
{ |
699 |
|
ok = false; |
700 |
|
goto cleanup; |
701 |
|
} |
702 |
|
frozen[elim] = was_frozen; |
703 |
|
} |
704 |
|
|
705 |
|
// At this point, the variable may have been set by assymetric branching, |
706 |
|
// so check it again. Also, don't eliminate frozen variables: |
707 |
738 |
if (use_elim && value(elim) == l_Undef && !frozen[elim] |
708 |
428 |
&& !eliminateVar(elim)) |
709 |
|
{ |
710 |
|
ok = false; |
711 |
|
goto cleanup; |
712 |
|
} |
713 |
|
|
714 |
246 |
checkGarbage(simp_garbage_frac); |
715 |
|
} |
716 |
|
|
717 |
4 |
Assert(subsumption_queue.size() == 0); |
718 |
|
} |
719 |
2 |
cleanup: |
720 |
|
|
721 |
|
// If no more simplification is needed, free all simplification-related data structures: |
722 |
2 |
if (turn_off_elim){ |
723 |
|
touched .clear(true); |
724 |
|
occurs .clear(true); |
725 |
|
n_occ .clear(true); |
726 |
|
elim_heap.clear(true); |
727 |
|
subsumption_queue.clear(true); |
728 |
|
|
729 |
|
use_simplification = false; |
730 |
|
remove_satisfied = true; |
731 |
|
ca.extra_clause_field = false; |
732 |
|
|
733 |
|
// Force full cleanup (this is safe and desirable since it only happens once): |
734 |
|
rebuildOrderHeap(); |
735 |
|
garbageCollect(); |
736 |
|
}else{ |
737 |
|
// Cheaper cleanup: |
738 |
2 |
cleanUpClauses(); // TODO: can we make 'cleanUpClauses()' not be linear in the problem size somehow? |
739 |
2 |
checkGarbage(); |
740 |
|
} |
741 |
|
|
742 |
2 |
if (verbosity >= 1 && elimclauses.size() > 0) |
743 |
|
printf( |
744 |
|
"| Eliminated clauses: %10.2f Mb " |
745 |
|
" |\n", |
746 |
|
double(elimclauses.size() * sizeof(uint32_t)) / (1024 * 1024)); |
747 |
|
|
748 |
2 |
return ok; |
749 |
|
|
750 |
|
|
751 |
|
|
752 |
|
} |
753 |
|
|
754 |
|
|
755 |
4 |
void SimpSolver::cleanUpClauses() |
756 |
|
{ |
757 |
4 |
occurs.cleanAll(); |
758 |
|
int i,j; |
759 |
1784 |
for (i = j = 0; i < clauses.size(); i++) |
760 |
1780 |
if (ca[clauses[i]].mark() == 0) |
761 |
1076 |
clauses[j++] = clauses[i]; |
762 |
4 |
clauses.shrink(i - j); |
763 |
4 |
} |
764 |
|
|
765 |
|
|
766 |
|
//================================================================================================= |
767 |
|
// Garbage Collection methods: |
768 |
|
|
769 |
|
|
770 |
2 |
void SimpSolver::relocAll(ClauseAllocator& to) |
771 |
|
{ |
772 |
2 |
if (!use_simplification) return; |
773 |
|
|
774 |
|
// All occurs lists: |
775 |
|
// |
776 |
288 |
for (int i = 0; i < nVars(); i++){ |
777 |
286 |
vec<CRef>& cs = occurs[i]; |
778 |
2014 |
for (int j = 0; j < cs.size(); j++) |
779 |
1728 |
ca.reloc(cs[j], to); |
780 |
|
} |
781 |
|
|
782 |
|
// Subsumption queue: |
783 |
|
// |
784 |
2 |
for (int i = 0; i < subsumption_queue.size(); i++) |
785 |
|
ca.reloc(subsumption_queue[i], to); |
786 |
|
|
787 |
|
// Temporary clause: |
788 |
|
// |
789 |
2 |
ca.reloc(bwdsub_tmpunit, to); |
790 |
|
} |
791 |
|
|
792 |
|
|
793 |
2 |
void SimpSolver::garbageCollect() |
794 |
|
{ |
795 |
|
// Initialize the next region to a size corresponding to the estimated utilization degree. This |
796 |
|
// is not precise but should avoid some unnecessary reallocations for the new region: |
797 |
4 |
ClauseAllocator to(ca.size() - ca.wasted()); |
798 |
|
|
799 |
2 |
cleanUpClauses(); |
800 |
2 |
to.extra_clause_field = ca.extra_clause_field; // NOTE: this is important to keep (or lose) the extra fields. |
801 |
2 |
relocAll(to); |
802 |
2 |
Solver::relocAll(to); |
803 |
2 |
if (verbosity >= 2) |
804 |
|
printf( |
805 |
|
"| Garbage collection: %12d bytes => %12d bytes |\n", |
806 |
|
ca.size() * ClauseAllocator::Unit_Size, |
807 |
|
to.size() * ClauseAllocator::Unit_Size); |
808 |
2 |
to.moveTo(ca); |
809 |
2 |
} |
810 |
|
|
811 |
|
} // namespace BVMinisat |
812 |
29505 |
} // namespace cvc5 |