#include "global.h" #include "internal.hpp" ABC_NAMESPACE_IMPL_START namespace CaDiCaL { /*------------------------------------------------------------------------*/ // Globally blocked clause elimination (which we call here 'conditioning') // is described first in the PhD thesis of Benjamin Kiesl from 2019. An // extended version, which in particular describes the algorithm implemented // below is in our invited ATVA'19 paper [KieslHeuleBiere-ATVA'19]. This // accordingly needs witnesses consisting potentially of more than one // literal. It is the first technique implemented in CaDiCaL with this // feature (PR clause elimination thus should work in principle too). // Basically globally blocked clauses are like set blocked clauses, except // that the witness cube (of literals to be flipped during reconstruction) // can contain variables which are not in the blocked clause. This // can simulate some interesting global optimizations like 'headlines' from // the FAN algorithm for ATPG. The technique was actually motivated to // simulate this optimization. It turns out that globally blocked clauses // can be seen as 'conditional autarkies', where in essence the condition // cube is the negation of the globally blocked redundant clause (it // needs to contain one autarky literal though) and the autarky part // represents the witness. /*------------------------------------------------------------------------*/ // Elimination of globally blocked clauses is first tried in regular // intervals in terms of the number of conflicts. Then the main heuristics // is to trigger 'condition' if the decision level is above the current // moving average of the back jump level. // TODO We might need to consider less frequent conditioning. bool Internal::conditioning () { if (!opts.condition) return false; if (!preprocessing && !opts.inprocessing) return false; if (preprocessing) CADICAL_assert (lim.preprocessing); // Triggered in regular 'opts.conditionint' conflict intervals. // if (lim.condition > stats.conflicts) return false; if (!level) return false; // One decision necessary. if (level <= averages.current.jump) return false; // Main heuristic. if (!stats.current.irredundant) return false; double remain = active (); if (!remain) return false; double ratio = stats.current.irredundant / remain; return ratio <= opts.conditionmaxrat; } /*------------------------------------------------------------------------*/ // We start with the current assignment and then temporarily unassign // literals. They are reassigned afterwards. The global state of the CDCL // solver should not change though. Thus we copied from 'search_unassign' // in 'backtrack.cpp' what is needed to unassign literals and then from // 'search_assign' in 'propagate.cpp' what is needed for reassigning // literals, but restricted the copied code to only updating the actual // assignment (in 'vals') and not changing anything else. // We use temporarily unassigning for two purposes. First, if a conditional // literal does not occur negated in a candidate clause it is unassigned. // Second, as a minor optimization, we first unassign all root-level // assigned (fixed) literals, to avoid checking the decision level of // literals during the procedure. void Internal::condition_unassign (int lit) { LOG ("condition unassign %d", lit); CADICAL_assert (val (lit) > 0); set_val (lit, 0); } void Internal::condition_assign (int lit) { LOG ("condition assign %d", lit); CADICAL_assert (!val (lit)); set_val (lit, 1); } /*------------------------------------------------------------------------*/ // The current partition into conditional part and autarky part during // refinement is represented through a conditional bit in 'marks'. inline bool Internal::is_conditional_literal (int lit) const { return val (lit) > 0 && getbit (lit, 0); } inline bool Internal::is_autarky_literal (int lit) const { return val (lit) > 0 && !getbit (lit, 0); } inline void Internal::mark_as_conditional_literal (int lit) { LOG ("marking %d as conditional literal", lit); CADICAL_assert (val (lit) > 0); setbit (lit, 0); CADICAL_assert (is_conditional_literal (lit)); CADICAL_assert (!is_autarky_literal (lit)); } inline void Internal::unmark_as_conditional_literal (int lit) { LOG ("unmarking %d as conditional literal", lit); CADICAL_assert (is_conditional_literal (lit)); unsetbit (lit, 0); } /*------------------------------------------------------------------------*/ // We also need to know the literals which are in the current clause. These // are just marked (also in 'marks' but with the (signed) upper two bits). // We need a signed mark here, since we have to distinguish positive and // negative occurrences of literals in the candidate clause. inline bool Internal::is_in_candidate_clause (int lit) const { return marked67 (lit) > 0; } inline void Internal::mark_in_candidate_clause (int lit) { LOG ("marking %d as literal of the candidate clause", lit); mark67 (lit); CADICAL_assert (is_in_candidate_clause (lit)); CADICAL_assert (!is_in_candidate_clause (-lit)); } inline void Internal::unmark_in_candidate_clause (int lit) { LOG ("unmarking %d as literal of the candidate clause", lit); CADICAL_assert (is_in_candidate_clause (lit)); unmark67 (lit); } /*------------------------------------------------------------------------*/ struct less_conditioned { bool operator() (Clause *a, Clause *b) { return !a->conditioned && b->conditioned; } }; // This is the function for eliminating globally blocked clauses. It is // triggered during CDCL search according to 'conditioning' above and uses // the current assignment as basis to find globally blocked clauses. long Internal::condition_round (long delta) { long limit; #ifndef CADICAL_QUIET long props = 0; #endif if (LONG_MAX - delta < stats.condprops) limit = LONG_MAX; else limit = stats.condprops + delta; size_t initial_trail_level = trail.size (); int initial_level = level; LOG ("initial trail level %zd", initial_trail_level); protect_reasons (); #if defined(LOGGING) || !defined(CADICAL_NDEBUG) int additionally_assigned = 0; #endif for (auto idx : vars) { const signed char tmp = val (idx); Var &v = var (idx); if (tmp) { if (v.level) { const int lit = tmp < 0 ? -idx : idx; if (!active (idx)) { LOG ("temporarily unassigning inactive literal %d", lit); condition_unassign (lit); } if (frozen (idx)) { LOG ("temporarily unassigning frozen literal %d", lit); condition_unassign (lit); } } } else if (frozen (idx)) { LOG ("keeping frozen literal %d unassigned", idx); } else if (!active (idx)) { LOG ("keeping inactive literal %d unassigned", idx); } else { // if (preprocessing) { if (initial_level == level) { level++; LOG ("new condition decision level"); } const int lit = decide_phase (idx, true); condition_assign (lit); v.level = level; trail.push_back (lit); #if defined(LOGGING) || !defined(CADICAL_NDEBUG) additionally_assigned++; #endif } } LOG ("assigned %d additional literals", additionally_assigned); // We compute statistics about the size of the assignments. // // The initial assignment consists of the non-root-level assigned literals // split into a conditional and an autarky part. The conditional part // consists of literals assigned true and occurring negated in a clause // (touch the clause), which does not contain another literal assigned to // true. This initial partition is the same for all refinements used in // checking whether a candidate clause is globally blocked. // // For each candidate clause some of the conditional literals have to be // unassigned, and the autarky is shrunken by turning some of the autarky // literals into conditional literals (which might get unassigned in a // later refinement though). // // The fix-point of this procedure produces a final assignment, which // consists of the remaining assigned literals, again split into a // conditional and an autarky part. // struct { size_t assigned, conditional, autarky; } initial, remain; initial.assigned = 0; for (auto idx : vars) { const signed char tmp = val (idx); if (!tmp) continue; if (!var (idx).level) continue; LOG ("initial assignment %ds", tmp < 0 ? -idx : idx); initial.assigned++; } PHASE ("condition", stats.conditionings, "initial assignment of size %zd", initial.assigned); // For each candidate clause we refine the assignment (monotonically), // by unassigning some conditional literals and turning some autarky // literals into conditionals. // // As the conditional part is usually smaller than the autarky part our // implementation only explicitly maintains the initial conditional part, // with conditional bit set to true through 'mark_as_conditional_literal'. // The autarky part consists of all literals assigned true which do not // have their conditional bit set to true. Since in both cases the // literal has to be assigned true, we only need a single bit for both the // literal as well as its negation (it does not have to be 'signed'). // vector conditional; vector candidates; // Gather candidate clauses. #ifndef CADICAL_QUIET size_t watched = 0; // Number of watched clauses. #endif initial.autarky = initial.assigned; // Initially all are in autarky initial.conditional = 0; // and none in conditional part. // Upper bound on the number of watched clauses. In principle one could // use 'SIZE_MAX' but this is not available by default (yet). // const size_t size_max = clauses.size () + 1; // Initialize additional occurrence lists. // init_occs (); // Number of previously conditioned and unconditioned candidates. // size_t conditioned = 0, unconditioned = 0; // Now go over all (non-garbage) irredundant clauses and check whether // they are candidates, have to be watched, or whether they force the // negation of some of their literals to be conditional initially. // for (const auto &c : clauses) { if (c->garbage) continue; // Can already be ignored. if (c->redundant) continue; // Ignore redundant clauses too. // First determine the following numbers for the candidate clause // (restricted to non-root-level assignments). // int positive = 0; // Number true literals. int negative = 0; // Number false literals. int watch = 0; // True Literal to watch. // size_t minsize = size_max; // Number of occurrences of 'watch'. // // But also ignore root-level satisfied but not yet garbage clauses. // bool satisfied = false; // Root level satisfied. // for (const_literal_iterator l = c->begin (); !satisfied && l != c->end (); l++) { const int lit = *l; const signed char tmp = val (lit); if (tmp && !var (lit).level) satisfied = (tmp > 0); else if (tmp < 0) negative++; else if (tmp > 0) { const size_t size = occs (lit).size (); if (size < minsize) watch = lit, minsize = size; positive++; } } if (satisfied) { // Ignore root-level satisfied clauses. mark_garbage (c); // But mark them as garbage already now. continue; // ... with next clause 'c'. } // Candidates are clauses with at least a positive literal in it. // if (positive > 0) { LOG (c, "found %d positive literals in candidate", positive); candidates.push_back (c); if (c->conditioned) conditioned++; else unconditioned++; } // Only one positive literal in each clauses with also at least one // negative literal has to be watched in occurrence lists. These // watched clauses will be checked to contain only negative literals as // soon such a positive literal is unassigned. If this is the case // these false literals have to be unassigned and potentially new // conditional literals have to be determined. // // Note that only conditional literals are unassigned. However it does // not matter that we might also watch autarky literals, because either // such an autarky literal remains a witness that the clause is // satisfied as long it remains an autarky literal. Otherwise at one // point it becomes conditional and is unassigned, but then a // replacement watch will be searched. // if (negative > 0 && positive > 0) { LOG (c, "found %d negative literals in candidate", negative); CADICAL_assert (watch); CADICAL_assert (val (watch) > 0); Occs &os = occs (watch); CADICAL_assert (os.size () == minsize); os.push_back (c); #ifndef CADICAL_QUIET watched++; #endif LOG (c, "watching %d with %zd occurrences in", watch, minsize); } // The initial global conditional part for the current assignment is // extracted from clauses with only negative literals. It is the same // for all considered candidate clauses. These negative literals make up // the global conditional part, are marked here. // if (negative > 0 && !positive) { size_t new_conditionals = 0; for (const_literal_iterator l = c->begin (); l != c->end (); l++) { const int lit = *l; signed char tmp = val (lit); if (!tmp) continue; CADICAL_assert (tmp < 0); if (!var (lit).level) continue; // Not unassigned yet! if (is_conditional_literal (-lit)) continue; mark_as_conditional_literal (-lit); conditional.push_back (-lit); new_conditionals++; } if (new_conditionals > 0) LOG (c, "marked %zu negations of literals as conditional in", new_conditionals); initial.conditional += new_conditionals; CADICAL_assert (initial.autarky >= new_conditionals); initial.autarky -= new_conditionals; } } // End of loop over all clauses to collect candidates etc. PHASE ("condition", stats.conditionings, "found %zd candidate clauses", candidates.size ()); PHASE ("condition", stats.conditionings, "watching %zu literals and clauses", watched); PHASE ("condition", stats.conditionings, "initially %zd conditional literals %.0f%%", initial.conditional, percent (initial.conditional, initial.assigned)); PHASE ("condition", stats.conditionings, "initially %zd autarky literals %.0f%%", initial.autarky, percent (initial.autarky, initial.assigned)); #ifdef LOGGING for (size_t i = 0; i < conditional.size (); i++) { LOG ("initial conditional %d", conditional[i]); CADICAL_assert (is_conditional_literal (conditional[i])); } for (size_t i = 0; i < trail.size (); i++) if (is_autarky_literal (trail[i])) LOG ("initial autarky %d", trail[i]); #endif CADICAL_assert (initial.conditional == conditional.size ()); CADICAL_assert (initial.assigned == initial.conditional + initial.autarky); stats.condassinit += initial.assigned; stats.condcondinit += initial.conditional; stats.condautinit += initial.autarky; stats.condassvars += active (); // To speed-up and particularly simplify the code we unassign all // root-level variables temporarily, actually all inactive assigned // variables. This allows us to avoid tests on whether an assigned // literal is actually root-level assigned and thus should be ignored (not // considered to be assigned). For this to work we have to ignore root // level satisfied clauses as done above. These are neither candidates // nor have to be watched. Remaining originally root-level assigned // literals in clauses are only set to false. // for (const auto &lit : trail) if (fixed (lit)) condition_unassign (lit); // Stack to save temporarily unassigned (conditional) literals. // vector unassigned; // Make sure to focus on clauses not tried before by marking clauses which // have been checked before using the 'conditioned' bit of clauses. If all // candidates have their bit set, we have to reset it. Since the // assignment might be completely different then last time and thus also // the set of candidates this method does not really exactly lead to a // round robin scheme of scheduling clauses. // // TODO consider computing conditioned and unconditioned over all clauses. // CADICAL_assert (conditioned + unconditioned == candidates.size ()); if (conditioned && unconditioned) { stable_sort (candidates.begin (), candidates.end (), less_conditioned ()); PHASE ("condition", stats.conditionings, "focusing on %zd candidates %.0f%% not tried last time", unconditioned, percent (unconditioned, candidates.size ())); } else if (conditioned && !unconditioned) { for (auto const &c : candidates) { CADICAL_assert (c->conditioned); c->conditioned = false; // Reset 'conditioned' bit. } PHASE ("condition", stats.conditionings, "all %zd candidates tried before", conditioned); } else { CADICAL_assert (!conditioned); PHASE ("condition", stats.conditionings, "all %zd candidates are fresh", unconditioned); } // TODO prune assignments further! // And thus might result in less watched clauses. // So watching should be done here and not earlier. // Also, see below, we might need to consider the negation of unassigned // literals in candidate clauses as being watched. // Now try to block all candidate clauses. // long blocked = 0; // Number of Successfully blocked clauses. // #ifndef CADICAL_QUIET size_t untried = candidates.size (); #endif for (const auto &c : candidates) { if (initial.autarky <= 0) break; if (c->reason) continue; bool terminated_or_limit_hit = true; if (terminated_asynchronously ()) LOG ("asynchronous termination detected"); else if (stats.condprops >= limit) LOG ("condition propagation limit %ld hit", limit); else terminated_or_limit_hit = false; if (terminated_or_limit_hit) { PHASE ("condition", stats.conditionings, "%zd candidates %.0f%% not tried after %ld propagations", untried, percent (untried, candidates.size ()), props); break; } #ifndef CADICAL_QUIET untried--; #endif CADICAL_assert (!c->garbage); CADICAL_assert (!c->redundant); LOG (c, "candidate"); c->conditioned = 1; // Next time later. // We watch an autarky literal in the clause, and can stop trying to // globally block the clause as soon it turns into a conditional // literal and we can not find another one. If the fix-point assignment // is reached and we still have an autarky literal left the watched one // is reported as witness for this clause being globally blocked. // int watched_autarky_literal = 0; // First mark all true literals in the candidate clause and find an // autarky literal which witnesses that this clause has still a chance // to be globally blocked. // for (const_literal_iterator l = c->begin (); l != c->end (); l++) { const int lit = *l; mark_in_candidate_clause (lit); if (watched_autarky_literal) continue; if (!is_autarky_literal (lit)) continue; watched_autarky_literal = lit; // TODO assign non-assigned literals to false? // Which might need to trigger watching additional clauses. } if (!watched_autarky_literal) { LOG ("no initial autarky literal found"); for (const_literal_iterator l = c->begin (); l != c->end (); l++) unmark_in_candidate_clause (*l); continue; } stats.condcands++; // Only now ... LOG ("watching first autarky literal %d", watched_autarky_literal); // Save assignment sizes for statistics, logging and checking. // remain = initial; // Position of next conditional and unassigned literal to process in the // 'conditional' and the 'unassigned' stack. // struct { size_t conditional, unassigned; } next = {0, 0}; CADICAL_assert (unassigned.empty ()); CADICAL_assert (conditional.size () == initial.conditional); while (watched_autarky_literal && stats.condprops < limit && next.conditional < conditional.size ()) { CADICAL_assert (next.unassigned == unassigned.size ()); const int conditional_lit = conditional[next.conditional++]; LOG ("processing next conditional %d", conditional_lit); CADICAL_assert (is_conditional_literal (conditional_lit)); if (is_in_candidate_clause (-conditional_lit)) { LOG ("conditional %d negated in candidate clause", conditional_lit); continue; } LOG ("conditional %d does not occur negated in candidate clause", conditional_lit); condition_unassign (conditional_lit); CADICAL_assert (!is_conditional_literal (conditional_lit)); unassigned.push_back (conditional_lit); CADICAL_assert (remain.assigned > 0); CADICAL_assert (remain.conditional > 0); remain.conditional--; remain.assigned--; while (watched_autarky_literal && stats.condprops < limit && next.unassigned < unassigned.size ()) { const int unassigned_lit = unassigned[next.unassigned++]; LOG ("processing next unassigned %d", unassigned_lit); CADICAL_assert (!val (unassigned_lit)); #ifndef CADICAL_QUIET props++; #endif stats.condprops++; Occs &os = occs (unassigned_lit); if (os.empty ()) continue; // Traverse all watched clauses of 'unassigned_lit' and find // replacement watches or if none is found turn the negation of all // false autarky literals in that clause into conditional literals. // If one of those autarky literals is the watched autarky literal // in the candidate clause, that one has to be updated too. // // We expect that this loop is a hot-spot for the procedure and thus // are more careful about accessing end points for iterating. // auto i = os.begin (), j = i; for (; watched_autarky_literal && j != os.end (); j++) { Clause *d = *i++ = *j; int replacement = 0; // New watched literal in 'd'. int negative = 0; // Negative autarky literals in 'd'. for (const_literal_iterator l = d->begin (); l != d->end (); l++) { const int lit = *l; const signed char tmp = val (lit); if (tmp > 0) replacement = lit; if (tmp < 0 && is_autarky_literal (-lit)) negative++; } if (replacement) { LOG ("found replacement %d for unassigned %d", replacement, unassigned_lit); LOG (d, "unwatching %d in", unassigned_lit); i--; // Drop watch! LOG (d, "watching %d in", replacement); CADICAL_assert (replacement != unassigned_lit); occs (replacement).push_back (d); continue; // ... with next watched clause 'd'. } LOG ("no replacement found for unassigned %d", unassigned_lit); // Keep watching 'd' by 'unassigned_lit' if no replacement found. if (!negative) { LOG (d, "no negative autarky literals left in"); continue; // ... with next watched clause 'd'. } LOG (d, "found %d negative autarky literals in", negative); for (const_literal_iterator l = d->begin (); watched_autarky_literal && l != d->end (); l++) { const int lit = *l; if (!is_autarky_literal (-lit)) continue; mark_as_conditional_literal (-lit); conditional.push_back (-lit); remain.conditional++; CADICAL_assert (remain.autarky > 0); remain.autarky--; if (-lit != watched_autarky_literal) continue; LOG ("need to replace autarky literal %d in candidate", -lit); replacement = 0; // TODO save starting point because we only move it forward? for (const_literal_iterator k = c->begin (); !replacement && k != c->end (); k++) { const int other = *k; if (is_autarky_literal (other)) replacement = other; } watched_autarky_literal = replacement; if (replacement) { LOG (c, "watching autarky %d instead %d in candidate", replacement, watched_autarky_literal); watched_autarky_literal = replacement; } else { LOG ("failed to find an autarky replacement"); watched_autarky_literal = 0; // Breaks out of 4 loops!!!!! } } // End of loop of turning autarky literals into conditionals. } // End of loop of all watched clauses of an unassigned literal. // // We might abort the occurrence traversal early but already // removed some watches, thus have to just copy the rest. // if (i < j) { while (j != os.end ()) *i++ = *j++; LOG ("flushed %zd occurrences of %d", os.end () - i, unassigned_lit); os.resize (i - os.begin ()); } } // End of loop which goes over all unprocessed unassigned literals. } // End of loop which goes over all unprocessed conditional literals. // We are still processing the candidate 'c' and now have reached a // final fix-point assignment partitioned into a conditional and an // autarky part, or during unassigned literals figured that there is no // positive autarky literal left in 'c'. LOG ("remaining assignment of size %zd", remain.assigned); LOG ("remaining conditional part of size %zd", remain.conditional); LOG ("remaining autarky part of size %zd", remain.autarky); // CADICAL_assert (remain.assigned - remain.conditional == remain.autarky); // #if defined(LOGGING) || !defined(CADICAL_NDEBUG) // // This is a sanity check, that the size of our implicit representation // of the autarky part matches our 'remain' counts. We need the same // code for determining autarky literals as in the loop below which adds // autarky literals to the extension stack. // struct { size_t assigned, conditional, autarky; } check; check.assigned = check.conditional = check.autarky = 0; for (size_t i = 0; i < trail.size (); i++) { const int lit = trail[i]; if (val (lit)) { check.assigned++; if (is_conditional_literal (lit)) { LOG ("remaining conditional %d", lit); CADICAL_assert (!is_autarky_literal (lit)); check.conditional++; } else { CADICAL_assert (is_autarky_literal (lit)); LOG ("remaining autarky %d", lit); check.autarky++; } } else { CADICAL_assert (!is_autarky_literal (lit)); CADICAL_assert (!is_conditional_literal (lit)); } } CADICAL_assert (remain.assigned == check.assigned); CADICAL_assert (remain.conditional == check.conditional); CADICAL_assert (remain.autarky == check.autarky); #endif // Success if an autarky literal is left in the clause and // we did not abort the loop too early because the propagation // limit was hit. // if (watched_autarky_literal && stats.condprops < limit) { CADICAL_assert (is_autarky_literal (watched_autarky_literal)); CADICAL_assert (is_in_candidate_clause (watched_autarky_literal)); blocked++; stats.conditioned++; LOG (c, "positive autarky literal %d globally blocks", watched_autarky_literal); LOG ("remaining %zd assigned literals %.0f%%", remain.assigned, percent (remain.assigned, initial.assigned)); LOG ("remaining %zd conditional literals %.0f%%", remain.conditional, percent (remain.conditional, remain.assigned)); LOG ("remaining %zd autarky literals %.0f%%", remain.autarky, percent (remain.autarky, remain.assigned)); // A satisfying assignment of a formula after removing a globally // blocked clause might not satisfy that clause. As for variable // elimination and classical blocked clauses, we thus maintain an // extension stack for reconstructing an assignment which both // satisfies the remaining formula as well as the clause. // // For globally blocked clauses we simply have to flip all literals in // the autarky part and thus save the autarky on the extension stack // in addition to the removed clause. In the classical situation (in // bounded variable elimination etc.) we simply save one literal on // the extension stack. // // TODO find a way to shrink the autarky part or some other way to // avoid pushing too many literals on the extension stack. // external->push_zero_on_extension_stack (); for (const auto &lit : trail) if (is_autarky_literal (lit)) external->push_witness_literal_on_extension_stack (lit); if (proof) proof->weaken_minus (c); external->push_clause_on_extension_stack (c); mark_garbage (c); stats.condassrem += remain.assigned; stats.condcondrem += remain.conditional; stats.condautrem += remain.autarky; stats.condassirem += initial.assigned; } // In this last part specific to one candidate clause, we have to get // back to the initial assignment and reset conditionals. First we // assign all the unassigned literals (if necessary). // if (!unassigned.empty ()) { LOG ("reassigning %zd literals", unassigned.size ()); while (!unassigned.empty ()) { const int lit = unassigned.back (); unassigned.pop_back (); condition_assign (lit); } } // Then we remove from the conditional stack autarky literals which // became conditional and also reset their 'conditional' bit. // if (initial.conditional < conditional.size ()) { LOG ("flushing %zd autarky literals from conditional stack", conditional.size () - initial.conditional); while (initial.conditional < conditional.size ()) { const int lit = conditional.back (); conditional.pop_back (); unmark_as_conditional_literal (lit); } } // Finally unmark all literals in the candidate clause. // for (const_literal_iterator l = c->begin (); l != c->end (); l++) unmark_in_candidate_clause (*l); } // End of loop over all candidate clauses. PHASE ("condition", stats.conditionings, "globally blocked %ld clauses %.0f%%", blocked, percent (blocked, candidates.size ())); // Unmark initial conditional variables. // for (const auto &lit : conditional) unmark_as_conditional_literal (lit); erase_vector (unassigned); erase_vector (conditional); erase_vector (candidates); // Unassign additionally assigned literals. // #if defined(LOGGING) || !defined(CADICAL_NDEBUG) int additionally_unassigned = 0; #endif while (trail.size () > initial_trail_level) { int lit = trail.back (); trail.pop_back (); condition_unassign (lit); #if defined(LOGGING) || !defined(CADICAL_NDEBUG) additionally_unassigned++; #endif } LOG ("unassigned %d additionally assigned literals", additionally_unassigned); CADICAL_assert (additionally_unassigned == additionally_assigned); if (level > initial_level) { LOG ("reset condition decision level"); level = initial_level; } reset_occs (); delete_garbage_clauses (); // Reassign previously assigned variables again. // LOG ("reassigning previously assigned variables"); for (size_t i = 0; i < initial_trail_level; i++) { const int lit = trail[i]; const signed char tmp = val (lit); CADICAL_assert (tmp >= 0); if (!tmp) condition_assign (lit); } #ifndef CADICAL_NDEBUG for (const auto &lit : trail) CADICAL_assert (!marked (lit)); #endif unprotect_reasons (); return blocked; } void Internal::condition (bool update_limits) { if (unsat) return; if (!stats.current.irredundant) return; START_SIMPLIFIER (condition, CONDITION); stats.conditionings++; // Propagation limit to avoid too much work in 'condition'. We mark // tried candidate clauses after giving up, such that next time we run // 'condition' we can try them. // long limit = stats.propagations.search; limit *= opts.conditioneffort; limit /= 1000; if (limit < opts.conditionmineff) limit = opts.conditionmineff; if (limit > opts.conditionmaxeff) limit = opts.conditionmaxeff; CADICAL_assert (stats.current.irredundant); limit *= 2.0 * active () / (double) stats.current.irredundant; limit = max (limit, 2l * active ()); PHASE ("condition", stats.conditionings, "started after %" PRIu64 " conflicts limited by %ld propagations", stats.conflicts, limit); long blocked = condition_round (limit); STOP_SIMPLIFIER (condition, CONDITION); report ('g', !blocked); if (!update_limits) return; long delta = opts.conditionint * (stats.conditionings + 1); lim.condition = stats.conflicts + delta; PHASE ("condition", stats.conditionings, "next limit at %" PRIu64 " after %ld conflicts", lim.condition, delta); } } // namespace CaDiCaL ABC_NAMESPACE_IMPL_END