ABC: A System for Sequential Synthesis and Verification
 
Loading...
Searching...
No Matches
cadical_propagate.cpp
Go to the documentation of this file.
1#include "global.h"
2
3#include "internal.hpp"
4
6
7namespace CaDiCaL {
8
9/*------------------------------------------------------------------------*/
10
11// We are using the address of 'decision_reason' as pseudo reason for
12// decisions to distinguish assignment decisions from other assignments.
13// Before we added chronological backtracking all learned units were
14// assigned at decision level zero ('Solver.level == 0') and we just used a
15// zero pointer as reason. After allowing chronological backtracking units
16// were also assigned at higher decision level (but with assignment level
17// zero), and it was not possible anymore to just distinguish the case
18// 'unit' versus 'decision' by just looking at the current level. Both had
19// a zero pointer as reason. Now only units have a zero reason and
20// decisions need to use the pseudo reason 'decision_reason'.
21
22// External propagation steps use the pseudo reason 'external_reason'.
23// The corresponding actual reason clauses are learned only when they are
24// relevant in conflict analysis or in root-level fixing steps.
25
26static Clause decision_reason_clause;
27static Clause *decision_reason = &decision_reason_clause;
28
29// If chronological backtracking is used the actual assignment level might
30// be lower than the current decision level. In this case the assignment
31// level is defined as the maximum level of the literals in the reason
32// clause except the literal for which the clause is a reason. This
33// function determines this assignment level. For non-chronological
34// backtracking as in classical CDCL this function always returns the
35// current decision level, the concept of assignment level does not make
36// sense, and accordingly this function can be skipped.
37
38// In case of external propagation, it is implicitly assumed that the
39// assignment level is the level of the literal (since the reason clause,
40// i.e., the set of other literals, is unknown).
41
42inline int Internal::assignment_level (int lit, Clause *reason) {
43
45 if (!reason || reason == external_reason)
46 return level;
47
48 int res = 0;
49
50 for (const auto &other : *reason) {
51 if (other == lit)
52 continue;
53 CADICAL_assert (val (other));
54 int tmp = var (other).level;
55 if (tmp > res)
56 res = tmp;
57 }
58
59 return res;
60}
61
62// calculate lrat_chain
63//
65 bool forced) {
66 if (!lrat)
67 return;
68 if (opts.chrono && assignment_level (lit, reason) && !forced)
69 return;
70 else if (!opts.chrono && level && !forced)
71 return; // not decision level 0
72 CADICAL_assert (lrat_chain.empty ());
73 for (auto &reason_lit : *reason) {
74 if (lit == reason_lit)
75 continue;
76 CADICAL_assert (val (reason_lit));
77 if (!val (reason_lit))
78 continue;
79 const int signed_reason_lit = val (reason_lit) * reason_lit;
80 int64_t id = unit_id (signed_reason_lit);
81 lrat_chain.push_back (id);
82 }
83 lrat_chain.push_back (reason->id);
84}
85
86// same code as above but reason is assumed to be conflict and lit is not
87// needed
88//
90 if (!lrat || !lrat_chain.empty ())
91 return;
93 CADICAL_assert (lrat_chain.empty ());
95 LOG (conflict, "lrat for global empty clause with conflict");
96 for (auto &lit : *conflict) {
97 CADICAL_assert (val (lit) < 0);
98 int64_t id = unit_id (-lit);
99 lrat_chain.push_back (id);
100 }
101 lrat_chain.push_back (conflict->id);
102}
103
104/*------------------------------------------------------------------------*/
105
106inline void Internal::search_assign (int lit, Clause *reason) {
107
108 if (level)
110
111 const int idx = vidx (lit);
112 const bool from_external = reason == external_reason;
113 CADICAL_assert (!val (idx));
114 CADICAL_assert (!flags (idx).eliminated () || reason == decision_reason ||
115 reason == external_reason);
116 Var &v = var (idx);
117 int lit_level;
118 CADICAL_assert (!lrat || level || reason == external_reason ||
119 reason == decision_reason || !lrat_chain.empty ());
120 // The following cases are explained in the two comments above before
121 // 'decision_reason' and 'assignment_level'.
122 //
123 // External decision reason means that the propagation was done by
124 // an external propagation and the reason clause not known (yet).
125 // In that case it is assumed that the propagation is NOT out of
126 // order (i.e. lit_level = level), because due to lazy explanation,
127 // we can not calculate the real assignment level.
128 // The function assignment_level () will also assign the current level
129 // to literals with external reason.
130 if (!reason)
131 lit_level = 0; // unit
132 else if (reason == decision_reason)
133 lit_level = level, reason = 0;
134 else if (opts.chrono)
135 lit_level = assignment_level (lit, reason);
136 else
137 lit_level = level;
138 if (!lit_level)
139 reason = 0;
140
141 v.level = lit_level;
142 v.trail = trail.size ();
143 v.reason = reason;
145 CADICAL_assert (num_assigned == trail.size ());
146 num_assigned++;
147 if (!lit_level && !from_external)
148 learn_unit_clause (lit); // increases 'stats.fixed'
149 CADICAL_assert (lit_level || !from_external);
150 const signed char tmp = sign (lit);
151 set_val (idx, tmp);
152 CADICAL_assert (val (lit) > 0); // Just a bit paranoid but useful.
153 CADICAL_assert (val (-lit) < 0); // Ditto.
155 phases.saved[idx] = tmp; // phase saving during search
156 trail.push_back (lit);
157#ifdef LOGGING
158 if (!lit_level)
159 LOG ("root-level unit assign %d @ 0", lit);
160 else
161 LOG (reason, "search assign %d @ %d", lit, lit_level);
162#endif
163
164 if (watching ()) {
165 const Watches &ws = watches (-lit);
166 if (!ws.empty ()) {
167 const Watch &w = ws[0];
168#ifndef WIN32
169 __builtin_prefetch (&w, 0, 1);
170#endif
171 }
172 }
173 lrat_chain.clear ();
174}
175
176/*------------------------------------------------------------------------*/
177
178// External versions of 'search_assign' which are not inlined. They either
179// are used to assign unit clauses on the root-level, in 'decide' to assign
180// a decision or in 'analyze' to assign the literal 'driven' by a learned
181// clause. This happens far less frequently than the 'search_assign' above,
182// which is called directly in 'propagate' below and thus is inlined.
183
186 search_assign (lit, 0);
187}
188
189// Just assume the given literal as decision (increase decision level and
190// assign it). This is used below in 'decide'.
191
194 CADICAL_assert (propagated == trail.size ());
197 LOG ("search decide %d", lit);
198 search_assign (lit, decision_reason);
199}
200
206
212
213/*------------------------------------------------------------------------*/
214
215// The 'propagate' function is usually the hot-spot of a CDCL SAT solver.
216// The 'trail' stack saves assigned variables and is used here as BFS queue
217// for checking clauses with the negation of assigned variables for being in
218// conflict or whether they produce additional assignments.
219
220// This version of 'propagate' uses lazy watches and keeps two watched
221// literals at the beginning of the clause. We also use 'blocking literals'
222// to reduce the number of times clauses have to be visited (2008 JSAT paper
223// by Chu, Harwood and Stuckey). The watches know if a watched clause is
224// binary, in which case it never has to be visited. If a binary clause is
225// falsified we continue propagating.
226
227// Finally, for long clauses we save the position of the last watch
228// replacement in 'pos', which in turn reduces certain quadratic accumulated
229// propagation costs (2013 JAIR article by Ian Gent) at the expense of four
230// more bytes for each clause.
231
233
234 if (level)
237 LOG ("starting propagate");
239
240 // Updating statistics counter in the propagation loops is costly so we
241 // delay until propagation ran to completion.
242 //
243 int64_t before = propagated;
244 int64_t ticks = 0;
245
246 while (!conflict && propagated != trail.size ()) {
247
248 const int lit = -trail[propagated++];
249 LOG ("propagating %d", -lit);
250 Watches &ws = watches (lit);
251
252 const const_watch_iterator eow = ws.end ();
253 watch_iterator j = ws.begin ();
255 ticks += 1 + cache_lines (ws.size (), sizeof *i);
256
257 while (i != eow) {
258
259 const Watch w = *j++ = *i++;
260 const signed char b = val (w.blit);
261 LOG (w.clause, "checking");
262
263 if (b > 0)
264 continue; // blocking literal satisfied
265
266 if (w.binary ()) {
267
268 // CADICAL_assert (w.clause->redundant || !w.clause->garbage);
269
270 // In principle we can ignore garbage binary clauses too, but that
271 // would require to dereference the clause pointer all the time with
272 //
273 // if (w.clause->garbage) { j--; continue; } // (*)
274 //
275 // This is too costly. It is however necessary to produce correct
276 // proof traces if binary clauses are traced to be deleted ('d ...'
277 // line) immediately as soon they are marked as garbage. Actually
278 // finding instances where this happens is pretty difficult (six
279 // parallel fuzzing jobs in parallel took an hour), but it does
280 // occur. Our strategy to avoid generating incorrect proofs now is
281 // to delay tracing the deletion of binary clauses marked as garbage
282 // until they are really deleted from memory. For large clauses
283 // this is not necessary since we have to access the clause anyhow.
284 //
285 // Thanks go to Mathias Fleury, who wanted me to explain why the
286 // line '(*)' above was in the code. Removing it actually really
287 // improved running times and thus I tried to find concrete
288 // instances where this happens (which I found), and then
289 // implemented the described fix.
290
291 // Binary clauses are treated separately since they do not require
292 // to access the clause at all (only during conflict analysis, and
293 // there also only to simplify the code).
294
295 if (b < 0)
296 conflict = w.clause; // but continue ...
297 else {
300 // lrat_chain.clear (); done in search_assign
301 ticks++;
302 }
303
304 } else {
305 CADICAL_assert (w.clause->size > 2);
306
307 if (conflict)
308 break; // Stop if there was a binary conflict already.
309
310 // The cache line with the clause data is forced to be loaded here
311 // and thus this first memory access below is the real hot-spot of
312 // the solver. Note, that this check is positive very rarely and
313 // thus branch prediction should be almost perfect here.
314
315 ticks++;
316
317 if (w.clause->garbage) {
318 j--;
319 continue;
320 }
321
323
324 // Simplify code by forcing 'lit' to be the second literal in the
325 // clause. This goes back to MiniSAT. We use a branch-less version
326 // for conditionally swapping the first two literals, since it
327 // turned out to be substantially faster than this one
328 //
329 // if (lits[0] == lit) swap (lits[0], lits[1]);
330 //
331 // which achieves the same effect, but needs a branch.
332 //
333 const int other = lits[0] ^ lits[1] ^ lit;
334 const signed char u = val (other); // value of the other watch
335
336 if (u > 0)
337 j[-1].blit = other; // satisfied, just replace blit
338 else {
339
340 // This follows Ian Gent's (JAIR'13) idea of saving the position
341 // of the last watch replacement. In essence it needs two copies
342 // of the default search for a watch replacement (in essence the
343 // code in the 'if (v < 0) { ... }' block below), one starting at
344 // the saved position until the end of the clause and then if that
345 // one failed to find a replacement another one starting at the
346 // first non-watched literal until the saved position.
347
348 const int size = w.clause->size;
349 const literal_iterator middle = lits + w.clause->pos;
350 const const_literal_iterator end = lits + size;
351 literal_iterator k = middle;
352
353 // Find replacement watch 'r' at position 'k' with value 'v'.
354
355 int r = 0;
356 signed char v = -1;
357
358 while (k != end && (v = val (r = *k)) < 0)
359 k++;
360
361 if (v < 0) { // need second search starting at the head?
362
363 k = lits + 2;
364 CADICAL_assert (w.clause->pos <= size);
365 while (k != middle && (v = val (r = *k)) < 0)
366 k++;
367 }
368
369 w.clause->pos = k - lits; // always save position
370
371 CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ());
372
373 if (v > 0) {
374
375 // Replacement satisfied, so just replace 'blit'.
376
377 j[-1].blit = r;
378
379 } else if (!v) {
380
381 // Found new unassigned replacement literal to be watched.
382
383 LOG (w.clause, "unwatch %d in", lit);
384
385 lits[0] = other;
386 lits[1] = r;
387 *k = lit;
388
389 watch_literal (r, lit, w.clause);
390
391 j--; // Drop this watch from the watch list of 'lit'.
392
393 ticks++;
394
395 } else if (!u) {
396
397 CADICAL_assert (v < 0);
398
399 // The other watch is unassigned ('!u') and all other literals
400 // assigned to false (still 'v < 0'), thus we found a unit.
401 //
402 build_chain_for_units (other, w.clause, 0);
403 search_assign (other, w.clause);
404 // lrat_chain.clear (); done in search_assign
405 ticks++;
406
407 // Similar code is in the implementation of the SAT'18 paper on
408 // chronological backtracking but in our experience, this code
409 // first does not really seem to be necessary for correctness,
410 // and further does not improve running time either.
411 //
412 if (opts.chrono > 1) {
413
414 const int other_level = var (other).level;
415
416 if (other_level > var (lit).level) {
417
418 // The assignment level of the new unit 'other' is larger
419 // than the assignment level of 'lit'. Thus we should find
420 // another literal in the clause at that higher assignment
421 // level and watch that instead of 'lit'.
422
423 CADICAL_assert (size > 2);
424
425 int pos, s = 0;
426
427 for (pos = 2; pos < size; pos++)
428 if (var (s = lits[pos]).level == other_level)
429 break;
430
431 CADICAL_assert (s);
432 CADICAL_assert (pos < size);
433
434 LOG (w.clause, "unwatch %d in", lit);
435 lits[pos] = lit;
436 lits[0] = other;
437 lits[1] = s;
438 watch_literal (s, lit, w.clause);
439
440 j--; // Drop this watch from the watch list of 'lit'.
441 }
442 }
443 } else {
444
445 CADICAL_assert (u < 0);
446 CADICAL_assert (v < 0);
447
448 // The other watch is assigned false ('u < 0') and all other
449 // literals as well (still 'v < 0'), thus we found a conflict.
450
451 conflict = w.clause;
452 break;
453 }
454 }
455 }
456 }
457
458 if (j != i) {
459
460 while (i != eow)
461 *j++ = *i++;
462
463 ws.resize (j - ws.begin ());
464 }
465 }
466
468
469 if (conflict)
470 LOG (conflict, "ignoring lucky conflict");
471
472 } else {
473
474 // Avoid updating stats eagerly in the hot-spot of the solver.
475 //
476 stats.propagations.search += propagated - before;
477 stats.ticks.search[stable] += ticks;
478
479 if (!conflict)
481 else {
482
483 if (stable)
484 stats.stabconflicts++;
485 stats.conflicts++;
486
487 LOG (conflict, "conflict");
488
489 // The trail before the current decision level was conflict free.
490 //
492 }
493 }
494
495 STOP (propagate);
496
497 return !conflict;
498}
499
500/*------------------------------------------------------------------------*/
501
503
505 CADICAL_assert (propagated == trail.size ());
506
507 while (propergated != trail.size ()) {
508
509 const int lit = -trail[propergated++];
510 LOG ("propergating %d", -lit);
511 Watches &ws = watches (lit);
512
513 const const_watch_iterator eow = ws.end ();
514 watch_iterator j = ws.begin ();
516
517 while (i != eow) {
518
519 const Watch w = *j++ = *i++;
520
521 if (w.binary ()) {
522 CADICAL_assert (val (w.blit) > 0);
523 continue;
524 }
525 if (w.clause->garbage) {
526 j--;
527 continue;
528 }
529
531
532 const int other = lits[0] ^ lits[1] ^ lit;
533 const signed char u = val (other);
534
535 // TODO: check if u == 0 can happen.
536 if (u > 0)
537 continue;
538 CADICAL_assert (u < 0);
539
540 const int size = w.clause->size;
541 const literal_iterator middle = lits + w.clause->pos;
542 const const_literal_iterator end = lits + size;
543 literal_iterator k = middle;
544
545 int r = 0;
546 signed char v = -1;
547
548 while (k != end && (v = val (r = *k)) < 0)
549 k++;
550
551 if (v < 0) {
552 k = lits + 2;
553 CADICAL_assert (w.clause->pos <= size);
554 while (k != middle && (v = val (r = *k)) < 0)
555 k++;
556 }
557
558 CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ());
559 w.clause->pos = k - lits;
560
561 CADICAL_assert (v > 0);
562
563 LOG (w.clause, "unwatch %d in", lit);
564
565 lits[0] = other;
566 lits[1] = r;
567 *k = lit;
568
569 watch_literal (r, lit, w.clause);
570
571 j--;
572 }
573
574 if (j != i) {
575
576 while (i != eow)
577 *j++ = *i++;
578
579 ws.resize (j - ws.begin ());
580 }
581 }
582}
583
584} // namespace CaDiCaL
585
#define ABC_NAMESPACE_IMPL_START
#define ABC_NAMESPACE_IMPL_END
#define CADICAL_assert(ignore)
Definition global.h:14
#define LOG(...)
bool pos
Definition globals.c:30
int sign(int lit)
Definition util.hpp:22
Watches::const_iterator const_watch_iterator
Definition watch.hpp:48
int * literal_iterator
Definition clause.hpp:17
const int * const_literal_iterator
Definition clause.hpp:18
vector< Watch > Watches
Definition watch.hpp:45
Watches::iterator watch_iterator
Definition watch.hpp:47
#define STOP(P)
Definition profile.hpp:158
#define START(P)
Definition profile.hpp:150
int lit
Definition satVec.h:130
literal_iterator begin()
Definition clause.hpp:131
void watch_literal(int lit, int blit, Clause *c)
Definition internal.hpp:623
int64_t cache_lines(size_t bytes)
Definition internal.hpp:722
void new_trail_level(int lit)
Var & var(int lit)
Definition internal.hpp:452
vector< int64_t > lrat_chain
Definition internal.hpp:210
bool searching_lucky_phases
Definition internal.hpp:189
int vidx(int lit) const
Definition internal.hpp:395
Flags & flags(int lit)
Definition internal.hpp:454
vector< int > trail
Definition internal.hpp:259
const Sange lits
Definition internal.hpp:325
void search_assume_decision(int decision)
bool watching() const
Definition internal.hpp:462
signed char val(int lit) const
int64_t unit_id(int lit) const
Definition internal.hpp:434
void set_val(int lit, signed char val)
size_t no_conflict_until
Definition internal.hpp:258
int assignment_level(int lit, Clause *)
void search_assign_external(int lit)
void require_mode(Mode m) const
Definition internal.hpp:173
Clause * external_reason
Definition internal.hpp:245
vector< Level > control
Definition internal.hpp:282
void learn_unit_clause(int lit)
void search_assign_driving(int lit, Clause *reason)
void search_assign(int lit, Clause *)
void build_chain_for_units(int lit, Clause *reason, bool forced)
int level
Definition var.hpp:19
int trail
Definition var.hpp:20
Clause * reason
Definition var.hpp:21
bool binary() const
Definition watch.hpp:42
Clause * clause
Definition watch.hpp:35
unsigned size
Definition vector.h:35
vector watches
Definition watch.h:49