1 |
|
/***************************************************************************************[Solver.cc] |
2 |
|
Copyright (c) 2003-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/core/Solver.h" |
22 |
|
|
23 |
|
#include <math.h> |
24 |
|
|
25 |
|
#include <iostream> |
26 |
|
#include <vector> |
27 |
|
|
28 |
|
#include "base/check.h" |
29 |
|
#include "base/exception.h" |
30 |
|
#include "base/output.h" |
31 |
|
#include "options/bv_options.h" |
32 |
|
#include "options/smt_options.h" |
33 |
|
#include "prop/bvminisat/mtl/Sort.h" |
34 |
|
#include "theory/interrupted.h" |
35 |
|
#include "util/utility.h" |
36 |
|
|
37 |
|
namespace cvc5 { |
38 |
|
namespace BVMinisat { |
39 |
|
|
40 |
|
#define OUTPUT_TAG "bvminisat: [a=" << assumptions.size() << ",l=" << decisionLevel() << "] " |
41 |
|
|
42 |
|
std::ostream& operator << (std::ostream& out, const BVMinisat::Lit& l) { |
43 |
|
out << (sign(l) ? "-" : "") << var(l) + 1; |
44 |
|
return out; |
45 |
|
} |
46 |
|
|
47 |
|
std::ostream& operator << (std::ostream& out, const BVMinisat::Clause& c) { |
48 |
|
for (int i = 0; i < c.size(); i++) { |
49 |
|
if (i > 0) { |
50 |
|
out << " "; |
51 |
|
} |
52 |
|
out << c[i]; |
53 |
|
} |
54 |
|
return out; |
55 |
|
} |
56 |
|
|
57 |
|
|
58 |
|
//================================================================================================= |
59 |
|
// Options: |
60 |
|
|
61 |
|
|
62 |
|
static const char* _cat = "CORE"; |
63 |
|
|
64 |
7585 |
static DoubleOption opt_var_decay (_cat, "var-decay", "The variable activity decay factor", 0.95, DoubleRange(0, false, 1, false)); |
65 |
7585 |
static DoubleOption opt_clause_decay (_cat, "cla-decay", "The clause activity decay factor", 0.999, DoubleRange(0, false, 1, false)); |
66 |
7585 |
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)); |
67 |
7585 |
static DoubleOption opt_random_seed (_cat, "rnd-seed", "Used by the random variable selection", 91648253, DoubleRange(0, false, HUGE_VAL, false)); |
68 |
7585 |
static IntOption opt_ccmin_mode (_cat, "ccmin-mode", "Controls conflict clause minimization (0=none, 1=basic, 2=deep)", 2, IntRange(0, 2)); |
69 |
7585 |
static IntOption opt_phase_saving (_cat, "phase-saving", "Controls the level of phase saving (0=none, 1=limited, 2=full)", 2, IntRange(0, 2)); |
70 |
7585 |
static BoolOption opt_rnd_init_act (_cat, "rnd-init", "Randomize the initial activity", false); |
71 |
7585 |
static BoolOption opt_luby_restart (_cat, "luby", "Use the Luby restart sequence", true); |
72 |
7585 |
static IntOption opt_restart_first (_cat, "rfirst", "The base restart interval", 25, IntRange(1, INT32_MAX)); |
73 |
7585 |
static DoubleOption opt_restart_inc (_cat, "rinc", "Restart interval increase factor", 3, DoubleRange(1, false, HUGE_VAL, false)); |
74 |
7585 |
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)); |
75 |
|
|
76 |
|
//================================================================================================= |
77 |
|
// Proof declarations |
78 |
|
CRef Solver::TCRef_Undef = CRef_Undef; |
79 |
|
CRef Solver::TCRef_Lazy = CRef_Undef - 1; // no real lazy ref here |
80 |
|
|
81 |
|
|
82 |
|
//================================================================================================= |
83 |
|
// Constructor/Destructor: |
84 |
|
|
85 |
3 |
Solver::Solver(cvc5::context::Context* context) |
86 |
|
: |
87 |
|
|
88 |
|
// Parameters (user settable): |
89 |
|
// |
90 |
|
d_notify(nullptr), |
91 |
|
c(context), |
92 |
|
verbosity(0), |
93 |
|
var_decay(opt_var_decay), |
94 |
|
clause_decay(opt_clause_decay), |
95 |
|
random_var_freq(opt_random_var_freq), |
96 |
|
random_seed(opt_random_seed), |
97 |
|
luby_restart(opt_luby_restart), |
98 |
|
ccmin_mode(opt_ccmin_mode), |
99 |
|
phase_saving(opt_phase_saving), |
100 |
|
rnd_pol(false), |
101 |
|
rnd_init_act(opt_rnd_init_act), |
102 |
|
garbage_frac(opt_garbage_frac), |
103 |
|
restart_first(opt_restart_first), |
104 |
|
restart_inc(opt_restart_inc) |
105 |
|
|
106 |
|
// Parameters (the rest): |
107 |
|
// |
108 |
|
, |
109 |
|
learntsize_factor((double)1 / (double)3), |
110 |
|
learntsize_inc(1.5) |
111 |
|
|
112 |
|
// Parameters (experimental): |
113 |
|
// |
114 |
|
, |
115 |
|
learntsize_adjust_start_confl(100), |
116 |
|
learntsize_adjust_inc(1.5) |
117 |
|
|
118 |
|
// Statistics: (formerly in 'SolverStats') |
119 |
|
// |
120 |
|
, |
121 |
|
solves(0), |
122 |
|
starts(0), |
123 |
|
decisions(0), |
124 |
|
rnd_decisions(0), |
125 |
|
propagations(0), |
126 |
|
conflicts(0), |
127 |
|
dec_vars(0), |
128 |
|
clauses_literals(0), |
129 |
|
learnts_literals(0), |
130 |
|
max_literals(0), |
131 |
|
tot_literals(0) |
132 |
|
|
133 |
|
, |
134 |
|
need_to_propagate(false), |
135 |
|
only_bcp(false), |
136 |
|
clause_added(false), |
137 |
|
ok(true), |
138 |
|
cla_inc(1), |
139 |
|
var_inc(1), |
140 |
6 |
watches(WatcherDeleted(ca)), |
141 |
|
qhead(0), |
142 |
|
simpDB_assigns(-1), |
143 |
|
simpDB_props(0), |
144 |
6 |
order_heap(VarOrderLt(activity)), |
145 |
|
progress_estimate(0), |
146 |
|
remove_satisfied(true) |
147 |
|
|
148 |
|
, |
149 |
|
ca() |
150 |
|
|
151 |
|
// even though these are temporaries and technically should be set |
152 |
|
// before calling, lets initialize them. this will reduces chances of |
153 |
|
// non-determinism in portfolio (parallel) solver if variables are |
154 |
|
// being (incorrectly) used without initialization. |
155 |
|
, |
156 |
|
seen(), |
157 |
|
analyze_stack(), |
158 |
|
analyze_toclear(), |
159 |
|
add_tmp(), |
160 |
|
max_learnts(0.0), |
161 |
|
learntsize_adjust_confl(0.0), |
162 |
|
learntsize_adjust_cnt(0) |
163 |
|
|
164 |
|
// Resource constraints: |
165 |
|
// |
166 |
|
, |
167 |
|
conflict_budget(-1), |
168 |
|
propagation_budget(-1), |
169 |
15 |
asynch_interrupt(false) |
170 |
|
{ |
171 |
|
// Create the constant variables |
172 |
3 |
varTrue = newVar(true, false); |
173 |
3 |
varFalse = newVar(false, false); |
174 |
|
|
175 |
|
// Assert the constants |
176 |
3 |
uncheckedEnqueue(mkLit(varTrue, false)); |
177 |
3 |
uncheckedEnqueue(mkLit(varFalse, true)); |
178 |
3 |
} |
179 |
|
|
180 |
|
|
181 |
3 |
Solver::~Solver() |
182 |
|
{ |
183 |
3 |
} |
184 |
|
|
185 |
|
|
186 |
|
//================================================================================================= |
187 |
|
// Minor methods: |
188 |
|
|
189 |
|
|
190 |
|
// Creates a new SAT variable in the solver. If 'decision' is cleared, variable will not be |
191 |
|
// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result). |
192 |
|
// |
193 |
288 |
Var Solver::newVar(bool sign, bool dvar) |
194 |
|
{ |
195 |
288 |
int v = nVars(); |
196 |
288 |
watches .init(mkLit(v, false)); |
197 |
288 |
watches .init(mkLit(v, true )); |
198 |
288 |
assigns .push(l_Undef); |
199 |
288 |
vardata .push(mkVarData(CRef_Undef, 0)); |
200 |
288 |
marker .push(0); |
201 |
|
//activity .push(0); |
202 |
288 |
activity .push(rnd_init_act ? drand(random_seed) * 0.00001 : 0); |
203 |
288 |
seen .push(0); |
204 |
288 |
polarity .push(sign); |
205 |
288 |
decision .push(); |
206 |
288 |
trail .capacity(v+1); |
207 |
288 |
setDecisionVar(v, dvar); |
208 |
|
|
209 |
288 |
return v; |
210 |
|
} |
211 |
|
|
212 |
|
|
213 |
1238 |
bool Solver::addClause_(vec<Lit>& ps, ClauseId& id) |
214 |
|
{ |
215 |
1238 |
if (decisionLevel() > 0) { |
216 |
|
cancelUntil(0); |
217 |
|
} |
218 |
|
|
219 |
1238 |
if (!ok) { |
220 |
|
id = ClauseIdUndef; |
221 |
|
return false; |
222 |
|
} |
223 |
|
|
224 |
|
// Check if clause is satisfied and remove false/duplicate literals: |
225 |
|
// TODO proof for duplicate literals removal? |
226 |
1238 |
sort(ps); |
227 |
|
Lit p; int i, j; |
228 |
1238 |
int falseLiteralsCount = 0; |
229 |
|
|
230 |
5172 |
for (i = j = 0, p = lit_Undef; i < ps.size(); i++) { |
231 |
|
// tautologies are ignored |
232 |
3934 |
if (value(ps[i]) == l_True || ps[i] == ~p) { |
233 |
|
id = ClauseIdUndef; |
234 |
|
return true; |
235 |
|
} |
236 |
|
|
237 |
|
// Ignore repeated literals |
238 |
3934 |
if (ps[i] == p) { |
239 |
|
continue; |
240 |
|
} |
241 |
|
|
242 |
3934 |
if (value(ps[i]) == l_False) { |
243 |
|
continue; |
244 |
|
} |
245 |
3934 |
ps[j++] = p = ps[i]; |
246 |
|
} |
247 |
|
|
248 |
1238 |
ps.shrink(i - j); |
249 |
|
|
250 |
1238 |
clause_added = true; |
251 |
|
|
252 |
1238 |
if(falseLiteralsCount == 0) { |
253 |
1238 |
if (ps.size() == 0) { |
254 |
|
return ok = false; |
255 |
|
} |
256 |
1238 |
else if (ps.size() == 1){ |
257 |
4 |
uncheckedEnqueue(ps[0]); |
258 |
4 |
CRef confl_ref = propagate(); |
259 |
4 |
ok = (confl_ref == CRef_Undef); |
260 |
4 |
return ok; |
261 |
|
} else { |
262 |
1234 |
CRef cr = ca.alloc(ps, false); |
263 |
1234 |
clauses.push(cr); |
264 |
1234 |
attachClause(cr); |
265 |
|
} |
266 |
1234 |
return ok; |
267 |
|
} |
268 |
|
return ok; |
269 |
|
} |
270 |
|
|
271 |
1470 |
void Solver::attachClause(CRef cr) { |
272 |
1470 |
const Clause& clause = ca[cr]; |
273 |
1470 |
Assert(clause.size() > 1); |
274 |
1470 |
watches[~clause[0]].push(Watcher(cr, clause[1])); |
275 |
1470 |
watches[~clause[1]].push(Watcher(cr, clause[0])); |
276 |
1470 |
if (clause.learnt()) |
277 |
158 |
learnts_literals += clause.size(); |
278 |
|
else |
279 |
1312 |
clauses_literals += clause.size(); |
280 |
1470 |
} |
281 |
|
|
282 |
820 |
void Solver::detachClause(CRef cr, bool strict) { |
283 |
820 |
const Clause& clause = ca[cr]; |
284 |
|
|
285 |
820 |
Assert(clause.size() > 1); |
286 |
|
|
287 |
820 |
if (strict) |
288 |
|
{ |
289 |
78 |
remove(watches[~clause[0]], Watcher(cr, clause[1])); |
290 |
78 |
remove(watches[~clause[1]], Watcher(cr, clause[0])); |
291 |
|
}else{ |
292 |
|
// Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) |
293 |
742 |
watches.smudge(~clause[0]); |
294 |
742 |
watches.smudge(~clause[1]); |
295 |
|
} |
296 |
|
|
297 |
820 |
if (clause.learnt()) |
298 |
38 |
learnts_literals -= clause.size(); |
299 |
|
else |
300 |
782 |
clauses_literals -= clause.size(); |
301 |
820 |
} |
302 |
|
|
303 |
742 |
void Solver::removeClause(CRef cr) { |
304 |
742 |
Clause& clause = ca[cr]; |
305 |
742 |
detachClause(cr); |
306 |
|
// Don't leave pointers to free'd memory! |
307 |
742 |
if (locked(clause)) vardata[var(clause[0])].reason = CRef_Undef; |
308 |
742 |
clause.mark(1); |
309 |
742 |
ca.free(cr); |
310 |
742 |
} |
311 |
|
|
312 |
106 |
bool Solver::satisfied(const Clause& clause) const |
313 |
|
{ |
314 |
452 |
for (int i = 0; i < clause.size(); i++) |
315 |
384 |
if (value(clause[i]) == l_True) return true; |
316 |
68 |
return false; |
317 |
|
} |
318 |
|
|
319 |
|
// Revert to the state at given level (keeping all assignment at 'level' but not beyond). |
320 |
|
// |
321 |
178 |
void Solver::cancelUntil(int level) { |
322 |
178 |
if (decisionLevel() > level){ |
323 |
176 |
Debug("bvminisat::explain") << OUTPUT_TAG << " backtracking to " << level << std::endl; |
324 |
3378 |
for (int clause = trail.size() - 1; clause >= trail_lim[level]; clause--) |
325 |
|
{ |
326 |
3202 |
Var x = var(trail[clause]); |
327 |
3202 |
assigns[x] = l_Undef; |
328 |
3202 |
if (marker[x] == 2) marker[x] = 1; |
329 |
6404 |
if (phase_saving > 1 |
330 |
3202 |
|| ((phase_saving == 1) && clause > trail_lim.last())) |
331 |
|
{ |
332 |
3202 |
polarity[x] = sign(trail[clause]); |
333 |
|
} |
334 |
3202 |
insertVarOrder(x); |
335 |
|
} |
336 |
176 |
qhead = trail_lim[level]; |
337 |
176 |
trail.shrink(trail.size() - trail_lim[level]); |
338 |
176 |
trail_lim.shrink(trail_lim.size() - level); |
339 |
|
} |
340 |
178 |
} |
341 |
|
|
342 |
|
|
343 |
|
//================================================================================================= |
344 |
|
// Major methods: |
345 |
|
|
346 |
|
|
347 |
876 |
Lit Solver::pickBranchLit() |
348 |
|
{ |
349 |
876 |
Var next = var_Undef; |
350 |
|
|
351 |
|
// Random decision: |
352 |
876 |
if (drand(random_seed) < random_var_freq && !order_heap.empty()){ |
353 |
|
next = order_heap[irand(random_seed,order_heap.size())]; |
354 |
|
if (value(next) == l_Undef && decision[next]) |
355 |
|
rnd_decisions++; } |
356 |
|
|
357 |
|
// Activity based decision: |
358 |
4568 |
while (next == var_Undef || value(next) != l_Undef || !decision[next]) |
359 |
1846 |
if (order_heap.empty()){ |
360 |
|
next = var_Undef; |
361 |
|
break; |
362 |
|
}else |
363 |
1846 |
next = order_heap.removeMin(); |
364 |
|
|
365 |
876 |
return next == var_Undef ? lit_Undef : mkLit(next, rnd_pol ? drand(random_seed) < 0.5 : polarity[next]); |
366 |
|
} |
367 |
|
|
368 |
|
/*_________________________________________________________________________________________________ |
369 |
|
| |
370 |
|
| analyze : (confl : Clause*) (out_learnt : vec<Lit>&) (out_btlevel : int&) -> |
371 |
|
[void] |
372 |
|
| |
373 |
|
| Description: |
374 |
|
| Analyze conflict and produce a reason clause. |
375 |
|
| |
376 |
|
| Pre-conditions: |
377 |
|
| * 'out_learnt' is assumed to be cleared. |
378 |
|
| * Current decision level must be greater than root level. |
379 |
|
| |
380 |
|
| Post-conditions: |
381 |
|
| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. |
382 |
|
| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision |
383 |
|
level of the | rest of literals. There may be others from the same level |
384 |
|
though. |
385 |
|
| |
386 |
|
|________________________________________________________________________________________________@*/ |
387 |
172 |
void Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel, UIP uip) |
388 |
|
{ |
389 |
172 |
int pathC = 0; |
390 |
172 |
Lit p = lit_Undef; |
391 |
|
|
392 |
|
// Generate conflict clause: |
393 |
|
// |
394 |
172 |
out_learnt.push(); // (leave room for the asserting literal) |
395 |
172 |
int index = trail.size() - 1; |
396 |
|
|
397 |
172 |
bool done = false; |
398 |
|
|
399 |
492 |
do{ |
400 |
664 |
Assert(confl != CRef_Undef); // (otherwise should be UIP) |
401 |
664 |
Clause& clause = ca[confl]; |
402 |
|
|
403 |
664 |
if (clause.learnt()) claBumpActivity(clause); |
404 |
|
|
405 |
2728 |
for (int j = (p == lit_Undef) ? 0 : 1; j < clause.size(); j++) |
406 |
|
{ |
407 |
2064 |
Lit q = clause[j]; |
408 |
|
|
409 |
2064 |
if (!seen[var(q)] && level(var(q)) > 0) |
410 |
|
{ |
411 |
1168 |
varBumpActivity(var(q)); |
412 |
1168 |
seen[var(q)] = 1; |
413 |
1168 |
if (level(var(q)) >= decisionLevel()) |
414 |
664 |
pathC++; |
415 |
|
else |
416 |
504 |
out_learnt.push(q); |
417 |
|
} |
418 |
|
} |
419 |
|
|
420 |
|
// Select next clause to look at: |
421 |
788 |
while (!seen[var(trail[index--])]); |
422 |
664 |
p = trail[index+1]; |
423 |
664 |
confl = reason(var(p)); |
424 |
664 |
seen[var(p)] = 0; |
425 |
664 |
pathC--; |
426 |
|
|
427 |
664 |
switch (uip) { |
428 |
664 |
case UIP_FIRST: |
429 |
664 |
done = pathC == 0; |
430 |
664 |
break; |
431 |
|
case UIP_LAST: |
432 |
|
done = confl == CRef_Undef || (pathC == 0 && marker[var(p)] == 2); |
433 |
|
break; |
434 |
|
default: |
435 |
|
Unreachable(); |
436 |
|
break; |
437 |
|
} |
438 |
664 |
} while (!done); |
439 |
172 |
out_learnt[0] = ~p; |
440 |
|
|
441 |
|
// Simplify conflict clause: |
442 |
|
// |
443 |
|
int i1, j; |
444 |
172 |
out_learnt.copyTo(analyze_toclear); |
445 |
172 |
if (ccmin_mode == 2){ |
446 |
|
uint32_t abstract_level = 0; |
447 |
|
for (i1 = 1; i1 < out_learnt.size(); i1++) |
448 |
|
abstract_level |= abstractLevel( |
449 |
|
var(out_learnt[i1])); // (maintain an abstraction of levels |
450 |
|
// involved in conflict) |
451 |
|
|
452 |
|
for (i1 = j = 1; i1 < out_learnt.size(); i1++) |
453 |
|
{ |
454 |
|
if (reason(var(out_learnt[i1])) == CRef_Undef) |
455 |
|
{ |
456 |
|
out_learnt[j++] = out_learnt[i1]; |
457 |
|
} |
458 |
|
else |
459 |
|
{ |
460 |
|
// Check if the literal is redundant |
461 |
|
if (!litRedundant(out_learnt[i1], abstract_level)) |
462 |
|
{ |
463 |
|
// Literal is not redundant |
464 |
|
out_learnt[j++] = out_learnt[i1]; |
465 |
|
} |
466 |
|
} |
467 |
|
} |
468 |
172 |
}else if (ccmin_mode == 1){ |
469 |
|
Unreachable(); |
470 |
|
for (i1 = j = 1; i1 < out_learnt.size(); i1++) |
471 |
|
{ |
472 |
|
Var x = var(out_learnt[i1]); |
473 |
|
|
474 |
|
if (reason(x) == CRef_Undef) |
475 |
|
out_learnt[j++] = out_learnt[i1]; |
476 |
|
else |
477 |
|
{ |
478 |
|
Clause& clause = ca[reason(var(out_learnt[i1]))]; |
479 |
|
for (int k = 1; k < clause.size(); k++) |
480 |
|
if (!seen[var(clause[k])] && level(var(clause[k])) > 0) |
481 |
|
{ |
482 |
|
out_learnt[j++] = out_learnt[i1]; |
483 |
|
break; |
484 |
|
} |
485 |
|
} |
486 |
|
} |
487 |
|
}else |
488 |
172 |
i1 = j = out_learnt.size(); |
489 |
|
|
490 |
172 |
max_literals += out_learnt.size(); |
491 |
172 |
out_learnt.shrink(i1 - j); |
492 |
172 |
tot_literals += out_learnt.size(); |
493 |
|
|
494 |
172 |
for (int i2 = 0; i2 < out_learnt.size(); ++i2) |
495 |
|
{ |
496 |
172 |
if (marker[var(out_learnt[i2])] == 0) |
497 |
|
{ |
498 |
172 |
break; |
499 |
|
} |
500 |
|
} |
501 |
|
|
502 |
|
// Find correct backtrack level: |
503 |
|
// |
504 |
172 |
if (out_learnt.size() == 1) { |
505 |
14 |
out_btlevel = 0; |
506 |
|
} |
507 |
|
else{ |
508 |
158 |
int max_i = 1; |
509 |
|
// Find the first literal assigned at the next-highest level: |
510 |
504 |
for (int i3 = 2; i3 < out_learnt.size(); i3++) |
511 |
346 |
if (level(var(out_learnt[i3])) > level(var(out_learnt[max_i]))) |
512 |
76 |
max_i = i3; |
513 |
|
// Swap-in this literal at i1 1: |
514 |
158 |
Lit p2 = out_learnt[max_i]; |
515 |
158 |
out_learnt[max_i] = out_learnt[1]; |
516 |
158 |
out_learnt[1] = p2; |
517 |
158 |
out_btlevel = level(var(p2)); |
518 |
|
} |
519 |
|
|
520 |
848 |
for (int j2 = 0; j2 < analyze_toclear.size(); j2++) |
521 |
676 |
seen[var(analyze_toclear[j2])] = 0; // ('seen[]' is now cleared) |
522 |
172 |
} |
523 |
|
|
524 |
|
|
525 |
|
// Check if 'p' can be removed. 'abstract_levels' is used to abort early if the algorithm is |
526 |
|
// visiting literals at levels that cannot be removed later. |
527 |
|
bool Solver::litRedundant(Lit p, uint32_t abstract_levels) |
528 |
|
{ |
529 |
|
analyze_stack.clear(); analyze_stack.push(p); |
530 |
|
int top = analyze_toclear.size(); |
531 |
|
while (analyze_stack.size() > 0){ |
532 |
|
CRef c_reason = reason(var(analyze_stack.last())); |
533 |
|
Assert(c_reason != CRef_Undef); |
534 |
|
Clause& clause = ca[c_reason]; |
535 |
|
int c_size = clause.size(); |
536 |
|
analyze_stack.pop(); |
537 |
|
|
538 |
|
for (int i = 1; i < c_size; i++){ |
539 |
|
Lit p2 = clause[i]; |
540 |
|
if (!seen[var(p2)] && level(var(p2)) > 0) |
541 |
|
{ |
542 |
|
if (reason(var(p2)) != CRef_Undef |
543 |
|
&& (abstractLevel(var(p2)) & abstract_levels) != 0) |
544 |
|
{ |
545 |
|
seen[var(p2)] = 1; |
546 |
|
analyze_stack.push(p2); |
547 |
|
analyze_toclear.push(p2); |
548 |
|
} |
549 |
|
else |
550 |
|
{ |
551 |
|
for (int j = top; j < analyze_toclear.size(); j++) |
552 |
|
seen[var(analyze_toclear[j])] = 0; |
553 |
|
analyze_toclear.shrink(analyze_toclear.size() - top); |
554 |
|
return false; |
555 |
|
} |
556 |
|
} |
557 |
|
} |
558 |
|
} |
559 |
|
|
560 |
|
return true; |
561 |
|
} |
562 |
|
|
563 |
|
/** |
564 |
|
* Specialized analyzeFinal procedure where we test the consistency |
565 |
|
* of the assumptions before backtracking bellow the assumption level. |
566 |
|
* |
567 |
|
* @param p the original uip (may be unit) |
568 |
|
* @param confl_clause the conflict clause |
569 |
|
* @param out_conflict the conflict in terms of assumptions we are building |
570 |
|
*/ |
571 |
|
void Solver::analyzeFinal2(Lit p, CRef confl_clause, vec<Lit>& out_conflict) { |
572 |
|
Assert(confl_clause != CRef_Undef); |
573 |
|
Assert(decisionLevel() == assumptions.size()); |
574 |
|
Assert(level(var(p)) == assumptions.size()); |
575 |
|
|
576 |
|
out_conflict.clear(); |
577 |
|
|
578 |
|
Clause& cl = ca[confl_clause]; |
579 |
|
for (int i = 0; i < cl.size(); ++i) { |
580 |
|
seen[var(cl[i])] = 1; |
581 |
|
} |
582 |
|
|
583 |
|
int end = trail_lim[0]; |
584 |
|
for (int i = trail.size() - 1; i >= end; i--) { |
585 |
|
Var x = var(trail[i]); |
586 |
|
if (seen[x]) { |
587 |
|
if (reason(x) == CRef_Undef) { |
588 |
|
// we skip p if was a learnt unit |
589 |
|
if (x != var(p)) { |
590 |
|
if (marker[x] == 2) { |
591 |
|
Assert(level(x) > 0); |
592 |
|
out_conflict.push(~trail[i]); |
593 |
|
} |
594 |
|
} |
595 |
|
} else { |
596 |
|
Clause& clause = ca[reason(x)]; |
597 |
|
|
598 |
|
for (int j = 1; j < clause.size(); j++) |
599 |
|
{ |
600 |
|
if (level(var(clause[j])) > 0) seen[var(clause[j])] = 1; |
601 |
|
} |
602 |
|
} |
603 |
|
seen[x] = 0; |
604 |
|
} |
605 |
|
Assert(seen[x] == 0); |
606 |
|
} |
607 |
|
Assert(out_conflict.size()); |
608 |
|
} |
609 |
|
|
610 |
|
/*_________________________________________________________________________________________________ |
611 |
|
| |
612 |
|
| analyzeFinal : (p : Lit) -> [void] |
613 |
|
| |
614 |
|
| Description: |
615 |
|
| Specialized analysis procedure to express the final conflict in terms of |
616 |
|
assumptions. | Calculates the (possibly empty) set of assumptions that led to |
617 |
|
the assignment of 'p', and | stores the result in 'out_conflict'. |
618 |
|
|________________________________________________________________________________________________@*/ |
619 |
|
void Solver::analyzeFinal(Lit p, vec<Lit>& out_conflict) |
620 |
|
{ |
621 |
|
out_conflict.clear(); |
622 |
|
if (marker[var(p)] == 2) { |
623 |
|
out_conflict.push(p); |
624 |
|
} |
625 |
|
|
626 |
|
if (decisionLevel() == 0) |
627 |
|
{ |
628 |
|
return; |
629 |
|
} |
630 |
|
|
631 |
|
seen[var(p)] = 1; |
632 |
|
int end = trail_lim[0]; |
633 |
|
|
634 |
|
for (int i = trail.size()-1; i >= end; i--){ |
635 |
|
Var x = var(trail[i]); |
636 |
|
if (seen[x]) { |
637 |
|
if (reason(x) == CRef_Undef) { |
638 |
|
Assert(marker[x] == 2); |
639 |
|
Assert(level(x) > 0); |
640 |
|
if (~trail[i] != p) |
641 |
|
{ |
642 |
|
out_conflict.push(~trail[i]); |
643 |
|
} |
644 |
|
} else { |
645 |
|
Clause& clause = ca[reason(x)]; |
646 |
|
for (int j = 1; j < clause.size(); j++) |
647 |
|
{ |
648 |
|
if (level(var(clause[j])) > 0) |
649 |
|
{ |
650 |
|
seen[var(clause[j])] = 1; |
651 |
|
} |
652 |
|
} |
653 |
|
} |
654 |
|
seen[x] = 0; |
655 |
|
} |
656 |
|
} |
657 |
|
|
658 |
|
seen[var(p)] = 0; |
659 |
|
Assert(out_conflict.size()); |
660 |
|
} |
661 |
|
|
662 |
|
|
663 |
3284 |
void Solver::uncheckedEnqueue(Lit p, CRef from) |
664 |
|
{ |
665 |
3284 |
Assert(value(p) == l_Undef); |
666 |
3284 |
assigns[var(p)] = lbool(!sign(p)); |
667 |
3284 |
vardata[var(p)] = mkVarData(from, decisionLevel()); |
668 |
3284 |
trail.push_(p); |
669 |
3284 |
if (decisionLevel() <= assumptions.size() && marker[var(p)] == 1) |
670 |
|
{ |
671 |
|
if (d_notify) |
672 |
|
{ |
673 |
|
Debug("bvminisat::explain") |
674 |
|
<< OUTPUT_TAG << "propagating " << p << std::endl; |
675 |
|
d_notify->notify(p); |
676 |
|
} |
677 |
|
} |
678 |
3284 |
} |
679 |
|
|
680 |
|
void Solver::popAssumption() { |
681 |
|
assumptions.pop(); |
682 |
|
conflict.clear(); |
683 |
|
cancelUntil(assumptions.size()); |
684 |
|
} |
685 |
|
|
686 |
|
lbool Solver::propagateAssumptions() { |
687 |
|
only_bcp = true; |
688 |
|
ccmin_mode = 0; |
689 |
|
return search(-1); |
690 |
|
} |
691 |
|
|
692 |
|
lbool Solver::assertAssumption(Lit p, bool propagate) { |
693 |
|
// TODO need to somehow mark the assumption as unit in the current context? |
694 |
|
// it's not always unit though, but this would be useful for debugging |
695 |
|
|
696 |
|
// Assert(marker[var(p)] == 1); |
697 |
|
|
698 |
|
if (decisionLevel() > assumptions.size()) { |
699 |
|
cancelUntil(assumptions.size()); |
700 |
|
} |
701 |
|
|
702 |
|
conflict.clear(); |
703 |
|
|
704 |
|
// add to the assumptions |
705 |
|
if (c->getLevel() > 0) { |
706 |
|
assumptions.push(p); |
707 |
|
} else { |
708 |
|
ClauseId id; |
709 |
|
if (!addClause(p, id)) { |
710 |
|
conflict.push(~p); |
711 |
|
return l_False; |
712 |
|
} |
713 |
|
} |
714 |
|
|
715 |
|
// run the propagation |
716 |
|
if (propagate) { |
717 |
|
only_bcp = true; |
718 |
|
ccmin_mode = 0; |
719 |
|
lbool result = search(-1); |
720 |
|
return result; |
721 |
|
} else { |
722 |
|
return l_True; |
723 |
|
} |
724 |
|
} |
725 |
|
|
726 |
|
void Solver::addMarkerLiteral(Var var) { |
727 |
|
// make sure it wasn't already marked |
728 |
|
Assert(marker[var] == 0); |
729 |
|
marker[var] = 1; |
730 |
|
} |
731 |
|
|
732 |
|
/*_________________________________________________________________________________________________ |
733 |
|
| |
734 |
|
| propagate : [void] -> [Clause*] |
735 |
|
| |
736 |
|
| Description: |
737 |
|
| Propagates all enqueued facts. If a conflict arises, the conflicting clause |
738 |
|
is returned, | otherwise CRef_Undef. |
739 |
|
| |
740 |
|
| Post-conditions: |
741 |
|
| * the propagation queue is empty, even if there was a conflict. |
742 |
|
|________________________________________________________________________________________________@*/ |
743 |
1114 |
CRef Solver::propagate() |
744 |
|
{ |
745 |
1114 |
CRef confl = CRef_Undef; |
746 |
1114 |
int num_props = 0; |
747 |
1114 |
watches.cleanAll(); |
748 |
|
|
749 |
7194 |
while (qhead < trail.size()){ |
750 |
3040 |
Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. |
751 |
3040 |
vec<Watcher>& ws = watches[p]; |
752 |
|
Watcher *i, *j, *end; |
753 |
3040 |
num_props++; |
754 |
|
|
755 |
14402 |
for (i = j = (Watcher*)ws, end = i + ws.size(); i != end;){ |
756 |
|
// Try to avoid inspecting the clause: |
757 |
11362 |
Lit blocker = i->blocker; |
758 |
17238 |
if (value(blocker) == l_True){ |
759 |
18352 |
*j++ = *i++; continue; } |
760 |
|
|
761 |
|
// Make sure the false literal is data[1]: |
762 |
5486 |
CRef cr = i->cref; |
763 |
5486 |
Clause& clause = ca[cr]; |
764 |
5486 |
Lit false_lit = ~p; |
765 |
5486 |
if (clause[0] == false_lit) |
766 |
2350 |
clause[0] = clause[1], clause[1] = false_lit; |
767 |
5486 |
Assert(clause[1] == false_lit); |
768 |
5486 |
i++; |
769 |
|
|
770 |
|
// If 0th watch is true, then clause is already satisfied. |
771 |
5486 |
Lit first = clause[0]; |
772 |
5486 |
Watcher w = Watcher(cr, first); |
773 |
6210 |
if (first != blocker && value(first) == l_True){ |
774 |
1448 |
*j++ = w; continue; } |
775 |
|
|
776 |
|
// Look for new watch: |
777 |
10888 |
for (int k = 2; k < clause.size(); k++) |
778 |
8488 |
if (value(clause[k]) != l_False) |
779 |
|
{ |
780 |
2362 |
clause[1] = clause[k]; |
781 |
2362 |
clause[k] = false_lit; |
782 |
2362 |
watches[~clause[1]].push(w); |
783 |
2362 |
goto NextClause; |
784 |
|
} |
785 |
|
|
786 |
|
// Did not find watch -- clause is unit under assignment: |
787 |
2400 |
*j++ = w; |
788 |
2400 |
if (value(first) == l_False){ |
789 |
174 |
confl = cr; |
790 |
174 |
qhead = trail.size(); |
791 |
|
// Copy the remaining watches: |
792 |
766 |
while (i < end) |
793 |
296 |
*j++ = *i++; |
794 |
|
}else |
795 |
2226 |
uncheckedEnqueue(first, cr); |
796 |
|
|
797 |
4762 |
NextClause:; |
798 |
|
} |
799 |
3040 |
ws.shrink(i - j); |
800 |
|
} |
801 |
1114 |
propagations += num_props; |
802 |
1114 |
simpDB_props -= num_props; |
803 |
|
|
804 |
1114 |
return confl; |
805 |
|
} |
806 |
|
|
807 |
|
/*_________________________________________________________________________________________________ |
808 |
|
| |
809 |
|
| reduceDB : () -> [void] |
810 |
|
| |
811 |
|
| Description: |
812 |
|
| Remove half of the learnt clauses, minus the clauses locked by the current |
813 |
|
assignment. Locked | clauses are clauses that are reason to some assignment. |
814 |
|
Binary clauses are never removed. |
815 |
|
|________________________________________________________________________________________________@*/ |
816 |
|
struct reduceDB_lt |
817 |
|
{ |
818 |
|
ClauseAllocator& ca; |
819 |
|
reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {} |
820 |
|
bool operator()(CRef x, CRef y) |
821 |
|
{ |
822 |
|
return ca[x].size() > 2 |
823 |
|
&& (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); |
824 |
|
} |
825 |
|
}; |
826 |
|
void Solver::reduceDB() |
827 |
|
{ |
828 |
|
int i, j; |
829 |
|
double extra_lim = cla_inc / learnts.size(); // Remove any clause below this activity |
830 |
|
|
831 |
|
sort(learnts, reduceDB_lt(ca)); |
832 |
|
// Don't delete binary or locked clauses. From the rest, delete clauses from the first half |
833 |
|
// and clauses with activity smaller than 'extra_lim': |
834 |
|
for (i = j = 0; i < learnts.size(); i++){ |
835 |
|
Clause& clause = ca[learnts[i]]; |
836 |
|
if (clause.size() > 2 && !locked(clause) |
837 |
|
&& (i < learnts.size() / 2 || clause.activity() < extra_lim)) |
838 |
|
removeClause(learnts[i]); |
839 |
|
else |
840 |
|
learnts[j++] = learnts[i]; |
841 |
|
} |
842 |
|
learnts.shrink(i - j); |
843 |
|
checkGarbage(); |
844 |
|
} |
845 |
|
|
846 |
|
|
847 |
4 |
void Solver::removeSatisfied(vec<CRef>& cs) |
848 |
|
{ |
849 |
|
int i, j; |
850 |
110 |
for (i = j = 0; i < cs.size(); i++){ |
851 |
106 |
Clause& clause = ca[cs[i]]; |
852 |
106 |
if (satisfied(clause)) |
853 |
|
{ |
854 |
38 |
removeClause(cs[i]); |
855 |
|
} |
856 |
|
else |
857 |
68 |
cs[j++] = cs[i]; |
858 |
|
} |
859 |
4 |
cs.shrink(i - j); |
860 |
4 |
} |
861 |
|
|
862 |
|
|
863 |
4 |
void Solver::rebuildOrderHeap() |
864 |
|
{ |
865 |
8 |
vec<Var> vs; |
866 |
576 |
for (Var v = 0; v < nVars(); v++) |
867 |
572 |
if (decision[v] && value(v) == l_Undef) |
868 |
368 |
vs.push(v); |
869 |
4 |
order_heap.build(vs); |
870 |
4 |
} |
871 |
|
|
872 |
|
/*_________________________________________________________________________________________________ |
873 |
|
| |
874 |
|
| simplify : [void] -> [bool] |
875 |
|
| |
876 |
|
| Description: |
877 |
|
| Simplify the clause database according to the current top-level assigment. |
878 |
|
Currently, the only | thing done here is the removal of satisfied clauses, |
879 |
|
but more things can be put here. |
880 |
|
|________________________________________________________________________________________________@*/ |
881 |
20 |
bool Solver::simplify() |
882 |
|
{ |
883 |
20 |
Assert(decisionLevel() == 0); |
884 |
|
|
885 |
20 |
if (!ok || propagate() != CRef_Undef) return ok = false; |
886 |
|
|
887 |
20 |
if (nAssigns() == simpDB_assigns || (simpDB_props > 0)) return true; |
888 |
|
|
889 |
4 |
d_notify->spendResource(Resource::BvSatSimplifyStep); |
890 |
|
|
891 |
|
// Remove satisfied clauses: |
892 |
4 |
removeSatisfied(learnts); |
893 |
4 |
if (remove_satisfied) // Can be turned off. |
894 |
|
removeSatisfied(clauses); |
895 |
4 |
checkGarbage(); |
896 |
4 |
rebuildOrderHeap(); |
897 |
|
|
898 |
4 |
simpDB_assigns = nAssigns(); |
899 |
4 |
simpDB_props = |
900 |
4 |
clauses_literals + learnts_literals; // (shouldn't depend on stats |
901 |
|
// really, but it will do for now) |
902 |
|
|
903 |
4 |
return true; |
904 |
|
} |
905 |
|
|
906 |
|
/*_________________________________________________________________________________________________ |
907 |
|
| |
908 |
|
| search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool] |
909 |
|
| |
910 |
|
| Description: |
911 |
|
| Search for a model the specified number of conflicts. |
912 |
|
| NOTE! Use negative value for 'nof_conflicts' indicate infinity. |
913 |
|
| |
914 |
|
| Output: |
915 |
|
| 'l_True' if a partial assigment that is consistent with respect to the |
916 |
|
clauseset is found. If | all variables are decision variables, this means |
917 |
|
that the clause set is satisfiable. 'l_False' | if the clause set is |
918 |
|
unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. |
919 |
|
|________________________________________________________________________________________________@*/ |
920 |
6 |
lbool Solver::search(int nof_conflicts, UIP uip) |
921 |
|
{ |
922 |
6 |
Assert(ok); |
923 |
|
int backtrack_level; |
924 |
6 |
int conflictC = 0; |
925 |
12 |
vec<Lit> learnt_clause; |
926 |
6 |
starts++; |
927 |
|
|
928 |
|
for (;;) |
929 |
|
{ |
930 |
1054 |
d_notify->safePoint(Resource::BvSatPropagateStep); |
931 |
1054 |
CRef confl = propagate(); |
932 |
1054 |
if (confl != CRef_Undef) |
933 |
|
{ |
934 |
|
// CONFLICT |
935 |
174 |
conflicts++; |
936 |
174 |
conflictC++; |
937 |
|
|
938 |
174 |
if (decisionLevel() == 0) |
939 |
|
{ |
940 |
|
// can this happen for bv? |
941 |
2 |
return l_False; |
942 |
|
} |
943 |
|
|
944 |
172 |
learnt_clause.clear(); |
945 |
172 |
analyze(confl, learnt_clause, backtrack_level, uip); |
946 |
|
|
947 |
172 |
Lit p = learnt_clause[0]; |
948 |
|
// bool assumption = marker[var(p)] == 2; |
949 |
|
|
950 |
172 |
CRef cr = CRef_Undef; |
951 |
172 |
if (learnt_clause.size() > 1) |
952 |
|
{ |
953 |
158 |
cr = ca.alloc(learnt_clause, true); |
954 |
158 |
learnts.push(cr); |
955 |
158 |
attachClause(cr); |
956 |
158 |
claBumpActivity(ca[cr]); |
957 |
|
} |
958 |
|
|
959 |
172 |
if (learnt_clause.size() == 1) |
960 |
|
{ |
961 |
|
// learning a unit clause |
962 |
|
} |
963 |
|
|
964 |
|
// if the uip was an assumption we are unsat |
965 |
172 |
if (level(var(p)) <= assumptions.size()) |
966 |
|
{ |
967 |
|
for (int i = 0; i < learnt_clause.size(); ++i) |
968 |
|
{ |
969 |
|
Assert(level(var(learnt_clause[i])) <= decisionLevel()); |
970 |
|
seen[var(learnt_clause[i])] = 1; |
971 |
|
} |
972 |
|
|
973 |
|
analyzeFinal(p, conflict); |
974 |
|
Debug("bvminisat::search") |
975 |
|
<< OUTPUT_TAG << " conflict on assumptions " << std::endl; |
976 |
|
return l_False; |
977 |
|
} |
978 |
|
|
979 |
172 |
if (!cvc5::options::bvEagerExplanations()) |
980 |
|
{ |
981 |
|
// check if uip leads to a conflict |
982 |
172 |
if (backtrack_level < assumptions.size()) |
983 |
|
{ |
984 |
|
cancelUntil(assumptions.size()); |
985 |
|
uncheckedEnqueue(p, cr); |
986 |
|
|
987 |
|
CRef new_confl = propagate(); |
988 |
|
if (new_confl != CRef_Undef) |
989 |
|
{ |
990 |
|
// we have a conflict we now need to explain it |
991 |
|
analyzeFinal2(p, new_confl, conflict); |
992 |
|
return l_False; |
993 |
|
} |
994 |
|
} |
995 |
|
} |
996 |
|
|
997 |
172 |
cancelUntil(backtrack_level); |
998 |
172 |
uncheckedEnqueue(p, cr); |
999 |
|
|
1000 |
172 |
varDecayActivity(); |
1001 |
172 |
claDecayActivity(); |
1002 |
|
|
1003 |
172 |
if (--learntsize_adjust_cnt == 0) |
1004 |
|
{ |
1005 |
|
learntsize_adjust_confl *= learntsize_adjust_inc; |
1006 |
|
learntsize_adjust_cnt = (int)learntsize_adjust_confl; |
1007 |
|
max_learnts *= learntsize_inc; |
1008 |
|
|
1009 |
|
if (verbosity >= 1) |
1010 |
|
printf("| %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n", |
1011 |
|
(int)conflicts, |
1012 |
|
(int)dec_vars |
1013 |
|
- (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), |
1014 |
|
nClauses(), |
1015 |
|
(int)clauses_literals, |
1016 |
|
(int)max_learnts, |
1017 |
|
nLearnts(), |
1018 |
|
(double)learnts_literals / nLearnts(), |
1019 |
|
progressEstimate() * 100); |
1020 |
|
} |
1021 |
|
} |
1022 |
|
else |
1023 |
|
{ |
1024 |
|
// NO CONFLICT |
1025 |
|
bool isWithinBudget; |
1026 |
|
try |
1027 |
|
{ |
1028 |
880 |
isWithinBudget = |
1029 |
|
withinBudget(Resource::BvSatConflictsStep); |
1030 |
|
} |
1031 |
|
catch (const cvc5::theory::Interrupted& e) |
1032 |
|
{ |
1033 |
|
// do some clean-up and rethrow |
1034 |
|
cancelUntil(assumptions.size()); |
1035 |
|
throw e; |
1036 |
|
} |
1037 |
|
|
1038 |
2622 |
if ((decisionLevel() > assumptions.size() && nof_conflicts >= 0 |
1039 |
862 |
&& conflictC >= nof_conflicts) |
1040 |
1756 |
|| !isWithinBudget) |
1041 |
|
{ |
1042 |
|
// Reached bound on number of conflicts: |
1043 |
4 |
Debug("bvminisat::search") << OUTPUT_TAG << " restarting " << std::endl; |
1044 |
4 |
progress_estimate = progressEstimate(); |
1045 |
4 |
cancelUntil(assumptions.size()); |
1046 |
4 |
return l_Undef; |
1047 |
|
} |
1048 |
|
|
1049 |
|
// Simplify the set of problem clauses: |
1050 |
876 |
if (decisionLevel() == 0 && !simplify()) |
1051 |
|
{ |
1052 |
|
Debug("bvminisat::search") |
1053 |
|
<< OUTPUT_TAG << " base level conflict, we're unsat" << std::endl; |
1054 |
|
return l_False; |
1055 |
|
} |
1056 |
|
|
1057 |
|
// We can't erase clauses if there is unprocessed assumptions, there might |
1058 |
|
// be some propagationg we need to redu |
1059 |
1752 |
if (decisionLevel() >= assumptions.size() |
1060 |
876 |
&& learnts.size() - nAssigns() >= max_learnts) |
1061 |
|
{ |
1062 |
|
// Reduce the set of learnt clauses: |
1063 |
|
Debug("bvminisat::search") |
1064 |
|
<< OUTPUT_TAG << " cleaning up database" << std::endl; |
1065 |
|
reduceDB(); |
1066 |
|
} |
1067 |
|
|
1068 |
876 |
Lit next = lit_Undef; |
1069 |
876 |
while (decisionLevel() < assumptions.size()) |
1070 |
|
{ |
1071 |
|
// Perform user provided assumption: |
1072 |
|
Lit p = assumptions[decisionLevel()]; |
1073 |
|
if (value(p) == l_True) |
1074 |
|
{ |
1075 |
|
// Dummy decision level: |
1076 |
|
newDecisionLevel(); |
1077 |
|
} |
1078 |
|
else if (value(p) == l_False) |
1079 |
|
{ |
1080 |
|
marker[var(p)] = 2; |
1081 |
|
|
1082 |
|
analyzeFinal(~p, conflict); |
1083 |
|
Debug("bvminisat::search") |
1084 |
|
<< OUTPUT_TAG << " assumption false, we're unsat" << std::endl; |
1085 |
|
return l_False; |
1086 |
|
} |
1087 |
|
else |
1088 |
|
{ |
1089 |
|
marker[var(p)] = 2; |
1090 |
|
next = p; |
1091 |
|
break; |
1092 |
|
} |
1093 |
|
} |
1094 |
|
|
1095 |
876 |
if (next == lit_Undef) |
1096 |
|
{ |
1097 |
876 |
if (only_bcp) |
1098 |
|
{ |
1099 |
|
Debug("bvminisat::search") |
1100 |
|
<< OUTPUT_TAG << " only bcp, skipping rest of the problem" |
1101 |
|
<< std::endl; |
1102 |
|
return l_True; |
1103 |
|
} |
1104 |
|
|
1105 |
|
// New variable decision: |
1106 |
876 |
decisions++; |
1107 |
876 |
next = pickBranchLit(); |
1108 |
|
|
1109 |
876 |
if (next == lit_Undef) |
1110 |
|
{ |
1111 |
|
Debug("bvminisat::search") |
1112 |
|
<< OUTPUT_TAG << " satisfiable" << std::endl; |
1113 |
|
// Model found: |
1114 |
|
return l_True; |
1115 |
|
} |
1116 |
|
} |
1117 |
|
|
1118 |
|
// Increase decision level and enqueue 'next' |
1119 |
876 |
newDecisionLevel(); |
1120 |
876 |
uncheckedEnqueue(next); |
1121 |
|
} |
1122 |
1048 |
} |
1123 |
|
} |
1124 |
|
|
1125 |
|
|
1126 |
4 |
double Solver::progressEstimate() const |
1127 |
|
{ |
1128 |
4 |
double progress = 0; |
1129 |
4 |
double F = 1.0 / nVars(); |
1130 |
|
|
1131 |
54 |
for (int i = 0; i <= decisionLevel(); i++){ |
1132 |
50 |
int beg = i == 0 ? 0 : trail_lim[i - 1]; |
1133 |
50 |
int end = i == decisionLevel() ? trail.size() : trail_lim[i]; |
1134 |
50 |
progress += pow(F, i) * (end - beg); |
1135 |
|
} |
1136 |
|
|
1137 |
4 |
return progress / nVars(); |
1138 |
|
} |
1139 |
|
|
1140 |
|
/* |
1141 |
|
Finite subsequences of the Luby-sequence: |
1142 |
|
|
1143 |
|
0: 1 |
1144 |
|
1: 1 1 2 |
1145 |
|
2: 1 1 2 1 1 2 4 |
1146 |
|
3: 1 1 2 1 1 2 4 1 1 2 1 1 2 4 8 |
1147 |
|
... |
1148 |
|
|
1149 |
|
|
1150 |
|
*/ |
1151 |
|
|
1152 |
6 |
static double luby(double y, int x){ |
1153 |
|
|
1154 |
|
// Find the finite subsequence that contains index 'x', and the |
1155 |
|
// size of that subsequence: |
1156 |
|
int size, seq; |
1157 |
6 |
for (size = 1, seq = 0; size < x+1; seq++, size = 2*size+1); |
1158 |
|
|
1159 |
10 |
while (size-1 != x){ |
1160 |
2 |
size = (size-1)>>1; |
1161 |
2 |
seq--; |
1162 |
2 |
x = x % size; |
1163 |
|
} |
1164 |
|
|
1165 |
6 |
return pow(y, seq); |
1166 |
|
} |
1167 |
|
|
1168 |
|
// NOTE: assumptions passed in member-variable 'assumptions'. |
1169 |
2 |
lbool Solver::solve_() |
1170 |
|
{ |
1171 |
2 |
Debug("bvminisat") <<"BVMinisat::Solving learned clauses " << learnts.size() <<"\n"; |
1172 |
2 |
Debug("bvminisat") <<"BVMinisat::Solving assumptions " << assumptions.size() <<"\n"; |
1173 |
|
|
1174 |
2 |
model.clear(); |
1175 |
2 |
conflict.clear(); |
1176 |
|
|
1177 |
2 |
ccmin_mode = 0; |
1178 |
|
|
1179 |
2 |
if (!ok) return l_False; |
1180 |
|
|
1181 |
2 |
solves++; |
1182 |
|
|
1183 |
2 |
max_learnts = nClauses() * learntsize_factor; |
1184 |
2 |
learntsize_adjust_confl = learntsize_adjust_start_confl; |
1185 |
2 |
learntsize_adjust_cnt = (int)learntsize_adjust_confl; |
1186 |
2 |
lbool status = l_Undef; |
1187 |
|
|
1188 |
2 |
if (verbosity >= 1){ |
1189 |
|
printf("============================[ Search Statistics ]==============================\n"); |
1190 |
|
printf("| Conflicts | ORIGINAL | LEARNT | Progress |\n"); |
1191 |
|
printf("| | Vars Clauses Literals | Limit Clauses Lit/Cl | |\n"); |
1192 |
|
printf("===============================================================================\n"); |
1193 |
|
} |
1194 |
|
|
1195 |
|
// Search: |
1196 |
2 |
int curr_restarts = 0; |
1197 |
14 |
while (status == l_Undef){ |
1198 |
6 |
double rest_base = luby_restart ? luby(restart_inc, curr_restarts) : pow(restart_inc, curr_restarts); |
1199 |
6 |
status = search(rest_base * restart_first); |
1200 |
6 |
if (!withinBudget(Resource::BvSatConflictsStep)) break; |
1201 |
6 |
curr_restarts++; |
1202 |
|
} |
1203 |
|
|
1204 |
2 |
if (verbosity >= 1) |
1205 |
|
printf("===============================================================================\n"); |
1206 |
|
|
1207 |
2 |
if (status == l_True){ |
1208 |
|
// Extend & copy model: |
1209 |
|
// model.growTo(nVars()); |
1210 |
|
// for (int i = 0; i < nVars(); i++) model[i] = value(i); |
1211 |
2 |
}else if (status == l_False && conflict.size() == 0) |
1212 |
2 |
ok = false; |
1213 |
|
|
1214 |
2 |
return status; |
1215 |
|
} |
1216 |
|
|
1217 |
|
//================================================================================================= |
1218 |
|
// Bitvector propagations |
1219 |
|
// |
1220 |
|
|
1221 |
|
void Solver::explain(Lit p, std::vector<Lit>& explanation) { |
1222 |
|
Debug("bvminisat::explain") << OUTPUT_TAG << "starting explain of " << p << std::endl; |
1223 |
|
|
1224 |
|
// top level fact, no explanation necessary |
1225 |
|
if (level(var(p)) == 0) { |
1226 |
|
return; |
1227 |
|
} |
1228 |
|
|
1229 |
|
seen[var(p)] = 1; |
1230 |
|
|
1231 |
|
// if we are called at decisionLevel = 0 trail_lim is empty |
1232 |
|
int bottom = trail_lim[0]; |
1233 |
|
for (int i = trail.size()-1; i >= bottom; i--){ |
1234 |
|
Var x = var(trail[i]); |
1235 |
|
if (seen[x]) { |
1236 |
|
if (reason(x) == CRef_Undef) { |
1237 |
|
if (marker[x] == 2) { |
1238 |
|
Assert(level(x) > 0); |
1239 |
|
explanation.push_back(trail[i]); |
1240 |
|
} else { |
1241 |
|
Assert(level(x) == 0); |
1242 |
|
} |
1243 |
|
} else { |
1244 |
|
Clause& clause = ca[reason(x)]; |
1245 |
|
for (int j = 1; j < clause.size(); j++) |
1246 |
|
{ |
1247 |
|
if (level(var(clause[j])) > 0) |
1248 |
|
{ |
1249 |
|
seen[var(clause[j])] = 1; |
1250 |
|
} |
1251 |
|
} |
1252 |
|
} |
1253 |
|
seen[x] = 0; |
1254 |
|
} |
1255 |
|
} |
1256 |
|
seen[var(p)] = 0; |
1257 |
|
} |
1258 |
|
|
1259 |
|
//================================================================================================= |
1260 |
|
// Writing CNF to DIMACS: |
1261 |
|
// |
1262 |
|
// FIXME: this needs to be rewritten completely. |
1263 |
|
|
1264 |
|
static Var mapVar(Var x, vec<Var>& map, Var& max) |
1265 |
|
{ |
1266 |
|
if (map.size() <= x || map[x] == -1){ |
1267 |
|
map.growTo(x+1, -1); |
1268 |
|
map[x] = max++; |
1269 |
|
} |
1270 |
|
return map[x]; |
1271 |
|
} |
1272 |
|
|
1273 |
|
void Solver::toDimacs(FILE* f, Clause& clause, vec<Var>& map, Var& max) |
1274 |
|
{ |
1275 |
|
if (satisfied(clause)) return; |
1276 |
|
|
1277 |
|
for (int i = 0; i < clause.size(); i++) |
1278 |
|
if (value(clause[i]) != l_False) |
1279 |
|
fprintf(f, |
1280 |
|
"%s%d ", |
1281 |
|
sign(clause[i]) ? "-" : "", |
1282 |
|
mapVar(var(clause[i]), map, max) + 1); |
1283 |
|
fprintf(f, "0\n"); |
1284 |
|
} |
1285 |
|
|
1286 |
|
|
1287 |
|
void Solver::toDimacs(const char *file, const vec<Lit>& assumps) |
1288 |
|
{ |
1289 |
|
FILE* f = fopen(file, "wr"); |
1290 |
|
if (f == NULL) |
1291 |
|
fprintf(stderr, "could not open file %s\n", file), exit(1); |
1292 |
|
toDimacs(f, assumps); |
1293 |
|
fclose(f); |
1294 |
|
} |
1295 |
|
|
1296 |
|
|
1297 |
|
void Solver::toDimacs(FILE* f, const vec<Lit>& assumps) |
1298 |
|
{ |
1299 |
|
// Handle case when solver is in contradictory state: |
1300 |
|
if (!ok){ |
1301 |
|
fprintf(f, "p cnf 1 2\n1 0\n-1 0\n"); |
1302 |
|
return; } |
1303 |
|
|
1304 |
|
vec<Var> map; Var max = 0; |
1305 |
|
|
1306 |
|
// Cannot use removeClauses here because it is not safe |
1307 |
|
// to deallocate them at this point. Could be improved. |
1308 |
|
int cnt = 0; |
1309 |
|
for (int i = 0; i < clauses.size(); i++) |
1310 |
|
if (!satisfied(ca[clauses[i]])) |
1311 |
|
cnt++; |
1312 |
|
|
1313 |
|
for (int i = 0; i < clauses.size(); i++) |
1314 |
|
if (!satisfied(ca[clauses[i]])){ |
1315 |
|
Clause& clause = ca[clauses[i]]; |
1316 |
|
for (int j = 0; j < clause.size(); j++) |
1317 |
|
if (value(clause[j]) != l_False) mapVar(var(clause[j]), map, max); |
1318 |
|
} |
1319 |
|
|
1320 |
|
// Assumptions are added as unit clauses: |
1321 |
|
cnt += assumps.size(); |
1322 |
|
|
1323 |
|
fprintf(f, "p cnf %d %d\n", max, cnt); |
1324 |
|
|
1325 |
|
for (int i = 0; i < assumps.size(); i++){ |
1326 |
|
Assert(value(assumps[i]) != l_False); |
1327 |
|
fprintf(f, |
1328 |
|
"%s%d 0\n", |
1329 |
|
sign(assumps[i]) ? "-" : "", |
1330 |
|
mapVar(var(assumps[i]), map, max) + 1); |
1331 |
|
} |
1332 |
|
|
1333 |
|
for (int i = 0; i < clauses.size(); i++) |
1334 |
|
toDimacs(f, ca[clauses[i]], map, max); |
1335 |
|
|
1336 |
|
if (verbosity > 0) |
1337 |
|
printf("Wrote %d clauses with %d variables.\n", cnt, max); |
1338 |
|
} |
1339 |
|
|
1340 |
|
|
1341 |
|
//================================================================================================= |
1342 |
|
// Garbage Collection methods: |
1343 |
|
|
1344 |
2 |
void Solver::relocAll(ClauseAllocator& to) |
1345 |
|
{ |
1346 |
|
// All watchers: |
1347 |
|
// |
1348 |
|
// for (int i = 0; i < watches.size(); i++) |
1349 |
2 |
watches.cleanAll(); |
1350 |
288 |
for (int v = 0; v < nVars(); v++) |
1351 |
858 |
for (int s = 0; s < 2; s++){ |
1352 |
572 |
Lit p = mkLit(v, s); |
1353 |
|
// printf(" >>> RELOCING: %s%d\n", sign(p)?"-":"", var(p)+1); |
1354 |
572 |
vec<Watcher>& ws = watches[p]; |
1355 |
572 |
for (int j = 0; j < ws.size(); j++) ca.reloc(ws[j].cref, to); |
1356 |
|
} |
1357 |
|
|
1358 |
|
// All reasons: |
1359 |
|
// |
1360 |
46 |
for (int i = 0; i < trail.size(); i++){ |
1361 |
44 |
Var v = var(trail[i]); |
1362 |
|
|
1363 |
44 |
if (reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))) |
1364 |
|
ca.reloc(vardata[v].reason, to); |
1365 |
|
} |
1366 |
|
|
1367 |
|
// All learnt: |
1368 |
|
// |
1369 |
2 |
for (int i = 0; i < learnts.size(); i++) ca.reloc(learnts[i], to); |
1370 |
|
|
1371 |
|
// All original: |
1372 |
|
// |
1373 |
2 |
for (int i = 0; i < clauses.size(); i++) ca.reloc(clauses[i], to); |
1374 |
2 |
} |
1375 |
|
|
1376 |
|
|
1377 |
|
void Solver::garbageCollect() |
1378 |
|
{ |
1379 |
|
// Initialize the next region to a size corresponding to the estimated utilization degree. This |
1380 |
|
// is not precise but should avoid some unnecessary reallocations for the new region: |
1381 |
|
ClauseAllocator to(ca.size() - ca.wasted()); |
1382 |
|
Debug("bvminisat") << " BVMinisat::Garbage collection \n"; |
1383 |
|
relocAll(to); |
1384 |
|
if (verbosity >= 2) |
1385 |
|
printf( |
1386 |
|
"| Garbage collection: %12d bytes => %12d bytes |\n", |
1387 |
|
ca.size() * ClauseAllocator::Unit_Size, |
1388 |
|
to.size() * ClauseAllocator::Unit_Size); |
1389 |
|
to.moveTo(ca); |
1390 |
|
} |
1391 |
|
|
1392 |
3368 |
void ClauseAllocator::reloc(CRef& cr, ClauseAllocator& to) |
1393 |
|
{ |
1394 |
3368 |
Clause& c = operator[](cr); |
1395 |
3368 |
if (c.reloced()) { cr = c.relocation(); return; } |
1396 |
|
|
1397 |
548 |
cr = to.alloc(c, c.learnt()); |
1398 |
548 |
c.relocate(cr); |
1399 |
|
|
1400 |
|
// Copy extra data-fields: |
1401 |
|
// (This could be cleaned-up. Generalize Clause-constructor to be applicable |
1402 |
|
// here instead?) |
1403 |
548 |
to[cr].mark(c.mark()); |
1404 |
548 |
if (to[cr].learnt()) to[cr].activity() = c.activity(); |
1405 |
548 |
else if (to[cr].has_extra()) to[cr].calcAbstraction(); |
1406 |
|
} |
1407 |
|
|
1408 |
3 |
void Solver::setNotify(Notify* toNotify) { d_notify = toNotify; } |
1409 |
886 |
bool Solver::withinBudget(Resource r) const |
1410 |
|
{ |
1411 |
886 |
AlwaysAssert(d_notify); |
1412 |
886 |
d_notify->safePoint(r); |
1413 |
|
|
1414 |
1772 |
return !asynch_interrupt && |
1415 |
2658 |
(conflict_budget < 0 || conflicts < conflict_budget) && |
1416 |
886 |
(propagation_budget < 0 || |
1417 |
886 |
propagations < propagation_budget); |
1418 |
|
} |
1419 |
|
|
1420 |
|
} // namespace BVMinisat |
1421 |
22755 |
} // namespace cvc5 |