mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 17:45:32 +00:00
Polysat: first pass at forbidden intervals (not yet fully integrated into solver) (#5227)
* Add interval class * Take dependency as const reference * Compute forbidden intervals for ule-constraints * Add class for evaluated interval * We need the evaluated bounds as well * Don't add constraint to cjust multiple times (hack, to be improved later) * typo * More interval helpers * Add constraint::ult factory function * Fix forbidden interval condition * Add solver::has_viable * Add conflict explanation using forbidden intervals (not yet fully integrated into solver)
This commit is contained in:
parent
60972de562
commit
f83705bf9f
14 changed files with 501 additions and 13 deletions
|
@ -430,6 +430,7 @@ namespace dd {
|
|||
unsigned max_pow2_divisor() const { return m.max_pow2_divisor(root); }
|
||||
unsigned_vector const& free_vars() const { return m.free_vars(*this); }
|
||||
|
||||
void swap(pdd& other) { std::swap(root, other.root); }
|
||||
|
||||
pdd_iterator begin() const;
|
||||
pdd_iterator end() const;
|
||||
|
@ -457,6 +458,8 @@ namespace dd {
|
|||
inline pdd& operator-=(pdd & p, pdd const& q) { p = p - q; return p; }
|
||||
inline pdd& operator+=(pdd & p, pdd const& q) { p = p + q; return p; }
|
||||
|
||||
inline void swap(pdd& p, pdd& q) { p.swap(q); }
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, pdd const& b);
|
||||
|
||||
struct pdd_monomial {
|
||||
|
|
|
@ -4,6 +4,7 @@ z3_add_component(polysat
|
|||
eq_constraint.cpp
|
||||
ule_constraint.cpp
|
||||
var_constraint.cpp
|
||||
forbidden_intervals.cpp
|
||||
justification.cpp
|
||||
log.cpp
|
||||
solver.cpp
|
||||
|
|
|
@ -29,16 +29,21 @@ namespace polysat {
|
|||
return *dynamic_cast<eq_constraint const*>(this);
|
||||
}
|
||||
|
||||
constraint* constraint::eq(unsigned lvl, bool_var bvar, csign_t sign, pdd const& p, p_dependency_ref& d) {
|
||||
constraint* constraint::eq(unsigned lvl, bool_var bvar, csign_t sign, pdd const& p, p_dependency_ref const& d) {
|
||||
return alloc(eq_constraint, lvl, bvar, sign, p, d);
|
||||
}
|
||||
|
||||
constraint* constraint::viable(unsigned lvl, bool_var bvar, csign_t sign, pvar v, bdd const& b, p_dependency_ref& d) {
|
||||
constraint* constraint::viable(unsigned lvl, bool_var bvar, csign_t sign, pvar v, bdd const& b, p_dependency_ref const& d) {
|
||||
return alloc(var_constraint, lvl, bvar, sign, v, b, d);
|
||||
}
|
||||
|
||||
constraint* constraint::ule(unsigned lvl, bool_var bvar, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref& d) {
|
||||
constraint* constraint::ule(unsigned lvl, bool_var bvar, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d) {
|
||||
return alloc(ule_constraint, lvl, bvar, sign, a, b, d);
|
||||
}
|
||||
|
||||
constraint* constraint::ult(unsigned lvl, bool_var bvar, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d) {
|
||||
// a < b <=> !(b <= a)
|
||||
return ule(lvl, bvar, static_cast<csign_t>(!sign), b, a, d);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ Author:
|
|||
--*/
|
||||
#pragma once
|
||||
#include "math/polysat/types.h"
|
||||
#include "math/polysat/interval.h"
|
||||
|
||||
namespace polysat {
|
||||
|
||||
|
@ -34,12 +35,13 @@ namespace polysat {
|
|||
bool_var m_bool_var;
|
||||
csign_t m_sign; ///< sign/polarity
|
||||
lbool m_status = l_undef; ///< current constraint status, computed from value of m_bool_var and m_sign
|
||||
constraint(unsigned lvl, bool_var bvar, csign_t sign, p_dependency_ref& dep, ckind_t k):
|
||||
constraint(unsigned lvl, bool_var bvar, csign_t sign, p_dependency_ref const& dep, ckind_t k):
|
||||
m_level(lvl), m_kind(k), m_dep(dep), m_bool_var(bvar), m_sign(sign) {}
|
||||
public:
|
||||
static constraint* eq(unsigned lvl, bool_var bvar, csign_t sign, pdd const& p, p_dependency_ref& d);
|
||||
static constraint* viable(unsigned lvl, bool_var bvar, csign_t sign, pvar v, bdd const& b, p_dependency_ref& d);
|
||||
static constraint* ule(unsigned lvl, bool_var bvar, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref& d);
|
||||
static constraint* eq(unsigned lvl, bool_var bvar, csign_t sign, pdd const& p, p_dependency_ref const& d);
|
||||
static constraint* viable(unsigned lvl, bool_var bvar, csign_t sign, pvar v, bdd const& b, p_dependency_ref const& d);
|
||||
static constraint* ule(unsigned lvl, bool_var bvar, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d);
|
||||
static constraint* ult(unsigned lvl, bool_var bvar, csign_t sign, pdd const& a, pdd const& b, p_dependency_ref const& d);
|
||||
virtual ~constraint() {}
|
||||
bool is_eq() const { return m_kind == ckind_t::eq_t; }
|
||||
bool is_ule() const { return m_kind == ckind_t::ule_t; }
|
||||
|
@ -63,6 +65,7 @@ namespace polysat {
|
|||
bool is_positive() const { return m_status == l_true; }
|
||||
bool is_negative() const { return m_status == l_false; }
|
||||
bool is_undef() const { return m_status == l_undef; }
|
||||
virtual bool forbidden_interval(solver& s, pvar v, eval_interval& i, constraint*& neg_condition) { return false; }
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, constraint const& c) { return c.display(out); }
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace polysat {
|
|||
class eq_constraint : public constraint {
|
||||
pdd m_poly;
|
||||
public:
|
||||
eq_constraint(unsigned lvl, bool_var bvar, csign_t sign, pdd const& p, p_dependency_ref& dep):
|
||||
eq_constraint(unsigned lvl, bool_var bvar, csign_t sign, pdd const& p, p_dependency_ref const& dep):
|
||||
constraint(lvl, bvar, sign, dep, ckind_t::eq_t), m_poly(p) {
|
||||
m_vars.append(p.free_vars());
|
||||
}
|
||||
|
|
|
@ -246,7 +246,7 @@ namespace polysat {
|
|||
numeral b = m.inv(a_ij >> (tz1 - tz2));
|
||||
M.mul(r_i, a);
|
||||
M.add(r_i, b, r_k);
|
||||
unsigned x_k = m_row[rk].m_base;
|
||||
unsigned x_k = m_rows[rk].m_base;
|
||||
|
||||
// TBD: redo according to slides
|
||||
// TBD: std::swap(m_row2base[ri], m_row2base[rk]);
|
||||
|
|
163
src/math/polysat/forbidden_intervals.cpp
Normal file
163
src/math/polysat/forbidden_intervals.cpp
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*++
|
||||
Copyright (c) 2021 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
Conflict explanation using forbidden intervals as described in
|
||||
"Solving bitvectors with MCSAT: explanations from bits and pieces"
|
||||
by S. Graham-Lengrand, D. Jovanovic, B. Dutertre.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2021-03-19
|
||||
Jakob Rath 2021-04-6
|
||||
|
||||
--*/
|
||||
#include "math/polysat/forbidden_intervals.h"
|
||||
#include "math/polysat/log.h"
|
||||
|
||||
namespace polysat {
|
||||
|
||||
struct fi_record {
|
||||
eval_interval interval;
|
||||
scoped_ptr<constraint> cond; // could be multiple constraints later
|
||||
constraint* src;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find a sequence of intervals that covers all of Z_modulus.
|
||||
*
|
||||
* \returns true iff such a covering exists
|
||||
* \param longest_i: the longest interval (as index into 'records')
|
||||
* \param out_seq: will contain the covering (as list of indices into 'records')
|
||||
*/
|
||||
static bool find_covering_sequence(vector<fi_record> const& records, unsigned longest_i, rational modulus, unsigned_vector& out_seq) {
|
||||
rational baseline = records[longest_i].interval.hi_val();
|
||||
while (!records[longest_i].interval.currently_contains(baseline)) {
|
||||
rational best_extent = rational::zero();
|
||||
unsigned furthest_i = UINT_MAX;
|
||||
for (unsigned i = records.size(); i-- > 0; ) {
|
||||
auto const& interval = records[i].interval;
|
||||
if (interval.currently_contains(baseline)) {
|
||||
rational extent = mod(interval.hi_val() - baseline, modulus);
|
||||
if (extent > best_extent) {
|
||||
best_extent = extent;
|
||||
furthest_i = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (furthest_i == UINT_MAX) {
|
||||
// There's a hole we can't cover.
|
||||
// This can happen if a constraint didn't produce an interval
|
||||
// (but not necessarily, values may be covered by multiple constraints)
|
||||
return false;
|
||||
}
|
||||
SASSERT(best_extent > 0);
|
||||
out_seq.push_back(furthest_i);
|
||||
baseline = records[furthest_i].interval.hi_val();
|
||||
}
|
||||
SASSERT(out_seq.size() > 0);
|
||||
if (!records[out_seq[0]].interval.currently_contains(baseline))
|
||||
out_seq.push_back(longest_i);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool forbidden_intervals::explain(solver& s, ptr_vector<constraint> const& conflict, pvar v, scoped_ptr_vector<constraint>& out_lemma) {
|
||||
|
||||
// Extract forbidden intervals from conflicting constraints
|
||||
vector<fi_record> records;
|
||||
bool has_full = false;
|
||||
rational longest_len;
|
||||
unsigned longest_i = UINT_MAX;
|
||||
for (constraint* c : conflict) {
|
||||
LOG("constraint: " << *c);
|
||||
eval_interval interval = eval_interval::full();
|
||||
constraint* cond = nullptr; // TODO: change to scoped_ptr
|
||||
if (c->forbidden_interval(s, v, interval, cond)) {
|
||||
LOG("~> interval: " << interval);
|
||||
if (cond)
|
||||
LOG(" cond: " << *cond);
|
||||
else
|
||||
LOG(" cond: <null>");
|
||||
if (interval.is_currently_empty()) {
|
||||
dealloc(cond);
|
||||
continue;
|
||||
}
|
||||
if (interval.is_full())
|
||||
has_full = true;
|
||||
else {
|
||||
auto const len = interval.current_len();
|
||||
if (len > longest_len) {
|
||||
longest_len = len;
|
||||
longest_i = records.size();
|
||||
}
|
||||
}
|
||||
records.push_back({std::move(interval), cond, c});
|
||||
if (has_full)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_full) {
|
||||
auto const& full_record = records.back();
|
||||
SASSERT(full_record.interval.is_full());
|
||||
LOG("has_full: " << std::boolalpha << has_full);
|
||||
// TODO: use full interval to explain
|
||||
NOT_IMPLEMENTED_YET();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (records.empty())
|
||||
return false;
|
||||
|
||||
SASSERT(longest_i != UINT_MAX);
|
||||
LOG("longest: i=" << longest_i << "; " << records[longest_i].interval);
|
||||
|
||||
rational const modulus = rational::power_of_two(s.size(v));
|
||||
|
||||
// Select a sequence of covering intervals
|
||||
unsigned_vector seq;
|
||||
if (!find_covering_sequence(records, longest_i, modulus, seq)) {
|
||||
return false;
|
||||
}
|
||||
LOG("seq: " << seq);
|
||||
|
||||
p_dependency* d = nullptr;
|
||||
unsigned lemma_lvl = 0;
|
||||
for (unsigned i : seq) {
|
||||
constraint* c = records[i].src;
|
||||
d = s.m_dm.mk_join(d, c->dep());
|
||||
lemma_lvl = std::max(lemma_lvl, c->level());
|
||||
}
|
||||
p_dependency_ref lemma_dep(d, s.m_dm);
|
||||
|
||||
// Create lemma
|
||||
// Idea:
|
||||
// - If the side conditions hold, and
|
||||
// - the upper bound of each interval is contained in the next interval,
|
||||
// then the forbidden intervals cover the whole domain and we have a conflict.
|
||||
// We learn the negation of this conjunction.
|
||||
for (unsigned seq_i = seq.size(); seq_i-- > 0; ) {
|
||||
unsigned const i = seq[seq_i];
|
||||
unsigned const next_i = seq[(seq_i+1) % seq.size()];
|
||||
// Build constraint: upper bound of each interval is not contained in the next interval,
|
||||
// using the equivalence: t \in [l;h[ <=> t-l < h-l
|
||||
auto const& hi = records[i].interval.hi();
|
||||
auto const& next_lo = records[next_i].interval.lo();
|
||||
auto const& next_hi = records[next_i].interval.hi();
|
||||
auto const& lhs = hi - next_lo;
|
||||
auto const& rhs = next_hi - next_lo;
|
||||
constraint* c = constraint::ult(lemma_lvl, s.m_next_bvar++, neg_t, lhs, rhs, lemma_dep);
|
||||
LOG("constraint: " << *c);
|
||||
out_lemma.push_back(c);
|
||||
// Side conditions
|
||||
// TODO: check whether the condition is subsumed by c? maybe at the end do a "lemma reduction" step, to try and reduce branching?
|
||||
scoped_ptr<constraint>& cond = records[i].cond;
|
||||
if (cond)
|
||||
out_lemma.push_back(cond.detach());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
29
src/math/polysat/forbidden_intervals.h
Normal file
29
src/math/polysat/forbidden_intervals.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*++
|
||||
Copyright (c) 2021 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
Conflict explanation using forbidden intervals as described in
|
||||
"Solving bitvectors with MCSAT: explanations from bits and pieces"
|
||||
by S. Graham-Lengrand, D. Jovanovic, B. Dutertre.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2021-03-19
|
||||
Jakob Rath 2021-04-6
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
#include "math/polysat/constraint.h"
|
||||
#include "math/polysat/interval.h"
|
||||
#include "math/polysat/solver.h"
|
||||
|
||||
namespace polysat {
|
||||
|
||||
class forbidden_intervals {
|
||||
|
||||
public:
|
||||
static bool explain(solver& s, ptr_vector<constraint> const& conflict, pvar v, scoped_ptr_vector<constraint>& out_lemma);
|
||||
|
||||
};
|
||||
}
|
107
src/math/polysat/interval.h
Normal file
107
src/math/polysat/interval.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*++
|
||||
Copyright (c) 2021 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
polysat intervals
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2021-03-19
|
||||
Jakob Rath 2021-04-6
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
#include "math/polysat/types.h"
|
||||
#include "util/optional.h"
|
||||
|
||||
namespace polysat {
|
||||
|
||||
enum class ikind_t { full, proper };
|
||||
|
||||
struct bounds {
|
||||
pdd lo; ///< lower bound, inclusive
|
||||
pdd hi; ///< upper bound, exclusive
|
||||
};
|
||||
|
||||
/**
|
||||
* An interval is either [lo; hi[ (excl. upper bound) or the full domain Z_{2^w}.
|
||||
* If lo > hi, the interval wraps around, i.e., represents the union of [lo; 2^w[ and [0; hi[.
|
||||
* Membership test t \in [lo; hi[ is equivalent to t - lo < hi - lo.
|
||||
*/
|
||||
class interval {
|
||||
ikind_t m_kind;
|
||||
optional<bounds> m_bounds;
|
||||
|
||||
interval(): m_kind(ikind_t::full) {}
|
||||
interval(pdd const& lo, pdd const& hi):
|
||||
m_kind(ikind_t::proper), m_bounds({lo, hi}) {}
|
||||
public:
|
||||
static interval empty(dd::pdd_manager& m) { return proper(m.zero(), m.zero()); }
|
||||
static interval full() { return {}; }
|
||||
static interval proper(pdd const& lo, pdd const& hi) { return {lo, hi}; }
|
||||
|
||||
bool is_full() const { return m_kind == ikind_t::full; }
|
||||
bool is_proper() const { return m_kind == ikind_t::proper; }
|
||||
bool is_always_empty() const { return is_proper() && lo() == hi(); }
|
||||
pdd const& lo() const { SASSERT(is_proper()); return m_bounds->lo; }
|
||||
pdd const& hi() const { SASSERT(is_proper()); return m_bounds->hi; }
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, interval const& i) {
|
||||
if (i.is_full())
|
||||
return os << "full";
|
||||
else
|
||||
return os << "[" << i.lo() << " ; " << i.hi() << "[";
|
||||
}
|
||||
|
||||
class eval_interval {
|
||||
interval m_symbolic;
|
||||
rational m_concrete_lo;
|
||||
rational m_concrete_hi;
|
||||
|
||||
eval_interval(interval&& i, rational const& lo_val, rational const& hi_val):
|
||||
m_symbolic(std::move(i)), m_concrete_lo(lo_val), m_concrete_hi(hi_val) {}
|
||||
public:
|
||||
static eval_interval empty(dd::pdd_manager &m) {
|
||||
return {interval::empty(m), rational::zero(), rational::zero()};
|
||||
}
|
||||
static eval_interval full() {
|
||||
return {interval::full(), rational::zero(), rational::zero()};
|
||||
}
|
||||
static eval_interval proper(pdd const &lo, rational const &lo_val, pdd const &hi, rational const &hi_val) {
|
||||
return {interval::proper(lo, hi), lo_val, hi_val};
|
||||
}
|
||||
|
||||
bool is_full() const { return m_symbolic.is_full(); }
|
||||
bool is_proper() const { return m_symbolic.is_proper(); }
|
||||
bool is_always_empty() const { return m_symbolic.is_always_empty(); }
|
||||
bool is_currently_empty() const { return is_proper() && lo_val() == hi_val(); }
|
||||
interval const& symbolic() const { return m_symbolic; }
|
||||
pdd const& lo() const { return m_symbolic.lo(); }
|
||||
pdd const& hi() const { return m_symbolic.hi(); }
|
||||
rational const& lo_val() const { SASSERT(is_proper()); return m_concrete_lo; }
|
||||
rational const& hi_val() const { SASSERT(is_proper()); return m_concrete_hi; }
|
||||
rational current_len() const {
|
||||
SASSERT(is_proper());
|
||||
return mod(hi_val() - lo_val(), rational::power_of_two(lo().power_of_2()));
|
||||
}
|
||||
bool currently_contains(rational const& val) const {
|
||||
if (is_full())
|
||||
return true;
|
||||
else if (lo_val() <= hi_val())
|
||||
return lo_val() <= val && val < hi_val();
|
||||
else
|
||||
return val < hi_val() || val >= lo_val();
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, eval_interval const& i) {
|
||||
if (i.is_full())
|
||||
return os << "full";
|
||||
else
|
||||
return os << i.symbolic() << " := [" << i.lo_val() << ";" << i.hi_val() << "[";
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -19,6 +19,7 @@ Author:
|
|||
#include "math/polysat/solver.h"
|
||||
#include "math/polysat/log.h"
|
||||
#include "math/polysat/fixplex_def.h"
|
||||
#include "math/polysat/forbidden_intervals.h"
|
||||
|
||||
namespace polysat {
|
||||
|
||||
|
@ -40,6 +41,10 @@ namespace polysat {
|
|||
return *bits;
|
||||
}
|
||||
|
||||
bool solver::has_viable(pvar v) {
|
||||
return !m_viable[v].is_false();
|
||||
}
|
||||
|
||||
bool solver::is_viable(pvar v, rational const& val) {
|
||||
return var2bits(v).contains(m_viable[v], val);
|
||||
}
|
||||
|
@ -421,17 +426,39 @@ namespace polysat {
|
|||
return;
|
||||
}
|
||||
|
||||
pvar conflict_var = null_var;
|
||||
scoped_ptr<constraint> lemma;
|
||||
reset_marks();
|
||||
for (constraint* c : m_conflict)
|
||||
for (auto v : c->vars())
|
||||
for (auto v : c->vars()) {
|
||||
set_mark(v);
|
||||
if (!has_viable(v)) {
|
||||
SASSERT(conflict_var == null_var || conflict_var == v); // at most one variable can be empty
|
||||
conflict_var = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (conflict_var != null_var) {
|
||||
LOG_H2("Conflict due to empty viable set for pvar " << conflict_var);
|
||||
scoped_ptr_vector<constraint> disjunctive_lemma;
|
||||
if (forbidden_intervals::explain(*this, m_conflict, conflict_var, disjunctive_lemma)) {
|
||||
LOG_H3("Learning disjunctive lemma"); // << disjunctive_lemma);
|
||||
SASSERT(disjunctive_lemma.size() > 0);
|
||||
if (disjunctive_lemma.size() == 1) {
|
||||
// TODO: continue normally?
|
||||
} else {
|
||||
// TODO: solver needs to return l_undef and allow external handling of disjunctive lemma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = m_search.size(); i-- > 0; ) {
|
||||
pvar v = m_search[i].first;
|
||||
LOG_H2("Working on pvar " << v);
|
||||
if (!is_marked(v))
|
||||
continue;
|
||||
justification& j = m_justification[v];
|
||||
LOG("Justification: " << j);
|
||||
if (j.level() <= base_level()) {
|
||||
report_unsat();
|
||||
return;
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace polysat {
|
|||
friend class eq_constraint;
|
||||
friend class var_constraint;
|
||||
friend class ule_constraint;
|
||||
friend class forbidden_intervals;
|
||||
|
||||
typedef ptr_vector<constraint> constraints;
|
||||
|
||||
|
@ -113,12 +114,20 @@ namespace polysat {
|
|||
}
|
||||
|
||||
void push_cjust(pvar v, constraint* c) {
|
||||
if (m_cjust[v].contains(c)) // TODO: better check (flag on constraint?)
|
||||
return;
|
||||
m_cjust[v].push_back(c);
|
||||
m_trail.push_back(trail_instr_t::just_i);
|
||||
m_cjust_trail.push_back(v);
|
||||
}
|
||||
|
||||
unsigned size(pvar v) const { return m_size[v]; }
|
||||
|
||||
/**
|
||||
* Check whether variable v has any viable values left according to m_viable.
|
||||
*/
|
||||
bool has_viable(pvar v);
|
||||
|
||||
/**
|
||||
* check if value is viable according to m_viable.
|
||||
*/
|
||||
|
@ -139,7 +148,6 @@ namespace polysat {
|
|||
*/
|
||||
void add_viable_dep(pvar v, p_dependency* dep);
|
||||
|
||||
|
||||
/**
|
||||
* Find a next viable value for variable.
|
||||
*/
|
||||
|
|
|
@ -136,4 +136,144 @@ namespace polysat {
|
|||
return p.is_val() && q.is_val() && p.val() > q.val();
|
||||
}
|
||||
|
||||
/**
|
||||
* Precondition: all variables other than v are assigned.
|
||||
*/
|
||||
bool ule_constraint::forbidden_interval(solver& s, pvar v, eval_interval& i, constraint*& neg_condition)
|
||||
{
|
||||
SASSERT(!is_undef());
|
||||
|
||||
// Current only works when degree(v) is at most one on both sides
|
||||
unsigned const deg1 = lhs().degree(v);
|
||||
unsigned const deg2 = rhs().degree(v);
|
||||
if (deg1 > 1 || deg2 > 1)
|
||||
return false;
|
||||
|
||||
if (deg1 == 0 && deg2 == 0) {
|
||||
UNREACHABLE(); // this case is not useful for conflict resolution (but it could be handled in principle)
|
||||
// i is empty or full, condition would be this constraint itself?
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned const sz = s.size(v);
|
||||
dd::pdd_manager& m = s.sz2pdd(sz);
|
||||
|
||||
pdd p1 = m.zero();
|
||||
pdd e1 = m.zero();
|
||||
if (deg1 == 0)
|
||||
e1 = lhs();
|
||||
else
|
||||
lhs().factor(v, 1, p1, e1);
|
||||
|
||||
pdd p2 = m.zero();
|
||||
pdd e2 = m.zero();
|
||||
if (deg2 == 0)
|
||||
e2 = rhs();
|
||||
else
|
||||
rhs().factor(v, 1, p2, e2);
|
||||
|
||||
// Interval extraction only works if v-coefficients are the same
|
||||
if (deg1 != 0 && deg2 != 0 && p1 != p2)
|
||||
return false;
|
||||
|
||||
// Currently only works if coefficient is a power of two
|
||||
if (!p1.is_val())
|
||||
return false;
|
||||
if (!p2.is_val())
|
||||
return false;
|
||||
rational a1 = p1.val();
|
||||
rational a2 = p2.val();
|
||||
// TODO: to express the interval for coefficient 2^i symbolically, we need right-shift/upper-bits-extract in the language.
|
||||
// So currently we can only do it if the coefficient is 1.
|
||||
if (!a1.is_zero() && !a1.is_one())
|
||||
return false;
|
||||
if (!a2.is_zero() && !a2.is_one())
|
||||
return false;
|
||||
/*
|
||||
unsigned j1 = 0;
|
||||
unsigned j2 = 0;
|
||||
if (!a1.is_zero() && !a1.is_power_of_two(j1))
|
||||
return false;
|
||||
if (!a2.is_zero() && !a2.is_power_of_two(j2))
|
||||
return false;
|
||||
*/
|
||||
|
||||
// Concrete values of evaluable terms
|
||||
auto e1s = e1.subst_val(s.m_search);
|
||||
auto e2s = e2.subst_val(s.m_search);
|
||||
SASSERT(e1s.is_val());
|
||||
SASSERT(e2s.is_val());
|
||||
|
||||
bool is_trivial;
|
||||
pdd condition_body = m.zero();
|
||||
pdd lo = m.zero();
|
||||
rational lo_val = rational::zero();
|
||||
pdd hi = m.zero();
|
||||
rational hi_val = rational::zero();
|
||||
|
||||
if (a2.is_zero()) {
|
||||
SASSERT(!a1.is_zero());
|
||||
// e1 + t <= e2, with t = 2^j1*y
|
||||
// condition for empty/full: e2 == -1
|
||||
is_trivial = (e2s + 1).is_zero();
|
||||
condition_body = e2 + 1;
|
||||
if (!is_trivial) {
|
||||
lo = e2 - e1 + 1;
|
||||
lo_val = (e2s - e1s + 1).val();
|
||||
hi = -e1;
|
||||
hi_val = (-e1s).val();
|
||||
}
|
||||
}
|
||||
else if (a1.is_zero()) {
|
||||
SASSERT(!a2.is_zero());
|
||||
// e1 <= e2 + t, with t = 2^j2*y
|
||||
// condition for empty/full: e1 == 0
|
||||
is_trivial = e1s.is_zero();
|
||||
condition_body = e1;
|
||||
if (!is_trivial) {
|
||||
lo = -e2;
|
||||
lo_val = (-e2s).val();
|
||||
hi = e1 - e2;
|
||||
hi_val = (e1s - e2s).val();
|
||||
}
|
||||
}
|
||||
else {
|
||||
SASSERT(!a1.is_zero());
|
||||
SASSERT(!a2.is_zero());
|
||||
// e1 + t <= e2 + t, with t = 2^j1*y = 2^j2*y
|
||||
// condition for empty/full: e1 == e2
|
||||
is_trivial = e1s.val() == e2s.val();
|
||||
condition_body = e1 - e2;
|
||||
if (!is_trivial) {
|
||||
lo = -e2;
|
||||
lo_val = (-e2s).val();
|
||||
hi = -e1;
|
||||
hi_val = (-e1s).val();
|
||||
}
|
||||
}
|
||||
|
||||
if (condition_body.is_val()) {
|
||||
// Condition is trivial; no need to create a constraint for that.
|
||||
SASSERT(is_trivial == condition_body.is_zero());
|
||||
neg_condition = nullptr;
|
||||
}
|
||||
else
|
||||
neg_condition = constraint::eq(level(), s.m_next_bvar++, is_trivial ? neg_t : pos_t, condition_body, m_dep);
|
||||
|
||||
if (is_trivial) {
|
||||
if (is_positive())
|
||||
i = eval_interval::empty(m);
|
||||
else
|
||||
i = eval_interval::full();
|
||||
} else {
|
||||
if (is_negative()) {
|
||||
swap(lo, hi);
|
||||
lo_val.swap(hi_val);
|
||||
}
|
||||
i = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace polysat {
|
|||
pdd m_lhs;
|
||||
pdd m_rhs;
|
||||
public:
|
||||
ule_constraint(unsigned lvl, bool_var bvar, csign_t sign, pdd const& l, pdd const& r, p_dependency_ref& dep):
|
||||
ule_constraint(unsigned lvl, bool_var bvar, csign_t sign, pdd const& l, pdd const& r, p_dependency_ref const& dep):
|
||||
constraint(lvl, bvar, sign, dep, ckind_t::ule_t), m_lhs(l), m_rhs(r) {
|
||||
m_vars.append(l.free_vars());
|
||||
for (auto v : r.free_vars())
|
||||
|
@ -39,6 +39,8 @@ namespace polysat {
|
|||
bool is_currently_false(solver& s) override;
|
||||
bool is_currently_true(solver& s) override;
|
||||
void narrow(solver& s) override;
|
||||
|
||||
bool forbidden_interval(solver& s, pvar v, eval_interval& i, constraint*& neg_condition) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace polysat {
|
|||
pvar m_var;
|
||||
bdd m_viable;
|
||||
public:
|
||||
var_constraint(unsigned lvl, bool_var bvar, csign_t sign, pvar v, bdd const & b, p_dependency_ref& dep):
|
||||
var_constraint(unsigned lvl, bool_var bvar, csign_t sign, pvar v, bdd const & b, p_dependency_ref const& dep):
|
||||
constraint(lvl, bvar, sign, dep, ckind_t::bit_t), m_var(v), m_viable(b) {}
|
||||
~var_constraint() override {}
|
||||
std::ostream& display(std::ostream& out) const override;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue