#include "global.h" #include "internal.hpp" #include ABC_NAMESPACE_IMPL_START namespace CaDiCaL { /*----------------------------------------------------------------------------*/ // // Mark a variable as an observed one. It can be a new variable. It is // assumed to be clean (not eliminated by previous simplifications). // void Internal::add_observed_var (int ilit) { int idx = vidx (ilit); if (idx >= (int64_t) relevanttab.size ()) relevanttab.resize (1 + (size_t) idx, 0); unsigned &ref = relevanttab[idx]; if (ref < UINT_MAX) { ref++; LOG ("variable %d is observed %u times", idx, ref); } else LOG ("variable %d remains observed forever", idx); // TODO: instead of actually backtracking, it would be enough to notify // backtrack and re-play again every levels' notification to the // propagator if (val (ilit) && level && !fixed (ilit)) { // The variable is already assigned, but we can not send a notification // about it because it happened on an earlier decision level. // To not break the stack-like view of the trail, we simply backtrack to // undo this unnotifiable assignment. const int assignment_level = var (ilit).level; backtrack (assignment_level - 1); } else if (level && fixed (ilit)) { backtrack (0); } } /*----------------------------------------------------------------------------*/ // // Removing an observed variable should happen only once it is ensured // that there is no unexplained propagation in the implication // graph involving this variable. // void Internal::remove_observed_var (int ilit) { if (!fixed (ilit) && level) { backtrack (); } CADICAL_assert (fixed (ilit) || !level); const int idx = vidx (ilit); CADICAL_assert ((size_t) idx < relevanttab.size ()); unsigned &ref = relevanttab[idx]; CADICAL_assert (fixed (ilit) || ref > 0); if (fixed (ilit)) ref = 0; else if (ref < UINT_MAX) { if (!--ref) { LOG ("variable %d is not observed anymore", idx); } else LOG ("variable %d is unobserved once but remains observed %u times", ilit, ref); } else LOG ("variable %d remains observed forever", idx); } /*----------------------------------------------------------------------------*/ // // Supposed to be used only by mobical. // bool Internal::observed (int ilit) const { CADICAL_assert ((size_t) vidx (ilit) < relevanttab.size ()); return relevanttab[vidx (ilit)] > 0; } /*----------------------------------------------------------------------------*/ // // Check for unexplained propagations upon disconnecting external propagator // void Internal::set_tainted_literal () { if (!opts.ilb) { return; } for (auto idx : vars) { if (!val (idx)) continue; if (var (idx).reason != external_reason) continue; if (!tainted_literal) { tainted_literal = idx; continue; } CADICAL_assert (val (tainted_literal)); if (var (idx).level < var (tainted_literal).level) { tainted_literal = idx; } } } void Internal::renotify_trail_after_ilb () { CADICAL_assert (opts.ilb); if (!external_prop || external_prop_is_lazy || !trail.size () || !opts.ilb) { return; } LOG ("notify external propagator about new assignments (after ilb)"); #ifndef CADICAL_NDEBUG LOG ("(decision level: %d, trail size: %zd, notified %zd)", level, trail.size (), notified); #endif renotify_full_trail (); } void Internal::renotify_trail_after_local_search () { if (!external_prop || external_prop_is_lazy || !trail.size ()) { return; } LOG ("notify external propagator about new assignments (after local " "search)"); #ifndef CADICAL_NDEBUG LOG ("(decision level: %d, trail size: %zd, notified %zd)", level, trail.size (), notified); #endif renotify_full_trail (); } void Internal::renotify_full_trail_between_trail_pos ( int start_level, int end_level, int propagator_level, std::vector &assigned, bool start_new_level) { CADICAL_assert (assigned.empty ()); int j = start_level; #ifdef LOGGING LOG ("starting notification of level %d from trail %d .. %d", propagator_level, start_level, end_level); #else (void) propagator_level; #endif if (start_new_level) { if (assigned.size ()) external->propagator->notify_assignment (assigned); assigned.clear (); external->propagator->notify_new_decision_level (); } for (; j < end_level; ++j) { int ilit = trail[j]; // In theory, 0 ilit can happen due to pseudo-decision levels if (!ilit) continue; if (!observed (ilit)) continue; int elit = externalize (ilit); // TODO: double-check tainting LOG ("notifying elit %d @ %d aka %s", propagator_level, elit, LOGLIT (ilit)); CADICAL_assert (elit); // Fixed variables might get mapped (during compact) to another // non-observed but fixed variable. // This happens on root level, so notification about their assignment // is already done. CADICAL_assert (external->observed (elit) || fixed (ilit)); if (!external->ervars[abs (elit)]) assigned.push_back (elit); } if (assigned.size ()) external->propagator->notify_assignment (assigned); assigned.clear (); } // It repeats ALL assignments of the trail, so the already notified // root-level assignments will be notified multiple times. // // As CaDiCaL is missing some '0' seperators, it is important to go // over slices from the control stack instead of going over the trail // directly. void Internal::renotify_full_trail () { const size_t end_of_trail = trail.size (); if (level) { notified = 0; // TODO: save the last notified root-level position // somewhere and use it here notify_backtrack (0); } std::vector assigned; int propagator_level = 0; const int c_size = control.size (); { // first all root-level literals const int start_level = 0; const int end_level = (control.size () > 1 ? control[1].trail : end_of_trail); renotify_full_trail_between_trail_pos ( start_level, end_level, propagator_level, assigned, false); } // notify all intermediate levels for (int i = 2; i < c_size; ++i) { const int start_level = control[i - 1].trail; const int end_level = control[i].trail; propagator_level++; LOG ("notification of %d", propagator_level); renotify_full_trail_between_trail_pos ( start_level, end_level, propagator_level, assigned, true); } // and the current level if there is non-root level one if (level) { const int start_level = control.back ().trail; propagator_level++; renotify_full_trail_between_trail_pos ( start_level, end_of_trail, propagator_level, assigned, true); } CADICAL_assert (propagator_level == level); notified = trail.size (); return; } /*----------------------------------------------------------------------------*/ // // Check if the variable is assigned by decision. // bool Internal::is_decision (int ilit) { if (!level || fixed (ilit) || !val (ilit)) return false; const int idx = vidx (ilit); Var &v = var (idx); #ifndef CADICAL_NDEBUG LOG (v.reason, "is_decision: i%d (current level: %d, is_fixed: %d, v.level: %d, " "is_external_reason: %d, v.reason: )", ilit, level, fixed (ilit), v.level, v.reason == external_reason); #endif if (!v.level || v.reason) return false; CADICAL_assert (!v.reason); return true; } void Internal::force_backtrack (size_t new_level) { if (!forced_backt_allowed || level <= 0 || new_level >= (size_t) level) return; #ifndef CADICAL_NDEBUG LOG ("external propagator forces backtrack to decision level" "%zd (from level %d)", new_level, level); #endif backtrack (new_level); } /*----------------------------------------------------------------------------*/ // // Call external propagator to check if there is a literal to be propagated. // The reason of the propagation is not necessarily asked at that point. // // In case the externally propagated literal is already falsified, the // reason is asked and conflict analysis starts. In case the externally // propagated literal is already satisfied, nothing happens. // bool Internal::external_propagate () { if (level) require_mode (SEARCH); CADICAL_assert (!unsat); size_t before = num_assigned; bool cb_repropagate_needed = true; while (cb_repropagate_needed && !conflict && external_prop && !external_prop_is_lazy && !private_steps) { #ifndef CADICAL_NDEBUG LOG ("external propagation starts (decision level: %d, trail size: " "%zd, notified %zd)", level, trail.size (), notified); #endif cb_repropagate_needed = false; // external->reset_extended (); //TODO for inprocessing notify_assignments (); int elit = external->propagator->cb_propagate (); stats.ext_prop.ext_cb++; stats.ext_prop.eprop_call++; while (elit) { CADICAL_assert (external->is_observed[abs (elit)]); int ilit = external->e2i[abs (elit)]; if (elit < 0) ilit = -ilit; int tmp = val (ilit); #ifndef CADICAL_NDEBUG CADICAL_assert (fixed (ilit) || observed (ilit)); LOG ("External propagation of e%d (i%d val: %d)", elit, ilit, tmp); #endif if (!tmp) { // variable is not assigned, it can be propagated if (!level) { Clause *res = learn_external_reason_clause (ilit, elit); #ifndef LOGGING LOG (res, "reason clause of external propagation of %d:", elit); #endif (void) res; } else search_assign_external (ilit); stats.ext_prop.eprop_prop++; if (unsat || conflict) break; propagate (); if (unsat || conflict) break; notify_assignments (); } else if (tmp < 0) { LOG ("External propgation of %d is falsified under current trail", ilit); stats.ext_prop.eprop_conf++; int level_before = level; size_t assigned = num_assigned; Clause *res = learn_external_reason_clause (ilit, elit); #ifndef LOGGING LOG (res, "reason clause of external propagation of %d:", elit); #endif (void) res; bool trail_changed = (num_assigned != assigned || level != level_before || propagated < trail.size ()); if (unsat || conflict) break; if (trail_changed) { propagate (); if (unsat || conflict) break; notify_assignments (); } } // else (tmp > 0) -> the case of a satisfied literal is ignored elit = external->propagator->cb_propagate (); stats.ext_prop.ext_cb++; stats.ext_prop.eprop_call++; } #ifndef CADICAL_NDEBUG LOG ("External propagation ends (decision level: %d, trail size: %zd, " "notified %zd)", level, trail.size (), notified); #endif if (!unsat && !conflict) { int level_before = level; size_t assigned = num_assigned; bool has_external_clause = ask_external_clause (); // New observed variable might have triggered a backtrack during this // ask_external_clause call, so we need to propagate before continuing stats.ext_prop.ext_cb++; stats.ext_prop.elearn_call++; bool trail_changed = (num_assigned != assigned || level != level_before || propagated < trail.size ()); if (trail_changed) { propagate (); // unsat or conflict will be caught later if (!unsat || !conflict) notify_assignments (); } #ifndef CADICAL_NDEBUG if (has_external_clause) LOG ("New external clauses are to be added."); else LOG ("No external clauses to be added."); #endif while (has_external_clause) { level_before = level; assigned = num_assigned; add_external_clause (0); trail_changed = (num_assigned != assigned || level != level_before || propagated < trail.size ()); cb_repropagate_needed = true; if (unsat || conflict) { cb_repropagate_needed = false; break; } if (trail_changed) { propagate (); if (unsat || conflict) { cb_repropagate_needed = false; break; } notify_assignments (); } has_external_clause = ask_external_clause (); stats.ext_prop.ext_cb++; stats.ext_prop.elearn_call++; } } #ifndef CADICAL_NDEBUG LOG ("External clause addition ends on decision level %d at trail " "size " "%zd (notified %zd)", level, trail.size (), notified); #endif } if (before < num_assigned) did_external_prop = true; return !conflict; } /*----------------------------------------------------------------------------*/ // // Helper function, calls 'cb_has_external_clause', while maintains the // related redundancy type of the clause. // bool Internal::ask_external_clause () { ext_clause_forgettable = false; bool res = external->propagator->cb_has_external_clause (ext_clause_forgettable); return res; } /*----------------------------------------------------------------------------*/ // // Literals of the externally learned clause must be reordered based on the // assignment levels of the literals. // void Internal::move_literals_to_watch () { if (clause.size () < 2) return; if (!level) return; for (int i = 0; i < 2; i++) { int highest_position = i; int highest_literal = clause[i]; int highest_level = var (highest_literal).level; int highest_value = val (highest_literal); for (size_t j = i + 1; j < clause.size (); j++) { const int other = clause[j]; const int other_level = var (other).level; const int other_value = val (other); if (other_value < 0) { if (highest_value >= 0) continue; if (other_level <= highest_level) continue; } else if (other_value > 0) { if (highest_value > 0 && other_level >= highest_level) continue; } else { if (highest_value >= 0) continue; } highest_position = j; highest_literal = other; highest_level = other_level; highest_value = other_value; } #ifndef CADICAL_NDEBUG LOG ("highest position: %d highest level: %d highest value: %d", highest_position, highest_level, highest_value); #endif if (highest_position == i) continue; if (highest_position > i) { std::swap (clause[i], clause[highest_position]); } } } /*----------------------------------------------------------------------------*/ // // Reads out from the external propagator the lemma/proapgation reason // clause literal by literal. In case propagated_elit is 0, it is about an // external clause via 'cb_add_external_clause_lit'. Otherwise, it is about // learning the reason of 'propagated_elit' via 'cb_add_reason_clause_lit'. // The learned clause is simplified by the current root-level assignment // (i.e. root-level falsified literals are removed, root satisfied clauses // are skipped). Duplicate literals are removed, tauotologies are detected // and skipped. It always adds the original (un-simplified) external clause // to the proof as an input clause and // the simplified version of it (except exceptions below) as a derived // clause. // // In case the external clause, after simplifications, is satisfied, no // clause is constructed, and the function returns 0. In case the external // clause, after simplifications, is empty, no clause is constructed, unsat // is set true and the function returns 0. In case the external clause, // after simplifications, is unit, no clause is constructed, // 'Internal::clause' has the unit literal (without 0) and the function // returns 0. // // In every other cases a new clause is constructed and the pointer is in // newest_clause // void Internal::add_external_clause (int propagated_elit, bool no_backtrack) { CADICAL_assert (original.empty ()); int elit = 0; if (propagated_elit) { // Propagation reason clauses are by default assumed to be forgettable // irredundant. In case they would be unforgettably important, the // propagator can add them as an explicit unforgettable external clause // or set 'are_reasons_forgettable' to false. ext_clause_forgettable = external->propagator->are_reasons_forgettable; #ifndef CADICAL_NDEBUG LOG ("add external reason of propagated lit: %d", propagated_elit); #endif elit = external->propagator->cb_add_reason_clause_lit (propagated_elit); } else elit = external->propagator->cb_add_external_clause_lit (); // we need to be build a new LRAT chain if we are already in the middle of // the analysis (like during failed assumptions) LOG (lrat_chain, "lrat chain before"); std::vector lrat_chain_ext = std::move (lrat_chain); lrat_chain.clear (); clause.clear (); // Read out the external lemma into original and simplify it into clause CADICAL_assert (clause.empty ()); CADICAL_assert (original.empty ()); CADICAL_assert (!force_no_backtrack); CADICAL_assert (!from_propagator); force_no_backtrack = no_backtrack; from_propagator = true; while (elit) { CADICAL_assert (external->is_observed[abs (elit)]); external->add (elit); if (propagated_elit) elit = external->propagator->cb_add_reason_clause_lit (propagated_elit); else elit = external->propagator->cb_add_external_clause_lit (); } external->add (elit); CADICAL_assert (original.empty ()); CADICAL_assert (clause.empty ()); force_no_backtrack = false; from_propagator = false; lrat_chain = std::move (lrat_chain_ext); LOG (lrat_chain, "lrat chain after"); } /*----------------------------------------------------------------------------*/ // // Recursively calls 'learn_external_reason_clause' to explain every // backward reachable externally propagated literal starting from 'ilit'. // void Internal::explain_reason (int ilit, Clause *reason, int &open) { if (!opts.exteagerreasons) return; #ifndef CADICAL_NDEBUG LOG (reason, "explain_reason of %d (open: %d)", ilit, open); #endif CADICAL_assert (reason); CADICAL_assert (reason != external_reason); for (const auto &other : *reason) { if (other == ilit) continue; Flags &f = flags (other); if (f.seen) continue; Var &v = var (other); if (!v.level) continue; CADICAL_assert (val (other) < 0); CADICAL_assert (v.level <= level); if (v.reason == external_reason) { v.reason = learn_external_reason_clause (-other, 0, true); } if (v.level && v.reason) { f.seen = true; open++; } } } /*----------------------------------------------------------------------------*/ // // In case external propagation was used, the reason clauses of the relevant // propagations must be learned lazily before/during conflict analysis. // While conflict analysis needs to analyze only the current level, lazy // clause learning must check every clause on every level that is backward // reachable from the conflicting clause to guarantee that the assignment // levels of the variables are accurate. So this explanation round is // separated from the conflict analysis, thereby guranteeing that the flags // and datastructures can be properly used later. // // This function must be called before the conflict analysis, in order to // guarantee that every relevant reason clause is indeed learned already and // to be sure that the levels of assignments are set correctly. // // Later TODO: experiment with bounded explanation (explain only those // literals that are directly used during conflict analysis + // minimizing/shrinking). The assignment levels are then only // over-approximated. // void Internal::explain_external_propagations () { CADICAL_assert (conflict); CADICAL_assert (clause.empty ()); Clause *reason = conflict; std::vector seen_lits; int open = 0; // Seen but not explained literal explain_reason (0, reason, open); // marks conflict clause lits as seen int i = trail.size (); // Start at end-of-trail while (i > 0) { const int lit = trail[--i]; if (!flags (lit).seen) continue; seen_lits.push_back (lit); Var &v = var (lit); if (!v.level) continue; if (v.reason) { open--; explain_reason (lit, v.reason, open); } if (!open) break; } CADICAL_assert (!open); if (!opts.exteagerrecalc) { for (auto lit : seen_lits) { Flags &f = flags (lit); f.seen = false; } #ifndef CADICAL_NDEBUG for (auto idx : vars) { CADICAL_assert (!flags (idx).seen); } #endif } // Traverse now in the opposite direction (from lower to higher levels) // and calculate the actual assignment level for the seen assignments. for (auto it = seen_lits.rbegin (); it != seen_lits.rend (); ++it) { const int lit = *it; Flags &f = flags (lit); Var &v = var (lit); if (v.reason) { int real_level = 0; for (const auto &other : *v.reason) { if (other == lit) continue; int tmp = var (other).level; if (tmp > real_level) real_level = tmp; } if (v.level && !real_level) { build_chain_for_units (lit, v.reason, 1); learn_unit_clause (lit); lrat_chain.clear (); v.reason = 0; } CADICAL_assert (v.level >= real_level); if (v.level > real_level) { v.level = real_level; } } f.seen = false; } #if 0 // has been fuzzed extensively for (auto idx : vars) { CADICAL_assert (!flags (idx).seen); } #endif } /*----------------------------------------------------------------------------*/ // // Learns the reason clause of the propagation of ilit from the // external propagator via 'add_external_clause'. // In case of falsified propagation steps, if the propagated literal is // already fixed to the opposite value, externalize will not necessarily // give back the original elit (but an equivalent one). To avoid that, in // falsified propagation cases the propagated elit is added as a second // argument. // Clause *Internal::learn_external_reason_clause (int ilit, int falsified_elit, bool no_backtrack) { CADICAL_assert (external->propagator); // we cannot modify clause during analysis auto clause_tmp = std::move (clause); CADICAL_assert (clause.empty ()); CADICAL_assert (original.empty ()); stats.ext_prop.eprop_expl++; int elit = 0; if (!falsified_elit) { CADICAL_assert (!fixed (ilit)); elit = externalize (ilit); } else elit = falsified_elit; LOG ("ilit: %d, elit: %d", ilit, elit); add_external_clause (elit, no_backtrack); #ifndef CADICAL_NDEBUG if (!falsified_elit && newest_clause) { // Check if external propagation is correct wrt to the topological order // defined by the trail. In case it is a falsified external propagation // step, the order does not matter, the reason simply supposed to be a // falsified clause. const int propagated_ilit = ilit; for (auto const reason_ilit : *newest_clause) { CADICAL_assert (var (reason_ilit).trail <= var (propagated_ilit).trail); } } #endif clause = std::move (clause_tmp); return newest_clause; } /*----------------------------------------------------------------------------*/ // // Helper function to be able to call learn_external_reason_clause when the // internal clause is already used in the caller side (for example during // proof checking). These calls are assumed to be without a falsified elit. // Dont use it in general instead of learn_external_reason_clause because it // does not support the corner cases where a literal remains in clause. // Clause *Internal::wrapped_learn_external_reason_clause (int ilit) { Clause *res; std::vector chain_tmp{std::move (lrat_chain)}; lrat_chain.clear (); if (clause.empty ()) { res = learn_external_reason_clause (ilit, 0, true); } else { std::vector clause_tmp{std::move (clause)}; clause.clear (); res = learn_external_reason_clause (ilit, 0, true); // The learn_external_reason clause can leave a literal in clause when // there is a falsified elit arg. Here it is not allowed to // happen. CADICAL_assert (clause.empty ()); clause = std::move (clause_tmp); clause_tmp.clear (); } CADICAL_assert (lrat_chain.empty ()); lrat_chain = std::move (chain_tmp); chain_tmp.clear (); return res; } /*----------------------------------------------------------------------------*/ // // Checks if the new clause forces backtracking, new assignments or conflict // analysis // void Internal::handle_external_clause (Clause *res) { if (from_propagator) stats.ext_prop.elearned++; // at level 0 we have to do nothing... if (!level) return; if (!res) { if (from_propagator) stats.ext_prop.elearn_prop++; // new unit clause. For now just backtrack. CADICAL_assert (!force_no_backtrack); CADICAL_assert (level); // if (!opts.chrono) { backtrack (); // } return; } if (from_propagator) stats.ext_prop.elearned++; CADICAL_assert (res->size >= 2); const int pos0 = res->literals[0]; const int pos1 = res->literals[1]; if (force_no_backtrack) { CADICAL_assert (val (pos1) < 0); CADICAL_assert (val (pos0) >= 0); return; // TODO: maybe fix levels } const int l1 = var (pos1).level; if (val (pos0) < 0) { // conflicting or propagating clause CADICAL_assert (0 < l1 && l1 <= var (pos0).level); if (!opts.chrono) { backtrack (l1); } if (val (pos0) < 0) { conflict = res; if (!from_propagator) { // its better to backtrack instead of analyze backtrack (l1 - 1); conflict = 0; CADICAL_assert (!val (pos0) && !val (pos1)); } } else { search_assign_driving (pos0, res); } if (from_propagator) stats.ext_prop.elearn_conf++; return; } if (val (pos1) < 0 && !val (pos0)) { // propagating clause if (!opts.chrono) { backtrack (l1); } search_assign_driving (pos0, res); if (from_propagator) stats.ext_prop.elearn_conf++; return; } } /*----------------------------------------------------------------------------*/ // // Asks the external propagator if the current solution is OK // by calling 'cb_check_found_model (model)'. // // The checked model is built up after everything is restored // from the reconstruction stack and every variable is reactivated // and so it is not just simply the trail (i.e. it can be expensive). // // If the external propagator approves the model, the function // returns true. // // If the propagator does not approve the model, the solver asks // the propagator to add an external clause. // This external clause, however, does NOT have to be falsified by // the current model. The possible cases and reactions are described // below in the function. The possible states after that function: // - A solution was found and accepted by the external propagator // - A conflicting clause was learned from the external propagator // - The empty clause was learned due to something new learned from // the external propagator. // // In case only new variables were introduced, but no new clauses were // added, the function will return without a conflict to the outer CDCL // loop, where the new (not yet satisfied) variables are recognized and // the search continues. bool Internal::external_check_solution () { if (!external_prop) return true; bool trail_changed = true; bool added_new_clauses = false; while (trail_changed || added_new_clauses) { notify_assignments (); if (!satisfied ()) break; trail_changed = false; // to be on the safe side added_new_clauses = false; LOG ("Final check by external propagator is invoked."); stats.ext_prop.echeck_call++; external->reset_extended (); external->extend (); std::vector etrail; // Here the variables must be filtered by external->is_observed, // because fixed variables are internally not necessarily observed // anymore. for (int idx = 1; idx <= std::min ((int) external->is_observed.size () - 1, external->max_var); idx++) { if (!external->is_observed[idx]) continue; const int lit = external->ival (idx); etrail.push_back (lit); #ifndef CADICAL_NDEBUG #ifdef LOGGING bool p = external->vals[idx]; LOG ("evals[%d]: %d ival(%d): %d", idx, p, idx, lit); #endif #endif } bool is_consistent = external->propagator->cb_check_found_model (etrail); stats.ext_prop.ext_cb++; if (is_consistent) { LOG ("Found solution is approved by external propagator."); return true; } bool has_external_clause = ask_external_clause (); stats.ext_prop.ext_cb++; stats.ext_prop.elearn_call++; if (has_external_clause) LOG ( "Found solution triggered new clauses from external propagator."); while (has_external_clause) { int level_before = level; size_t assigned = num_assigned; add_external_clause (0); bool trail_changed = (num_assigned != assigned || level != level_before || propagated < trail.size ()); added_new_clauses = true; // // There are many possible scenarios here: // - Learned conflicting clause: return to CDCL loop (conflict true) // - Learned conflicting unit clause that after backtrack+BCP leads to // a new complete solution: force the outer loop to check the new // model (trail_changed is true, but (conflict & unsat) is false) // - Learned empty clause: return to CDCL loop (unsat true) // - Learned a non-conflicting unit clause: // Though it does not invalidate the current solution, the solver // will backtrack to the root level and will repropagate it. The // search will start again (saved phases hopefully make it quick), // but it is needed in order to guarantee that every fixed variable // is properly handled+notified (important for incremental use // cases). // - Otherwise: the solution is considered approved and the CDCL-loop // can return with res = 10. // if (unsat || conflict || trail_changed) break; has_external_clause = ask_external_clause (); stats.ext_prop.ext_cb++; stats.ext_prop.elearn_call++; } LOG ("No more external clause to add."); if (unsat || conflict) break; } if (!unsat && conflict) { const int conflict_level = var (conflict->literals[0]).level; if (conflict_level != level) { backtrack (conflict_level); } } return !conflict; } /*----------------------------------------------------------------------------*/ // // Notify the external propagator that an observed variable got assigned. // void Internal::notify_assignments () { if (!external_prop || external_prop_is_lazy || private_steps) return; const size_t end_of_trail = trail.size (); if (notified >= end_of_trail) return; LOG ("notify external propagator about new assignments"); std::vector assigned; while (notified < end_of_trail) { int ilit = trail[notified++]; if (!observed (ilit)) continue; int elit = externalize (ilit); // TODO: double-check tainting CADICAL_assert (elit); if (external->ervars[abs (elit)]) continue; // Fixed variables might get mapped (during compact) to another // non-observed but fixed variable. // This happens on root level, so notification about their assignment is // already done. CADICAL_assert (external->observed (elit) || (fixed (ilit) && !external->ervars[abs (elit)])); assigned.push_back (elit); } external->propagator->notify_assignment (assigned); return; } /*----------------------------------------------------------------------------*/ void Internal::connect_propagator () { if (level) backtrack (); } /*----------------------------------------------------------------------------*/ // // Notify the external propagator that a new decision level is started. // void Internal::notify_decision () { if (!external_prop || external_prop_is_lazy || private_steps) return; external->propagator->notify_new_decision_level (); } /*----------------------------------------------------------------------------*/ // // Notify the external propagator that backtrack to new_level. // void Internal::notify_backtrack (size_t new_level) { if (!external_prop || external_prop_is_lazy || private_steps) return; external->propagator->notify_backtrack (new_level); } /*----------------------------------------------------------------------------*/ // // Ask the external propagator if there is a suggested literal as next // decision. // int Internal::ask_decision () { if (!external_prop || external_prop_is_lazy || private_steps) return 0; CADICAL_assert (!unsat); CADICAL_assert (!conflict); notify_assignments (); int level_before = level; forced_backt_allowed = true; int elit = external->propagator->cb_decide (); forced_backt_allowed = false; stats.ext_prop.ext_cb++; if (level_before != level) { propagate (); CADICAL_assert (!unsat); CADICAL_assert (!conflict); notify_assignments (); // In case the external propagator forced to backtrack below the // pseduo decision levels, we must go back to the CDCL loop instead of // making a decision. if ((size_t) level < assumptions.size () || ((size_t) level == assumptions.size () && constraint.size ())) { return 0; } } if (!elit) return 0; LOG ("external propagator proposes decision: %d", elit); CADICAL_assert (external->is_observed[abs (elit)]); if (!external->is_observed[abs (elit)]) return 0; int ilit = external->e2i[abs (elit)]; if (elit < 0) ilit = -ilit; CADICAL_assert (fixed (ilit) || observed (ilit)); LOG ("Asking external propagator for decision returned: %d (internal: " "%d, fixed: %d, val: %d)", elit, ilit, fixed (ilit), val (ilit)); if (fixed (ilit) || val (ilit)) { LOG ("Proposed decision variable is already assigned, falling back to " "internal decision."); return 0; } return ilit; } /*----------------------------------------------------------------------------*/ // // Check if the clause is a forgettable clause coming from the external // propagator. // bool Internal::is_external_forgettable (int64_t id) { CADICAL_assert (opts.check); return (external->forgettable_original.find (id) != external->forgettable_original.end ()); } /*----------------------------------------------------------------------------*/ // // When an external forgettable clause is deleted, it is marked in the // 'forgettable_original' hash, so that the internal model checking can // ignore it. // void Internal::mark_garbage_external_forgettable (int64_t id) { CADICAL_assert (opts.check); CADICAL_assert (is_external_forgettable (id)); LOG (external->forgettable_original[id], "forgettable external lemma is deleted:"); // Mark as removed by flipping the first flag to false. external->forgettable_original[id][0] = 0; } /*----------------------------------------------------------------------------*/ // // Check that the literals in the clause are properly ordered. Used only // internally for debug purposes. // void Internal::check_watched_literal_invariants () { #ifndef CADICAL_NDEBUG int v0 = 0; int v1 = 0; if (val (clause[0]) > 0) v0 = 1; else if (val (clause[0]) < 0) v0 = -1; if (val (clause[1]) > 0) v1 = 1; else if (val (clause[1]) < 0) v1 = -1; CADICAL_assert (v0 >= v1); #endif if (val (clause[0]) > 0) { if (val (clause[1]) > 0) { // Case 1: Both literals are satisfied // They are ordered by lower to higher decision level CADICAL_assert (var (clause[0]).level <= var (clause[1]).level); // Every other literal of the clause is either // - satisfied at higher level // - unassigned // - falsified for (size_t i = 2; i < clause.size (); i++) CADICAL_assert (val (clause[i]) <= 0 || (var (clause[1]).level <= var (clause[i]).level)); } else if (val (clause[1]) == 0) { // Case 2: First satisfied, next unassigned // Every other literal of the clause is either // - unassigned // - falsified for (size_t i = 2; i < clause.size (); i++) CADICAL_assert (val (clause[i]) <= 0); } else { // Case 3: First satisfied, next falsified -> could have been a // reason of a previous propagation // Every other literal of the clause is falsified but at a lower // decision level for (size_t i = 2; i < clause.size (); i++) CADICAL_assert (val (clause[i]) < 0 && (var (clause[1]).level >= var (clause[i]).level)); } } else if (val (clause[0]) == 0) { if (val (clause[1]) == 0) { // Case 4: Both literals are unassigned // Every other literal of the clause is either // - unassigned // - falsified for (size_t i = 2; i < clause.size (); i++) CADICAL_assert (val (clause[i]) <= 0); } else { // Case 5: First unassigned, next falsified -> PROPAGATE // Every other literal of the clause is falsified but at a lower // decision level for (size_t i = 2; i < clause.size (); i++) CADICAL_assert (val (clause[i]) < 0 && (var (clause[1]).level >= var (clause[i]).level)); } } else { CADICAL_assert (val (clause[0]) < 0 && val (clause[1]) < 0); // Case 6: Both literals are falsified // They are ordered by higher to lower decision level CADICAL_assert (var (clause[0]).level >= var (clause[1]).level); // Every other literal of the clause is falsified, but at a lower level for (size_t i = 2; i < clause.size (); i++) CADICAL_assert (val (clause[i]) < 0 && (var (clause[1]).level >= var (clause[i]).level)); } } #ifndef CADICAL_NDEBUG /*----------------------------------------------------------------------------*/ // // An expensive function that can be used for deep-debug trail-related // issues in mobical. Do not use it unless it is really unavoidable. // // eq_class contains all the merged external literals that are currently // compacted to the internal literal of trail[0] and return true. // // In case trail[0] does not exists or is not on the root level, the // function returns false (indicating that there was no merger literal // found). // bool Internal::get_merged_literals (std::vector &eq_class) { eq_class.clear (); if (!trail.size ()) return false; int ilit = trail[0]; size_t lit_level = var (ilit).level; if (!lit_level) { // Collect all the variables that are merged and mapped to that ilit size_t e2i_size = external->e2i.size (); int ivar = abs (ilit); for (size_t i = 0; i < e2i_size; i++) { int other = abs (external->e2i[i]); if (other == ivar) { if (external->e2i[i] == ilit) eq_class.push_back (i); else eq_class.push_back (-1 * i); } } return true; } return false; } /*----------------------------------------------------------------------------*/ // // Collect all external variables that are FIXED internally. Again an // expensive function that should be called only for debugging in mobical. // // Do not use it unless it is really unavoidable. // void Internal::get_all_fixed_literals (std::vector &fixed_lits) { fixed_lits.clear (); if (!trail.size ()) return; int e2i_size = external->e2i.size (); int ilit; for (int eidx = 1; eidx < e2i_size; eidx++) { ilit = external->e2i[eidx]; if (ilit && !external->ervars[eidx]) { Flags &f = flags (ilit); if (f.status == Flags::FIXED) { fixed_lits.push_back (vals[abs (ilit)] * eidx); } } } } #endif } // namespace CaDiCaL ABC_NAMESPACE_IMPL_END