ABC: A System for Sequential Synthesis and Verification
 
Loading...
Searching...
No Matches
cadical_elim.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// Implements a variant of bounded variable elimination as originally
12// described in our SAT'05 paper introducing 'SATeLite'. This is an
13// inprocessing version, i.e., it is interleaved with search and triggers
14// subsumption and strengthening, blocked and covered clause elimination
15// during elimination rounds. It focuses only those variables which
16// occurred in removed irredundant clauses since the last time an
17// elimination round was run. By bounding the maximum resolvent size we can
18// run each elimination round until completion. See the code of 'elim' for
19// how elimination rounds are interleaved with blocked clause elimination
20// and subsumption (which in turn also calls vivification and transitive
21// reduction of the binary implication graph).
22
23/*------------------------------------------------------------------------*/
24
25inline double Internal::compute_elim_score (unsigned lit) {
26 CADICAL_assert (1 <= lit), CADICAL_assert (lit <= (unsigned) max_var);
27 const unsigned uidx = 2 * lit;
28 const double pos = internal->ntab[uidx];
29 const double neg = internal->ntab[uidx + 1];
30 if (!pos)
31 return -neg;
32 if (!neg)
33 return -pos;
34 double sum = 0, prod = 0;
35 if (opts.elimsum)
36 sum = opts.elimsum * (pos + neg);
37 if (opts.elimprod)
38 prod = opts.elimprod * (pos * neg);
39 return prod + sum;
40}
41
42/*------------------------------------------------------------------------*/
43
44bool elim_more::operator() (unsigned a, unsigned b) {
45 const auto s = internal->compute_elim_score (a);
46 const auto t = internal->compute_elim_score (b);
47 if (s > t)
48 return true;
49 if (s < t)
50 return false;
51 return a > b;
52}
53
54/*------------------------------------------------------------------------*/
55
56// Note that the new fast subsumption algorithm implemented in 'subsume'
57// does not distinguish between irredundant and redundant clauses and is
58// also run during search to strengthen and remove 'sticky' redundant
59// clauses but also irredundant ones. So beside learned units during search
60// or as consequence of other preprocessors, these subsumption rounds during
61// search can remove (irredundant) clauses (and literals), which in turn
62// might make new bounded variable elimination possible. This is tested
63// in the 'bool ineliminating ()' guard.
64
66
67 if (!opts.elim)
68 return false;
69 if (!preprocessing && !opts.inprocessing)
70 return false;
71 if (preprocessing)
72 CADICAL_assert (lim.preprocessing);
73
74 // Respect (increasing) conflict limit.
75 //
76 if (lim.elim >= stats.conflicts)
77 return false;
78
79 // Wait until there are new units or new removed variables
80 // (in removed or shrunken irredundant clauses and thus marked).
81 //
82 if (last.elim.fixed < stats.all.fixed)
83 return true;
84 if (last.elim.marked < stats.mark.elim)
85 return true;
86
87 // VERBOSE (3, "elim not scheduled due to fixpoint");
88 return false;
89}
90
91/*------------------------------------------------------------------------*/
92
93// Update the global elimination schedule after adding or removing a clause.
94
96 Clause *c) {
98 ElimSchedule &schedule = eliminator.schedule;
99 for (const auto &lit : *c) {
100 if (!active (lit))
101 continue;
102 occs (lit).push_back (c);
103 if (frozen (lit))
104 continue;
105 noccs (lit)++;
106 const int idx = abs (lit);
107 if (schedule.contains (idx))
108 schedule.update (idx);
109 }
110}
111
113 if (!active (lit))
114 return;
115 if (frozen (lit))
116 return;
117 int64_t &score = noccs (lit);
118 CADICAL_assert (score > 0);
119 score--;
120 const int idx = abs (lit);
121 ElimSchedule &schedule = eliminator.schedule;
122 if (schedule.contains (idx))
123 schedule.update (idx);
124 else {
125 LOG ("rescheduling %d for elimination after removing clause", idx);
126 schedule.push_back (idx);
127 }
128}
129
131 Clause *c, int except) {
133 for (const auto &lit : *c) {
134 if (lit == except)
135 continue;
136 CADICAL_assert (lit != -except);
137 elim_update_removed_lit (eliminator, lit);
138 }
139}
140
141/*------------------------------------------------------------------------*/
142
143// Since we do not have watches we have to do our own unit propagation
144// during elimination as soon we find a unit clause. This finds new units
145// and also marks clauses satisfied by those units as garbage immediately.
146
147void Internal::elim_propagate (Eliminator &eliminator, int root) {
148 CADICAL_assert (val (root) > 0);
149 vector<int> work;
150 size_t i = 0;
151 work.push_back (root);
152 while (i < work.size ()) {
153 int lit = work[i++];
154 LOG ("elimination propagation of %d", lit);
155 CADICAL_assert (val (lit) > 0);
156 const Occs &ns = occs (-lit);
157 for (const auto &c : ns) {
158 if (c->garbage)
159 continue;
160 int unit = 0, satisfied = 0;
161 for (const auto &other : *c) {
162 const signed char tmp = val (other);
163 if (tmp < 0)
164 continue;
165 if (tmp > 0) {
166 satisfied = other;
167 break;
168 }
169 if (unit)
170 unit = INT_MIN;
171 else
172 unit = other;
173 }
174 if (satisfied) {
175 LOG (c, "elimination propagation of %d finds %d satisfied", lit,
176 satisfied);
177 elim_update_removed_clause (eliminator, c, satisfied);
178 mark_garbage (c);
179 } else if (!unit) {
180 LOG ("empty clause during elimination propagation of %d", lit);
181 // need to set conflict = c for lrat
182 conflict = c;
184 conflict = 0;
185 break;
186 } else if (unit != INT_MIN) {
187 LOG ("new unit %d during elimination propagation of %d", unit, lit);
188 build_chain_for_units (unit, c, 0);
189 assign_unit (unit);
190 work.push_back (unit);
191 }
192 }
193 if (unsat)
194 break;
195 const Occs &ps = occs (lit);
196 for (const auto &c : ps) {
197 if (c->garbage)
198 continue;
199 LOG (c, "elimination propagation of %d produces satisfied", lit);
200 elim_update_removed_clause (eliminator, c, lit);
201 mark_garbage (c);
202 }
203 }
204}
205
206/*------------------------------------------------------------------------*/
207
208// On-the-fly self-subsuming resolution during variable elimination is due
209// to HyoJung Han, Fabio Somenzi, SAT'09. Basically while resolving two
210// clauses we test the resolvent to be smaller than one of the antecedents.
211// If this is the case the pivot can be removed from the antecedent
212// on-the-fly and the resolution can be skipped during elimination.
213
215 Clause *c, int pivot) {
216 LOG (c, "pivot %d on-the-fly self-subsuming resolution", pivot);
217 stats.elimotfstr++;
218 stats.strengthened++;
219 CADICAL_assert (clause.empty ());
220 for (const auto &lit : *c) {
221 if (lit == pivot)
222 continue;
223 const signed char tmp = val (lit);
224 CADICAL_assert (tmp <= 0);
225 if (tmp < 0)
226 continue;
227 clause.push_back (lit);
228 }
230 elim_update_added_clause (eliminator, r);
231 clause.clear ();
232 lrat_chain.clear ();
233 elim_update_removed_clause (eliminator, c, pivot);
234 mark_garbage (c);
235}
236
237/*------------------------------------------------------------------------*/
238
239// Resolve two clauses on the pivot literal 'pivot', which is assumed to
240// occur in opposite phases in 'c' and 'd'. The actual resolvent is stored
241// in the temporary global 'clause' if it is not redundant. It is
242// considered redundant if one of the clauses is already marked as garbage
243// it is root level satisfied, the resolvent is empty, a unit, or produces a
244// self-subsuming resolution, which results in the pivot to be removed from
245// at least one of the antecedents.
246
247// Note that current root level assignments are taken into account, i.e., by
248// removing root level falsified literals. The function returns true if the
249// resolvent is not redundant and for instance has to be taken into account
250// during bounded variable elimination.
251
252// Detected units are immediately assigned and in case the last argument is
253// true also propagated eagerly in a elimination specific propagation
254// routine, which not only finds units but also updates the schedule.
255
256// When this function is called during computation of the number of
257// non-trivial (or non-satisfied) resolvents we can eagerly propagate units.
258// But during actually adding the resolvents this results in problems as we
259// found one rare test case '../test/trace/reg0056.trace' (out of billions),
260// where the pivot itself was assigned during such a propagation while
261// adding resolvents and lead to pushing a clause to the reconstruction
262// stack that later flipped the value of the pivot (while all other literals
263// in that clause were unit implied too). Not pushing the pivot clauses to
264// the reconstruction stack produced a wrong model too. Our fix is to only
265// eagerly propagate during computation of the number of resolvents and
266// otherwise delay propagation until the end of elimination (which is less
267// precise regarding scheduling but very rarely happens).
268
270 int pivot, Clause *d,
271 const bool propagate_eagerly) {
272
275
276 stats.elimres++;
277
278 if (c->garbage || d->garbage)
279 return false;
280 if (c->size > d->size) {
281 pivot = -pivot;
282 swap (c, d);
283 }
284
286 CADICAL_assert (clause.empty ());
287
288 int satisfied = 0; // Contains this satisfying literal.
289 int tautological = 0; // Clashing literal if tautological.
290
291 int s = 0; // Actual literals from 'c'.
292 int t = 0; // Actual literals from 'd'.
293
294 // First determine whether the first antecedent is satisfied, add its
295 // literals to 'clause' and mark them (except for 'pivot').
296 //
297 for (const auto &lit : *c) {
298 if (lit == pivot) {
299 s++;
300 continue;
301 }
302 CADICAL_assert (lit != -pivot);
303 const signed char tmp = val (lit);
304 if (tmp > 0) {
305 satisfied = lit;
306 break;
307 } else if (tmp < 0) {
308 if (!lrat)
309 continue;
310 Flags &f = flags (lit);
311 if (f.seen)
312 continue;
313 analyzed.push_back (lit);
314 f.seen = true;
315 int64_t id = unit_id (-lit);
316 lrat_chain.push_back (id);
317 continue;
318 } else
319 mark (lit), clause.push_back (lit), s++;
320 }
321 if (satisfied) {
322 LOG (c, "satisfied by %d antecedent", satisfied);
323 elim_update_removed_clause (eliminator, c, satisfied);
324 mark_garbage (c);
325 clause.clear ();
326 lrat_chain.clear ();
328 unmark (c);
329 return false;
330 }
331
332 // Then determine whether the second antecedent is satisfied, add its
333 // literal to 'clause' and check whether a clashing literal is found, such
334 // that the resolvent would be tautological.
335 //
336 for (const auto &lit : *d) {
337 if (lit == -pivot) {
338 t++;
339 continue;
340 }
341 CADICAL_assert (lit != pivot);
342 signed char tmp = val (lit);
343 if (tmp > 0) {
344 satisfied = lit;
345 break;
346 } else if (tmp < 0) {
347 if (!lrat)
348 continue;
349 Flags &f = flags (lit);
350 if (f.seen)
351 continue;
352 analyzed.push_back (lit);
353 f.seen = true;
354 int64_t id = unit_id (-lit);
355 lrat_chain.push_back (id);
356 continue;
357 } else if ((tmp = marked (lit)) < 0) {
358 tautological = lit;
359 break;
360 } else if (!tmp)
361 clause.push_back (lit), t++;
362 else
363 CADICAL_assert (tmp > 0), t++;
364 }
365
367 unmark (c);
368 const int64_t size = clause.size ();
369
370 if (lrat) {
371 lrat_chain.push_back (d->id);
372 lrat_chain.push_back (c->id);
373 }
374
375 if (satisfied) {
376 LOG (d, "satisfied by %d antecedent", satisfied);
377 elim_update_removed_clause (eliminator, d, satisfied);
378 mark_garbage (d);
379 clause.clear ();
380 lrat_chain.clear ();
381 return false;
382 }
383
384 LOG (c, "first antecedent");
385 LOG (d, "second antecedent");
386
387 if (tautological) {
388 clause.clear ();
389 LOG ("resolvent tautological on %d", tautological);
390 lrat_chain.clear ();
391 return false;
392 }
393
394 if (!size) {
395 clause.clear ();
396 LOG ("empty resolvent");
397 learn_empty_clause (); // already clears lrat_chain.
398 return false;
399 }
400
401 if (size == 1) {
402 int unit = clause[0];
403 LOG ("unit resolvent %d", unit);
404 clause.clear ();
405 assign_unit (unit); // already clears lrat_chain.
406 if (propagate_eagerly)
407 elim_propagate (eliminator, unit);
408 return false;
409 }
410
411 LOG (clause, "resolvent");
412 CADICAL_assert (!lrat || !lrat_chain.empty ());
413
414 // Double self-subsuming resolution. The clauses 'c' and 'd' are
415 // identical except for the pivot which occurs in different phase. The
416 // resolvent subsumes both antecedents.
417
418 if (s > size && t > size) {
419 CADICAL_assert (s == size + 1);
420 CADICAL_assert (t == size + 1);
421 clause.clear ();
422 // LRAT is c + d (+ eventual units)
423 elim_on_the_fly_self_subsumption (eliminator, c, pivot);
424 LOG (d, "double pivot %d on-the-fly self-subsuming resolution", -pivot);
425 stats.elimotfsub++;
426 stats.subsumed++;
427 elim_update_removed_clause (eliminator, d, -pivot);
428 mark_garbage (d);
429 return false;
430 }
431
432 // Single self-subsuming resolution: The pivot can be removed from 'c',
433 // which is implemented by adding a clause which is the same as 'c' but
434 // with 'pivot' removed and then marking 'c' as garbage.
435
436 if (s > size) {
437 CADICAL_assert (s == size + 1);
438 clause.clear ();
439 // LRAT is c + d (+ eventual units)
440 elim_on_the_fly_self_subsumption (eliminator, c, pivot);
441 return false;
442 }
443
444 // Same single self-subsuming resolution situation, but only for 'd'.
445
446 if (t > size) {
447 CADICAL_assert (t == size + 1);
448 clause.clear ();
449 // LRAT is c + d (+ eventual units) -> same.
450 elim_on_the_fly_self_subsumption (eliminator, d, -pivot);
451 return false;
452 }
453 if (propagate_eagerly)
454 lrat_chain.clear ();
455 return true;
456}
457
458/*------------------------------------------------------------------------*/
459
460// Check whether the number of non-tautological resolvents on 'pivot' is
461// smaller or equal to the number of clauses with 'pivot' or '-pivot'. This
462// is the main criteria of bounded variable elimination. As a side effect
463// it flushes garbage clauses with that variable, sorts its occurrence lists
464// (smallest clauses first) and also negates pivot if it has more positive
465// than negative occurrences.
466
468 int pivot) {
469 const bool substitute = !eliminator.gates.empty ();
470 const bool resolve_gates = eliminator.definition_unit;
471 if (substitute)
472 LOG ("trying to substitute %d", pivot);
473
474 stats.elimtried++;
475
477 CADICAL_assert (active (pivot));
478
479 const Occs &ps = occs (pivot);
480 const Occs &ns = occs (-pivot);
481 const int64_t pos = ps.size ();
482 const int64_t neg = ns.size ();
483 if (!pos || !neg)
484 return lim.elimbound >= 0;
485 const int64_t bound = pos + neg + lim.elimbound;
486
487 LOG ("checking number resolvents on %d bounded by "
488 "%" PRId64 " = %" PRId64 " + %" PRId64 " + %" PRId64,
489 pivot, bound, pos, neg, lim.elimbound);
490
491 // Try all resolutions between a positive occurrence (outer loop) of
492 // 'pivot' and a negative occurrence of 'pivot' (inner loop) as long the
493 // bound on non-tautological resolvents is not hit and the size of the
494 // generated resolvents does not exceed the resolvent clause size limit.
495
496 int64_t resolvents = 0; // Non-tautological resolvents.
497
498 for (const auto &c : ps) {
499 CADICAL_assert (!c->redundant);
500 if (c->garbage)
501 continue;
502 for (const auto &d : ns) {
503 CADICAL_assert (!d->redundant);
504 if (d->garbage)
505 continue;
506 if (!resolve_gates && substitute && c->gate == d->gate)
507 continue;
508 stats.elimrestried++;
509 if (resolve_clauses (eliminator, c, pivot, d, true)) {
510 resolvents++;
511 int size = clause.size ();
512 clause.clear ();
513 LOG ("now at least %" PRId64
514 " non-tautological resolvents on pivot %d",
515 resolvents, pivot);
516 if (size > opts.elimclslim) {
517 LOG ("resolvent size %d too big after %" PRId64
518 " resolvents on %d",
519 size, resolvents, pivot);
520 return false;
521 }
522 if (resolvents > bound) {
523 LOG ("too many non-tautological resolvents on %d", pivot);
524 return false;
525 }
526 } else if (unsat)
527 return false;
528 else if (val (pivot))
529 return false;
530 }
531 }
532
533 LOG ("need %" PRId64 " <= %" PRId64 " non-tautological resolvents",
534 resolvents, bound);
535
536 return true;
537}
538
539/*------------------------------------------------------------------------*/
540// Add all resolvents on 'pivot' and connect them.
541
543 int pivot) {
544
545 const bool substitute = !eliminator.gates.empty ();
546 const bool resolve_gates = eliminator.definition_unit;
547 if (substitute) {
548 LOG ("substituting pivot %d by resolving with %zd gate clauses", pivot,
549 eliminator.gates.size ());
550 stats.elimsubst++;
551 }
552 switch (eliminator.gatetype) {
553 case EQUI:
554 stats.eliminated_equi++;
555 break;
556 case AND:
557 stats.eliminated_and++;
558 break;
559 case ITE:
560 stats.eliminated_ite++;
561 break;
562 case XOR:
563 stats.eliminated_xor++;
564 break;
565 case DEF:
566 stats.eliminated_def++;
567 break;
568 default:
569 CADICAL_assert (eliminator.gatetype == NO);
570 }
571
572 LOG ("adding all resolvents on %d", pivot);
573
574 CADICAL_assert (!val (pivot));
575 CADICAL_assert (!flags (pivot).eliminated ());
576
577 const Occs &ps = occs (pivot);
578 const Occs &ns = occs (-pivot);
579#ifdef LOGGING
580 int64_t resolvents = 0;
581#endif
582 for (auto &c : ps) {
583 if (unsat)
584 break;
585 if (c->garbage)
586 continue;
587 for (auto &d : ns) {
588 if (unsat)
589 break;
590 if (d->garbage)
591 continue;
592 if (!resolve_gates && substitute && c->gate == d->gate)
593 continue;
594 if (!resolve_clauses (eliminator, c, pivot, d, false))
595 continue;
596 CADICAL_assert (!lrat || !lrat_chain.empty ());
598 elim_update_added_clause (eliminator, r);
599 eliminator.enqueue (r);
600 lrat_chain.clear ();
601 clause.clear ();
602#ifdef LOGGING
603 resolvents++;
604#endif
605 }
606 }
607
608 LOG ("added %" PRId64 " resolvents to eliminate %d", resolvents, pivot);
609}
610
611/*------------------------------------------------------------------------*/
612
613// Remove clauses with 'pivot' and '-pivot' by marking them as garbage and
614// push them on the extension stack.
615
617 Eliminator &eliminator, int pivot, bool &deleted_binary_clause) {
619
620 LOG ("marking irredundant clauses with %d as garbage", pivot);
621
622 const int64_t substitute = eliminator.gates.size ();
623 if (substitute)
624 LOG ("pushing %" PRId64 " gate clauses on extension stack", substitute);
625#ifndef CADICAL_NDEBUG
626 int64_t pushed = 0;
627#endif
628 Occs &ps = occs (pivot);
629 for (const auto &c : ps) {
630 if (c->garbage)
631 continue;
632 CADICAL_assert (!c->redundant);
633 if (!substitute || c->gate) {
634 if (proof)
635 proof->weaken_minus (c);
636 if (c->size == 2)
637 deleted_binary_clause = true;
638 external->push_clause_on_extension_stack (c, pivot);
639#ifndef CADICAL_NDEBUG
640 pushed++;
641#endif
642 }
643 mark_garbage (c);
644 elim_update_removed_clause (eliminator, c, pivot);
645 }
646 erase_occs (ps);
647
648 LOG ("marking irredundant clauses with %d as garbage", -pivot);
649
650 Occs &ns = occs (-pivot);
651 for (const auto &d : ns) {
652 if (d->garbage)
653 continue;
654 CADICAL_assert (!d->redundant);
655 if (!substitute || d->gate) {
656 if (proof)
657 proof->weaken_minus (d);
658 if (d->size == 2)
659 deleted_binary_clause = true;
660 external->push_clause_on_extension_stack (d, -pivot);
661#ifndef CADICAL_NDEBUG
662 pushed++;
663#endif
664 }
665 mark_garbage (d);
666 elim_update_removed_clause (eliminator, d, -pivot);
667 }
668 erase_occs (ns);
669
670 if (substitute)
671 CADICAL_assert (pushed <= substitute);
672
673 // Unfortunately, we can not use the trick by Niklas Soerensson anymore,
674 // which avoids saving all clauses on the extension stack. This would
675 // break our new incremental 'restore' logic.
676}
677
678/*------------------------------------------------------------------------*/
679
680// Try to eliminate 'pivot' by bounded variable elimination.
682 bool &deleted_binary_clause) {
683
684 if (!active (pivot))
685 return;
686 CADICAL_assert (!frozen (pivot));
687
688 // First flush garbage clauses.
689 //
690 int64_t pos = flush_occs (pivot);
691 int64_t neg = flush_occs (-pivot);
692
693 if (pos > neg) {
694 pivot = -pivot;
695 swap (pos, neg);
696 }
697 LOG ("pivot %d occurs positively %" PRId64
698 " times and negatively %" PRId64 " times",
699 pivot, pos, neg);
700 CADICAL_assert (!eliminator.schedule.contains (abs (pivot)));
701 CADICAL_assert (pos <= neg);
702
703 if (pos && neg > opts.elimocclim) {
704 LOG ("too many occurrences thus not eliminated %d", pivot);
705 CADICAL_assert (!eliminator.schedule.contains (abs (pivot)));
706 return;
707 }
708
709 LOG ("trying to eliminate %d", pivot);
710 CADICAL_assert (!flags (pivot).eliminated ());
711
712 // Sort occurrence lists, such that shorter clauses come first.
713 Occs &ps = occs (pivot);
714 stable_sort (ps.begin (), ps.end (), clause_smaller_size ());
715 Occs &ns = occs (-pivot);
716 stable_sort (ns.begin (), ns.end (), clause_smaller_size ());
717
718 if (pos)
719 find_gate_clauses (eliminator, pivot);
720
721 if (!unsat && !val (pivot)) {
722 if (elim_resolvents_are_bounded (eliminator, pivot)) {
723 LOG ("number of resolvents on %d are bounded", pivot);
724 elim_add_resolvents (eliminator, pivot);
725 if (!unsat)
726 mark_eliminated_clauses_as_garbage (eliminator, pivot,
727 deleted_binary_clause);
728 if (active (pivot))
729 mark_eliminated (pivot);
730 } else {
731 LOG ("too many resolvents on %d so not eliminated", pivot);
732 }
733 }
734
735 unmark_gate_clauses (eliminator);
736 elim_backward_clauses (eliminator);
737}
738
739/*------------------------------------------------------------------------*/
740
743 for (const auto &c : clauses) {
744 if (c->garbage || !c->redundant)
745 continue;
746 bool clean = true;
747 for (const auto &lit : *c) {
748 Flags &f = flags (lit);
749 if (f.eliminated ()) {
750 clean = false;
751 break;
752 }
753 if (f.pure ()) {
754 clean = false;
755 break;
756 }
757 }
758 if (!clean)
759 mark_garbage (c);
760 }
761}
762
763/*------------------------------------------------------------------------*/
764
765// This function performs one round of bounded variable elimination and
766// returns the number of eliminated variables. The additional result
767// 'completed' is true if this elimination round ran to completion (all
768// variables have been tried). Otherwise it was asynchronously terminated
769// or the resolution limit was hit.
770
771int Internal::elim_round (bool &completed, bool &deleted_binary_clause) {
772
773 CADICAL_assert (opts.elim);
775
777 stats.elimrounds++;
778
779 int64_t marked_before = last.elim.marked;
780 last.elim.marked = stats.mark.elim;
782
783 int64_t resolution_limit;
784
785 if (opts.elimlimited) {
786 int64_t delta = stats.propagations.search;
787 delta *= 1e-3 * opts.elimeffort;
788 if (delta < opts.elimmineff)
789 delta = opts.elimmineff;
790 if (delta > opts.elimmaxeff)
791 delta = opts.elimmaxeff;
792 delta = max (delta, (int64_t) 2l * active ());
793
794 PHASE ("elim-round", stats.elimrounds,
795 "limit of %" PRId64 " resolutions", delta);
796
797 resolution_limit = stats.elimres + delta;
798 } else {
799 PHASE ("elim-round", stats.elimrounds, "resolutions unlimited");
800 resolution_limit = LONG_MAX;
801 }
802
803 init_noccs ();
804
805 // First compute the number of occurrences of each literal and at the same
806 // time mark satisfied clauses and update 'elim' flags of variables in
807 // clauses with root level assigned literals (both false and true).
808 //
809 for (const auto &c : clauses) {
810 if (c->garbage || c->redundant)
811 continue;
812 bool satisfied = false, falsified = false;
813 for (const auto &lit : *c) {
814 const signed char tmp = val (lit);
815 if (tmp > 0)
816 satisfied = true;
817 else if (tmp < 0)
818 falsified = true;
819 else
821 }
822 if (satisfied)
823 mark_garbage (c); // forces more precise counts
824 else {
825 for (const auto &lit : *c) {
826 if (!active (lit))
827 continue;
828 if (falsified)
829 mark_elim (lit); // simulate unit propagation
830 noccs (lit)++;
831 }
832 }
833 }
834
835 init_occs ();
836
837 Eliminator eliminator (this);
838 ElimSchedule &schedule = eliminator.schedule;
839 CADICAL_assert (schedule.empty ());
840
841 // Now find elimination candidates which occurred in clauses removed since
842 // the last time we ran bounded variable elimination, which in turned
843 // triggered their 'elim' bit to be set.
844 //
845 for (auto idx : vars) {
846 if (!active (idx))
847 continue;
848 if (frozen (idx))
849 continue;
850 if (!flags (idx).elim)
851 continue;
852 LOG ("scheduling %d for elimination initially", idx);
853 schedule.push_back (idx);
854 }
855
856 schedule.shrink ();
857
858#ifndef CADICAL_QUIET
859 int64_t scheduled = schedule.size ();
860#endif
861
862 PHASE ("elim-round", stats.elimrounds,
863 "scheduled %" PRId64 " variables %.0f%% for elimination",
864 scheduled, percent (scheduled, active ()));
865
866 // Connect irredundant clauses.
867 //
868 for (const auto &c : clauses)
869 if (!c->garbage && !c->redundant)
870 for (const auto &lit : *c)
871 if (active (lit))
872 occs (lit).push_back (c);
873
874#ifndef CADICAL_QUIET
875 const int64_t old_resolutions = stats.elimres;
876#endif
877 const int old_eliminated = stats.all.eliminated;
878 const int old_fixed = stats.all.fixed;
879
880 // Limit on garbage literals during variable elimination. If the limit is
881 // hit a garbage collection is performed.
882 //
883 const int64_t garbage_limit = (2 * stats.irrlits / 3) + (1 << 20);
884
885 // Main loops tries to eliminate variables according to the schedule. The
886 // schedule is updated dynamically and variables are potentially
887 // rescheduled to be tried again if they occur in a removed clause.
888 //
889#ifndef CADICAL_QUIET
890 int64_t tried = 0;
891#endif
892 while (!unsat && !terminated_asynchronously () &&
893 stats.elimres <= resolution_limit && !schedule.empty ()) {
894 int idx = schedule.front ();
895 schedule.pop_front ();
896 flags (idx).elim = false;
897 try_to_eliminate_variable (eliminator, idx, deleted_binary_clause);
898#ifndef CADICAL_QUIET
899 tried++;
900#endif
901 if (stats.garbage.literals <= garbage_limit)
902 continue;
905 }
906
907 // If the schedule is empty all variables have been tried (even
908 // rescheduled ones). Otherwise asynchronous termination happened or we
909 // ran into the resolution limit (or derived unsatisfiability).
910 //
911 completed = !schedule.size ();
912
913 if (!completed)
914 last.elim.marked = marked_before;
915
916 PHASE ("elim-round", stats.elimrounds,
917 "tried to eliminate %" PRId64 " variables %.0f%% (%zd remain)",
918 tried, percent (tried, scheduled), schedule.size ());
919
920 schedule.erase ();
921
922 // Collect potential literal clause instantiation pairs, which needs full
923 // occurrence lists and thus we have it here before resetting them.
924 //
925 Instantiator instantiator;
926 if (!unsat && !terminated_asynchronously () && opts.instantiate)
928
929 reset_occs ();
930 reset_noccs ();
931
932 // Mark all redundant clauses with eliminated variables as garbage.
933 //
934 if (!unsat)
936
937 int eliminated = stats.all.eliminated - old_eliminated;
938#ifndef CADICAL_QUIET
939 int64_t resolutions = stats.elimres - old_resolutions;
940 PHASE ("elim-round", stats.elimrounds,
941 "eliminated %d variables %.0f%% in %" PRId64 " resolutions",
942 eliminated, percent (eliminated, scheduled), resolutions);
943#endif
944
945 last.elim.subsumephases = stats.subsumephases;
946 const int units = stats.all.fixed - old_fixed;
947 report ('e', !opts.reportall && !(eliminated + units));
949
950 if (!unsat && !terminated_asynchronously () &&
951 instantiator) // Do we have candidate pairs?
952 instantiate (instantiator);
953
954 return eliminated; // non-zero if successful
955}
956
957/*------------------------------------------------------------------------*/
958
959// Increase elimination bound (additional clauses allowed during variable
960// elimination), which is triggered if elimination with last bound completed
961// (including no new subsumptions). This was pioneered by GlueMiniSAT in
962// the SAT Race 2015 and then picked up Chanseok Oh in his COMinisatPS
963// solver, which in turn is used in the Maple series of SAT solvers.
964// The bound is no increased if the maximum bound is reached.
965
967
968 if (lim.elimbound >= opts.elimboundmax)
969 return;
970
971 if (lim.elimbound < 0)
972 lim.elimbound = 0;
973 else if (!lim.elimbound)
974 lim.elimbound = 1;
975 else
976 lim.elimbound *= 2;
977
978 if (lim.elimbound > opts.elimboundmax)
979 lim.elimbound = opts.elimboundmax;
980
981 PHASE ("elim-phase", stats.elimphases,
982 "new elimination bound %" PRId64 "", lim.elimbound);
983
984 // Now reschedule all active variables for elimination again.
985 //
986#ifdef LOGGING
987 int count = 0;
988#endif
989 for (auto idx : vars) {
990 if (!active (idx))
991 continue;
992 if (flags (idx).elim)
993 continue;
994 mark_elim (idx);
995#ifdef LOGGING
996 count++;
997#endif
998 }
999 LOG ("marked %d variables as elimination candidates", count);
1000
1001 report ('^');
1002}
1003
1005 if (!opts.elimdef)
1006 return;
1009}
1010
1012 if (citten) {
1014 citten = 0;
1015 }
1016}
1017
1018/*------------------------------------------------------------------------*/
1019
1020void Internal::elim (bool update_limits) {
1021
1022 if (unsat)
1023 return;
1024 if (level)
1025 backtrack ();
1026 if (!propagate ()) {
1028 return;
1029 }
1030
1031 stats.elimphases++;
1032 PHASE ("elim-phase", stats.elimphases,
1033 "starting at most %d elimination rounds", opts.elimrounds);
1034
1035 if (external_prop) {
1037 private_steps = true;
1038 }
1039
1040#ifndef CADICAL_QUIET
1041 int old_active_variables = active ();
1042 int old_eliminated = stats.all.eliminated;
1043#endif
1044
1045 // Make sure there was a complete subsumption phase since last
1046 // elimination including vivification etc.
1047 //
1048 if (last.elim.subsumephases == stats.subsumephases)
1049 subsume ();
1050
1051 reset_watches (); // saves lots of memory
1052
1053 init_citten ();
1054
1055 // Alternate one round of bounded variable elimination ('elim_round') and
1056 // subsumption ('subsume_round'), blocked ('block') and covered clause
1057 // elimination ('cover') until nothing changes, or the round limit is hit.
1058 // The loop also aborts early if no variable could be eliminated, the
1059 // empty clause is resolved, it is asynchronously terminated or a
1060 // resolution limit is hit.
1061
1062 // This variable determines whether the whole loop of this bounded
1063 // variable elimination phase ('elim') ran until completion. This
1064 // potentially triggers an incremental increase of the elimination bound.
1065 //
1066 bool phase_complete = false, deleted_binary_clause = false;
1067
1068 int round = 1;
1069#ifndef CADICAL_QUIET
1070 int eliminated = 0;
1071#endif
1072
1073 bool round_complete = false;
1074 while (!unsat && !phase_complete && !terminated_asynchronously ()) {
1075#ifndef CADICAL_QUIET
1076 int eliminated =
1077#endif
1078 elim_round (round_complete, deleted_binary_clause);
1079
1080 if (!round_complete) {
1081 PHASE ("elim-phase", stats.elimphases, "last round %d incomplete %s",
1082 round, eliminated ? "but successful" : "and unsuccessful");
1083 CADICAL_assert (!phase_complete);
1084 break;
1085 }
1086
1087 if (round++ >= opts.elimrounds) {
1088 PHASE ("elim-phase", stats.elimphases, "round limit %d hit (%s)",
1089 round - 1,
1090 eliminated ? "though last round successful"
1091 : "last round unsuccessful anyhow");
1092 CADICAL_assert (!phase_complete);
1093 break;
1094 }
1095
1096 // Prioritize 'subsumption' over blocked and covered clause elimination.
1097
1098 if (subsume_round ())
1099 continue;
1100 if (block ())
1101 continue;
1102 if (cover ())
1103 continue;
1104
1105 // Was not able to generate new variable elimination candidates after
1106 // variable elimination round, neither through subsumption, nor blocked,
1107 // nor covered clause elimination.
1108 //
1109 PHASE ("elim-phase", stats.elimphases,
1110 "no new variable elimination candidates");
1111
1112 CADICAL_assert (round_complete);
1113 phase_complete = true;
1114 }
1115
1116 if (phase_complete) {
1117 stats.elimcompleted++;
1118 PHASE ("elim-phase", stats.elimphases,
1119 "fully completed elimination %" PRId64
1120 " at elimination bound %" PRId64 "",
1121 stats.elimcompleted, lim.elimbound);
1122 } else {
1123 PHASE ("elim-phase", stats.elimphases,
1124 "incomplete elimination %" PRId64
1125 " at elimination bound %" PRId64 "",
1126 stats.elimcompleted + 1, lim.elimbound);
1127 }
1128
1129 reset_citten ();
1130 if (deleted_binary_clause)
1132 init_watches ();
1133 connect_watches ();
1134
1135 if (unsat)
1136 LOG ("elimination derived empty clause");
1137 else if (propagated < trail.size ()) {
1138 LOG ("elimination produced %zd units",
1139 (size_t) (trail.size () - propagated));
1140 if (!propagate ()) {
1141 LOG ("propagating units after elimination results in empty clause");
1143 }
1144 }
1145
1146 // If we ran variable elimination until completion we increase the
1147 // variable elimination bound and reschedule elimination of all variables.
1148 //
1149 if (phase_complete)
1151
1152#ifndef CADICAL_QUIET
1153 eliminated = stats.all.eliminated - old_eliminated;
1154 PHASE ("elim-phase", stats.elimphases, "eliminated %d variables %.2f%%",
1155 eliminated, percent (eliminated, old_active_variables));
1156#endif
1157
1158 if (external_prop) {
1160 private_steps = false;
1161 }
1162
1163 if (!update_limits)
1164 return;
1165
1166 int64_t delta = scale (opts.elimint * (stats.elimphases + 1));
1167 lim.elim = stats.conflicts + delta;
1168
1169 PHASE ("elim-phase", stats.elimphases,
1170 "new limit at %" PRId64 " conflicts after %" PRId64 " conflicts",
1171 lim.elim, delta);
1172
1173 last.elim.fixed = stats.all.fixed;
1174}
1175
1176} // namespace CaDiCaL
1177
#define ABC_NAMESPACE_IMPL_START
#define ABC_NAMESPACE_IMPL_END
#define CADICAL_assert(ignore)
Definition global.h:14
cadical_kitten * cadical_kitten_init(void)
#define LOG(...)
void cadical_kitten_release(cadical_kitten *cadical_kitten)
void erase()
Definition heap.hpp:194
bool empty() const
Definition heap.hpp:135
void shrink()
Definition heap.hpp:199
bool contains(unsigned e) const
Definition heap.hpp:139
size_t size() const
Definition heap.hpp:131
void push_back(unsigned e)
Definition heap.hpp:147
unsigned pop_front()
Definition heap.hpp:167
unsigned front() const
Definition heap.hpp:160
void update(unsigned e)
Definition heap.hpp:182
bool pos
Definition globals.c:30
#define PHASE(...)
Definition message.hpp:52
heap< elim_more > ElimSchedule
Definition elim.hpp:20
void erase_occs(Occs &os)
Definition occs.hpp:21
vector< Clause * > Occs
Definition occs.hpp:18
const char * flags()
@ DEF
Definition elim.hpp:31
@ NO
Definition elim.hpp:31
@ EQUI
Definition elim.hpp:31
@ ITE
Definition elim.hpp:31
@ AND
Definition elim.hpp:31
@ XOR
Definition elim.hpp:31
double percent(double a, double b)
Definition util.hpp:21
#define START_SIMPLIFIER(S, M)
Definition profile.hpp:172
#define STOP_SIMPLIFIER(S, M)
Definition profile.hpp:197
int lit
Definition satVec.h:130
unsigned definition_unit
Definition elim.hpp:49
ElimSchedule schedule
Definition elim.hpp:36
vector< Clause * > gates
Definition elim.hpp:48
GateType gatetype
Definition elim.hpp:52
bool eliminated() const
Definition flags.hpp:71
bool pure() const
Definition flags.hpp:73
void increase_elimination_bound()
void elim_update_removed_lit(Eliminator &, int lit)
External * external
Definition internal.hpp:312
vector< int > analyzed
Definition internal.hpp:267
void elim_update_added_clause(Eliminator &, Clause *)
void mark_garbage(Clause *)
double scale(double v) const
vector< int64_t > lrat_chain
Definition internal.hpp:210
int elim_round(bool &completed, bool &)
cadical_kitten * citten
Definition internal.hpp:277
void report(char type, int verbose_level=0)
void mark_redundant_clauses_with_eliminated_variables_as_garbage()
void unmark(int lit)
Definition internal.hpp:490
Flags & flags(int lit)
Definition internal.hpp:454
vector< int > trail
Definition internal.hpp:259
void find_gate_clauses(Eliminator &, int pivot)
bool terminated_asynchronously(int factor=1)
bool frozen(int lit)
void mark_eliminated(int)
bool resolve_clauses(Eliminator &, Clause *, int pivot, Clause *, bool)
void try_to_eliminate_variable(Eliminator &, int pivot, bool &)
void elim(bool update_limits=true)
signed char val(int lit) const
signed char marked(int lit) const
Definition internal.hpp:478
int64_t unit_id(int lit) const
Definition internal.hpp:434
void collect_instantiation_candidates(Instantiator &)
void unmark_gate_clauses(Eliminator &)
bool active(int lit)
Definition internal.hpp:360
double compute_elim_score(unsigned lit)
void backtrack(int target_level=0)
void elim_update_removed_clause(Eliminator &, Clause *, int except=0)
int64_t & noccs(int lit)
Definition internal.hpp:466
void elim_backward_clauses(Eliminator &)
void mark_elim(int lit)
vector< Clause * > clauses
Definition internal.hpp:283
void mark_eliminated_clauses_as_garbage(Eliminator &, int pivot, bool &)
Clause * new_resolved_irredundant_clause()
const Range vars
Definition internal.hpp:324
double & score(int lit)
Definition internal.hpp:457
Occs & occs(int lit)
Definition internal.hpp:465
Internal * internal
Definition internal.hpp:311
int active() const
Definition internal.hpp:362
void elim_propagate(Eliminator &, int unit)
bool elim_resolvents_are_bounded(Eliminator &, int pivot)
void build_chain_for_units(int lit, Clause *reason, bool forced)
size_t flush_occs(int lit)
void instantiate(Instantiator &)
void elim_on_the_fly_self_subsumption(Eliminator &, Clause *, int)
vector< int > clause
Definition internal.hpp:260
void elim_add_resolvents(Eliminator &, int pivot)
void connect_watches(bool irredundant_only=false)
Internal * internal
Definition elim.hpp:15
bool operator()(unsigned a, unsigned b)
unsigned size
Definition vector.h:35
signed char mark
Definition value.h:8