mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 17:45:32 +00:00
Contract bit information to large unit-intervals
This commit is contained in:
parent
0dae2d40b5
commit
3f8edb9aac
8 changed files with 346 additions and 118 deletions
|
@ -234,7 +234,7 @@ namespace polysat {
|
|||
m_lemmas.push_back(&cl);
|
||||
|
||||
SASSERT(!empty());
|
||||
logger().begin_conflict();
|
||||
logger().begin_conflict(cl.name());
|
||||
}
|
||||
|
||||
void conflict::init_by_viable_interval(pvar v) {
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace polysat {
|
|||
backtrack _backtrack(fi.side_cond);
|
||||
|
||||
fi.coeff = 1;
|
||||
fi.src = c;
|
||||
fi.src.push_back(c);
|
||||
|
||||
// eval(lhs) = a1*v + eval(e1) = a1*v + b1
|
||||
// eval(rhs) = a2*v + eval(e2) = a2*v + b2
|
||||
|
@ -135,7 +135,7 @@ namespace polysat {
|
|||
backtrack _backtrack(fi.side_cond);
|
||||
|
||||
fi.coeff = 1;
|
||||
fi.src = c;
|
||||
fi.src.push_back(c);
|
||||
|
||||
struct show {
|
||||
forbidden_intervals& f;
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace polysat {
|
|||
struct fi_record {
|
||||
eval_interval interval;
|
||||
vector<signed_constraint> side_cond;
|
||||
signed_constraint src;
|
||||
vector<signed_constraint> src; // only units may have multiple src (as they can consist of contracted bit constraints)
|
||||
rational coeff;
|
||||
|
||||
/** Create invalid fi_record */
|
||||
|
|
|
@ -275,7 +275,7 @@ namespace polysat {
|
|||
}
|
||||
else {
|
||||
// forward propagation
|
||||
SASSERT(!(pv.is_val() && qv.is_val() && rv.is_val()));
|
||||
/*SASSERT(!(pv.is_val() && qv.is_val() && rv.is_val()));
|
||||
if (qv.is_val() && !rv.is_val()) {
|
||||
const rational& qr = qv.val();
|
||||
if (qr >= m.power_of_2())
|
||||
|
@ -285,7 +285,7 @@ namespace polysat {
|
|||
const rational& pr = pv.val();
|
||||
return s.mk_clause(~lshr, ~s.eq(p(), m.mk_val(pr)), ~s.eq(q(), m.mk_val(qr)), s.eq(r(), m.mk_val(machine_div(pr, rational::power_of_two(qr.get_unsigned())))), true);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -365,7 +365,7 @@ namespace polysat {
|
|||
}
|
||||
else {
|
||||
// forward propagation
|
||||
SASSERT(!(pv.is_val() && qv.is_val() && rv.is_val()));
|
||||
/*SASSERT(!(pv.is_val() && qv.is_val() && rv.is_val()));
|
||||
if (qv.is_val() && !rv.is_val()) {
|
||||
const rational& qr = qv.val();
|
||||
if (qr >= m.power_of_2())
|
||||
|
@ -375,7 +375,7 @@ namespace polysat {
|
|||
const rational& pr = pv.val();
|
||||
return s.mk_clause(~shl, ~s.eq(p(), m.mk_val(pr)), ~s.eq(q(), m.mk_val(qr)), s.eq(r(), m.mk_val(rational::power_of_two(qr.get_unsigned()) * pr)), true);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -544,16 +544,25 @@ namespace polysat {
|
|||
bool rb = rv.val().get_bit(i);
|
||||
if (rb == (pb && qb))
|
||||
continue;
|
||||
if (pb && qb && !rb)
|
||||
return s.mk_clause(~andc, ~s.bit(p(), i), ~s.bit(q(), i), s.bit(r(), i), true);
|
||||
else if (!pb && rb)
|
||||
return s.mk_clause(~andc, s.bit(p(), i), ~s.bit(r(), i), true);
|
||||
else if (!qb && rb)
|
||||
return s.mk_clause(~andc, s.bit(q(), i), ~s.bit(r(), i), true);
|
||||
if (pb && qb && !rb) {
|
||||
verbose_stream() << "Conflict propagation " << pv << " (" << p() << ") & " << qv << " (" << q() << ") = " << bitwise_and(pv.val(), qv.val()) << " (" << r() << ")\n";
|
||||
verbose_stream() << "1 & 1 = 0 bit " << i << "\n";
|
||||
s.add_clause(s.mk_clause(~andc, ~s.bit(p(), i), ~s.bit(q(), i), s.bit(r(), i), true));
|
||||
}
|
||||
else if (!pb && rb) {
|
||||
verbose_stream() << "Conflict propagation " << pv << " (" << p() << ") & " << qv << " (" << q() << ") = " << bitwise_and(pv.val(), qv.val()) << " (" << r() << ")\n";
|
||||
verbose_stream() << "0 & ? = 1 bit " << i << "\n";
|
||||
s.add_clause(s.mk_clause(~andc, s.bit(p(), i), ~s.bit(r(), i), true));
|
||||
}
|
||||
else if (!qb && rb) {
|
||||
verbose_stream() << "Conflict propagation " << pv << " (" << p() << ") & " << qv << " (" << q() << ") = " << bitwise_and(pv.val(), qv.val()) << " (" << r() << ")\n";
|
||||
verbose_stream() << "? & 0 = 1 bit " << i << "\n";
|
||||
s.add_clause(s.mk_clause(~andc, s.bit(q(), i), ~s.bit(r(), i), true));
|
||||
}
|
||||
else
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Propagate r if p or q are 0
|
||||
|
@ -562,8 +571,9 @@ namespace polysat {
|
|||
if (qv.is_zero() && !rv.is_zero()) // rv not necessarily fully evaluated
|
||||
return s.mk_clause(~andc, s.ule(r(), q()), true);
|
||||
// p = a && q = b ==> r = a & b
|
||||
if (pv.is_val() && qv.is_val() && !rv.is_val())
|
||||
/*if (pv.is_val() && qv.is_val() && !rv.is_val()) {
|
||||
return s.mk_clause(~andc, ~s.eq(p(), pv), ~s.eq(q(), qv), s.eq(r(), bitwise_and(pv.val(), qv.val())), true);
|
||||
}*/
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -652,8 +662,8 @@ namespace polysat {
|
|||
|
||||
// forward propagation: p assigned ==> r = pseudo_inverse(eval(p))
|
||||
// TODO: (later) this should be propagated instead of adding a clause
|
||||
if (pv.is_val() && !rv.is_val())
|
||||
return s.mk_clause(~invc, ~s.eq(p(), pv), s.eq(r(), pv.val().pseudo_inverse(m.power_of_2())), true);
|
||||
/*if (pv.is_val() && !rv.is_val())
|
||||
return s.mk_clause(~invc, ~s.eq(p(), pv), s.eq(r(), pv.val().pseudo_inverse(m.power_of_2())), true);*/
|
||||
|
||||
if (!pv.is_val() || !rv.is_val())
|
||||
return {};
|
||||
|
|
|
@ -443,7 +443,9 @@ namespace polysat {
|
|||
#if ENABLE_LINEAR_SOLVER
|
||||
m_linear_solver.push();
|
||||
#endif
|
||||
#if 0
|
||||
m_fixed_bits.push();
|
||||
#endif
|
||||
}
|
||||
|
||||
void solver::pop_levels(unsigned num_levels) {
|
||||
|
@ -458,8 +460,9 @@ namespace polysat {
|
|||
#if ENABLE_LINEAR_SOLVER
|
||||
m_linear_solver.pop(num_levels);
|
||||
#endif
|
||||
#if 0
|
||||
m_fixed_bits.pop();
|
||||
|
||||
#endif
|
||||
while (num_levels > 0) {
|
||||
switch (m_trail.back()) {
|
||||
case trail_instr_t::qhead_i: {
|
||||
|
|
|
@ -78,6 +78,7 @@ namespace polysat {
|
|||
if (m_alloc.empty())
|
||||
return alloc(entry);
|
||||
auto* e = m_alloc.back();
|
||||
e->src.reset();
|
||||
e->side_cond.reset();
|
||||
e->coeff = 1;
|
||||
e->refined = nullptr;
|
||||
|
@ -311,8 +312,9 @@ namespace polysat {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool viable::refine_viable(pvar v, rational const& val) {
|
||||
return refine_equal_lin(v, val) && refine_disequal_lin(v, val);
|
||||
template<bool FORWARD>
|
||||
bool viable::refine_viable(pvar v, rational const& val, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications) {
|
||||
return refine_bits<FORWARD>(v, val, fixed, justifications) && refine_equal_lin(v, val) && refine_disequal_lin(v, val);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -518,6 +520,35 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
template<bool FORWARD>
|
||||
bool viable::refine_bits(pvar v, rational const& val, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications) {
|
||||
|
||||
pdd v_pdd = s.var(v);
|
||||
|
||||
// TODO: We might also extend simultaneously up and downwards if we want the actual interval (however, this might make use of more fixed bits and is weaker - worse - therefore)
|
||||
entry* ne = alloc_entry();
|
||||
rational new_val = extend_by_bits<FORWARD>(v_pdd, val, fixed, justifications, ne->src, ne->side_cond);
|
||||
|
||||
if (new_val == val) {
|
||||
m_alloc.push_back(ne);
|
||||
return true;
|
||||
}
|
||||
|
||||
ne->coeff = 1;
|
||||
if (FORWARD) {
|
||||
LOG("refine-bits for v" << v << " [" << val << ", " << new_val << "[");
|
||||
ne->interval = eval_interval::proper(v_pdd.manager().mk_val(val), val, v_pdd.manager().mk_val(new_val), new_val);
|
||||
}
|
||||
else {
|
||||
rational upper_bound = val == v_pdd.manager().max_value() ? rational::zero() : val + 1;;
|
||||
LOG("refine-bits for v" << v << " [" << new_val << ", " << upper_bound << "[");
|
||||
ne->interval = eval_interval::proper(v_pdd.manager().mk_val(new_val), new_val, v_pdd.manager().mk_val(upper_bound), upper_bound);
|
||||
}
|
||||
SASSERT(ne->interval.currently_contains(val));
|
||||
intersect(v, ne);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse all interval constraints with coefficients to check whether current value 'val' for
|
||||
* 'v' is feasible. If not, extract a (maximal) interval to block 'v' from being assigned val.
|
||||
|
@ -545,7 +576,11 @@ namespace {
|
|||
do {
|
||||
rational coeff_val = mod(e->coeff * val, mod_value);
|
||||
if (e->interval.currently_contains(coeff_val)) {
|
||||
LOG("refine-equal-lin for v" << v << " in src: " << lit_pp(s, e->src));
|
||||
IF_LOGGING(
|
||||
verbose_stream() << "refine-equal-lin for v" << v << " in src: ";
|
||||
for (const auto& src : e->src)
|
||||
verbose_stream() << lit_pp(s, src) << "\n";
|
||||
);
|
||||
LOG("forbidden interval v" << v << " " << num_pp(s, v, val) << " " << num_pp(s, v, e->coeff, true) << " * " << e->interval);
|
||||
|
||||
if (mod(e->interval.hi_val() + 1, mod_value) == e->interval.lo_val()) {
|
||||
|
@ -668,7 +703,11 @@ namespace {
|
|||
m_diseq_lin[v] = m_diseq_lin[v]->next();
|
||||
|
||||
do {
|
||||
LOG("refine-disequal-lin for src: " << e->src);
|
||||
IF_LOGGING(
|
||||
verbose_stream() << "refine-disequal-lin for v" << v << " in src: ";
|
||||
for (const auto& src : e->src)
|
||||
verbose_stream() << lit_pp(s, src) << "\n";
|
||||
);
|
||||
// We compute an interval if the concrete value 'val' violates the constraint:
|
||||
// p*val + q > r*val + s if e->src.is_positive()
|
||||
// p*val + q >= r*val + s if e->src.is_negative()
|
||||
|
@ -684,7 +723,7 @@ namespace {
|
|||
rational const b = mod(r * val + s_, mod_value);
|
||||
rational const np = mod_value - p;
|
||||
rational const nr = mod_value - r;
|
||||
int const corr = e->src.is_negative() ? 1 : 0;
|
||||
int const corr = e->src[0].is_negative() ? 1 : 0;
|
||||
|
||||
auto delta_l = [&](rational const& val) {
|
||||
rational num = a - b + corr;
|
||||
|
@ -717,7 +756,7 @@ namespace {
|
|||
return std::min(max_value - val, dmax);
|
||||
};
|
||||
|
||||
if (a > b || (e->src.is_negative() && a == b)) {
|
||||
if (a > b || (e->src[0].is_negative() && a == b)) {
|
||||
rational lo = val - delta_l(val);
|
||||
rational hi = val + delta_u(val) + 1;
|
||||
|
||||
|
@ -742,72 +781,185 @@ namespace {
|
|||
while (e != first);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool viable::quick_bit_check(pvar v) {
|
||||
#if 0
|
||||
return true;
|
||||
#endif
|
||||
|
||||
// Skips all values that are not feasible w.r.t. fixed bits
|
||||
template<bool FORWARD>
|
||||
rational viable::extend_by_bits(const pdd& var, const rational& bound, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications, vector<signed_constraint>& src, vector<signed_constraint>& side_cond) const {
|
||||
unsigned k = var.power_of_2();
|
||||
if (fixed.empty())
|
||||
return bound;
|
||||
|
||||
SASSERT(k == fixed.size());
|
||||
|
||||
auto add_justification = [&](unsigned i) {
|
||||
auto& to_add = justifications[i];
|
||||
SASSERT(!to_add.empty());
|
||||
for (auto& add : to_add) {
|
||||
// TODO: Check for duplicates; maybe we add the same src/side_cond over and over again
|
||||
for (auto& sc : add->side_cond)
|
||||
side_cond.push_back(sc);
|
||||
for (auto& c : add->src)
|
||||
src.push_back(c);
|
||||
}
|
||||
};
|
||||
|
||||
unsigned firstFail;
|
||||
for (firstFail = k; firstFail > 0; firstFail--) {
|
||||
if (fixed[firstFail - 1] != l_undef) {
|
||||
lbool current = bound.get_bit(firstFail - 1) ? l_true : l_false;
|
||||
if (current != fixed[firstFail - 1])
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstFail == 0)
|
||||
return bound; // the value is feasible according to fixed bits
|
||||
|
||||
svector<lbool> new_bound(fixed.size());
|
||||
|
||||
for (unsigned i = 0; i < firstFail; i++) {
|
||||
if (fixed[i] != l_undef) {
|
||||
SASSERT(fixed[i] == l_true || fixed[i] == l_false);
|
||||
new_bound[i] = fixed[i];
|
||||
if (FORWARD != (fixed[i] == l_false))
|
||||
add_justification(i); // Minimize number of responsible fixed bits; we only add those justifications we need for sure
|
||||
}
|
||||
else
|
||||
new_bound[i] = FORWARD ? l_false : l_true;
|
||||
}
|
||||
|
||||
bool carry = fixed[firstFail - 1] == (FORWARD ? l_false : l_true);
|
||||
|
||||
for (unsigned i = firstFail; i < new_bound.size(); i++) {
|
||||
if (fixed[i] == l_undef) {
|
||||
lbool current = bound.get_bit(i) ? l_true : l_false;
|
||||
if (carry) {
|
||||
if (FORWARD) {
|
||||
if (current == l_false) {
|
||||
new_bound[i] = l_true;
|
||||
carry = false;
|
||||
}
|
||||
else
|
||||
new_bound[i] = l_false;
|
||||
}
|
||||
else {
|
||||
if (current == l_true) {
|
||||
new_bound[i] = l_false;
|
||||
carry = false;
|
||||
}
|
||||
else
|
||||
new_bound[i] = l_true;
|
||||
}
|
||||
}
|
||||
else
|
||||
new_bound[i] = current;
|
||||
}
|
||||
else {
|
||||
new_bound[i] = fixed[i];
|
||||
if (carry)
|
||||
add_justification(i); // Again, we need this justification; if carry is false we don't need it
|
||||
}
|
||||
}
|
||||
if (carry) {
|
||||
// We covered everything
|
||||
/*if (FORWARD)
|
||||
return rational::power_of_two(k);
|
||||
else*/
|
||||
return rational::zero();
|
||||
}
|
||||
|
||||
// TODO: Directly convert new_bound in rational?
|
||||
rational ret = rational::zero();
|
||||
for (unsigned i = new_bound.size(); i > 0; i--) {
|
||||
ret *= 2;
|
||||
SASSERT(new_bound[i - 1] != l_undef);
|
||||
ret += new_bound[i - 1] == l_true ? 1 : 0;
|
||||
}
|
||||
if (!FORWARD)
|
||||
return ret + 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// returns true iff no conflict was encountered
|
||||
bool viable::collect_bit_information(pvar v, bool add_conflict, svector<lbool>& fixed, vector<ptr_vector<entry>>& justifications) {
|
||||
|
||||
pdd p = s.var(v);
|
||||
// maybe pass them as arguments rather than having them as fields...
|
||||
fixed.clear();
|
||||
justifications.clear();
|
||||
fixed.resize(p.power_of_2(), l_undef);
|
||||
justifications.resize(p.power_of_2(), ptr_vector<entry>());
|
||||
|
||||
auto* e = m_equal_lin[v];
|
||||
auto* first = e;
|
||||
if (!e)
|
||||
return true;
|
||||
|
||||
pdd p = s.var(v);
|
||||
|
||||
clause_builder builder(s, "bit check");
|
||||
svector<lbool> fixed(p.power_of_2(), l_undef);
|
||||
vector<ptr_vector<entry>> justifications(p.power_of_2(), ptr_vector<entry>());
|
||||
vector<std::pair<entry*, trailing_bits>> postponed;
|
||||
|
||||
|
||||
auto add_entry = [&builder](entry* e) {
|
||||
for (const auto& sc : e->side_cond) {
|
||||
builder.insert_eval(~sc);
|
||||
LOG("Side cond: " << sc);
|
||||
}
|
||||
builder.insert_eval(~e->src);
|
||||
LOG("Adding to core: " << e->src);
|
||||
SASSERT(e->src.size() == 1);
|
||||
for (const auto& src : e->src) {
|
||||
builder.insert_eval(~src);
|
||||
LOG("Adding to core: " << e->src);
|
||||
}
|
||||
};
|
||||
|
||||
auto add_entry_list = [add_entry](const ptr_vector<entry>& list) {
|
||||
for (const auto& e : list)
|
||||
for (const auto& e : list)
|
||||
add_entry(e);
|
||||
};
|
||||
|
||||
unsigned largest_mask = 0;
|
||||
|
||||
do {
|
||||
if (e->src.size() != 1) {
|
||||
e = e->next();
|
||||
continue;
|
||||
}
|
||||
signed_constraint& src = e->src[0];
|
||||
single_bit bit;
|
||||
trailing_bits mask;
|
||||
if (e->src->is_ule() &&
|
||||
simplify_clause::get_bit(s.subst(e->src->to_ule().lhs()), s.subst(e->src->to_ule().rhs()), p, bit, e->src.is_positive()) && p.is_var()) {
|
||||
if (src->is_ule() &&
|
||||
simplify_clause::get_bit(s.subst(src->to_ule().lhs()), s.subst(src->to_ule().rhs()), p, bit, src.is_positive()) && p.is_var()) {
|
||||
|
||||
lbool prev = fixed[bit.position];
|
||||
fixed[bit.position] = bit.positive ? l_true : l_false;
|
||||
//verbose_stream() << "Setting bit " << bit.position << " to " << bit.positive << " because of " << e->src << "\n";
|
||||
if (prev != l_undef && fixed[bit.position] != prev) {
|
||||
LOG("Bit conflicting " << e->src << " with " << justifications[bit.position][0]->src);
|
||||
add_entry_list(justifications[bit.position]);
|
||||
add_entry(e);
|
||||
s.set_conflict(*builder.build());
|
||||
verbose_stream() << "Bit conflicting " << e->src << " with " << justifications[bit.position][0]->src << "\n";
|
||||
if (add_conflict) {
|
||||
add_entry_list(justifications[bit.position]);
|
||||
add_entry(e);
|
||||
s.set_conflict(*builder.build());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// just override; we prefer bit constraints over parity as those are easier for subsumption to remove
|
||||
// verbose_stream() << "Adding bit constraint: " << e->src[0] << " (" << bit.position << ")\n";
|
||||
justifications[bit.position].clear();
|
||||
justifications[bit.position].push_back(e);
|
||||
}
|
||||
else if ((e->src->is_eq() || e->src.is_diseq()) &&
|
||||
simplify_clause::get_trailing_mask(s.subst(e->src->to_ule().lhs()), s.subst(e->src->to_ule().rhs()), p, mask, e->src.is_positive()) && p.is_var()) {
|
||||
else if ((src->is_eq() || src.is_diseq()) &&
|
||||
simplify_clause::get_trailing_mask(s.subst(src->to_ule().lhs()), s.subst(src->to_ule().rhs()), p, mask, src.is_positive()) && p.is_var()) {
|
||||
|
||||
if (e->src.is_positive()) {
|
||||
if (src.is_positive()) {
|
||||
for (unsigned i = 0; i < mask.length; i++) {
|
||||
lbool prev = fixed[i];
|
||||
fixed[i] = mask.bits.get_bit(i) ? l_true : l_false;
|
||||
//verbose_stream() << "Setting bit " << i << " to " << mask.bits.get_bit(i) << " because of parity " << e->src << "\n";
|
||||
if (prev != l_undef) {
|
||||
if (fixed[i] != prev) {
|
||||
LOG("Positive parity conflicting " << e->src << " with " << justifications[i][0]->src);
|
||||
add_entry_list(justifications[i]);
|
||||
add_entry(e);
|
||||
s.set_conflict(*builder.build());
|
||||
verbose_stream() << "Positive parity conflicting " << e->src << " with " << justifications[i][0]->src << "\n";
|
||||
if (add_conflict) {
|
||||
add_entry_list(justifications[i]);
|
||||
add_entry(e);
|
||||
s.set_conflict(*builder.build());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
|
@ -816,12 +968,14 @@ namespace {
|
|||
largest_mask = mask.length;
|
||||
justifications[i].clear();
|
||||
justifications[i].push_back(e);
|
||||
// verbose_stream() << "Adding parity constraint: " << e->src[0] << " (" << i << ")\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
SASSERT(justifications[i].empty());
|
||||
justifications[i].push_back(e);
|
||||
// verbose_stream() << "Adding parity constraint: " << e->src[0] << " (" << i << ")\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -859,11 +1013,13 @@ namespace {
|
|||
if (i == neg.second.length) {
|
||||
if (indet == 0) {
|
||||
// Already false
|
||||
LOG("Found conflict with constraint " << neg.first->src);
|
||||
for (unsigned k = 0; k < neg.second.length; k++)
|
||||
add_entry_list(justifications[k]);
|
||||
add_entry(neg.first);
|
||||
s.set_conflict(*builder.build());
|
||||
verbose_stream() << "Found conflict with constraint " << neg.first->src << "\n";
|
||||
if (add_conflict) {
|
||||
for (unsigned k = 0; k < neg.second.length; k++)
|
||||
add_entry_list(justifications[k]);
|
||||
add_entry(neg.first);
|
||||
s.set_conflict(*builder.build());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (indet == 1) {
|
||||
|
@ -891,10 +1047,17 @@ namespace {
|
|||
}
|
||||
|
||||
bool viable::has_viable(pvar v) {
|
||||
|
||||
svector<lbool> fixed;
|
||||
vector<ptr_vector<entry>> justifications;
|
||||
|
||||
if (!collect_bit_information(v, false, fixed, justifications))
|
||||
return false;
|
||||
|
||||
refined:
|
||||
auto* e = m_units[v];
|
||||
|
||||
#define CHECK_RETURN(val) { if (refine_viable(v, val)) return true; else goto refined; }
|
||||
#define CHECK_RETURN(val) { if (refine_viable<true>(v, val, fixed, justifications)) return true; else goto refined; }
|
||||
|
||||
if (!e)
|
||||
CHECK_RETURN(rational::zero());
|
||||
|
@ -929,9 +1092,15 @@ namespace {
|
|||
}
|
||||
|
||||
bool viable::is_viable(pvar v, rational const& val) {
|
||||
|
||||
svector<lbool> fixed;
|
||||
vector<ptr_vector<entry>> justifications;
|
||||
|
||||
if (!collect_bit_information(v, false, fixed, justifications))
|
||||
return false;
|
||||
auto* e = m_units[v];
|
||||
if (!e)
|
||||
return refine_viable(v, val);
|
||||
return refine_viable<true>(v, val, fixed, justifications);
|
||||
entry* first = e;
|
||||
entry* last = first->prev();
|
||||
if (last->interval.currently_contains(val))
|
||||
|
@ -940,9 +1109,9 @@ namespace {
|
|||
if (e->interval.currently_contains(val))
|
||||
return false;
|
||||
if (val < e->interval.lo_val())
|
||||
return refine_viable(v, val);
|
||||
return refine_viable<true>(v, val, fixed, justifications);
|
||||
}
|
||||
return refine_viable(v, val);
|
||||
return refine_viable<true>(v, val, fixed, justifications);
|
||||
}
|
||||
|
||||
find_t viable::find_viable(pvar v, rational& lo) {
|
||||
|
@ -985,12 +1154,14 @@ namespace {
|
|||
auto const& hi = e->interval.hi();
|
||||
if (lo.is_val() && hi.is_val()) {
|
||||
if (out_c.empty() && lo.val() > hi.val()) {
|
||||
out_c.push_back(e->src);
|
||||
for (const auto& src : e->src)
|
||||
out_c.push_back(src);
|
||||
out_hi = lo.val() - 1;
|
||||
found = true;
|
||||
}
|
||||
else if (!out_c.empty() && lo.val() <= out_hi && out_hi < hi.val()) {
|
||||
out_c.push_back(e->src);
|
||||
for (const auto& src : e->src)
|
||||
out_c.push_back(src);
|
||||
out_hi = lo.val() - 1;
|
||||
found = true;
|
||||
}
|
||||
|
@ -1019,12 +1190,14 @@ namespace {
|
|||
auto const& hi = e->interval.hi();
|
||||
if (lo.is_val() && hi.is_val()) {
|
||||
if (out_c.empty() && hi.val() != 0 && (lo.val() == 0 || lo.val() > hi.val())) {
|
||||
out_c.push_back(e->src);
|
||||
for (const auto& src : e->src)
|
||||
out_c.push_back(src);
|
||||
out_lo = hi.val();
|
||||
found = true;
|
||||
}
|
||||
else if (!out_c.empty() && lo.val() <= out_lo && out_lo < hi.val()) {
|
||||
out_c.push_back(e->src);
|
||||
for (const auto& src : e->src)
|
||||
out_c.push_back(src);
|
||||
out_lo = hi.val();
|
||||
found = true;
|
||||
}
|
||||
|
@ -1048,14 +1221,17 @@ namespace {
|
|||
if (!e)
|
||||
return false;
|
||||
|
||||
bool found = false;
|
||||
|
||||
do {
|
||||
if (e->src == c)
|
||||
found = e->src.contains(c);
|
||||
if (found)
|
||||
break;
|
||||
e = e->next();
|
||||
}
|
||||
while (e != first);
|
||||
|
||||
if (e->src != c)
|
||||
if (!found)
|
||||
return false;
|
||||
entry const* e0 = e;
|
||||
// display_one(verbose_stream() << "selected e0 = ", v, e0) << "\n";
|
||||
|
@ -1101,7 +1277,8 @@ namespace {
|
|||
out_c.push_back(sc);
|
||||
}
|
||||
// verbose_stream() << "E: " << lit_pp(s, e->src) << "\n";
|
||||
out_c.push_back(e->src);
|
||||
for (const auto& src : e->src)
|
||||
out_c.push_back(src);
|
||||
}
|
||||
e = n;
|
||||
}
|
||||
|
@ -1117,8 +1294,13 @@ namespace {
|
|||
|
||||
template <query_t mode>
|
||||
lbool viable::query(pvar v, typename query_result<mode>::result_t& result) {
|
||||
if (!quick_bit_check(v))
|
||||
return l_false;
|
||||
|
||||
svector<lbool> fixed;
|
||||
vector<ptr_vector<entry>> justifications;
|
||||
|
||||
if (!collect_bit_information(v, true, fixed, justifications))
|
||||
return l_false; // conflict already added
|
||||
|
||||
// max number of interval refinements before falling back to the univariate solver
|
||||
unsigned const refinement_budget = 1000;
|
||||
unsigned refinements = refinement_budget;
|
||||
|
@ -1127,11 +1309,11 @@ namespace {
|
|||
lbool res = l_undef;
|
||||
|
||||
if constexpr (mode == query_t::find_viable)
|
||||
res = query_find(v, result.first, result.second);
|
||||
res = query_find(v, result.first, result.second, fixed, justifications);
|
||||
else if constexpr (mode == query_t::min_viable)
|
||||
res = query_min(v, result);
|
||||
res = query_min(v, result, fixed, justifications);
|
||||
else if constexpr (mode == query_t::max_viable)
|
||||
res = query_max(v, result);
|
||||
res = query_max(v, result, fixed, justifications);
|
||||
else if constexpr (mode == query_t::has_viable) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
}
|
||||
|
@ -1147,7 +1329,7 @@ namespace {
|
|||
return query_fallback<mode>(v, result);
|
||||
}
|
||||
|
||||
lbool viable::query_find(pvar v, rational& lo, rational& hi) {
|
||||
lbool viable::query_find(pvar v, rational& lo, rational& hi, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications) {
|
||||
auto const& max_value = s.var2pdd(v).max_value();
|
||||
lbool const refined = l_undef;
|
||||
|
||||
|
@ -1158,9 +1340,9 @@ namespace {
|
|||
hi = max_value;
|
||||
|
||||
auto* e = m_units[v];
|
||||
if (!e && !refine_viable(v, lo))
|
||||
if (!e && !refine_viable<true>(v, lo, fixed, justifications))
|
||||
return refined;
|
||||
if (!e && !refine_viable(v, hi))
|
||||
if (!e && !refine_viable<false>(v, hi, fixed, justifications))
|
||||
return refined;
|
||||
if (!e)
|
||||
return l_true;
|
||||
|
@ -1177,9 +1359,9 @@ namespace {
|
|||
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))
|
||||
if (!refine_viable<true>(v, lo, fixed, justifications))
|
||||
return refined;
|
||||
if (!refine_viable(v, max_value))
|
||||
if (!refine_viable<false>(v, max_value, fixed, justifications))
|
||||
return refined;
|
||||
return l_true;
|
||||
}
|
||||
|
@ -1211,18 +1393,18 @@ namespace {
|
|||
}
|
||||
while (e != last);
|
||||
|
||||
if (!refine_viable(v, lo))
|
||||
if (!refine_viable<true>(v, lo, fixed, justifications))
|
||||
return refined;
|
||||
if (!refine_viable(v, hi))
|
||||
if (!refine_viable<false>(v, hi, fixed, justifications))
|
||||
return refined;
|
||||
return l_true;
|
||||
}
|
||||
|
||||
lbool viable::query_min(pvar v, rational& lo) {
|
||||
lbool viable::query_min(pvar v, rational& lo, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications) {
|
||||
// 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<true>(v, lo, fixed, justifications))
|
||||
return l_undef;
|
||||
if (!e)
|
||||
return l_true;
|
||||
|
@ -1237,17 +1419,17 @@ namespace {
|
|||
e = e->next();
|
||||
}
|
||||
while (e != first);
|
||||
if (!refine_viable(v, lo))
|
||||
if (!refine_viable<true>(v, lo, fixed, justifications))
|
||||
return l_undef;
|
||||
SASSERT(is_viable(v, lo));
|
||||
return l_true;
|
||||
}
|
||||
|
||||
lbool viable::query_max(pvar v, rational& hi) {
|
||||
lbool viable::query_max(pvar v, rational& hi, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications) {
|
||||
// TODO: should be able to deal with UNSAT case; since also max_viable has to deal with it due to fallback solver
|
||||
hi = s.var2pdd(v).max_value();
|
||||
auto* e = m_units[v];
|
||||
if (!e && !refine_viable(v, hi))
|
||||
if (!e && !refine_viable<false>(v, hi, fixed, justifications))
|
||||
return l_undef;
|
||||
if (!e)
|
||||
return l_true;
|
||||
|
@ -1260,7 +1442,7 @@ namespace {
|
|||
e = e->prev();
|
||||
}
|
||||
while (e != last);
|
||||
if (!refine_viable(v, hi))
|
||||
if (!refine_viable<false>(v, hi, fixed, justifications))
|
||||
return l_undef;
|
||||
SASSERT(is_viable(v, hi));
|
||||
return l_true;
|
||||
|
@ -1282,13 +1464,15 @@ namespace {
|
|||
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);
|
||||
LOG("Adding " << lit_pp(s, lit));
|
||||
IF_VERBOSE(10, verbose_stream() << ";; " << lit_pp(s, lit) << "\n");
|
||||
c.add_to_univariate_solver(v, s, *us, lit.to_uint());
|
||||
for (const auto& src : origin->src) {
|
||||
sat::literal const lit = src.blit();
|
||||
if (!added.contains(lit)) {
|
||||
added.insert(lit);
|
||||
LOG("Adding " << lit_pp(s, lit));
|
||||
IF_VERBOSE(10, verbose_stream() << ";; " << lit_pp(s, lit) << "\n");
|
||||
verbose_stream() << ";; " << lit_pp(s, lit) << "\n";
|
||||
src.add_to_univariate_solver(v, s, *us, lit.to_uint());
|
||||
}
|
||||
}
|
||||
e = e->next();
|
||||
}
|
||||
|
@ -1391,9 +1575,23 @@ namespace {
|
|||
entry const* first = e;
|
||||
SASSERT(e);
|
||||
// If there is a full interval, all others would have been removed
|
||||
SASSERT(!e->interval.is_full() || e->next() == e);
|
||||
SASSERT(e->interval.is_full() || all_of(*e, [](entry const& f) { return !f.interval.is_full(); }));
|
||||
clause_builder lemma(s);
|
||||
if (first->interval.is_full()) {
|
||||
SASSERT(first->next() == first);
|
||||
for (auto sc : first->side_cond)
|
||||
lemma.insert_eval(~sc);
|
||||
for (const auto& src : first->src) {
|
||||
lemma.insert(~src);
|
||||
core.insert(src);
|
||||
core.insert_vars(src);
|
||||
}
|
||||
core.add_lemma("viable", lemma.build());
|
||||
core.logger().log(inf_fi(*this, v));
|
||||
return true;
|
||||
}
|
||||
|
||||
SASSERT(all_of(*first, [](entry const& f) { return !f.interval.is_full(); }));
|
||||
|
||||
do {
|
||||
// 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
|
||||
|
@ -1439,15 +1637,16 @@ namespace {
|
|||
|
||||
// verbose_stream() << e->interval << " " << e->side_cond << " " << e->src << ";\n";
|
||||
|
||||
if (!e->interval.is_full()) {
|
||||
signed_constraint c = s.m_constraints.elem(e->interval.hi(), n->interval.symbolic());
|
||||
lemma.insert_try_eval(~c);
|
||||
}
|
||||
signed_constraint c = s.m_constraints.elem(e->interval.hi(), n->interval.symbolic());
|
||||
lemma.insert_try_eval(~c);
|
||||
|
||||
for (auto sc : e->side_cond)
|
||||
lemma.insert_eval(~sc);
|
||||
lemma.insert(~e->src);
|
||||
core.insert(e->src);
|
||||
core.insert_vars(e->src);
|
||||
for (const auto& src : e->src) {
|
||||
lemma.insert(~src);
|
||||
core.insert(src);
|
||||
core.insert_vars(src);
|
||||
}
|
||||
e = n;
|
||||
}
|
||||
while (e != first);
|
||||
|
@ -1471,7 +1670,12 @@ namespace {
|
|||
return;
|
||||
entry* first = e;
|
||||
do {
|
||||
LOG("v" << v << ": " << e->interval << " " << e->side_cond << " " << e->src);
|
||||
IF_LOGGING(
|
||||
verbose_stream() << "v" << v << ": " << e->interval << " " << e->side_cond << " ";
|
||||
for (const auto& src : e->src)
|
||||
verbose_stream() << src << " ";
|
||||
verbose_stream() << "\n";
|
||||
);
|
||||
e = e->next();
|
||||
}
|
||||
while (e != first);
|
||||
|
@ -1495,7 +1699,7 @@ namespace {
|
|||
rational const& s_ = e->interval.hi().val();
|
||||
out << "[ ";
|
||||
out << val_pp(m, p, true) << "*v" << v << " + " << val_pp(m, q_);
|
||||
out << (e->src.is_positive() ? " > " : " >= ");
|
||||
out << (e->src[0].is_positive() ? " > " : " >= ");
|
||||
out << val_pp(m, r, true) << "*v" << v << " + " << val_pp(m, s_);
|
||||
out << " ] ";
|
||||
}
|
||||
|
@ -1503,7 +1707,9 @@ namespace {
|
|||
out << e->coeff << " * v" << v << " " << e->interval << " ";
|
||||
else
|
||||
out << e->interval << " ";
|
||||
out << e->side_cond << " " << e->src << "; ";
|
||||
out << e->side_cond << " ";
|
||||
for (const auto& src : e->src)
|
||||
out << src << "; ";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
@ -94,13 +94,20 @@ namespace polysat {
|
|||
|
||||
bool intersect(pvar v, entry* e);
|
||||
|
||||
bool refine_viable(pvar v, rational const& val);
|
||||
template<bool FORWARD>
|
||||
bool refine_viable(pvar v, rational const& val, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications);
|
||||
|
||||
template<bool FORWARD>
|
||||
bool refine_bits(pvar v, rational const& val, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications);
|
||||
|
||||
bool refine_equal_lin(pvar v, rational const& val);
|
||||
|
||||
bool refine_disequal_lin(pvar v, rational const& val);
|
||||
|
||||
bool quick_bit_check(pvar v);
|
||||
|
||||
template<bool FORWARD>
|
||||
rational extend_by_bits(const pdd& var, const rational& bounds, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications, vector<signed_constraint>& src, vector<signed_constraint>& side_cond) const;
|
||||
|
||||
bool collect_bit_information(pvar v, bool add_conflict, svector<lbool>& fixed, vector<ptr_vector<entry>>& justifications);
|
||||
|
||||
std::ostream& display_one(std::ostream& out, pvar v, entry const* e) const;
|
||||
std::ostream& display_all(std::ostream& out, pvar v, entry const* e, char const* delimiter = "") const;
|
||||
|
@ -113,9 +120,9 @@ namespace polysat {
|
|||
* Interval-based queries
|
||||
* @return l_true on success, l_false on conflict, l_undef on refinement
|
||||
*/
|
||||
lbool query_min(pvar v, rational& out_lo);
|
||||
lbool query_max(pvar v, rational& out_hi);
|
||||
lbool query_find(pvar v, rational& out_lo, rational& out_hi);
|
||||
lbool query_min(pvar v, rational& out_lo, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications);
|
||||
lbool query_max(pvar v, rational& out_hi, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications);
|
||||
lbool query_find(pvar v, rational& out_lo, rational& out_hi, const svector<lbool>& fixed, const vector<ptr_vector<entry>>& justifications);
|
||||
|
||||
/**
|
||||
* Bitblasting-based queries.
|
||||
|
@ -261,7 +268,7 @@ namespace polysat {
|
|||
}
|
||||
|
||||
signed_constraint& operator*() {
|
||||
return idx < curr->side_cond.size() ? curr->side_cond[idx] : curr->src;
|
||||
return idx < curr->side_cond.size() ? curr->side_cond[idx] : curr->src[idx - curr->side_cond.size()];
|
||||
}
|
||||
|
||||
bool operator==(iterator const& other) const {
|
||||
|
|
|
@ -678,8 +678,9 @@ namespace polysat {
|
|||
s.add_clause(c2, false);
|
||||
s.m_viable.intersect(x.var(), c1);
|
||||
s.m_viable.intersect(x.var(), c2);
|
||||
|
||||
VERIFY(!s.m_viable.quick_bit_check(x.var()));
|
||||
svector<lbool> fixed;
|
||||
vector<ptr_vector<viable::entry>> justifications;
|
||||
VERIFY(!s.m_viable.collect_bit_information(x.var(), false, fixed, justifications));
|
||||
}
|
||||
|
||||
// parity(x) >= 3 and bit_1(x)
|
||||
|
@ -692,8 +693,9 @@ namespace polysat {
|
|||
s.add_clause(c2, false);
|
||||
s.m_viable.intersect(x.var(), c1);
|
||||
s.m_viable.intersect(x.var(), c2);
|
||||
|
||||
VERIFY(!s.m_viable.quick_bit_check(x.var()));
|
||||
svector<lbool> fixed;
|
||||
vector<ptr_vector<viable::entry>> justifications;
|
||||
VERIFY(!s.m_viable.collect_bit_information(x.var(), false, fixed, justifications));
|
||||
}
|
||||
|
||||
// 8 * x + 3 == 0 or 8 * x + 5 == 0 is unsat
|
||||
|
@ -1490,9 +1492,9 @@ namespace polysat {
|
|||
}
|
||||
|
||||
// x*y <= b & a <= x & !Omega(x*y) => a*y <= b
|
||||
static void test_ineq_axiom4(unsigned bw = 32) {
|
||||
static void test_ineq_axiom4(unsigned bw = 32, unsigned i = 0) {
|
||||
auto const bound = rational::power_of_two(bw/2);
|
||||
for (unsigned i = 0; i < 24; ++i) {
|
||||
for (; i < 24; ++i) {
|
||||
scoped_solver s(concat(__func__, " bw=", bw, " perm=", i));
|
||||
auto x = s.var(s.add_var(bw));
|
||||
auto y = s.var(s.add_var(bw));
|
||||
|
@ -1950,7 +1952,7 @@ static void STD_CALL polysat_on_ctrl_c(int) {
|
|||
|
||||
void tst_polysat() {
|
||||
using namespace polysat;
|
||||
|
||||
test_polysat::test_ineq_axiom4(32, 7);
|
||||
#if 0 // Enable this block to run a single unit test with detailed output.
|
||||
collect_test_records = false;
|
||||
test_max_conflicts = 50;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue