mirror of
https://github.com/Z3Prover/z3
synced 2025-04-27 10:55:50 +00:00
Polysat: conflict resolution wip (#5529)
* conflict_core doesn't need gc() anymore * update comments, ensure_bvar for new constraints * Make sure constraints can only be created through constraint_manager * fix constraint::display if no boolean variable is assigned * Move clause into separate file * Add conflict_core binary resolution * conflict_core additions * reactivate conflict resolution outer loop * wip * seems commented includes break CI build
This commit is contained in:
parent
8b374c3745
commit
dc547510db
16 changed files with 423 additions and 335 deletions
|
@ -20,6 +20,7 @@ Author:
|
|||
#include "math/polysat/explain.h"
|
||||
#include "math/polysat/log.h"
|
||||
#include "math/polysat/forbidden_intervals.h"
|
||||
#include "math/polysat/variable_elimination.h"
|
||||
|
||||
// For development; to be removed once the linear solver works well enough
|
||||
#define ENABLE_LINEAR_SOLVER 0
|
||||
|
@ -142,6 +143,7 @@ namespace polysat {
|
|||
VERIFY(at_base_level());
|
||||
SASSERT(c);
|
||||
SASSERT(activate || dep != null_dependency); // if we don't activate the constraint, we need the dependency to access it again later.
|
||||
m_constraints.ensure_bvar(c.get());
|
||||
clause* unit = m_constraints.store(clause::from_unit(c, mk_dep_ref(dep)));
|
||||
c->set_unit_clause(unit);
|
||||
if (dep != null_dependency)
|
||||
|
@ -399,17 +401,19 @@ namespace polysat {
|
|||
}
|
||||
|
||||
void solver::set_conflict(pvar v) {
|
||||
m_conflict.set(v, m_cjust[v]);
|
||||
m_conflict.set(v);
|
||||
}
|
||||
|
||||
void solver::set_marks(conflict_core const& cc) {
|
||||
if (cc.conflict_var() != null_var)
|
||||
set_mark(cc.conflict_var());
|
||||
for (auto c : cc.constraints())
|
||||
if (c)
|
||||
set_marks(*c);
|
||||
}
|
||||
|
||||
void solver::set_marks(constraint const& c) {
|
||||
if (c.bvar() != sat::null_bool_var)
|
||||
if (c.has_bvar())
|
||||
m_bvars.set_mark(c.bvar());
|
||||
for (auto v : c.vars())
|
||||
set_mark(v);
|
||||
|
@ -440,183 +444,110 @@ namespace polysat {
|
|||
|
||||
SASSERT(is_conflict());
|
||||
|
||||
NOT_IMPLEMENTED_YET(); // TODO: needs to be refactored to use conflict_core, will be moved to conflict_explainer
|
||||
|
||||
/*
|
||||
if (m_conflict.units().size() == 1 && !m_conflict.units()[0]) {
|
||||
if (m_conflict.is_bailout()) {
|
||||
report_unsat();
|
||||
return;
|
||||
}
|
||||
|
||||
pvar conflict_var = null_var;
|
||||
clause_ref lemma;
|
||||
for (auto v : m_conflict.vars(m_constraints))
|
||||
if (!m_viable.has_viable(v)) {
|
||||
SASSERT(conflict_var == null_var || conflict_var == v); // at most one variable can be empty
|
||||
conflict_var = v;
|
||||
}
|
||||
reset_marks();
|
||||
m_bvars.reset_marks();
|
||||
set_marks(m_conflict);
|
||||
|
||||
if (m_conflict.clauses().empty() && conflict_var != null_var) {
|
||||
LOG_H2("Conflict due to empty viable set for pvar " << conflict_var);
|
||||
clause_ref new_lemma;
|
||||
if (forbidden_intervals::explain(*this, m_conflict.units(), conflict_var, new_lemma)) {
|
||||
SASSERT(new_lemma);
|
||||
clause& cl = *new_lemma.get();
|
||||
LOG_H3("Lemma from forbidden intervals (size: " << cl.size() << ")");
|
||||
for (sat::literal lit : cl) {
|
||||
LOG("Literal: " << lit);
|
||||
constraint* c = m_constraints.lookup(lit.var());
|
||||
for (auto v : c->vars())
|
||||
set_mark(v);
|
||||
}
|
||||
SASSERT(cl.size() > 0);
|
||||
lemma = std::move(new_lemma);
|
||||
m_conflict.reset();
|
||||
m_conflict.push_back(lemma);
|
||||
reset_marks();
|
||||
m_bvars.reset_marks();
|
||||
set_marks(*lemma.get());
|
||||
}
|
||||
else {
|
||||
conflict_explainer cx(*this, m_conflict);
|
||||
lemma = cx.resolve(conflict_var, {});
|
||||
LOG("resolved: " << show_deref(lemma));
|
||||
// SASSERT(false && "pause on explanation");
|
||||
}
|
||||
if (m_conflict.conflict_var() != null_var) {
|
||||
// This case corresponds to a propagation of conflict_var, except it's not explicitly on the stack.
|
||||
resolve_value(m_conflict.conflict_var());
|
||||
}
|
||||
|
||||
reset_marks();
|
||||
set_marks(m_conflict);
|
||||
|
||||
for (unsigned i = m_search.size(); i-- > 0; ) {
|
||||
LOG("Conflict: " << m_conflict);
|
||||
auto const& item = m_search[i];
|
||||
if (item.is_assignment()) {
|
||||
// Resolve over variable assignment
|
||||
pvar v = item.var();
|
||||
LOG_H2("Working on pvar " << v);
|
||||
LOG_H2("Working on pvar v" << v);
|
||||
if (!is_marked(v))
|
||||
continue;
|
||||
justification& j = m_justification[v];
|
||||
LOG("Justification: " << j);
|
||||
if (j.level() <= base_level()) {
|
||||
report_unsat();
|
||||
return;
|
||||
}
|
||||
if (j.level() <= base_level())
|
||||
break;
|
||||
if (j.is_decision()) {
|
||||
revert_decision(v, lemma);
|
||||
revert_decision(v);
|
||||
return;
|
||||
}
|
||||
SASSERT(j.is_propagation());
|
||||
LOG("Lemma: " << show_deref(lemma));
|
||||
clause_ref new_lemma = resolve(v);
|
||||
LOG("New Lemma: " << show_deref(new_lemma));
|
||||
// SASSERT(new_lemma); // TODO: only for debugging, to have a breakpoint on resolution failure
|
||||
if (!new_lemma) {
|
||||
backtrack(i, lemma);
|
||||
if (!resolve_value(v)) {
|
||||
resolve_bailout(i);
|
||||
return;
|
||||
}
|
||||
if (new_lemma->is_always_false(*this)) {
|
||||
clause* cl = new_lemma.get();
|
||||
learn_lemma(v, std::move(new_lemma));
|
||||
m_conflict.reset();
|
||||
m_conflict.push_back(cl);
|
||||
report_unsat();
|
||||
return;
|
||||
}
|
||||
if (!new_lemma->is_currently_false(*this)) {
|
||||
backtrack(i, lemma);
|
||||
return;
|
||||
}
|
||||
lemma = std::move(new_lemma);
|
||||
reset_marks();
|
||||
m_bvars.reset_marks();
|
||||
set_marks(*lemma.get());
|
||||
m_conflict.reset();
|
||||
m_conflict.push_back(lemma.get());
|
||||
set_marks(m_conflict);
|
||||
}
|
||||
else {
|
||||
// Resolve over boolean literal
|
||||
SASSERT(item.is_boolean());
|
||||
sat::literal const lit = item.lit();
|
||||
LOG_H2("Working on boolean literal " << lit);
|
||||
LOG_H2("Working on blit " << lit);
|
||||
sat::bool_var const var = lit.var();
|
||||
if (!m_bvars.is_marked(var))
|
||||
continue;
|
||||
if (m_bvars.level(var) <= base_level()) {
|
||||
report_unsat();
|
||||
return;
|
||||
}
|
||||
if (m_bvars.level(var) <= base_level())
|
||||
break;
|
||||
if (m_bvars.is_decision(var)) {
|
||||
// SASSERT(std::count(lemma->begin(), lemma->end(), ~lit) > 0);
|
||||
revert_bool_decision(lit, lemma);
|
||||
revert_bool_decision(lit);
|
||||
return;
|
||||
}
|
||||
SASSERT(m_bvars.is_propagation(var));
|
||||
LOG("Lemma: " << show_deref(lemma));
|
||||
clause_ref new_lemma = resolve_bool(lit);
|
||||
if (!new_lemma) {
|
||||
backtrack(i, lemma);
|
||||
return;
|
||||
}
|
||||
SASSERT(new_lemma);
|
||||
LOG("new_lemma: " << show_deref(new_lemma));
|
||||
LOG("new_lemma is always false: " << new_lemma->is_always_false(*this));
|
||||
if (new_lemma->is_always_false(*this)) {
|
||||
// learn_lemma(v, new_lemma);
|
||||
m_conflict.reset();
|
||||
m_conflict.push_back(std::move(new_lemma));
|
||||
report_unsat();
|
||||
return;
|
||||
}
|
||||
LOG("new_lemma is currently false: " << new_lemma->is_currently_false(*this));
|
||||
// if (!new_lemma->is_currently_false(*this)) {
|
||||
// backtrack(i, lemma);
|
||||
// return;
|
||||
// }
|
||||
lemma = std::move(new_lemma);
|
||||
resolve_bool(lit);
|
||||
reset_marks();
|
||||
m_bvars.reset_marks();
|
||||
set_marks(*lemma.get());
|
||||
m_conflict.reset();
|
||||
m_conflict.push_back(lemma.get());
|
||||
set_marks(m_conflict);
|
||||
}
|
||||
}
|
||||
report_unsat();
|
||||
*/
|
||||
}
|
||||
|
||||
clause_ref solver::resolve_bool(sat::literal lit) {
|
||||
NOT_IMPLEMENTED_YET(); return nullptr;
|
||||
/*
|
||||
if (m_conflict.size() != 1)
|
||||
return nullptr;
|
||||
if (m_conflict.clauses().size() != 1)
|
||||
return nullptr;
|
||||
LOG_H3("resolve_bool");
|
||||
clause* lemma = m_conflict.clauses()[0];
|
||||
SASSERT(lemma);
|
||||
SASSERT(m_bvars.is_propagation(lit.var()));
|
||||
clause* other = m_bvars.reason(lit.var());
|
||||
SASSERT(other);
|
||||
LOG("lemma: " << show_deref(lemma));
|
||||
LOG("other: " << show_deref(other));
|
||||
VERIFY(lemma->resolve(lit.var(), *other));
|
||||
LOG("resolved: " << show_deref(lemma));
|
||||
/** Conflict resolution case where propagation 'v := ...' is on top of the stack */
|
||||
bool solver::resolve_value(pvar v) {
|
||||
SASSERT(m_justification[v].is_propagation());
|
||||
// Conceptually:
|
||||
// - Value Resolution
|
||||
// - Variable Elimination
|
||||
// - if VE isn't possible, try to derive new constraints using core saturation
|
||||
|
||||
// unassign constraints whose current value does not agree with their occurrence in the lemma
|
||||
for (sat::literal lit : *lemma) {
|
||||
constraint *c = m_constraints.lookup(lit.var());
|
||||
if (!c->is_undef() && c ->blit() != lit) {
|
||||
LOG("unassigning: " << show_deref(c));
|
||||
c->unassign();
|
||||
}
|
||||
// Value Resolution
|
||||
for (auto c : m_cjust[v])
|
||||
m_conflict.push(c);
|
||||
|
||||
// Variable elimination
|
||||
while (true) {
|
||||
// TODO:
|
||||
// 1. Try variable elimination of 'v'
|
||||
// 2. If not possible, try saturation and core reduction (actually reduction could be one specific VE method?).
|
||||
// 3. as a last resort, substitute v by m_value[v]?
|
||||
|
||||
variable_elimination ve;
|
||||
if (ve.perform(v, m_conflict))
|
||||
return true;
|
||||
|
||||
core_saturation cs;
|
||||
if (!cs.saturate(v, m_conflict))
|
||||
return false;
|
||||
}
|
||||
|
||||
return lemma; // currently modified in-place
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
void solver::backtrack(unsigned i, clause_ref lemma) {
|
||||
/** Conflict resolution case where boolean literal 'lit' is on top of the stack */
|
||||
void solver::resolve_bool(sat::literal lit) {
|
||||
LOG_H3("resolve_bool: " << lit);
|
||||
SASSERT(m_bvars.is_propagation(lit.var()));
|
||||
|
||||
clause* other = m_bvars.reason(lit.var());
|
||||
m_conflict.resolve(m_constraints, lit.var(), *other);
|
||||
}
|
||||
|
||||
void solver::resolve_bailout(unsigned i) {
|
||||
// TODO: conflict resolution failed or was aborted. what to do with the current conflict core?
|
||||
// (we could still use it as lemma, but it probably doesn't help much)
|
||||
NOT_IMPLEMENTED_YET();
|
||||
/*
|
||||
do {
|
||||
|
@ -766,7 +697,7 @@ namespace polysat {
|
|||
* In general form it can rely on factoring.
|
||||
* Root finding can further prune viable.
|
||||
*/
|
||||
void solver::revert_decision(pvar v, clause_ref reason) {
|
||||
void solver::revert_decision(pvar v) {
|
||||
rational val = m_value[v];
|
||||
LOG_H3("Reverting decision: pvar " << v << " := " << val);
|
||||
NOT_IMPLEMENTED_YET();
|
||||
|
@ -818,7 +749,7 @@ namespace polysat {
|
|||
*/
|
||||
}
|
||||
|
||||
void solver::revert_bool_decision(sat::literal lit, clause_ref reason) {
|
||||
void solver::revert_bool_decision(sat::literal lit) {
|
||||
sat::bool_var const var = lit.var();
|
||||
LOG_H3("Reverting boolean decision: " << lit);
|
||||
SASSERT(m_bvars.is_decision(var));
|
||||
|
@ -1010,6 +941,7 @@ namespace polysat {
|
|||
}
|
||||
|
||||
void solver::reset_marks() {
|
||||
m_bvars.reset_marks();
|
||||
LOG_V("-------------------------- (reset variable marks)");
|
||||
m_marks.reserve(m_vars.size());
|
||||
m_clock++;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue