mirror of
https://github.com/Z3Prover/z3
synced 2025-08-01 08:53:18 +00:00
Add bitblasting fallback to viable::query
(integration between conflict/viable is still messy)
This commit is contained in:
parent
44cb528300
commit
afde0e993c
8 changed files with 336 additions and 121 deletions
|
@ -182,6 +182,15 @@ namespace polysat {
|
||||||
SASSERT(s.at_base_level());
|
SASSERT(s.at_base_level());
|
||||||
m_level = s.m_level;
|
m_level = s.m_level;
|
||||||
SASSERT(!empty());
|
SASSERT(!empty());
|
||||||
|
// TODO: logger().begin_conflict???
|
||||||
|
// TODO: check uses of logger().begin_conflict(). sometimes we call it before adding constraints, sometimes after...
|
||||||
|
}
|
||||||
|
|
||||||
|
void conflict::init_empty() {
|
||||||
|
SASSERT(empty());
|
||||||
|
m_level = s.m_level;
|
||||||
|
SASSERT(!empty());
|
||||||
|
// TODO: logger().begin_conflict???
|
||||||
}
|
}
|
||||||
|
|
||||||
void conflict::init(signed_constraint c) {
|
void conflict::init(signed_constraint c) {
|
||||||
|
@ -193,11 +202,13 @@ namespace polysat {
|
||||||
logger().begin_conflict();
|
logger().begin_conflict();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
void conflict::set(signed_constraint c) {
|
void conflict::set(signed_constraint c) {
|
||||||
SASSERT(!empty());
|
SASSERT(!empty());
|
||||||
remove_all();
|
remove_all();
|
||||||
set_impl(c);
|
set_impl(c);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void conflict::set_impl(signed_constraint c) {
|
void conflict::set_impl(signed_constraint c) {
|
||||||
if (c.bvalue(s) == l_false) {
|
if (c.bvalue(s) == l_false) {
|
||||||
|
@ -233,6 +244,7 @@ namespace polysat {
|
||||||
void conflict::init(pvar v, bool by_viable_fallback) {
|
void conflict::init(pvar v, bool by_viable_fallback) {
|
||||||
LOG("Conflict: viable v" << v);
|
LOG("Conflict: viable v" << v);
|
||||||
SASSERT(empty());
|
SASSERT(empty());
|
||||||
|
SASSERT(!s.is_assigned(v));
|
||||||
m_level = s.m_level;
|
m_level = s.m_level;
|
||||||
if (by_viable_fallback) {
|
if (by_viable_fallback) {
|
||||||
logger().begin_conflict(header_with_var("unsat core from viable fallback for v", v));
|
logger().begin_conflict(header_with_var("unsat core from viable fallback for v", v));
|
||||||
|
|
|
@ -85,7 +85,7 @@ namespace polysat {
|
||||||
scoped_ptr<conflict_resolver> m_resolver;
|
scoped_ptr<conflict_resolver> m_resolver;
|
||||||
|
|
||||||
// current conflict core consists of m_literals and m_vars
|
// current conflict core consists of m_literals and m_vars
|
||||||
indexed_uint_set m_literals; // set of boolean literals in the conflict
|
indexed_uint_set m_literals; // set of boolean literals in the conflict; TODO: why not sat::literal_set
|
||||||
uint_set m_vars; // variable assignments used as premises, shorthand for literals (x := v)
|
uint_set m_vars; // variable assignments used as premises, shorthand for literals (x := v)
|
||||||
|
|
||||||
unsigned_vector m_var_occurrences; // for each variable, the number of constraints in m_literals that contain it
|
unsigned_vector m_var_occurrences; // for each variable, the number of constraints in m_literals that contain it
|
||||||
|
@ -135,9 +135,13 @@ namespace polysat {
|
||||||
void init(clause const& cl);
|
void init(clause const& cl);
|
||||||
/** conflict because there is no viable value for the variable v */
|
/** conflict because there is no viable value for the variable v */
|
||||||
void init(pvar v, bool by_viable_fallback);
|
void init(pvar v, bool by_viable_fallback);
|
||||||
|
/** start empty conflict, constraints to be added by caller */
|
||||||
|
void init_empty(); // TODO: remove... rather have all types of conflicts explicitly listed here.
|
||||||
|
|
||||||
|
#if 0
|
||||||
/** replace the current conflict by a single constraint */
|
/** replace the current conflict by a single constraint */
|
||||||
void set(signed_constraint c);
|
void set(signed_constraint c);
|
||||||
|
#endif
|
||||||
|
|
||||||
bool contains(signed_constraint c) const { SASSERT(c); return contains(c.blit()); }
|
bool contains(signed_constraint c) const { SASSERT(c); return contains(c.blit()); }
|
||||||
bool contains(sat::literal lit) const;
|
bool contains(sat::literal lit) const;
|
||||||
|
|
|
@ -652,7 +652,7 @@ namespace polysat {
|
||||||
// (fail here in debug mode so we notice if we miss some)
|
// (fail here in debug mode so we notice if we miss some)
|
||||||
DEBUG_CODE( UNREACHABLE(); );
|
DEBUG_CODE( UNREACHABLE(); );
|
||||||
m_free_pvars.unassign_var_eh(v);
|
m_free_pvars.unassign_var_eh(v);
|
||||||
set_conflict(v, false);
|
SASSERT(is_conflict());
|
||||||
return;
|
return;
|
||||||
case find_t::singleton:
|
case find_t::singleton:
|
||||||
// NOTE: this case may happen legitimately if all other possibilities were excluded by brute force search
|
// NOTE: this case may happen legitimately if all other possibilities were excluded by brute force search
|
||||||
|
|
|
@ -26,6 +26,12 @@ Author:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
|
univariate_solver::dep_vector univariate_solver::unsat_core() {
|
||||||
|
dep_vector deps;
|
||||||
|
unsat_core(deps);
|
||||||
|
return deps;
|
||||||
|
}
|
||||||
|
|
||||||
class univariate_bitblast_solver : public univariate_solver {
|
class univariate_bitblast_solver : public univariate_solver {
|
||||||
// TODO: does it make sense to share m and bv between different solver instances?
|
// TODO: does it make sense to share m and bv between different solver instances?
|
||||||
// TODO: consider pooling solvers to save setup overhead, see if solver/solver_pool.h can be used
|
// TODO: consider pooling solvers to save setup overhead, see if solver/solver_pool.h can be used
|
||||||
|
@ -33,6 +39,7 @@ namespace polysat {
|
||||||
scoped_ptr<bv_util> bv;
|
scoped_ptr<bv_util> bv;
|
||||||
scoped_ptr<solver> s;
|
scoped_ptr<solver> s;
|
||||||
unsigned bit_width;
|
unsigned bit_width;
|
||||||
|
unsigned m_scope_level = 0;
|
||||||
func_decl_ref x_decl;
|
func_decl_ref x_decl;
|
||||||
expr_ref x;
|
expr_ref x;
|
||||||
vector<rational> model_cache;
|
vector<rational> model_cache;
|
||||||
|
@ -67,15 +74,22 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void push() override {
|
void push() override {
|
||||||
|
m_scope_level++;
|
||||||
push_cache();
|
push_cache();
|
||||||
s->push();
|
s->push();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(unsigned n) override {
|
void pop(unsigned n) override {
|
||||||
|
SASSERT(scope_level() >= n);
|
||||||
|
m_scope_level -= n;
|
||||||
pop_cache();
|
pop_cache();
|
||||||
s->pop(n);
|
s->pop(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned scope_level() override {
|
||||||
|
return m_scope_level;
|
||||||
|
}
|
||||||
|
|
||||||
expr* mk_numeral(rational const& r) const {
|
expr* mk_numeral(rational const& r) const {
|
||||||
return bv->mk_numeral(r, bit_width);
|
return bv->mk_numeral(r, bit_width);
|
||||||
}
|
}
|
||||||
|
@ -198,8 +212,8 @@ namespace polysat {
|
||||||
return s->check_sat();
|
return s->check_sat();
|
||||||
}
|
}
|
||||||
|
|
||||||
dep_vector unsat_core() override {
|
void unsat_core(dep_vector& deps) override {
|
||||||
dep_vector deps;
|
deps.reset();
|
||||||
expr_ref_vector core(m);
|
expr_ref_vector core(m);
|
||||||
s->get_unsat_core(core);
|
s->get_unsat_core(core);
|
||||||
for (expr* a : core) {
|
for (expr* a : core) {
|
||||||
|
@ -207,7 +221,6 @@ namespace polysat {
|
||||||
deps.push_back(dep);
|
deps.push_back(dep);
|
||||||
}
|
}
|
||||||
SASSERT(deps.size() > 0);
|
SASSERT(deps.size() > 0);
|
||||||
return deps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rational model() override {
|
rational model() override {
|
||||||
|
|
|
@ -39,13 +39,15 @@ namespace polysat {
|
||||||
|
|
||||||
virtual void push() = 0;
|
virtual void push() = 0;
|
||||||
virtual void pop(unsigned n) = 0;
|
virtual void pop(unsigned n) = 0;
|
||||||
|
virtual unsigned scope_level() = 0;
|
||||||
|
|
||||||
virtual lbool check() = 0;
|
virtual lbool check() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Precondition: check() returned l_false
|
* Precondition: check() returned l_false
|
||||||
*/
|
*/
|
||||||
virtual dep_vector unsat_core() = 0;
|
dep_vector unsat_core();
|
||||||
|
virtual void unsat_core(dep_vector& out_deps) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Precondition: check() returned l_true
|
* Precondition: check() returned l_true
|
||||||
|
|
|
@ -23,6 +23,10 @@ TODO: improve management of the fallback univariate solvers:
|
||||||
- incrementally add/remove constraints
|
- incrementally add/remove constraints
|
||||||
- set resource limit of univariate solver
|
- set resource limit of univariate solver
|
||||||
|
|
||||||
|
TODO: plan to fix the FI "pumping":
|
||||||
|
1. simple looping detection and bitblasting fallback.
|
||||||
|
2. intervals at multiple bit widths
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,6 +37,8 @@ TODO: improve management of the fallback univariate solvers:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
|
using namespace viable_query;
|
||||||
|
|
||||||
struct inf_fi : public inference {
|
struct inf_fi : public inference {
|
||||||
viable& v;
|
viable& v;
|
||||||
pvar var;
|
pvar var;
|
||||||
|
@ -137,7 +143,7 @@ namespace polysat {
|
||||||
prop = true;
|
prop = true;
|
||||||
break;
|
break;
|
||||||
case find_t::empty:
|
case find_t::empty:
|
||||||
s.set_conflict(v, false);
|
SASSERT(s.is_conflict());
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -543,15 +549,22 @@ namespace polysat {
|
||||||
return refine_viable(v, val);
|
return refine_viable(v, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lbool viable::min_viable(pvar v, rational& lo) {
|
||||||
|
return query<query_t::min_viable>(v, lo);
|
||||||
|
}
|
||||||
|
|
||||||
rational viable::min_viable(pvar v) {
|
lbool viable::max_viable(pvar v, rational& hi) {
|
||||||
refined:
|
return query<query_t::max_viable>(v, hi);
|
||||||
rational lo(0);
|
}
|
||||||
auto* e = m_units[v];
|
|
||||||
|
bool viable::query_min(pvar v, rational& lo) {
|
||||||
|
// TODO: should be able to deal with UNSAT case; since also min_viable has to deal with it due to fallback solver
|
||||||
|
lo = 0;
|
||||||
|
entry* e = m_units[v];
|
||||||
if (!e && !refine_viable(v, lo))
|
if (!e && !refine_viable(v, lo))
|
||||||
goto refined;
|
return false;
|
||||||
if (!e)
|
if (!e)
|
||||||
return lo;
|
return true;
|
||||||
entry* first = e;
|
entry* first = e;
|
||||||
entry* last = first->prev();
|
entry* last = first->prev();
|
||||||
if (last->interval.currently_contains(lo))
|
if (last->interval.currently_contains(lo))
|
||||||
|
@ -564,19 +577,19 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
while (e != first);
|
while (e != first);
|
||||||
if (!refine_viable(v, lo))
|
if (!refine_viable(v, lo))
|
||||||
goto refined;
|
return false;
|
||||||
SASSERT(is_viable(v, lo));
|
SASSERT(is_viable(v, lo));
|
||||||
return lo;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
rational viable::max_viable(pvar v) {
|
bool viable::query_max(pvar v, rational& hi) {
|
||||||
refined:
|
// TODO: should be able to deal with UNSAT case; since also max_viable has to deal with it due to fallback solver
|
||||||
rational hi = s.var2pdd(v).max_value();
|
hi = s.var2pdd(v).max_value();
|
||||||
auto* e = m_units[v];
|
auto* e = m_units[v];
|
||||||
if (!e && !refine_viable(v, hi))
|
if (!e && !refine_viable(v, hi))
|
||||||
goto refined;
|
return false;
|
||||||
if (!e)
|
if (!e)
|
||||||
return hi;
|
return true;
|
||||||
entry* last = e->prev();
|
entry* last = e->prev();
|
||||||
e = last;
|
e = last;
|
||||||
do {
|
do {
|
||||||
|
@ -587,17 +600,13 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
while (e != last);
|
while (e != last);
|
||||||
if (!refine_viable(v, hi))
|
if (!refine_viable(v, hi))
|
||||||
goto refined;
|
return false;
|
||||||
SASSERT(is_viable(v, hi));
|
SASSERT(is_viable(v, hi));
|
||||||
return hi;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// template <viable::query_t mode>
|
template <query_t mode>
|
||||||
find_t viable::query(query_t mode, pvar v, rational& lo, rational& hi) {
|
lbool viable::query(pvar v, typename query_result<mode>::result_t& result) {
|
||||||
SASSERT(mode == query_t::find_viable); // other modes are TODO
|
|
||||||
|
|
||||||
auto const& max_value = s.var2pdd(v).max_value();
|
|
||||||
|
|
||||||
// max number of interval refinements before falling back to the univariate solver
|
// max number of interval refinements before falling back to the univariate solver
|
||||||
unsigned const refinement_budget = 1000;
|
unsigned const refinement_budget = 1000;
|
||||||
unsigned refinements = refinement_budget;
|
unsigned refinements = refinement_budget;
|
||||||
|
@ -606,25 +615,152 @@ namespace polysat {
|
||||||
|
|
||||||
if (!refinements) {
|
if (!refinements) {
|
||||||
LOG("Refinement budget exhausted! Fall back to univariate solver.");
|
LOG("Refinement budget exhausted! Fall back to univariate solver.");
|
||||||
return find_t::resource_out;
|
return query_fallback<mode>(v, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
refinements--;
|
refinements--;
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::find_viable) {
|
||||||
|
lbool res = query_find(v, result.first, result.second);
|
||||||
|
if (res == l_undef)
|
||||||
|
goto refined;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::min_viable) {
|
||||||
|
if (!query_min(v, result))
|
||||||
|
goto refined;
|
||||||
|
return l_true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::max_viable) {
|
||||||
|
if (!query_max(v, result))
|
||||||
|
goto refined;
|
||||||
|
return l_true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::has_viable) {
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
UNREACHABLE();
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <query_t mode>
|
||||||
|
lbool viable::query_fallback(pvar v, typename query_result<mode>::result_t& result) {
|
||||||
|
unsigned const bit_width = s.size(v);
|
||||||
|
univariate_solver* us = s.m_viable_fallback.usolver(bit_width);
|
||||||
|
sat::literal_set added;
|
||||||
|
|
||||||
|
// First step: only query the looping constraints and see if they alone are already UNSAT.
|
||||||
|
// The constraints which caused the refinement loop will be reached from m_units.
|
||||||
|
entry const* first = m_units[v];
|
||||||
|
entry const* e = first;
|
||||||
|
do {
|
||||||
|
entry const* origin = e;
|
||||||
|
while (origin->refined)
|
||||||
|
origin = origin->refined;
|
||||||
|
signed_constraint const c = origin->src;
|
||||||
|
sat::literal const lit = c.blit();
|
||||||
|
if (!added.contains(lit)) {
|
||||||
|
added.insert(lit);
|
||||||
|
c.add_to_univariate_solver(s, *us, lit.to_uint());
|
||||||
|
}
|
||||||
|
e = e->next();
|
||||||
|
}
|
||||||
|
while (e != first);
|
||||||
|
|
||||||
|
switch (us->check()) {
|
||||||
|
case l_false:
|
||||||
|
set_fallback_conflict(v, *us);
|
||||||
|
return l_false;
|
||||||
|
case l_true:
|
||||||
|
// At this point we don't know much because we did not add all relevant constraints
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// resource limit
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second step: looping constraints aren't UNSAT, so add the remaining relevant constraints
|
||||||
|
auto const& cs = s.m_viable_fallback.m_constraints[v];
|
||||||
|
for (unsigned i = cs.size(); i-- > 0; ) {
|
||||||
|
sat::literal const lit = cs[i].blit();
|
||||||
|
if (added.contains(lit))
|
||||||
|
continue;
|
||||||
|
added.insert(lit);
|
||||||
|
cs[i].add_to_univariate_solver(s, *us, lit.to_uint());
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (us->check()) {
|
||||||
|
case l_false:
|
||||||
|
set_fallback_conflict(v, *us);
|
||||||
|
return l_false;
|
||||||
|
case l_true:
|
||||||
|
// pass solver to mode-specific query
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// resource limit
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::find_viable)
|
||||||
|
return query_find_fallback(v, *us, result.first, result.second);
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::min_viable)
|
||||||
|
return query_min_fallback(v, *us, result);
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::max_viable)
|
||||||
|
return query_max_fallback(v, *us, result);
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::has_viable) {
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
UNREACHABLE();
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbool viable::query_find_fallback(pvar v, univariate_solver& us, rational& lo, rational& hi) {
|
||||||
|
if (!us.find_min(lo))
|
||||||
|
return l_undef;
|
||||||
|
if (!us.find_max(hi))
|
||||||
|
return l_undef;
|
||||||
|
return l_true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbool viable::query_min_fallback(pvar v, univariate_solver& us, rational& lo) {
|
||||||
|
return us.find_min(lo) ? l_true : l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbool viable::query_max_fallback(pvar v, univariate_solver& us, rational& hi) {
|
||||||
|
return us.find_max(hi) ? l_true : l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbool viable::query_find(pvar v, rational& lo, rational& hi) {
|
||||||
|
auto const& max_value = s.var2pdd(v).max_value();
|
||||||
|
lbool const refined = l_undef;
|
||||||
|
|
||||||
// After a refinement, any of the existing entries may have been replaced
|
// After a refinement, any of the existing entries may have been replaced
|
||||||
// (if it is subsumed by the new entry created during refinement).
|
// (if it is subsumed by the new entry created during refinement).
|
||||||
// For this reason, we start chasing the intervals from the start again.
|
// For this reason, we start chasing the intervals from the start again.
|
||||||
lo = 0;
|
lo = 0;
|
||||||
|
hi = max_value;
|
||||||
|
|
||||||
auto* e = m_units[v];
|
auto* e = m_units[v];
|
||||||
if (!e && !refine_viable(v, lo))
|
if (!e && !refine_viable(v, lo))
|
||||||
goto refined;
|
return refined;
|
||||||
if (!e && !refine_viable(v, rational::one()))
|
if (!e && !refine_viable(v, hi))
|
||||||
goto refined;
|
return refined;
|
||||||
if (!e)
|
if (!e)
|
||||||
return find_t::multiple;
|
return l_true;
|
||||||
if (e->interval.is_full())
|
if (e->interval.is_full()) {
|
||||||
return find_t::empty;
|
set_interval_conflict(v);
|
||||||
|
return l_false;
|
||||||
|
}
|
||||||
|
|
||||||
entry* first = e;
|
entry* first = e;
|
||||||
entry* last = first->prev();
|
entry* last = first->prev();
|
||||||
|
@ -635,10 +771,10 @@ namespace polysat {
|
||||||
last->interval.hi_val() < max_value) {
|
last->interval.hi_val() < max_value) {
|
||||||
lo = last->interval.hi_val();
|
lo = last->interval.hi_val();
|
||||||
if (!refine_viable(v, lo))
|
if (!refine_viable(v, lo))
|
||||||
goto refined;
|
return refined;
|
||||||
if (!refine_viable(v, max_value))
|
if (!refine_viable(v, max_value))
|
||||||
goto refined;
|
return refined;
|
||||||
return find_t::multiple;
|
return l_true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find lower bound
|
// find lower bound
|
||||||
|
@ -652,8 +788,10 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
while (e != first);
|
while (e != first);
|
||||||
|
|
||||||
if (e->interval.currently_contains(lo))
|
if (e->interval.currently_contains(lo)) {
|
||||||
return find_t::empty;
|
set_interval_conflict(v);
|
||||||
|
return l_false;
|
||||||
|
}
|
||||||
|
|
||||||
// find upper bound
|
// find upper bound
|
||||||
hi = max_value;
|
hi = max_value;
|
||||||
|
@ -666,83 +804,59 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
while (e != last);
|
while (e != last);
|
||||||
if (!refine_viable(v, lo))
|
if (!refine_viable(v, lo))
|
||||||
goto refined;
|
return refined;
|
||||||
if (!refine_viable(v, hi))
|
if (!refine_viable(v, hi))
|
||||||
goto refined;
|
return refined;
|
||||||
|
return l_true;
|
||||||
|
}
|
||||||
|
|
||||||
if (lo == hi)
|
lbool viable::find_viable(pvar v, rational& lo, rational& hi) {
|
||||||
return find_t::singleton;
|
std::pair<rational&, rational&> args{lo, hi};
|
||||||
else
|
return query<query_t::find_viable>(v, args);
|
||||||
return find_t::multiple;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
find_t viable::find_viable(pvar v, rational& lo) {
|
find_t viable::find_viable(pvar v, rational& lo) {
|
||||||
#if 1
|
|
||||||
rational hi;
|
rational hi;
|
||||||
// return query<query_t::find_viable>(v, lo, hi);
|
switch (find_viable(v, lo, hi)) {
|
||||||
return query(query_t::find_viable, v, lo, hi);
|
case l_true:
|
||||||
#else
|
return (lo == hi) ? find_t::singleton : find_t::multiple;
|
||||||
refined:
|
case l_false:
|
||||||
lo = 0;
|
|
||||||
auto* e = m_units[v];
|
|
||||||
if (!e && !refine_viable(v, lo))
|
|
||||||
goto refined;
|
|
||||||
if (!e && !refine_viable(v, rational::one()))
|
|
||||||
goto refined;
|
|
||||||
if (!e)
|
|
||||||
return find_t::multiple;
|
|
||||||
if (e->interval.is_full())
|
|
||||||
return find_t::empty;
|
return find_t::empty;
|
||||||
|
default:
|
||||||
entry* first = e;
|
return find_t::resource_out;
|
||||||
entry* last = first->prev();
|
|
||||||
|
|
||||||
// quick check: last interval does not wrap around
|
|
||||||
// and has space for 2 unassigned values.
|
|
||||||
auto& max_value = s.var2pdd(v).max_value();
|
|
||||||
if (last->interval.lo_val() < last->interval.hi_val() &&
|
|
||||||
last->interval.hi_val() < max_value) {
|
|
||||||
lo = last->interval.hi_val();
|
|
||||||
if (!refine_viable(v, lo))
|
|
||||||
goto refined;
|
|
||||||
if (!refine_viable(v, max_value))
|
|
||||||
goto refined;
|
|
||||||
return find_t::multiple;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// find lower bound
|
void viable::set_fallback_conflict(pvar v, univariate_solver& us) {
|
||||||
if (last->interval.currently_contains(lo))
|
SASSERT(!s.is_assigned(v));
|
||||||
lo = last->interval.hi_val();
|
conflict& core = s.m_conflict;
|
||||||
do {
|
core.init_empty();
|
||||||
if (!e->interval.currently_contains(lo))
|
core.logger().begin_conflict(); //header_with_var("unsat core from viable fallback for v", v)); // TODO: begin_conflict before or after adding constraints?
|
||||||
break;
|
// The conflict is the unsat core of the univariate solver,
|
||||||
lo = e->interval.hi_val();
|
// and the current assignment (under which the constraints are univariate in v)
|
||||||
e = e->next();
|
// TODO:
|
||||||
|
// - currently we add variables directly, which is sound:
|
||||||
|
// e.g.: v^2 + w^2 == 0; w := 1
|
||||||
|
// - but we could use side constraints on the coefficients instead (coefficients when viewed as polynomial over v):
|
||||||
|
// e.g.: v^2 + w^2 == 0; w^2 == 1
|
||||||
|
for (unsigned dep : us.unsat_core()) {
|
||||||
|
sat::literal lit = sat::to_literal(dep);
|
||||||
|
signed_constraint c = s.lit2cnstr(lit);
|
||||||
|
core.insert(c);
|
||||||
|
core.insert_vars(c);
|
||||||
}
|
}
|
||||||
while (e != first);
|
SASSERT(!core.vars().contains(v));
|
||||||
|
core.add_lemma("viable unsat core", core.build_lemma());
|
||||||
|
core.revert_pvar(v); // at this point, v is not assigned
|
||||||
|
}
|
||||||
|
|
||||||
if (e->interval.currently_contains(lo))
|
void viable::set_interval_conflict(pvar v) {
|
||||||
return find_t::empty;
|
SASSERT(!s.is_assigned(v));
|
||||||
|
conflict& core = s.m_conflict;
|
||||||
// find upper bound
|
core.init_empty();
|
||||||
rational hi = max_value;
|
core.logger().begin_conflict(); //header_with_var("forbidden interval lemma for v", v));
|
||||||
e = last;
|
VERIFY(resolve(v, core)); // TODO: merge?
|
||||||
do {
|
core.revert_pvar(v); // at this point, v is not assigned
|
||||||
if (!e->interval.currently_contains(hi))
|
|
||||||
break;
|
|
||||||
hi = e->interval.lo_val() - 1;
|
|
||||||
e = e->prev();
|
|
||||||
}
|
|
||||||
while (e != last);
|
|
||||||
if (!refine_viable(v, lo))
|
|
||||||
goto refined;
|
|
||||||
if (!refine_viable(v, hi))
|
|
||||||
goto refined;
|
|
||||||
if (lo == hi)
|
|
||||||
return find_t::singleton;
|
|
||||||
else
|
|
||||||
return find_t::multiple;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool viable::resolve(pvar v, conflict& core) {
|
bool viable::resolve(pvar v, conflict& core) {
|
||||||
|
@ -960,6 +1074,7 @@ namespace polysat {
|
||||||
m_usolver.insert(bit_width, mk_solver(bit_width));
|
m_usolver.insert(bit_width, mk_solver(bit_width));
|
||||||
us = m_usolver[bit_width].get();
|
us = m_usolver[bit_width].get();
|
||||||
}
|
}
|
||||||
|
SASSERT_EQ(us->scope_level(), 0);
|
||||||
|
|
||||||
// push once on the empty solver so we can reset it before the next use
|
// push once on the empty solver so we can reset it before the next use
|
||||||
us->push();
|
us->push();
|
||||||
|
|
|
@ -25,6 +25,7 @@ Author:
|
||||||
#include "math/polysat/conflict.h"
|
#include "math/polysat/conflict.h"
|
||||||
#include "math/polysat/constraint.h"
|
#include "math/polysat/constraint.h"
|
||||||
#include "math/polysat/forbidden_intervals.h"
|
#include "math/polysat/forbidden_intervals.h"
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
|
@ -39,6 +40,34 @@ namespace polysat {
|
||||||
resource_out,
|
resource_out,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace viable_query {
|
||||||
|
enum class query_t {
|
||||||
|
has_viable, // currently only used internally in resolve_viable
|
||||||
|
min_viable, // currently unused
|
||||||
|
max_viable, // currently unused
|
||||||
|
find_viable,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <query_t mode>
|
||||||
|
struct query_result {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct query_result<query_t::min_viable> {
|
||||||
|
using result_t = rational;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct query_result<query_t::max_viable> {
|
||||||
|
using result_t = rational;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct query_result<query_t::find_viable> {
|
||||||
|
using result_t = std::pair<rational&, rational&>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, find_t x);
|
std::ostream& operator<<(std::ostream& out, find_t x);
|
||||||
|
|
||||||
class viable {
|
class viable {
|
||||||
|
@ -76,15 +105,38 @@ namespace polysat {
|
||||||
|
|
||||||
void propagate(pvar v, rational const& val);
|
void propagate(pvar v, rational const& val);
|
||||||
|
|
||||||
enum class query_t {
|
// return true if done, false if refined
|
||||||
has_viable, // currently only used internally in resolve_viable
|
bool query_min(pvar v, rational& out_lo);
|
||||||
min_viable, // currently unused
|
|
||||||
max_viable, // currently unused
|
|
||||||
find_viable,
|
|
||||||
};
|
|
||||||
|
|
||||||
// template <query_t mode>
|
// return true if done, false if refined
|
||||||
find_t query(query_t mode, pvar v, rational& out_lo, rational& out_hi);
|
bool query_max(pvar v, rational& out_hi);
|
||||||
|
|
||||||
|
// return true if done, false if resource out
|
||||||
|
lbool query_min_fallback(pvar v, univariate_solver& us, rational& out_lo);
|
||||||
|
lbool query_max_fallback(pvar v, univariate_solver& us, rational& out_hi);
|
||||||
|
|
||||||
|
// return resource_out if refined
|
||||||
|
lbool query_find(pvar v, rational& out_lo, rational& out_hi);
|
||||||
|
lbool query_find_fallback(pvar v, univariate_solver& us, rational& out_lo, rational& out_hi);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interval query with bounded refinement and fallback to bitblasting.
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
|
*/
|
||||||
|
template <viable_query::query_t mode>
|
||||||
|
lbool query(pvar v, typename viable_query::query_result<mode>::result_t& out_result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
|
*/
|
||||||
|
template <viable_query::query_t mode>
|
||||||
|
lbool query_fallback(pvar v, typename viable_query::query_result<mode>::result_t& out_result);
|
||||||
|
|
||||||
|
/** Set viable conflict due to interval cover */
|
||||||
|
void set_interval_conflict(pvar v);
|
||||||
|
|
||||||
|
/** Set viable conflict due to fallback solver */
|
||||||
|
void set_fallback_conflict(pvar v, univariate_solver& us);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
viable(solver& s);
|
viable(solver& s);
|
||||||
|
@ -122,17 +174,29 @@ namespace polysat {
|
||||||
*/
|
*/
|
||||||
bool is_viable(pvar v, rational const& val);
|
bool is_viable(pvar v, rational const& val);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Extract min and max viable values for v
|
* Extract min viable value for v.
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
*/
|
*/
|
||||||
rational min_viable(pvar v);
|
lbool min_viable(pvar v, rational& out_lo);
|
||||||
rational max_viable(pvar v);
|
|
||||||
|
/**
|
||||||
|
* Extract max viable value for v.
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
|
*/
|
||||||
|
lbool max_viable(pvar v, rational& out_hi);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a next viable value for variable.
|
* Find a next viable value for variable.
|
||||||
*/
|
*/
|
||||||
find_t find_viable(pvar v, rational& out_val);
|
find_t find_viable(pvar v, rational& out_val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a next viable value for variable by determining currently viable lower and upper bounds.
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
|
*/
|
||||||
|
lbool find_viable(pvar v, rational& out_lo, rational& out_hi);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the unsat core for v,
|
* Retrieve the unsat core for v,
|
||||||
* and add the forbidden interval lemma for v (which eliminates v from the unsat core).
|
* and add the forbidden interval lemma for v (which eliminates v from the unsat core).
|
||||||
|
@ -251,6 +315,8 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
class viable_fallback {
|
class viable_fallback {
|
||||||
|
friend class viable;
|
||||||
|
|
||||||
solver& s;
|
solver& s;
|
||||||
|
|
||||||
scoped_ptr<univariate_solver_factory> m_usolver_factory;
|
scoped_ptr<univariate_solver_factory> m_usolver_factory;
|
||||||
|
|
|
@ -38,16 +38,19 @@ namespace polysat {
|
||||||
auto c = s.ule(x + 3, x + 5);
|
auto c = s.ule(x + 3, x + 5);
|
||||||
s.v.intersect(xv, c);
|
s.v.intersect(xv, c);
|
||||||
std::cout << s.v << "\n";
|
std::cout << s.v << "\n";
|
||||||
std::cout << "min-max " << s.v.min_viable(xv) << " " << s.v.max_viable(xv) << "\n";
|
std::cout << "min " << s.v.min_viable(xv, val) << " " << val << "\n";
|
||||||
|
std::cout << "max " << s.v.max_viable(xv, val) << " " << val << "\n";
|
||||||
s.v.intersect(xv, s.ule(x, 2));
|
s.v.intersect(xv, s.ule(x, 2));
|
||||||
std::cout << s.v << "\n";
|
std::cout << s.v << "\n";
|
||||||
s.v.intersect(xv, s.ule(1, x));
|
s.v.intersect(xv, s.ule(1, x));
|
||||||
std::cout << s.v << "\n";
|
std::cout << s.v << "\n";
|
||||||
std::cout << "min-max " << s.v.min_viable(xv) << " " << s.v.max_viable(xv) << "\n";
|
std::cout << "min " << s.v.min_viable(xv, val) << " " << val << "\n";
|
||||||
|
std::cout << "max " << s.v.max_viable(xv, val) << " " << val << "\n";
|
||||||
s.v.intersect(xv, s.ule(x, 3));
|
s.v.intersect(xv, s.ule(x, 3));
|
||||||
std::cout << s.v << "\n";
|
std::cout << s.v << "\n";
|
||||||
std::cout << s.v.find_viable(xv, val) << " " << val << "\n";
|
std::cout << s.v.find_viable(xv, val) << " " << val << "\n";
|
||||||
std::cout << "min-max " << s.v.min_viable(xv) << " " << s.v.max_viable(xv) << "\n";
|
std::cout << "min " << s.v.min_viable(xv, val) << " " << val << "\n";
|
||||||
|
std::cout << "max " << s.v.max_viable(xv, val) << " " << val << "\n";
|
||||||
s.v.intersect(xv, s.ule(3, x));
|
s.v.intersect(xv, s.ule(3, x));
|
||||||
std::cout << s.v << "\n";
|
std::cout << s.v << "\n";
|
||||||
std::cout << s.v.find_viable(xv, val) << " " << val << "\n";
|
std::cout << s.v.find_viable(xv, val) << " " << val << "\n";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue