mirror of
https://github.com/Z3Prover/z3
synced 2025-06-27 08:28:44 +00:00
Remove decisions on lemmas
This commit is contained in:
parent
d5f20dcf0e
commit
bab8d817ef
3 changed files with 4 additions and 153 deletions
|
@ -43,13 +43,6 @@ namespace polysat {
|
||||||
out << " @" << s.m_bvars.level(lit);
|
out << " @" << s.m_bvars.level(lit);
|
||||||
if (s.m_bvars.is_assumption(lit))
|
if (s.m_bvars.is_assumption(lit))
|
||||||
out << " assumption";
|
out << " assumption";
|
||||||
else if (s.m_bvars.is_decision(lit)) {
|
|
||||||
clause* lemma = s.m_bvars.lemma(lit.var());
|
|
||||||
out << " decision on lemma: " << show_deref(lemma);
|
|
||||||
for (auto l2 : *lemma) {
|
|
||||||
out << "\n\t" << rpad(4, l2) << ": " << rpad(16, s.lit2cnstr(l2)) << " " << bool_justification_pp(s.m_bvars, l2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (s.m_bvars.is_bool_propagation(lit)) {
|
else if (s.m_bvars.is_bool_propagation(lit)) {
|
||||||
clause* reason = s.m_bvars.reason(lit);
|
clause* reason = s.m_bvars.reason(lit);
|
||||||
out << " bool propagation " << show_deref(reason);
|
out << " bool propagation " << show_deref(reason);
|
||||||
|
|
|
@ -77,7 +77,6 @@ namespace polysat {
|
||||||
else if (m_constraints.should_gc()) m_constraints.gc(*this);
|
else if (m_constraints.should_gc()) m_constraints.gc(*this);
|
||||||
else if (m_simplify.should_apply()) m_simplify();
|
else if (m_simplify.should_apply()) m_simplify();
|
||||||
else if (m_restart.should_apply()) m_restart();
|
else if (m_restart.should_apply()) m_restart();
|
||||||
else if (can_decide_on_lemma()) decide_on_lemma();
|
|
||||||
else decide();
|
else decide();
|
||||||
}
|
}
|
||||||
LOG_H2("UNDEF (resource limit)");
|
LOG_H2("UNDEF (resource limit)");
|
||||||
|
@ -451,7 +450,6 @@ namespace polysat {
|
||||||
#if ENABLE_LINEAR_SOLVER
|
#if ENABLE_LINEAR_SOLVER
|
||||||
m_linear_solver.pop(num_levels);
|
m_linear_solver.pop(num_levels);
|
||||||
#endif
|
#endif
|
||||||
drop_enqueued_lemma();
|
|
||||||
while (num_levels > 0) {
|
while (num_levels > 0) {
|
||||||
switch (m_trail.back()) {
|
switch (m_trail.back()) {
|
||||||
case trail_instr_t::qhead_i: {
|
case trail_instr_t::qhead_i: {
|
||||||
|
@ -666,13 +664,8 @@ namespace polysat {
|
||||||
LOG(bool_justification_pp(m_bvars, lit));
|
LOG(bool_justification_pp(m_bvars, lit));
|
||||||
LOG("Literal " << lit << " is " << lit2cnstr(lit));
|
LOG("Literal " << lit << " is " << lit2cnstr(lit));
|
||||||
LOG("Conflict: " << m_conflict);
|
LOG("Conflict: " << m_conflict);
|
||||||
if (m_bvars.is_assumption(var))
|
if (m_bvars.is_assumption(var)) // TODO: wouldn't this mean we're already at the base level?
|
||||||
continue;
|
continue;
|
||||||
else if (m_bvars.is_decision(var)) {
|
|
||||||
m_conflict.end_conflict();
|
|
||||||
revert_bool_decision(lit);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (m_bvars.is_bool_propagation(var))
|
else if (m_bvars.is_bool_propagation(var))
|
||||||
m_conflict.resolve(lit, *m_bvars.reason(lit));
|
m_conflict.resolve(lit, *m_bvars.reason(lit));
|
||||||
else {
|
else {
|
||||||
|
@ -771,13 +764,8 @@ namespace polysat {
|
||||||
LOG(bool_justification_pp(m_bvars, lit));
|
LOG(bool_justification_pp(m_bvars, lit));
|
||||||
LOG("Literal " << lit << " is " << lit2cnstr(lit));
|
LOG("Literal " << lit << " is " << lit2cnstr(lit));
|
||||||
LOG("Conflict: " << m_conflict);
|
LOG("Conflict: " << m_conflict);
|
||||||
if (m_bvars.is_assumption(var))
|
if (m_bvars.is_assumption(var)) // TODO: wouldn't this mean we're already at the base level?
|
||||||
continue;
|
continue;
|
||||||
else if (m_bvars.is_decision(var)) {
|
|
||||||
m_conflict.end_conflict();
|
|
||||||
revert_bool_decision(lit);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (m_bvars.is_bool_propagation(var))
|
else if (m_bvars.is_bool_propagation(var))
|
||||||
m_conflict.resolve(lit, *m_bvars.reason(lit));
|
m_conflict.resolve(lit, *m_bvars.reason(lit));
|
||||||
else {
|
else {
|
||||||
|
@ -838,70 +826,7 @@ namespace polysat {
|
||||||
LOG("Learning: "<< lemma);
|
LOG("Learning: "<< lemma);
|
||||||
SASSERT(!lemma.empty());
|
SASSERT(!lemma.empty());
|
||||||
add_clause(lemma);
|
add_clause(lemma);
|
||||||
enqueue_decision_on_lemma(lemma);
|
// TODO: add pwatch?
|
||||||
}
|
|
||||||
|
|
||||||
void solver::enqueue_decision_on_lemma(clause& lemma) {
|
|
||||||
m_lemmas.push_back(&lemma);
|
|
||||||
}
|
|
||||||
|
|
||||||
void solver::drop_enqueued_lemma() {
|
|
||||||
m_lemmas.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool solver::can_decide_on_lemma() {
|
|
||||||
return !m_lemmas.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void solver::decide_on_lemma() {
|
|
||||||
SASSERT(!is_conflict());
|
|
||||||
SASSERT(m_lemmas.size() == 1); // we expect to only have a single one
|
|
||||||
// TODO: maybe it would be nicer to have a queue of lemmas, instead of storing lemmas per-literal in m_bvars?
|
|
||||||
while (!m_lemmas.empty()) {
|
|
||||||
clause* lemma = m_lemmas.back();
|
|
||||||
m_lemmas.pop_back();
|
|
||||||
decide_on_lemma(*lemma);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void solver::decide_on_lemma(clause& lemma) {
|
|
||||||
LOG_H3("Guessing literal in lemma: " << lemma);
|
|
||||||
|
|
||||||
// To make a guess, we need to find an unassigned literal that is not false in the current model.
|
|
||||||
|
|
||||||
sat::literal choice = sat::null_literal;
|
|
||||||
unsigned num_choices = 0; // TODO: should probably cache this? (or rather the suitability of each literal... it won't change until we backtrack beyond the current point)
|
|
||||||
|
|
||||||
for (sat::literal lit : lemma) {
|
|
||||||
switch (m_bvars.value(lit)) {
|
|
||||||
case l_true:
|
|
||||||
LOG("Already satisfied because literal " << lit << " is true");
|
|
||||||
return;
|
|
||||||
case l_false:
|
|
||||||
break;
|
|
||||||
case l_undef:
|
|
||||||
if (lit2cnstr(lit).is_currently_false(*this))
|
|
||||||
assign_eval(lit);
|
|
||||||
else {
|
|
||||||
num_choices++;
|
|
||||||
choice = lit;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG_V("num_choices: " << num_choices);
|
|
||||||
switch (num_choices) {
|
|
||||||
case 0:
|
|
||||||
set_conflict(lemma);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
assign_propagate(choice, lemma);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
push_level();
|
|
||||||
assign_decision(choice, lemma);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -930,60 +855,6 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool solver::is_decision(search_item const& item) const {
|
|
||||||
if (item.is_assignment())
|
|
||||||
return m_justification[item.var()].is_decision();
|
|
||||||
else
|
|
||||||
return m_bvars.is_decision(item.lit().var());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current situation: we have a decision for boolean literal L on top of the stack, and a conflict core.
|
|
||||||
//
|
|
||||||
// In a CDCL solver, this means ~L is in the lemma (actually, as the asserting literal). We drop the decision and replace it by the propagation (~L)^lemma.
|
|
||||||
//
|
|
||||||
// - we know L must be false
|
|
||||||
// - if L isn't in the core, we can still add it (weakening the lemma) to obtain "core => ~L"
|
|
||||||
// - then we can add the propagation (~L)^lemma and continue with the next guess
|
|
||||||
|
|
||||||
// Note that if we arrive at this point, the variables in L are "relevant" to the conflict (otherwise we would have skipped L).
|
|
||||||
// So the subsequent steps must have contained one of these:
|
|
||||||
// - propagation of some variable v from L (and maybe other constraints)
|
|
||||||
// (v := val)^{L, ...}
|
|
||||||
// this means L is in core, unless we core-reduced it away
|
|
||||||
// - propagation of L' from L
|
|
||||||
// (L')^{L' \/ ¬L \/ ...}
|
|
||||||
// again L is in core, unless we core-reduced it away
|
|
||||||
|
|
||||||
void solver::revert_bool_decision(sat::literal lit) {
|
|
||||||
sat::bool_var const var = lit.var();
|
|
||||||
LOG_H3("Reverting boolean decision: " << lit << " " << m_conflict);
|
|
||||||
SASSERT(m_bvars.is_decision(var));
|
|
||||||
|
|
||||||
clause_builder reason_builder = m_conflict.build_lemma();
|
|
||||||
|
|
||||||
SASSERT(std::find(reason_builder.begin(), reason_builder.end(), ~lit));
|
|
||||||
clause_ref reason = reason_builder.build();
|
|
||||||
SASSERT(reason);
|
|
||||||
|
|
||||||
if (reason->empty()) {
|
|
||||||
report_unsat();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_conflict.reset();
|
|
||||||
|
|
||||||
// The lemma where 'lit' comes from.
|
|
||||||
// Currently, boolean decisions always come from guessing a literal of a learned non-unit lemma.
|
|
||||||
clause* lemma = m_bvars.lemma(var); // need to grab this while 'lit' is still assigned
|
|
||||||
|
|
||||||
backjump(m_bvars.level(var) - 1);
|
|
||||||
|
|
||||||
// reason should force ~lit after propagation
|
|
||||||
add_clause(*reason);
|
|
||||||
|
|
||||||
if (lemma)
|
|
||||||
enqueue_decision_on_lemma(*lemma);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned solver::level(sat::literal lit0, clause const& cl) {
|
unsigned solver::level(sat::literal lit0, clause const& cl) {
|
||||||
unsigned lvl = 0;
|
unsigned lvl = 0;
|
||||||
for (auto lit : cl) {
|
for (auto lit : cl) {
|
||||||
|
@ -1002,12 +873,6 @@ namespace polysat {
|
||||||
m_search.push_boolean(lit);
|
m_search.push_boolean(lit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::assign_decision(sat::literal lit, clause& lemma) {
|
|
||||||
m_bvars.decide(lit, m_level, lemma);
|
|
||||||
m_trail.push_back(trail_instr_t::assign_bool_i);
|
|
||||||
m_search.push_boolean(lit);
|
|
||||||
}
|
|
||||||
|
|
||||||
void solver::assign_eval(sat::literal lit) {
|
void solver::assign_eval(sat::literal lit) {
|
||||||
unsigned level = 0;
|
unsigned level = 0;
|
||||||
// NOTE: constraint may be evaluated even if some variables are still unassigned (e.g., 0*x doesn't depend on x).
|
// NOTE: constraint may be evaluated even if some variables are still unassigned (e.g., 0*x doesn't depend on x).
|
||||||
|
|
|
@ -116,7 +116,6 @@ namespace polysat {
|
||||||
std::optional<pvar> m_locked_wlist; // restrict watch list modification while it is being propagated
|
std::optional<pvar> m_locked_wlist; // restrict watch list modification while it is being propagated
|
||||||
bool m_propagating = false; // set to true during propagation
|
bool m_propagating = false; // set to true during propagation
|
||||||
#endif
|
#endif
|
||||||
ptr_vector<clause> m_lemmas;
|
|
||||||
|
|
||||||
unsigned_vector m_activity;
|
unsigned_vector m_activity;
|
||||||
vector<pdd> m_vars;
|
vector<pdd> m_vars;
|
||||||
|
@ -168,15 +167,9 @@ namespace polysat {
|
||||||
void deactivate_constraint(signed_constraint c);
|
void deactivate_constraint(signed_constraint c);
|
||||||
unsigned level(sat::literal lit, clause const& cl);
|
unsigned level(sat::literal lit, clause const& cl);
|
||||||
|
|
||||||
bool can_decide_on_lemma();
|
|
||||||
void decide_on_lemma();
|
|
||||||
void decide_on_lemma(clause& lemma);
|
|
||||||
void enqueue_decision_on_lemma(clause& lemma);
|
|
||||||
void drop_enqueued_lemma();
|
|
||||||
|
|
||||||
void assign_core(pvar v, rational const& val, justification const& j);
|
void assign_core(pvar v, rational const& val, justification const& j);
|
||||||
bool is_assigned(pvar v) const { return !m_justification[v].is_unassigned(); }
|
bool is_assigned(pvar v) const { return !m_justification[v].is_unassigned(); }
|
||||||
bool is_decision(search_item const& item) const;
|
bool is_decision(pvar v) const { return m_justification[v].is_decision(); }
|
||||||
|
|
||||||
bool should_search();
|
bool should_search();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue