ABC: A System for Sequential Synthesis and Verification
 
Loading...
Searching...
No Matches
cadical_external_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// Mark a variable as an observed one. It can be a new variable. It is
12// assumed to be clean (not eliminated by previous simplifications).
13//
15 int idx = vidx (ilit);
16 if (idx >= (int64_t) relevanttab.size ())
17 relevanttab.resize (1 + (size_t) idx, 0);
18 unsigned &ref = relevanttab[idx];
19 if (ref < UINT_MAX) {
20 ref++;
21 LOG ("variable %d is observed %u times", idx, ref);
22 } else
23 LOG ("variable %d remains observed forever", idx);
24 // TODO: instead of actually backtracking, it would be enough to notify
25 // backtrack and re-play again every levels' notification to the
26 // propagator
27 if (val (ilit) && level && !fixed (ilit)) {
28 // The variable is already assigned, but we can not send a notification
29 // about it because it happened on an earlier decision level.
30 // To not break the stack-like view of the trail, we simply backtrack to
31 // undo this unnotifiable assignment.
32 const int assignment_level = var (ilit).level;
34 } else if (level && fixed (ilit)) {
35 backtrack (0);
36 }
37}
38
39/*----------------------------------------------------------------------------*/
40//
41// Removing an observed variable should happen only once it is ensured
42// that there is no unexplained propagation in the implication
43// graph involving this variable.
44//
46 if (!fixed (ilit) && level) {
47 backtrack ();
48 }
49
50 CADICAL_assert (fixed (ilit) || !level);
51
52 const int idx = vidx (ilit);
53 CADICAL_assert ((size_t) idx < relevanttab.size ());
54 unsigned &ref = relevanttab[idx];
55 CADICAL_assert (fixed (ilit) || ref > 0);
56 if (fixed (ilit))
57 ref = 0;
58 else if (ref < UINT_MAX) {
59 if (!--ref) {
60 LOG ("variable %d is not observed anymore", idx);
61 } else
62 LOG ("variable %d is unobserved once but remains observed %u times",
63 ilit, ref);
64 } else
65 LOG ("variable %d remains observed forever", idx);
66}
67
68/*----------------------------------------------------------------------------*/
69//
70// Supposed to be used only by mobical.
71//
72bool Internal::observed (int ilit) const {
73 CADICAL_assert ((size_t) vidx (ilit) < relevanttab.size ());
74 return relevanttab[vidx (ilit)] > 0;
75}
76
77/*----------------------------------------------------------------------------*/
78//
79// Check for unexplained propagations upon disconnecting external propagator
80//
82 if (!opts.ilb) {
83 return;
84 }
85 for (auto idx : vars) {
86 if (!val (idx))
87 continue;
88 if (var (idx).reason != external_reason)
89 continue;
90 if (!tainted_literal) {
91 tainted_literal = idx;
92 continue;
93 }
95 if (var (idx).level < var (tainted_literal).level) {
96 tainted_literal = idx;
97 }
98 }
99}
100
102 if (!external_prop || external_prop_is_lazy || !trail.size () ||
103 !opts.ilb) {
104 return;
105 }
106 LOG ("notify external propagator about new assignments (after ilb)");
107#ifndef CADICAL_NDEBUG
108 LOG ("(decision level: %d, trail size: %zd, notified %zd)", level,
109 trail.size (), notified);
110#endif
112}
113
115 if (!external_prop || external_prop_is_lazy || !trail.size ()) {
116 return;
117 }
118 LOG ("notify external propagator about new assignments (after local "
119 "search)");
120#ifndef CADICAL_NDEBUG
121 LOG ("(decision level: %d, trail size: %zd, notified %zd)", level,
122 trail.size (), notified);
123#endif
125}
126
127// It repeats ALL assignments of the trail, so the already notified
128// root-level assignments will be notified multiple times.
129
131 const size_t end_of_trail = trail.size ();
132 if (level) {
133 notified = 0; // TODO: save the last notified root-level position
134 // somewhere and use it here
136 }
137 std::vector<int> assigned;
138
139 int prev_max_level = 0;
140 int current_level = 0;
141 int propagator_level = 0;
142
143 while (notified < end_of_trail) {
144 int ilit = trail[notified++];
145 // In theory, 0 ilit can happen due to pseudo-decision levels
146 if (!ilit)
147 current_level = prev_max_level + 1;
148 else
149 current_level = var (ilit).level;
150
151 if (current_level > propagator_level) {
152 if (assigned.size ())
153 external->propagator->notify_assignment (assigned);
154 while (current_level > propagator_level) {
155 external->propagator->notify_new_decision_level ();
156 propagator_level++;
157 }
158 assigned.clear ();
159 }
160 // Current level can be smaller than prev_max_level due to chrono
161 if (current_level > prev_max_level)
162 prev_max_level = current_level;
163
164 if (!observed (ilit))
165 continue;
166
167 int elit = externalize (ilit); // TODO: double-check tainting
168 CADICAL_assert (elit);
169 // Fixed variables might get mapped (during compact) to another
170 // non-observed but fixed variable.
171 // This happens on root level, so notification about their assignment is
172 // already done.
173 CADICAL_assert (external->observed (elit) || fixed (ilit));
174 if (!external->ervars[abs (elit)])
175 assigned.push_back (elit);
176 }
177 if (assigned.size ())
178 external->propagator->notify_assignment (assigned);
179 assigned.clear ();
180
181 // In case there are some left over empty levels on the top of the trail,
182 // the external propagtor must be notified about them so the levels are
183 // synced
184 while (level > propagator_level) {
185 external->propagator->notify_new_decision_level ();
186 propagator_level++;
187 }
188
189 return;
190}
191
192/*----------------------------------------------------------------------------*/
193//
194// Check if the variable is assigned by decision.
195//
196bool Internal::is_decision (int ilit) {
197 if (!level || fixed (ilit) || !val (ilit))
198 return false;
199
200 const int idx = vidx (ilit);
201 Var &v = var (idx);
202#ifndef CADICAL_NDEBUG
203 LOG (v.reason,
204 "is_decision: i%d (current level: %d, is_fixed: %d, v.level: %d, "
205 "is_external_reason: %d, v.reason: )",
206 ilit, level, fixed (ilit), v.level, v.reason == external_reason);
207#endif
208 if (!v.level || v.reason)
209 return false;
211 return true;
212}
213
214void Internal::force_backtrack (size_t new_level) {
216 return;
217
218#ifndef CADICAL_NDEBUG
219 LOG ("external propagator forces backtrack to decision level"
220 "%zd (from level %d)",
221 new_level, level);
222#endif
223 backtrack (new_level);
224}
225
226/*----------------------------------------------------------------------------*/
227//
228// Call external propagator to check if there is a literal to be propagated.
229// The reason of the propagation is not necessarily asked at that point.
230//
231// In case the externally propagated literal is already falsified, the
232// reason is asked and conflict analysis starts. In case the externally
233// propagated literal is already satisfied, nothing happens.
234//
236 if (level)
239
240 size_t before = num_assigned;
241 bool cb_repropagate_needed = true;
242 while (cb_repropagate_needed && !conflict && external_prop &&
244#ifndef CADICAL_NDEBUG
245 LOG ("external propagation starts (decision level: %d, trail size: "
246 "%zd, notified %zd)",
247 level, trail.size (), notified);
248#endif
249 cb_repropagate_needed = false;
250 // external->reset_extended (); //TODO for inprocessing
251
253
254 int elit = external->propagator->cb_propagate ();
255 stats.ext_prop.ext_cb++;
256 stats.ext_prop.eprop_call++;
257 while (elit) {
258 CADICAL_assert (external->is_observed[abs (elit)]);
259 int ilit = external->e2i[abs (elit)];
260 if (elit < 0)
261 ilit = -ilit;
262 int tmp = val (ilit);
263#ifndef CADICAL_NDEBUG
264 CADICAL_assert (fixed (ilit) || observed (ilit));
265 LOG ("External propagation of e%d (i%d val: %d)", elit, ilit, tmp);
266#endif
267 if (!tmp) {
268 // variable is not assigned, it can be propagated
269 if (!level) {
270 Clause *res = learn_external_reason_clause (ilit, elit);
271#ifndef LOGGING
272 LOG (res, "reason clause of external propagation of %d:", elit);
273#endif
274 (void) res;
275 } else
277 stats.ext_prop.eprop_prop++;
278
279 if (unsat || conflict)
280 break;
281 propagate ();
282 if (unsat || conflict)
283 break;
285 } else if (tmp < 0) {
286 LOG ("External propgation of %d is falsified under current trail",
287 ilit);
288 stats.ext_prop.eprop_conf++;
289 int level_before = level;
290 size_t assigned = num_assigned;
291 Clause *res = learn_external_reason_clause (ilit, elit);
292#ifndef LOGGING
293 LOG (res, "reason clause of external propagation of %d:", elit);
294#endif
295 (void) res;
296 bool trail_changed =
297 (num_assigned != assigned || level != level_before ||
298 propagated < trail.size ());
299
300 if (unsat || conflict)
301 break;
302
303 if (trail_changed) {
304 propagate ();
305 if (unsat || conflict)
306 break;
308 }
309 } // else (tmp > 0) -> the case of a satisfied literal is ignored
310 elit = external->propagator->cb_propagate ();
311 stats.ext_prop.ext_cb++;
312 stats.ext_prop.eprop_call++;
313 }
314
315#ifndef CADICAL_NDEBUG
316 LOG ("External propagation ends (decision level: %d, trail size: %zd, "
317 "notified %zd)",
318 level, trail.size (), notified);
319#endif
320 if (!unsat && !conflict) {
321 int level_before = level;
322 size_t assigned = num_assigned;
323 bool has_external_clause = ask_external_clause ();
324 // New observed variable might have triggered a backtrack during this
325 // ask_external_clause call, so we need to propagate before continuing
326 stats.ext_prop.ext_cb++;
327 stats.ext_prop.elearn_call++;
328
329 bool trail_changed =
330 (num_assigned != assigned || level != level_before ||
331 propagated < trail.size ());
332 if (trail_changed) {
333 propagate (); // unsat or conflict will be caught later
334 if (!unsat || !conflict)
336 }
337
338#ifndef CADICAL_NDEBUG
339 if (has_external_clause)
340 LOG ("New external clauses are to be added.");
341 else
342 LOG ("No external clauses to be added.");
343#endif
344
345 while (has_external_clause) {
346 level_before = level;
348
350 trail_changed =
351 (num_assigned != assigned || level != level_before ||
352 propagated < trail.size ());
353 cb_repropagate_needed = true;
354
355 if (unsat || conflict) {
356 cb_repropagate_needed = false;
357 break;
358 }
359
360 if (trail_changed) {
361 propagate ();
362 if (unsat || conflict) {
363 cb_repropagate_needed = false;
364 break;
365 }
366
368 }
369 has_external_clause = ask_external_clause ();
370 stats.ext_prop.ext_cb++;
371 stats.ext_prop.elearn_call++;
372 }
373 }
374#ifndef CADICAL_NDEBUG
375 LOG ("External clause addition ends on decision level %d at trail "
376 "size "
377 "%zd (notified %zd)",
378 level, trail.size (), notified);
379#endif
380 }
381 if (before < num_assigned)
382 did_external_prop = true;
383 return !conflict;
384}
385
386/*----------------------------------------------------------------------------*/
387//
388// Helper function, calls 'cb_has_external_clause', while maintains the
389// related redundancy type of the clause.
390//
391
394 bool res =
395 external->propagator->cb_has_external_clause (ext_clause_forgettable);
396
397 return res;
398}
399/*----------------------------------------------------------------------------*/
400//
401// Literals of the externally learned clause must be reordered based on the
402// assignment levels of the literals.
403//
405 if (clause.size () < 2)
406 return;
407 if (!level)
408 return;
409
410 for (int i = 0; i < 2; i++) {
411 int highest_position = i;
412 int highest_literal = clause[i];
413
414 int highest_level = var (highest_literal).level;
415 int highest_value = val (highest_literal);
416
417 for (size_t j = i + 1; j < clause.size (); j++) {
418 const int other = clause[j];
419 const int other_level = var (other).level;
420 const int other_value = val (other);
421
422 if (other_value < 0) {
423 if (highest_value >= 0)
424 continue;
425 if (other_level <= highest_level)
426 continue;
427 } else if (other_value > 0) {
428 if (highest_value > 0 && other_level >= highest_level)
429 continue;
430 } else {
431 if (highest_value >= 0)
432 continue;
433 }
434
435 highest_position = j;
436 highest_literal = other;
437 highest_level = other_level;
438 highest_value = other_value;
439 }
440#ifndef CADICAL_NDEBUG
441 LOG ("highest position: %d highest level: %d highest value: %d",
442 highest_position, highest_level, highest_value);
443#endif
444
445 if (highest_position == i)
446 continue;
447 if (highest_position > i) {
448 std::swap (clause[i], clause[highest_position]);
449 }
450 }
451}
452
453/*----------------------------------------------------------------------------*/
454//
455// Reads out from the external propagator the lemma/proapgation reason
456// clause literal by literal. In case propagated_elit is 0, it is about an
457// external clause via 'cb_add_external_clause_lit'. Otherwise, it is about
458// learning the reason of 'propagated_elit' via 'cb_add_reason_clause_lit'.
459// The learned clause is simplified by the current root-level assignment
460// (i.e. root-level falsified literals are removed, root satisfied clauses
461// are skipped). Duplicate literals are removed, tauotologies are detected
462// and skipped. It always adds the original (un-simplified) external clause
463// to the proof as an input clause and
464// the simplified version of it (except exceptions below) as a derived
465// clause.
466//
467// In case the external clause, after simplifications, is satisfied, no
468// clause is constructed, and the function returns 0. In case the external
469// clause, after simplifications, is empty, no clause is constructed, unsat
470// is set true and the function returns 0. In case the external clause,
471// after simplifications, is unit, no clause is constructed,
472// 'Internal::clause' has the unit literal (without 0) and the function
473// returns 0.
474//
475// In every other cases a new clause is constructed and the pointer is in
476// newest_clause
477//
478void Internal::add_external_clause (int propagated_elit,
479 bool no_backtrack) {
480 CADICAL_assert (original.empty ());
481 int elit = 0;
482
483 if (propagated_elit) {
484 // Propagation reason clauses are by default assumed to be forgettable
485 // irredundant. In case they would be unforgettably important, the
486 // propagator can add them as an explicit unforgettable external clause
487 // or set 'are_reasons_forgettable' to false.
488 ext_clause_forgettable = external->propagator->are_reasons_forgettable;
489#ifndef CADICAL_NDEBUG
490 LOG ("add external reason of propagated lit: %d", propagated_elit);
491#endif
492 elit = external->propagator->cb_add_reason_clause_lit (propagated_elit);
493 } else
494 elit = external->propagator->cb_add_external_clause_lit ();
495
496 // we need to be build a new LRAT chain if we are already in the middle of
497 // the analysis (like during failed assumptions)
498 LOG (lrat_chain, "lrat chain before");
499 std::vector<int64_t> lrat_chain_ext = std::move (lrat_chain);
500 lrat_chain.clear ();
501 clause.clear ();
502
503 // Read out the external lemma into original and simplify it into clause
504 CADICAL_assert (clause.empty ());
505 CADICAL_assert (original.empty ());
506
509 force_no_backtrack = no_backtrack;
510 from_propagator = true;
511 while (elit) {
512 CADICAL_assert (external->is_observed[abs (elit)]);
513 external->add (elit);
514 if (propagated_elit)
515 elit =
516 external->propagator->cb_add_reason_clause_lit (propagated_elit);
517 else
518 elit = external->propagator->cb_add_external_clause_lit ();
519 }
520 external->add (elit);
521 CADICAL_assert (original.empty ());
522 CADICAL_assert (clause.empty ());
523 force_no_backtrack = false;
524 from_propagator = false;
525 lrat_chain = std::move (lrat_chain_ext);
526 LOG (lrat_chain, "lrat chain after");
527}
528
529/*----------------------------------------------------------------------------*/
530//
531// Recursively calls 'learn_external_reason_clause' to explain every
532// backward reachable externally propagated literal starting from 'ilit'.
533//
534void Internal::explain_reason (int ilit, Clause *reason, int &open) {
535 if (!opts.exteagerreasons)
536 return;
537#ifndef CADICAL_NDEBUG
538 LOG (reason, "explain_reason of %d (open: %d)", ilit, open);
539#endif
540 CADICAL_assert (reason);
542 for (const auto &other : *reason) {
543 if (other == ilit)
544 continue;
545 Flags &f = flags (other);
546 if (f.seen)
547 continue;
548 Var &v = var (other);
549 if (!v.level)
550 continue;
551 CADICAL_assert (val (other) < 0);
553 if (v.reason == external_reason) {
554 v.reason = learn_external_reason_clause (-other, 0, true);
555 }
556 if (v.level && v.reason) {
557 f.seen = true;
558 open++;
559 }
560 }
561}
562
563/*----------------------------------------------------------------------------*/
564//
565// In case external propagation was used, the reason clauses of the relevant
566// propagations must be learned lazily before/during conflict analysis.
567// While conflict analysis needs to analyze only the current level, lazy
568// clause learning must check every clause on every level that is backward
569// reachable from the conflicting clause to guarantee that the assignment
570// levels of the variables are accurate. So this explanation round is
571// separated from the conflict analysis, thereby guranteeing that the flags
572// and datastructures can be properly used later.
573//
574// This function must be called before the conflict analysis, in order to
575// guarantee that every relevant reason clause is indeed learned already and
576// to be sure that the levels of assignments are set correctly.
577//
578// Later TODO: experiment with bounded explanation (explain only those
579// literals that are directly used during conflict analysis +
580// minimizing/shrinking). The assignment levels are then only
581// over-approximated.
582//
585 CADICAL_assert (clause.empty ());
586
587 Clause *reason = conflict;
588 std::vector<int> seen_lits;
589 int open = 0; // Seen but not explained literal
590
591 explain_reason (0, reason, open); // marks conflict clause lits as seen
592 int i = trail.size (); // Start at end-of-trail
593 while (i > 0) {
594 const int lit = trail[--i];
595 if (!flags (lit).seen)
596 continue;
597 seen_lits.push_back (lit);
598 Var &v = var (lit);
599 if (!v.level)
600 continue;
601 if (v.reason) {
602 open--;
603 explain_reason (lit, v.reason, open);
604 }
605 if (!open)
606 break;
607 }
608 CADICAL_assert (!open);
609
610 if (!opts.exteagerrecalc) {
611 for (auto lit : seen_lits) {
612 Flags &f = flags (lit);
613 f.seen = false;
614 }
615#ifndef CADICAL_NDEBUG
616 for (auto idx : vars) {
617 CADICAL_assert (!flags (idx).seen);
618 }
619#endif
620 }
621
622 // Traverse now in the opposite direction (from lower to higher levels)
623 // and calculate the actual assignment level for the seen assignments.
624 for (auto it = seen_lits.rbegin (); it != seen_lits.rend (); ++it) {
625 const int lit = *it;
626 Flags &f = flags (lit);
627 Var &v = var (lit);
628 if (v.reason) {
629 int real_level = 0;
630 for (const auto &other : *v.reason) {
631 if (other == lit)
632 continue;
633 int tmp = var (other).level;
634 if (tmp > real_level)
635 real_level = tmp;
636 }
637 if (v.level && !real_level) {
640 lrat_chain.clear ();
641 v.reason = 0;
642 }
643 CADICAL_assert (v.level >= real_level);
644 if (v.level > real_level) {
645 v.level = real_level;
646 }
647 }
648 f.seen = false;
649 }
650
651#if 0 // has been fuzzed extensively
652 for (auto idx : vars) {
653 CADICAL_assert (!flags (idx).seen);
654 }
655#endif
656}
657
658/*----------------------------------------------------------------------------*/
659//
660// Learns the reason clause of the propagation of ilit from the
661// external propagator via 'add_external_clause'.
662// In case of falsified propagation steps, if the propagated literal is
663// already fixed to the opposite value, externalize will not necessarily
664// give back the original elit (but an equivalent one). To avoid that, in
665// falsified propagation cases the propagated elit is added as a second
666// argument.
667//
669 int falsified_elit,
670 bool no_backtrack) {
671 CADICAL_assert (external->propagator);
672 // we cannot modify clause during analysis
673 auto clause_tmp = std::move (clause);
674
675 CADICAL_assert (clause.empty ());
676 CADICAL_assert (original.empty ());
677
678 stats.ext_prop.eprop_expl++;
679
680 int elit = 0;
681 if (!falsified_elit) {
682 CADICAL_assert (!fixed (ilit));
683 elit = externalize (ilit);
684 } else
685 elit = falsified_elit;
686
687 LOG ("ilit: %d, elit: %d", ilit, elit);
688 add_external_clause (elit, no_backtrack);
689
690#ifndef CADICAL_NDEBUG
691 if (!falsified_elit && newest_clause) {
692 // Check if external propagation is correct wrt to the topological order
693 // defined by the trail. In case it is a falsified external propagation
694 // step, the order does not matter, the reason simply supposed to be a
695 // falsified clause.
696 const int propagated_ilit = ilit;
697 for (auto const reason_ilit : *newest_clause) {
698 CADICAL_assert (var (reason_ilit).trail <= var (propagated_ilit).trail);
699 }
700 }
701#endif
702
703 clause = std::move (clause_tmp);
704 return newest_clause;
705}
706
707/*----------------------------------------------------------------------------*/
708//
709// Helper function to be able to call learn_external_reason_clause when the
710// internal clause is already used in the caller side (for example during
711// proof checking). These calls are assumed to be without a falsified elit.
712// Dont use it in general instead of learn_external_reason_clause because it
713// does not support the corner cases where a literal remains in clause.
714//
716 Clause *res;
717 std::vector<int64_t> chain_tmp{std::move (lrat_chain)};
718 lrat_chain.clear ();
719 if (clause.empty ()) {
720 res = learn_external_reason_clause (ilit, 0, true);
721 } else {
722 std::vector<int> clause_tmp{std::move (clause)};
723 clause.clear ();
724 res = learn_external_reason_clause (ilit, 0, true);
725 // The learn_external_reason clause can leave a literal in clause when
726 // there is a falsified elit arg. Here it is not allowed to
727 // happen.
728 CADICAL_assert (clause.empty ());
729
730 clause = std::move (clause_tmp);
731 clause_tmp.clear ();
732 }
733 CADICAL_assert (lrat_chain.empty ());
734 lrat_chain = std::move (chain_tmp);
735 chain_tmp.clear ();
736 return res;
737}
738
739/*----------------------------------------------------------------------------*/
740//
741// Checks if the new clause forces backtracking, new assignments or conflict
742// analysis
743//
745 if (from_propagator)
746 stats.ext_prop.elearned++;
747 // at level 0 we have to do nothing...
748 if (!level)
749 return;
750 if (!res) {
751 if (from_propagator)
752 stats.ext_prop.elearn_prop++;
753 // new unit clause. For now just backtrack.
756 // if (!opts.chrono) {
757 backtrack ();
758 // }
759 return;
760 }
761 if (from_propagator)
762 stats.ext_prop.elearned++;
763 CADICAL_assert (res->size >= 2);
764 const int pos0 = res->literals[0];
765 const int pos1 = res->literals[1];
766 if (force_no_backtrack) {
767 CADICAL_assert (val (pos1) < 0);
768 CADICAL_assert (val (pos0) >= 0);
769 return;
770 // TODO: maybe fix levels
771 }
772 const int l1 = var (pos1).level;
773 if (val (pos0) < 0) { // conflicting or propagating clause
774 CADICAL_assert (0 < l1 && l1 <= var (pos0).level);
775 if (!opts.chrono) {
776 backtrack (l1);
777 }
778 if (val (pos0) < 0) {
779 conflict = res;
780 if (!from_propagator) {
781 // its better to backtrack instead of analyze
782 backtrack (l1 - 1);
783 conflict = 0;
784 CADICAL_assert (!val (pos0) && !val (pos1));
785 }
786 } else {
787 search_assign_driving (pos0, res);
788 }
789 if (from_propagator)
790 stats.ext_prop.elearn_conf++;
791 return;
792 }
793 if (val (pos1) < 0 && !val (pos0)) { // propagating clause
794 if (!opts.chrono) {
795 backtrack (l1);
796 }
797 search_assign_driving (pos0, res);
798 if (from_propagator)
799 stats.ext_prop.elearn_conf++;
800 return;
801 }
802}
803
804/*----------------------------------------------------------------------------*/
805//
806// Asks the external propagator if the current solution is OK
807// by calling 'cb_check_found_model (model)'.
808//
809// The checked model is built up after everything is restored
810// from the reconstruction stack and every variable is reactivated
811// and so it is not just simply the trail (i.e. it can be expensive).
812//
813// If the external propagator approves the model, the function
814// returns true.
815//
816// If the propagator does not approve the model, the solver asks
817// the propagator to add an external clause.
818// This external clause, however, does NOT have to be falsified by
819// the current model. The possible cases and reactions are described
820// below in the function. The possible states after that function:
821// - A solution was found and accepted by the external propagator
822// - A conflicting clause was learned from the external propagator
823// - The empty clause was learned due to something new learned from
824// the external propagator.
825//
826// In case only new variables were introduced, but no new clauses were
827// added, the function will return without a conflict to the outer CDCL
828// loop, where the new (not yet satisfied) variables are recognized and
829// the search continues.
831 if (!external_prop)
832 return true;
833
834 bool trail_changed = true;
835 bool added_new_clauses = false;
836 while (trail_changed || added_new_clauses) {
838 if (!satisfied ())
839 break;
840 trail_changed = false; // to be on the safe side
841 added_new_clauses = false;
842 LOG ("Final check by external propagator is invoked.");
843 stats.ext_prop.echeck_call++;
844 external->reset_extended ();
845 external->extend ();
846
847 std::vector<int> etrail;
848
849 // Here the variables must be filtered by external->is_observed,
850 // because fixed variables are internally not necessarily observed
851 // anymore.
852 for (int idx = 1; idx <= external->max_var; idx++) {
853 if (!external->is_observed[idx])
854 continue;
855 const int lit = external->ival (idx);
856 etrail.push_back (lit);
857#ifndef CADICAL_NDEBUG
858#ifdef LOGGING
859 bool p = external->vals[idx];
860 LOG ("evals[%d]: %d ival(%d): %d", idx, p, idx, lit);
861#endif
862#endif
863 }
864
865 bool is_consistent =
866 external->propagator->cb_check_found_model (etrail);
867 stats.ext_prop.ext_cb++;
868 if (is_consistent) {
869 LOG ("Found solution is approved by external propagator.");
870 return true;
871 }
872
873 bool has_external_clause = ask_external_clause ();
874
875 stats.ext_prop.ext_cb++;
876 stats.ext_prop.elearn_call++;
877
878 if (has_external_clause)
879 LOG (
880 "Found solution triggered new clauses from external propagator.");
881
882 while (has_external_clause) {
883 int level_before = level;
884 size_t assigned = num_assigned;
886 bool trail_changed =
887 (num_assigned != assigned || level != level_before ||
888 propagated < trail.size ());
889 added_new_clauses = true;
890 //
891 // There are many possible scenarios here:
892 // - Learned conflicting clause: return to CDCL loop (conflict true)
893 // - Learned conflicting unit clause that after backtrack+BCP leads to
894 // a new complete solution: force the outer loop to check the new
895 // model (trail_changed is true, but (conflict & unsat) is false)
896 // - Learned empty clause: return to CDCL loop (unsat true)
897 // - Learned a non-conflicting unit clause:
898 // Though it does not invalidate the current solution, the solver
899 // will backtrack to the root level and will repropagate it. The
900 // search will start again (saved phases hopefully make it quick),
901 // but it is needed in order to guarantee that every fixed variable
902 // is properly handled+notified (important for incremental use
903 // cases).
904 // - Otherwise: the solution is considered approved and the CDCL-loop
905 // can return with res = 10.
906 //
907 if (unsat || conflict || trail_changed)
908 break;
909 has_external_clause = ask_external_clause ();
910 stats.ext_prop.ext_cb++;
911 stats.ext_prop.elearn_call++;
912 }
913 LOG ("No more external clause to add.");
914 if (unsat || conflict)
915 break;
916 }
917
918 if (!unsat && conflict) {
919 const int conflict_level = var (conflict->literals[0]).level;
920 if (conflict_level != level) {
921 backtrack (conflict_level);
922 }
923 }
924
925 return !conflict;
926}
927
928/*----------------------------------------------------------------------------*/
929//
930// Notify the external propagator that an observed variable got assigned.
931//
934 return;
935
936 const size_t end_of_trail = trail.size ();
937
938 if (notified >= end_of_trail)
939 return;
940
941 LOG ("notify external propagator about new assignments");
942 std::vector<int> assigned;
943
944 while (notified < end_of_trail) {
945 int ilit = trail[notified++];
946 if (!observed (ilit))
947 continue;
948
949 int elit = externalize (ilit); // TODO: double-check tainting
950 CADICAL_assert (elit);
951 if (external->ervars[abs (elit)])
952 continue;
953 // Fixed variables might get mapped (during compact) to another
954 // non-observed but fixed variable.
955 // This happens on root level, so notification about their assignment is
956 // already done.
957 CADICAL_assert (external->observed (elit) ||
958 (fixed (ilit) && !external->ervars[abs (elit)]));
959 assigned.push_back (elit);
960 }
961
962 external->propagator->notify_assignment (assigned);
963 return;
964}
965
966/*----------------------------------------------------------------------------*/
967
969 if (level)
970 backtrack ();
971}
972
973/*----------------------------------------------------------------------------*/
974//
975// Notify the external propagator that a new decision level is started.
976//
979 return;
980 external->propagator->notify_new_decision_level ();
981}
982
983/*----------------------------------------------------------------------------*/
984//
985// Notify the external propagator that backtrack to new_level.
986//
987void Internal::notify_backtrack (size_t new_level) {
989 return;
990 external->propagator->notify_backtrack (new_level);
991}
992
993/*----------------------------------------------------------------------------*/
994//
995// Ask the external propagator if there is a suggested literal as next
996// decision.
997//
1000 return 0;
1001
1005 int level_before = level;
1006 forced_backt_allowed = true;
1007 int elit = external->propagator->cb_decide ();
1008 forced_backt_allowed = false;
1009 stats.ext_prop.ext_cb++;
1010
1011 if (level_before != level) {
1012
1013 propagate ();
1017
1018 // In case the external propagator forced to backtrack below the
1019 // pseduo decision levels, we must go back to the CDCL loop instead of
1020 // making a decision.
1021 if ((size_t) level < assumptions.size () ||
1022 ((size_t) level == assumptions.size () && constraint.size ())) {
1023 return 0;
1024 }
1025 }
1026
1027 if (!elit)
1028 return 0;
1029 LOG ("external propagator proposes decision: %d", elit);
1030 CADICAL_assert (external->is_observed[abs (elit)]);
1031 if (!external->is_observed[abs (elit)])
1032 return 0;
1033
1034 int ilit = external->e2i[abs (elit)];
1035 if (elit < 0)
1036 ilit = -ilit;
1037
1038 CADICAL_assert (fixed (ilit) || observed (ilit));
1039
1040 LOG ("Asking external propagator for decision returned: %d (internal: "
1041 "%d, fixed: %d, val: %d)",
1042 elit, ilit, fixed (ilit), val (ilit));
1043
1044 if (fixed (ilit) || val (ilit)) {
1045 LOG ("Proposed decision variable is already assigned, falling back to "
1046 "internal decision.");
1047 return 0;
1048 }
1049
1050 return ilit;
1051}
1052
1053/*----------------------------------------------------------------------------*/
1054//
1055// Check if the clause is a forgettable clause coming from the external
1056// propagator.
1057//
1059 CADICAL_assert (opts.check);
1060 return (external->forgettable_original.find (id) !=
1061 external->forgettable_original.end ());
1062}
1063
1064/*----------------------------------------------------------------------------*/
1065//
1066// When an external forgettable clause is deleted, it is marked in the
1067// 'forgettable_original' hash, so that the internal model checking can
1068// ignore it.
1069//
1071 CADICAL_assert (opts.check);
1073
1074 LOG (external->forgettable_original[id],
1075 "forgettable external lemma is deleted:");
1076 // Mark as removed by flipping the first flag to false.
1077 external->forgettable_original[id][0] = 0;
1078}
1079
1080/*----------------------------------------------------------------------------*/
1081//
1082// Check that the literals in the clause are properly ordered. Used only
1083// internally for debug purposes.
1084//
1086#ifndef CADICAL_NDEBUG
1087 int v0 = 0;
1088 int v1 = 0;
1089
1090 if (val (clause[0]) > 0)
1091 v0 = 1;
1092 else if (val (clause[0]) < 0)
1093 v0 = -1;
1094
1095 if (val (clause[1]) > 0)
1096 v1 = 1;
1097 else if (val (clause[1]) < 0)
1098 v1 = -1;
1099 CADICAL_assert (v0 >= v1);
1100#endif
1101 if (val (clause[0]) > 0) {
1102 if (val (clause[1]) > 0) { // Case 1: Both literals are satisfied
1103 // They are ordered by lower to higher decision level
1104 CADICAL_assert (var (clause[0]).level <= var (clause[1]).level);
1105
1106 // Every other literal of the clause is either
1107 // - satisfied at higher level
1108 // - unassigned
1109 // - falsified
1110 for (size_t i = 2; i < clause.size (); i++)
1111 CADICAL_assert (val (clause[i]) <= 0 ||
1112 (var (clause[1]).level <= var (clause[i]).level));
1113
1114 } else if (val (clause[1]) ==
1115 0) { // Case 2: First satisfied, next unassigned
1116
1117 // Every other literal of the clause is either
1118 // - unassigned
1119 // - falsified
1120 for (size_t i = 2; i < clause.size (); i++)
1121 CADICAL_assert (val (clause[i]) <= 0);
1122
1123 } else { // Case 3: First satisfied, next falsified -> could have been a
1124 // reason of a previous propagation
1125 // Every other literal of the clause is falsified but at a lower
1126 // decision level
1127 for (size_t i = 2; i < clause.size (); i++)
1128 CADICAL_assert (val (clause[i]) < 0 &&
1129 (var (clause[1]).level >= var (clause[i]).level));
1130 }
1131 } else if (val (clause[0]) == 0) {
1132 if (val (clause[1]) == 0) { // Case 4: Both literals are unassigned
1133
1134 // Every other literal of the clause is either
1135 // - unassigned
1136 // - falsified
1137 for (size_t i = 2; i < clause.size (); i++)
1138 CADICAL_assert (val (clause[i]) <= 0);
1139
1140 } else { // Case 5: First unassigned, next falsified -> PROPAGATE
1141 // Every other literal of the clause is falsified but at a lower
1142 // decision level
1143 for (size_t i = 2; i < clause.size (); i++)
1144 CADICAL_assert (val (clause[i]) < 0 &&
1145 (var (clause[1]).level >= var (clause[i]).level));
1146 }
1147 } else {
1148 CADICAL_assert (val (clause[0]) < 0 &&
1149 val (clause[1]) < 0); // Case 6: Both literals are falsified
1150
1151 // They are ordered by higher to lower decision level
1152 CADICAL_assert (var (clause[0]).level >= var (clause[1]).level);
1153
1154 // Every other literal of the clause is falsified, but at a lower level
1155 for (size_t i = 2; i < clause.size (); i++)
1156 CADICAL_assert (val (clause[i]) < 0 &&
1157 (var (clause[1]).level >= var (clause[i]).level));
1158 }
1159}
1160
1161#ifndef CADICAL_NDEBUG
1162
1163/*----------------------------------------------------------------------------*/
1164//
1165// An expensive function that can be used for deep-debug trail-related
1166// issues in mobical. Do not use it unless it is really unavoidable.
1167//
1168// eq_class contains all the merged external literals that are currently
1169// compacted to the internal literal of trail[0] and return true.
1170//
1171// In case trail[0] does not exists or is not on the root level, the
1172// function returns false (indicating that there was no merger literal
1173// found).
1174//
1175bool Internal::get_merged_literals (std::vector<int> &eq_class) {
1176 eq_class.clear ();
1177
1178 if (!trail.size ())
1179 return false;
1180
1181 int ilit = trail[0];
1182 size_t lit_level = var (ilit).level;
1183
1184 if (!lit_level) {
1185 // Collect all the variables that are merged and mapped to that ilit
1186 size_t e2i_size = external->e2i.size ();
1187 int ivar = abs (ilit);
1188 for (size_t i = 0; i < e2i_size; i++) {
1189 int other = abs (external->e2i[i]);
1190 if (other == ivar) {
1191 if (external->e2i[i] == ilit)
1192 eq_class.push_back (i);
1193 else
1194 eq_class.push_back (-1 * i);
1195 }
1196 }
1197
1198 return true;
1199 }
1200
1201 return false;
1202}
1203
1204/*----------------------------------------------------------------------------*/
1205//
1206// Collect all external variables that are FIXED internally. Again an
1207// expensive function that should be called only for debugging in mobical.
1208//
1209// Do not use it unless it is really unavoidable.
1210//
1211void Internal::get_all_fixed_literals (std::vector<int> &fixed_lits) {
1212 fixed_lits.clear ();
1213 if (!trail.size ())
1214 return;
1215
1216 int e2i_size = external->e2i.size ();
1217 int ilit;
1218 for (int eidx = 1; eidx < e2i_size; eidx++) {
1219 ilit = external->e2i[eidx];
1220 if (ilit && !external->ervars[eidx]) {
1221 Flags &f = flags (ilit);
1222 if (f.status == Flags::FIXED) {
1223 fixed_lits.push_back (vals[abs (ilit)] * eidx);
1224 }
1225 }
1226 }
1227}
1228#endif
1229
1230} // namespace CaDiCaL
1231
#define ABC_NAMESPACE_IMPL_START
#define ABC_NAMESPACE_IMPL_END
#define CADICAL_assert(ignore)
Definition global.h:14
#define LOG(...)
Cube * p
Definition exorList.c:222
const char * flags()
int lit
Definition satVec.h:130
vector< int > e2i
Definition external.hpp:67
External * external
Definition internal.hpp:312
vector< unsigned > relevanttab
Definition internal.hpp:225
Var & var(int lit)
Definition internal.hpp:452
bool ext_clause_forgettable
Definition internal.hpp:249
vector< int64_t > lrat_chain
Definition internal.hpp:210
Clause * wrapped_learn_external_reason_clause(int lit)
int vidx(int lit) const
Definition internal.hpp:395
vector< int > constraint
Definition internal.hpp:262
void force_backtrack(size_t new_level)
void add_external_clause(int propagated_lit=0, bool no_backtrack=false)
Flags & flags(int lit)
Definition internal.hpp:454
vector< int > trail
Definition internal.hpp:259
int fixed(int lit)
int externalize(int lit)
void mark_garbage_external_forgettable(int64_t id)
Clause * learn_external_reason_clause(int lit, int falsified_elit=0, bool no_backtrack=false)
signed char val(int lit) const
Clause * newest_clause
Definition internal.hpp:246
vector< int > assumptions
Definition internal.hpp:261
void explain_reason(int lit, Clause *, int &open)
void backtrack(int target_level=0)
signed char * vals
Definition internal.hpp:221
const Range vars
Definition internal.hpp:324
int assignment_level(int lit, Clause *)
void search_assign_external(int lit)
void require_mode(Mode m) const
Definition internal.hpp:173
vector< int > original
Definition internal.hpp:265
Clause * external_reason
Definition internal.hpp:245
void notify_backtrack(size_t new_level)
void learn_unit_clause(int lit)
bool observed(int ilit) const
void search_assign_driving(int lit, Clause *reason)
void build_chain_for_units(int lit, Clause *reason, bool forced)
vector< int > clause
Definition internal.hpp:260
int level
Definition var.hpp:19
Clause * reason
Definition var.hpp:21
unsigned size
Definition vector.h:35