3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

conflict2

This commit is contained in:
Jakob Rath 2022-09-19 16:01:45 +02:00
parent 68e313ed24
commit a416e16566
9 changed files with 216 additions and 31 deletions

View file

@ -29,6 +29,38 @@ Notes:
namespace polysat {
struct inf_resolve_bool : public inference {
sat::literal m_lit;
clause const& m_clause;
inf_resolve_bool(sat::literal lit, clause const& cl) : m_lit(lit), m_clause(cl) {}
std::ostream& display(std::ostream& out) const override {
return out << "Resolve upon " << m_lit << " with " << m_clause;
}
};
struct inf_resolve_with_assignment : public inference {
solver& s;
sat::literal lit;
signed_constraint c;
inf_resolve_with_assignment(solver& s, sat::literal lit, signed_constraint c) : s(s), lit(lit), c(c) {}
std::ostream& display(std::ostream& out) const override {
out << "Resolve upon " << lit << " with assignment:";
for (pvar v : c->vars())
if (s.is_assigned(v))
out << " " << assignment_pp(s, v, s.get_value(v), true);
return out;
}
};
struct inf_resolve_value : public inference {
solver& s;
pvar v;
inf_resolve_value(solver& s, pvar v) : s(s), v(v) {}
std::ostream& display(std::ostream& out) const override {
return out << "Value resolution with " << assignment_pp(s, v, s.get_value(v), true);
}
};
conflict2::conflict2(solver& s) : s(s) {
// TODO: m_log_conflicts is always false even if "polysat.log_conflicts=true" is given on the command line
if (true || s.get_config().m_log_conflicts)
@ -50,9 +82,21 @@ namespace polysat {
m_vars.reset();
m_var_occurrences.reset();
m_lemmas.reset();
m_kind = conflict2_kind_t::ok;
SASSERT(empty());
}
void conflict2::set_bailout() {
SASSERT(m_kind == conflict2_kind_t::ok);
m_kind = conflict2_kind_t::bailout;
s.m_stats.m_num_bailouts++;
}
void conflict2::set_backjump() {
SASSERT(m_kind == conflict2_kind_t::ok);
m_kind = conflict2_kind_t::backjump;
}
void conflict2::init(signed_constraint c) {
SASSERT(empty());
if (c.bvalue(s) == l_false) {
@ -66,25 +110,23 @@ namespace polysat {
insert_vars(c);
}
SASSERT(!empty());
logger().begin_conflict();
}
void conflict2::init(pvar v, bool by_viable_fallback) {
// NOTE: do forbidden interval projection in this method (rather than keeping a separate state like we did before)
// Option 1: forbidden interval projection
// Option 2: add all constraints from m_viable + dependent variables
if (by_viable_fallback) {
// Conflict detected by viable fallback:
// initial conflict is the unsat core of the univariate solver
signed_constraints unsat_core = s.m_viable_fallback.unsat_core(v);
for (auto c : unsat_core)
insert(c);
return;
logger().begin_conflict("unsat core from viable fallback");
// TODO: apply conflict resolution plugins here too?
} else {
VERIFY(s.m_viable.resolve(v, *this));
set_backjump();
logger().begin_conflict("forbidden interval lemma");
}
// TODO:
// VERIFY(s.m_viable.resolve(v, *this));
// log_inference(inf_fi_lemma(v));
}
bool conflict2::contains(sat::literal lit) const {
@ -108,11 +150,17 @@ namespace polysat {
}
}
void conflict2::remove(signed_constraint c) {
SASSERT(contains(c));
m_literals.remove(c.blit().index());
for (pvar v : c->vars())
m_var_occurrences[v]--;
void conflict2::insert_eval(signed_constraint c) {
switch (c.bvalue(s)) {
case l_undef:
s.assign_eval(c.blit());
break;
case l_true:
break;
case l_false:
break;
}
insert(c);
}
void conflict2::insert_vars(signed_constraint c) {
@ -121,7 +169,99 @@ namespace polysat {
m_vars.insert(v);
}
void conflict2::remove(signed_constraint c) {
SASSERT(contains(c));
m_literals.remove(c.blit().index());
for (pvar v : c->vars())
m_var_occurrences[v]--;
}
void conflict2::resolve_bool(sat::literal lit, clause const& cl) {
// Note: core: x, y, z; corresponds to clause ~x \/ ~y \/ ~z
// clause: x \/ u \/ v
// resolvent: ~y \/ ~z \/ u \/ v; as core: y, z, ~u, ~v
SASSERT(contains(lit));
SASSERT(std::count(cl.begin(), cl.end(), lit) > 0);
SASSERT(!contains(~lit));
SASSERT(std::count(cl.begin(), cl.end(), ~lit) == 0);
remove(s.lit2cnstr(lit));
for (sat::literal other : cl)
if (other != lit)
insert(s.lit2cnstr(~other));
logger().log(inf_resolve_bool(lit, cl));
}
void conflict2::resolve_with_assignment(sat::literal lit) {
// The reason for lit is conceptually:
// x1 = v1 /\ ... /\ xn = vn ==> lit
SASSERT(contains(lit));
SASSERT(!contains(~lit));
unsigned const lvl = s.m_bvars.level(lit);
signed_constraint c = s.lit2cnstr(lit);
/*
// TODO: why bail_vars?
bool has_decision = false;
for (pvar v : c->vars())
if (s.is_assigned(v) && s.m_justification[v].is_decision())
m_bail_vars.insert(v), has_decision = true;
if (!has_decision) {
remove(c);
for (pvar v : c->vars())
if (s.is_assigned(v) && s.get_level(v) <= lvl)
m_vars.insert(v);
}
*/
remove(c);
for (pvar v : c->vars())
if (s.is_assigned(v) && s.get_level(v) <= lvl)
m_vars.insert(v);
logger().log(inf_resolve_with_assignment(s, lit, c));
}
bool conflict2::resolve_value(pvar v) {
SASSERT(contains_pvar(v));
if (is_bailout())
return false;
auto const& j = s.m_justification[v];
// if (j.is_decision() && m_bail_vars.contains(v))
// return false;
s.inc_activity(v);
m_vars.remove(v);
if (j.is_propagation()) {
for (auto const& c : s.m_viable.get_constraints(v))
insert_eval(c);
for (auto const& i : s.m_viable.units(v)) {
insert_eval(s.eq(i.lo(), i.lo_val()));
insert_eval(s.eq(i.hi(), i.hi_val()));
}
}
logger().log(inf_resolve_value(s, v));
// TODO: call conflict resolution plugins here; "return true" if successful
// No conflict resolution plugin succeeded => give up and bail out
set_bailout();
// Need to keep the variable in case of decision
if (s.is_assigned(v) && j.is_decision())
m_vars.insert(v);
logger().log("bailout");
return false;
}
std::ostream& conflict2::display(std::ostream& out) const {
out << "TODO\n";
return out;
}
}

View file

@ -12,8 +12,9 @@ Author:
Notes:
A conflict state is of the form <Vars, Constraints>
Where Vars are shorthand for the constraints v = value(v) for v in Vars and value(v) is the assignent.
A conflict state is of the form <Vars, Constraints, Lemmas>
Where Vars are shorthand for the constraints v = value(v) for v in Vars and value(v) is the assignment.
Lemmas provide justifications for newly created constraints.
The conflict state is unsatisfiable under background clauses F.
Dually, the negation is a consequence of F.
@ -23,14 +24,14 @@ Notes:
Assignments are of the form:
lit <- D => lit - lit is propagated by the clause D => lit
lit <- ? - lit is a decision literal.
lit <- asserted - lit is asserted
lit <- Vars - lit is propagated from variable evaluation.
v = value <- D - v is assigned value by constraints D
v = value <- ? - v is a decision literal.
- All literals should be assigned in the stack prior to their use.
- All literals should be assigned in the stack prior to their use;
or justified by one of the side lemmas.
l <- D => l, < Vars, { l } u C > ===> < Vars, C u D >
l <- ?, < Vars, { l } u C > ===> ~l <- (C & Vars = value(Vars) => ~l)
@ -66,6 +67,15 @@ Lemma: y < z or xz <= xy or O(x,y)
TODO:
- update notes/example above
- question: if a side lemma justifies a constraint, then we resolve over one of the premises of the lemma; do we want to update the lemma or not?
- conflict resolution plugins
- may generate lemma
- post-processing/simplification on lemma (e.g., literal subsumption; can be done in solver)
- may force backjumping without further conflict resolution (e.g., if applicable lemma was found by global analysis of search state)
- bailout lemma if no method applies (log these cases in particular because it indicates where we are missing something)
- force a restart if we get a bailout lemma or non-asserting conflict?
--*/
@ -84,6 +94,15 @@ namespace polysat {
class conflict_iterator;
class inference_logger;
enum class conflict2_kind_t {
// standard conflict resolution
ok,
// bailout lemma because no appropriate conflict resolution method applies
bailout,
// force backjumping without further conflict resolution because a good lemma has been found
backjump,
};
class conflict2 {
solver& s;
scoped_ptr<inference_logger> m_logger;
@ -91,17 +110,14 @@ namespace polysat {
// current conflict core consists of m_literals and m_vars
indexed_uint_set m_literals; // set of boolean literals in the conflict
uint_set m_vars; // variable assignments used as premises, shorthand for literals (x := v)
// uint_set m_bail_vars; // tracked for cone of influence but not directly involved in conflict resolution
unsigned_vector m_var_occurrences; // for each variable, the number of constraints in m_literals that contain it
// additional lemmas generated during conflict resolution
vector<clause_ref> m_lemmas;
// TODO:
// conflict resolution plugins
// - may generate lemma
// - may force backjumping without further conflict resolution
// - bailout lemma if no method applies (log these cases in particular because it indicates where we are missing something)
conflict2_kind_t m_kind = conflict2_kind_t::ok;
public:
conflict2(solver& s);
@ -111,6 +127,13 @@ namespace polysat {
bool empty() const;
void reset();
uint_set const& vars() const { return m_vars; }
bool is_bailout() const { return m_kind == conflict2_kind_t::bailout; }
bool is_backjumping() const { return m_kind == conflict2_kind_t::backjump; }
void set_bailout();
void set_backjump();
/** conflict because the constraint c is false under current variable assignment */
void init(signed_constraint c);
/** conflict because there is no viable value for the variable v */
@ -118,6 +141,7 @@ namespace polysat {
bool contains(signed_constraint c) const { SASSERT(c); return contains(c.blit()); }
bool contains(sat::literal lit) const;
bool contains_pvar(pvar v) const { return m_vars.contains(v) /* || m_bail_vars.contains(v) */; }
/**
* Insert constraint c into conflict state.
@ -130,9 +154,21 @@ namespace polysat {
/** Insert assigned variables of c */
void insert_vars(signed_constraint c);
/** Evaluate constraint under assignment and insert it into conflict state. */
void insert_eval(signed_constraint c);
/** Remove c from core */
void remove(signed_constraint c);
/** Perform boolean resolution with the clause upon the given literal. */
void resolve_bool(sat::literal lit, clause const& cl);
/** lit was fully evaluated under the assignment. */
void resolve_with_assignment(sat::literal lit);
/** Perform resolution with "v = value <- ..." */
bool resolve_value(pvar v);
std::ostream& display(std::ostream& out) const;
};

View file

@ -85,7 +85,7 @@ namespace polysat {
out().flush();
}
void file_inference_logger::log_inference(inference const& inf) {
void file_inference_logger::log(inference const& inf) {
out() << hline() << "\n";
out() << inf << "\n";
log_conflict_state();

View file

@ -48,8 +48,8 @@ namespace polysat {
/// Begin next conflict
virtual void begin_conflict(char const* text = nullptr) = 0;
/// Log inference and the current state.
virtual void log_inference(inference const& inf) = 0;
virtual void log_inference(char const* name) { log_inference(inference_named(name)); }
virtual void log(inference const& inf) = 0;
virtual void log(char const* name) { log(inference_named(name)); }
virtual void log_var(pvar v) = 0;
/// Log relevant part of search state and viable.
virtual void end_conflict() = 0;
@ -63,7 +63,7 @@ namespace polysat {
class dummy_inference_logger : public inference_logger {
public:
virtual void begin_conflict(char const* text) override {}
virtual void log_inference(inference const& inf) override {}
virtual void log(inference const& inf) override {}
virtual void log_var(pvar v) override {}
virtual void end_conflict() override {}
virtual void log_conflict_state() override {}
@ -89,7 +89,7 @@ namespace polysat {
/// Begin next conflict
void begin_conflict(char const* text) override;
/// Log inference and the current state.
void log_inference(inference const& inf) override;
void log(inference const& inf) override;
void log_var(pvar v) override;
/// Log relevant part of search state and viable.
void end_conflict() override;

View file

@ -141,11 +141,12 @@ namespace polysat {
};
inline std::ostream& operator<<(std::ostream& os, eval_interval const& i) {
auto& m = i.hi().manager();
if (i.is_full())
return os << "full";
else
else {
auto& m = i.hi().manager();
return os << i.symbolic() << " := [" << m.normalize(i.lo_val()) << ";" << m.normalize(i.hi_val()) << "[";
}
}

View file

@ -645,7 +645,7 @@ namespace polysat {
LOG_H2("Working on " << search_item_pp(m_search, item));
LOG(m_justification[v]);
LOG("Conflict: " << m_conflict);
inc_activity(v);
// inc_activity(v); // done in resolve_value
justification& j = m_justification[v];
if (j.level() > base_level() && !m_conflict.resolve_value(v) && j.is_decision()) {
m_conflict.end_conflict();
@ -887,6 +887,7 @@ namespace polysat {
}
void solver::assign_eval(sat::literal lit) {
SASSERT(lit2cnstr(lit).is_currently_true(*this));
unsigned level = 0;
// NOTE: constraint may be evaluated even if some variables are still unassigned (e.g., 0*x doesn't depend on x).
for (auto v : lit2cnstr(lit)->vars())

View file

@ -95,6 +95,7 @@ namespace polysat {
friend class test_polysat;
friend class test_fi;
friend struct inference_resolve_with_assignment;
friend struct inf_resolve_with_assignment;
reslimit& m_lim;
params_ref m_params;

View file

@ -629,6 +629,10 @@ namespace polysat {
}
};
bool viable::resolve(pvar v, conflict2& core) {
NOT_IMPLEMENTED_YET();
}
bool viable::resolve(pvar v, conflict& core) {
if (has_viable(v))
return false;

View file

@ -23,6 +23,7 @@ Author:
#include "util/small_object_allocator.h"
#include "math/polysat/types.h"
#include "math/polysat/conflict.h"
#include "math/polysat/conflict2.h"
#include "math/polysat/constraint.h"
#include "math/polysat/forbidden_intervals.h"
#include "math/polysat/univariate/univariate_solver.h"
@ -111,6 +112,7 @@ namespace polysat {
* \pre there are no viable values for v
*/
bool resolve(pvar v, conflict& core);
bool resolve(pvar v, conflict2& core);
/** Log all viable values for the given variable.
* (Inefficient, but useful for debugging small instances.)