ABC: A System for Sequential Synthesis and Verification
 
Loading...
Searching...
No Matches
cadical_cover.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// Covered clause elimination (CCE) is described in our short LPAR-10 paper
12// and later in more detail in our JAIR'15 article. Actually implement
13// the asymmetric version which adds asymmetric literals too but still call
14// it 'CCE' in the following (and not 'ACCE'). This implementation provides
15// a simplified and cleaner version of the one implemented before in
16// Lingeling. We still follow quite closely the original description in the
17// literature, which is based on asymmetric literal addition (ALA) and
18// covered literal addition (CLA). Both can be seen as kind of propagation,
19// where the literals in the original and then extended clause are assigned
20// to false, and the literals on the trail (actually we use our own 'added'
21// stack for that) make up the extended clause. The ALA steps can be
22// implemented by simple propagation (copied from 'propagate.cpp') using
23// watches, while the CLA steps need full occurrence lists to determine the
24// resolution candidate clauses. The CCE is successful if a conflict is
25// found during ALA steps or if during a CLA step all resolution candidates
26// of a literal on the trail are satisfied (the extended clause is blocked).
27
28struct Coveror {
29 std::vector<int> added; // acts as trail
30 std::vector<int> extend; // extension stack for witness
31 std::vector<int> covered; // clause literals or added through CLA
32 std::vector<int> intersection; // of literals in resolution candidates
33
34 size_t alas, clas; // actual number of ALAs and CLAs
35
36 struct {
37 size_t added, covered;
38 } next; // propagate next ...
39
40 Coveror () : alas (0), clas (0) {}
41};
42
43/*------------------------------------------------------------------------*/
44
45// Push on the extension stack a clause made up of the given literal, the
46// original clause (initially copied to 'covered') and all the added covered
47// literals so far. The given literal will act as blocking literal for that
48// clause, if CCE is successful. Only in this case, this private extension
49// stack is copied to the actual extension stack of the solver. Note, that
50// even though all 'added' clauses correspond to the extended clause, we
51// only need to save the original and added covered literals.
52
53inline void Internal::cover_push_extension (int lit, Coveror &coveror) {
54 coveror.extend.push_back (0);
55 coveror.extend.push_back (lit); // blocking literal comes first
56 bool found = false;
57 for (const auto &other : coveror.covered)
58 if (lit == other)
59 CADICAL_assert (!found), found = true;
60 else
61 coveror.extend.push_back (other);
62 CADICAL_assert (found);
63 (void) found;
64}
65
66// Successful covered literal addition (CLA) step.
67
68inline void Internal::covered_literal_addition (int lit, Coveror &coveror) {
70 CADICAL_assert (level == 1);
71 cover_push_extension (lit, coveror);
72 for (const auto &other : coveror.intersection) {
73 LOG ("covered literal addition %d", other);
74 CADICAL_assert (!vals[other]), CADICAL_assert (!vals[-other]);
75 set_val (other, -1);
76 coveror.covered.push_back (other);
77 coveror.added.push_back (other);
78 coveror.clas++;
79 }
80 coveror.next.covered = 0;
81}
82
83// Successful asymmetric literal addition (ALA) step.
84
86 Coveror &coveror) {
88 CADICAL_assert (level == 1);
89 LOG ("initial asymmetric literal addition %d", lit);
91 set_val (lit, -1);
92 coveror.added.push_back (lit);
93 coveror.alas++;
94 coveror.next.covered = 0;
95}
96
97/*------------------------------------------------------------------------*/
98
99// In essence copied and adapted from 'propagate' in 'propagate.cpp'. Since
100// this function is also a hot-spot here in 'cover' we specialize it in the
101// same spirit as 'probe_propagate' and 'vivify_propagate'. Please refer to
102// the detailed comments for 'propagate' in 'propagate.cpp' for details.
103
105 Coveror &coveror) {
107 stats.propagations.cover++;
108 CADICAL_assert (val (lit) < 0);
109 bool subsumed = false;
110 LOG ("asymmetric literal propagation of %d", lit);
111 Watches &ws = watches (lit);
112 const const_watch_iterator eow = ws.end ();
113 watch_iterator j = ws.begin ();
115 while (!subsumed && i != eow) {
116 const Watch w = *j++ = *i++;
117 if (w.clause == ignore)
118 continue; // costly but necessary here ...
119 const signed char b = val (w.blit);
120 if (b > 0)
121 continue;
122 if (w.clause->garbage)
123 j--;
124 else if (w.binary ()) {
125 if (b < 0) {
126 LOG (w.clause, "found subsuming");
127 subsumed = true;
128 } else
129 asymmetric_literal_addition (-w.blit, coveror);
130 } else {
132 const int other = lits[0] ^ lits[1] ^ lit;
133 lits[0] = other, lits[1] = lit;
134 const signed char u = val (other);
135 if (u > 0)
136 j[-1].blit = other;
137 else {
138 const int size = w.clause->size;
139 const const_literal_iterator end = lits + size;
140 const literal_iterator middle = lits + w.clause->pos;
141 literal_iterator k = middle;
142 signed char v = -1;
143 int r = 0;
144 while (k != end && (v = val (r = *k)) < 0)
145 k++;
146 if (v < 0) {
147 k = lits + 2;
148 CADICAL_assert (w.clause->pos <= size);
149 while (k != middle && (v = val (r = *k)) < 0)
150 k++;
151 }
152 w.clause->pos = k - lits;
153 CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ());
154 if (v > 0)
155 j[-1].blit = r;
156 else if (!v) {
157 LOG (w.clause, "unwatch %d in", lit);
158 lits[1] = r;
159 *k = lit;
160 watch_literal (r, lit, w.clause);
161 j--;
162 } else if (!u) {
163 CADICAL_assert (v < 0);
164 asymmetric_literal_addition (-other, coveror);
165 } else {
166 CADICAL_assert (u < 0), CADICAL_assert (v < 0);
167 LOG (w.clause, "found subsuming");
168 subsumed = true;
169 break;
170 }
171 }
172 }
173 }
174 if (j != i) {
175 while (i != eow)
176 *j++ = *i++;
177 ws.resize (j - ws.begin ());
178 }
179 return subsumed;
180}
181
182// Covered literal addition (which needs full occurrence lists). The
183// function returns 'true' if the extended clause is blocked on 'lit.'
184
187
188 CADICAL_assert (val (lit) < 0);
189 if (frozen (lit)) {
190 LOG ("no covered propagation on frozen literal %d", lit);
191 return false;
192 }
193
194 stats.propagations.cover++;
195
196 LOG ("covered propagation of %d", lit);
197 CADICAL_assert (coveror.intersection.empty ());
198
199 Occs &os = occs (-lit);
200 const auto end = os.end ();
201 bool first = true;
202
203 // Compute the intersection of the literals in all the clauses with
204 // '-lit'. If all these clauses are double satisfied then we know that
205 // the extended clauses (in 'added') is blocked. All literals in the
206 // intersection can be added as covered literal. As soon the intersection
207 // becomes empty (during traversal of clauses with '-lit') we abort.
208
209 for (auto i = os.begin (); i != end; i++) {
210
211 Clause *c = *i;
212 if (c->garbage)
213 continue;
214
215 // First check whether clause is 'blocked', i.e., is double satisfied.
216
217 bool blocked = false;
218 for (const auto &other : *c) {
219 if (other == -lit)
220 continue;
221 const signed char tmp = val (other);
222 if (tmp < 0)
223 continue;
224 if (tmp > 0) {
225 blocked = true;
226 break;
227 }
228 }
229 if (blocked) { // ... if 'c' is double satisfied.
230 LOG (c, "blocked");
231 continue; // with next clause with '-lit'.
232 }
233
234 if (first) {
235
236 // Copy and mark literals of first clause.
237
238 for (const auto &other : *c) {
239 if (other == -lit)
240 continue;
241 const signed char tmp = val (other);
242 if (tmp < 0)
243 continue;
244 CADICAL_assert (!tmp);
245 coveror.intersection.push_back (other);
246 mark (other);
247 }
248
249 first = false;
250
251 } else {
252
253 // Unmark all literals in current clause.
254
255 for (const auto &other : *c) {
256 if (other == -lit)
257 continue;
258 signed char tmp = val (other);
259 if (tmp < 0)
260 continue;
261 CADICAL_assert (!tmp);
262 tmp = marked (other);
263 if (tmp > 0)
264 unmark (other);
265 }
266
267 // Then remove from intersection all marked literals.
268
269 const auto end = coveror.intersection.end ();
270 auto j = coveror.intersection.begin ();
271 for (auto k = j; k != end; k++) {
272 const int other = *j++ = *k;
273 const int tmp = marked (other);
274 CADICAL_assert (tmp >= 0);
275 if (tmp)
276 j--, unmark (other); // remove marked and unmark it
277 else
278 mark (other); // keep unmarked and mark it
279 }
280 const size_t new_size = j - coveror.intersection.begin ();
281 coveror.intersection.resize (new_size);
282
283 if (!coveror.intersection.empty ())
284 continue;
285
286 // No covered literal addition candidates in the intersection left!
287 // Move this clause triggering early abort to the beginning.
288 // This is a common move to front strategy to minimize effort.
289
290 auto begin = os.begin ();
291 while (i != begin) {
292 auto prev = i - 1;
293 *i = *prev;
294 i = prev;
295 }
296 *begin = c;
297
298 break; // early abort ...
299 }
300 }
301
302 bool res = false;
303 if (first) {
304 LOG ("all resolution candidates with %d blocked", -lit);
305 CADICAL_assert (coveror.intersection.empty ());
306 cover_push_extension (lit, coveror);
307 res = true;
308 } else if (coveror.intersection.empty ()) {
309 LOG ("empty intersection of resolution candidate literals");
310 } else {
311 LOG (coveror.intersection,
312 "non-empty intersection of resolution candidate literals");
313 covered_literal_addition (lit, coveror);
314 unmark (coveror.intersection);
315 coveror.intersection.clear ();
316 coveror.next.covered = 0; // Restart covering.
317 }
318
319 unmark (coveror.intersection);
320 coveror.intersection.clear ();
321
322 return res;
323}
324
325/*------------------------------------------------------------------------*/
326
328
331
332 LOG (c, "trying covered clauses elimination on");
333 bool satisfied = false;
334 for (const auto &lit : *c)
335 if (val (lit) > 0)
336 satisfied = true;
337
338 if (satisfied) {
339 LOG (c, "clause already satisfied");
340 mark_garbage (c);
341 return false;
342 }
343
344 CADICAL_assert (coveror.added.empty ());
345 CADICAL_assert (coveror.extend.empty ());
346 CADICAL_assert (coveror.covered.empty ());
347
349 level = 1;
350 LOG ("assuming literals of candidate clause");
351 for (const auto &lit : *c) {
352 if (val (lit))
353 continue;
355 coveror.covered.push_back (lit);
356 }
357
358 bool tautological = false;
359
360 coveror.next.added = coveror.next.covered = 0;
361
362 while (!tautological) {
363 if (coveror.next.added < coveror.added.size ()) {
364 const int lit = coveror.added[coveror.next.added++];
365 tautological = cover_propagate_asymmetric (lit, c, coveror);
366 } else if (coveror.next.covered < coveror.covered.size ()) {
367 const int lit = coveror.covered[coveror.next.covered++];
368 tautological = cover_propagate_covered (lit, coveror);
369 } else
370 break;
371 }
372
373 if (tautological) {
374 if (coveror.extend.empty ()) {
375 stats.cover.asymmetric++;
376 stats.cover.total++;
377 LOG (c, "asymmetric tautological");
378 } else {
379 stats.cover.blocked++;
380 stats.cover.total++;
381 // Only copy extension stack if successful.
382 int prev = INT_MIN;
383 bool already_pushed = false;
384 int64_t last_id = 0;
385 LOG (c, "covered tautological");
386 CADICAL_assert (clause.empty ());
387 LOG (coveror.extend, "extension = ");
388 for (const auto &other : coveror.extend) {
389 if (!prev) {
390 // are we finishing a clause?
391 if (already_pushed) {
392 // add missing literals that are not needed for covering
393 // but avoid RAT proofs
394 for (auto i = 0, j = 0; i < c->size; ++i, ++j) {
395 const int lit = c->literals[i];
396 if (j >= (int) coveror.covered.size () ||
397 c->literals[i] != coveror.covered[j]) {
398 --j;
399 LOG ("adding lit %d not needed for ATA", lit);
400 clause.push_back (lit);
401 external->push_clause_literal_on_extension_stack (lit);
402 }
403 }
404 }
405 if (proof && already_pushed) {
406 if (lrat)
407 lrat_chain.push_back (c->id);
408 LOG ("LEARNING clause with id %" PRId64, last_id);
409 proof->add_derived_clause (last_id, false, clause, lrat_chain);
410 proof->weaken_plus (last_id, clause);
411 lrat_chain.clear ();
412 }
413 last_id = ++clause_id;
414 external->push_zero_on_extension_stack ();
415 external->push_witness_literal_on_extension_stack (other);
416 external->push_zero_on_extension_stack ();
417 external->push_id_on_extension_stack (last_id);
418 external->push_zero_on_extension_stack ();
419 clause.clear ();
420 already_pushed = true;
421 }
422 if (other) {
423 external->push_clause_literal_on_extension_stack (other);
424 clause.push_back (other);
425 LOG (clause, "current clause is");
426 }
427 prev = other;
428 }
429
430 if (proof) {
431 // add missing literals that are not needed for covering
432 // but avoid RAT proofs
433 for (auto i = 0, j = 0; i < c->size; ++i, ++j) {
434 const int lit = c->literals[i];
435 if (j >= (int) coveror.covered.size () ||
436 c->literals[i] != coveror.covered[j]) {
437 --j;
438 LOG ("adding lit %d not needed for ATA", lit);
439 clause.push_back (lit);
440 external->push_clause_literal_on_extension_stack (lit);
441 }
442 }
443 if (lrat)
444 lrat_chain.push_back (c->id);
445 proof->add_derived_clause (last_id, false, clause, lrat_chain);
446 proof->weaken_plus (last_id, clause);
447 lrat_chain.clear ();
448 }
449 clause.clear ();
450
451 mark_garbage (c);
452 }
453 }
454
455 // Backtrack and 'unassign' all literals.
456
457 CADICAL_assert (level == 1);
458 for (const auto &lit : coveror.added)
459 set_val (lit, 0);
460 level = 0;
461
462 coveror.covered.clear ();
463 coveror.extend.clear ();
464 coveror.added.clear ();
465
466 return tautological;
467}
468
469/*------------------------------------------------------------------------*/
470
471// Not yet tried and larger clauses are tried first.
472
474 bool operator() (const Clause *a, const Clause *b) {
475 if (a->covered && !b->covered)
476 return true;
477 if (!a->covered && b->covered)
478 return false;
479 return a->size < b->size;
480 }
481};
482
484
485 if (unsat)
486 return 0;
487
488 init_watches ();
489 connect_watches (true); // irredundant watches only is enough
490
491 int64_t delta = stats.propagations.search;
492 delta *= 1e-3 * opts.covereffort;
493 if (delta < opts.covermineff)
494 delta = opts.covermineff;
495 if (delta > opts.covermaxeff)
496 delta = opts.covermaxeff;
497 delta = max (delta, ((int64_t) 2) * active ());
498
499 PHASE ("cover", stats.cover.count,
500 "covered clause elimination limit of %" PRId64 " propagations",
501 delta);
502
503 int64_t limit = stats.propagations.cover + delta;
504
505 init_occs ();
506
507 vector<Clause *> schedule;
508 Coveror coveror;
509
510 // First connect all clauses and find all not yet tried clauses.
511 //
512#ifndef CADICAL_QUIET
513 int64_t untried = 0;
514#endif
515 //
516 for (auto c : clauses) {
517 CADICAL_assert (!c->frozen);
518 if (c->garbage)
519 continue;
520 if (c->redundant)
521 continue;
522 bool satisfied = false, allfrozen = true;
523 for (const auto &lit : *c)
524 if (val (lit) > 0) {
525 satisfied = true;
526 break;
527 } else if (allfrozen && !frozen (lit))
528 allfrozen = false;
529 if (satisfied) {
530 mark_garbage (c);
531 continue;
532 }
533 if (allfrozen) {
534 c->frozen = true;
535 continue;
536 }
537 for (const auto &lit : *c)
538 occs (lit).push_back (c);
539 if (c->size < opts.coverminclslim)
540 continue;
541 if (c->size > opts.covermaxclslim)
542 continue;
543 if (c->covered)
544 continue;
545 schedule.push_back (c);
546#ifndef CADICAL_QUIET
547 untried++;
548#endif
549 }
550
551 if (schedule.empty ()) {
552
553 PHASE ("cover", stats.cover.count, "no previously untried clause left");
554
555 for (auto c : clauses) {
556 if (c->garbage)
557 continue;
558 if (c->redundant)
559 continue;
560 if (c->frozen) {
561 c->frozen = false;
562 continue;
563 }
564 if (c->size < opts.coverminclslim)
565 continue;
566 if (c->size > opts.covermaxclslim)
567 continue;
568 CADICAL_assert (c->covered);
569 c->covered = false;
570 schedule.push_back (c);
571 }
572 } else { // Mix of tried and not tried clauses ....
573
574 for (auto c : clauses) {
575 if (c->garbage)
576 continue;
577 if (c->redundant)
578 continue;
579 if (c->frozen) {
580 c->frozen = false;
581 continue;
582 }
583 if (c->size < opts.coverminclslim)
584 continue;
585 if (c->size > opts.covermaxclslim)
586 continue;
587 if (!c->covered)
588 continue;
589 schedule.push_back (c);
590 }
591 }
592
593 stable_sort (schedule.begin (), schedule.end (),
595
596#ifndef CADICAL_QUIET
597 const size_t scheduled = schedule.size ();
598 PHASE ("cover", stats.cover.count,
599 "scheduled %zd clauses %.0f%% with %" PRId64 " untried %.0f%%",
600 scheduled, percent (scheduled, stats.current.irredundant), untried,
601 percent (untried, scheduled));
602#endif
603
604 // Heuristically it should be beneficial to intersect with smaller clauses
605 // first, since then the chances are higher that the intersection of
606 // resolution candidates becomes emptier earlier.
607
608 for (auto lit : lits) {
609 if (!active (lit))
610 continue;
611 Occs &os = occs (lit);
612 stable_sort (os.begin (), os.end (), clause_smaller_size ());
613 }
614
615 // This is the main loop of trying to do CCE of candidate clauses.
616 //
617 int64_t covered = 0;
618 //
619 while (!terminated_asynchronously () && !schedule.empty () &&
620 stats.propagations.cover < limit) {
621 Clause *c = schedule.back ();
622 schedule.pop_back ();
623 c->covered = true;
624 if (cover_clause (c, coveror))
625 covered++;
626 }
627
628#ifndef CADICAL_QUIET
629 const size_t remain = schedule.size ();
630 const size_t tried = scheduled - remain;
631 PHASE ("cover", stats.cover.count,
632 "eliminated %" PRId64 " covered clauses out of %zd tried %.0f%%",
633 covered, tried, percent (covered, tried));
634 if (remain)
635 PHASE ("cover", stats.cover.count,
636 "remaining %zu clauses %.0f%% untried", remain,
637 percent (remain, scheduled));
638 else
639 PHASE ("cover", stats.cover.count, "all scheduled clauses tried");
640#endif
641 reset_occs ();
642 reset_watches ();
643
644 return covered;
645}
646
647/*------------------------------------------------------------------------*/
648
650
651 if (!opts.cover)
652 return false;
653 if (unsat)
654 return false;
656 return false;
657 if (!stats.current.irredundant)
658 return false;
659
660 // TODO: Our current algorithm for producing the necessary clauses on the
661 // reconstruction stack for extending the witness requires a covered
662 // literal addition step which (empirically) conflicts with flushing
663 // during restoring clauses (see 'regr00{48,51}.trace') even though
664 // flushing during restore is disabled by default (as is covered clause
665 // elimination). The consequence of combining these two options
666 // ('opts.cover' and 'opts.restoreflush') can thus produce incorrect
667 // witness reconstruction and thus invalid witnesses. This is quite
668 // infrequent (one out of half billion mobical test cases) but as the two
669 // regression traces show, does happen. Thus we disable the combination.
670 //
671 if (opts.restoreflush)
672 return false;
673
675
676 stats.cover.count++;
677
678 // During variable elimination unit clauses can be generated which need to
679 // be propagated properly over redundant clauses too. Since variable
680 // elimination avoids to have occurrence lists and watches at the same
681 // time this propagation is delayed until the end of variable elimination.
682 // Since we want to interleave CCE with it, we have to propagate here.
683 // Otherwise this triggers inconsistencies.
684 //
685 if (propagated < trail.size ()) {
686 init_watches ();
687 connect_watches (); // need to propagated over all clauses!
688 LOG ("elimination produced %zd units",
689 (size_t) (trail.size () - propagated));
690 if (!propagate ()) {
691 LOG ("propagating units before covered clause elimination "
692 "results in empty clause");
695 }
696 reset_watches ();
697 }
698 CADICAL_assert (unsat || propagated == trail.size ());
699
700 int64_t covered = cover_round ();
701
703 report ('c', !opts.reportall && !covered);
704
705 return covered;
706}
707
708} // namespace CaDiCaL
709
#define ABC_NAMESPACE_IMPL_START
#define ABC_NAMESPACE_IMPL_END
#define CADICAL_assert(ignore)
Definition global.h:14
#define LOG(...)
#define PHASE(...)
Definition message.hpp:52
Watches::const_iterator const_watch_iterator
Definition watch.hpp:48
vector< Clause * > Occs
Definition occs.hpp:18
int * literal_iterator
Definition clause.hpp:17
const int * const_literal_iterator
Definition clause.hpp:18
vector< Watch > Watches
Definition watch.hpp:45
double percent(double a, double b)
Definition util.hpp:21
Watches::iterator watch_iterator
Definition watch.hpp:47
#define START_SIMPLIFIER(S, M)
Definition profile.hpp:172
#define STOP_SIMPLIFIER(S, M)
Definition profile.hpp:197
int lit
Definition satVec.h:130
literal_iterator begin()
Definition clause.hpp:131
std::vector< int > covered
struct CaDiCaL::Coveror::@205155162354335127052365031027306272263210135257 next
std::vector< int > added
std::vector< int > intersection
std::vector< int > extend
bool cover_clause(Clause *c, Coveror &)
void watch_literal(int lit, int blit, Clause *c)
Definition internal.hpp:623
External * external
Definition internal.hpp:312
void covered_literal_addition(int lit, Coveror &)
void mark_garbage(Clause *)
vector< int64_t > lrat_chain
Definition internal.hpp:210
void report(char type, int verbose_level=0)
void unmark(int lit)
Definition internal.hpp:490
vector< int > trail
Definition internal.hpp:259
bool terminated_asynchronously(int factor=1)
const Sange lits
Definition internal.hpp:325
void cover_push_extension(int lit, Coveror &)
bool frozen(int lit)
signed char val(int lit) const
bool limit(const char *name, int)
signed char marked(int lit) const
Definition internal.hpp:478
void set_val(int lit, signed char val)
bool active(int lit)
Definition internal.hpp:360
void asymmetric_literal_addition(int lit, Coveror &)
bool cover_propagate_asymmetric(int lit, Clause *ignore, Coveror &)
bool cover_propagate_covered(int lit, Coveror &)
vector< Clause * > clauses
Definition internal.hpp:283
signed char * vals
Definition internal.hpp:221
void require_mode(Mode m) const
Definition internal.hpp:173
Occs & occs(int lit)
Definition internal.hpp:465
vector< int > clause
Definition internal.hpp:260
void connect_watches(bool irredundant_only=false)
bool binary() const
Definition watch.hpp:42
Clause * clause
Definition watch.hpp:35
bool operator()(const Clause *a, const Clause *b)
unsigned size
Definition vector.h:35
signed char mark
Definition value.h:8
vector watches
Definition watch.h:49