mirror of
https://github.com/Z3Prover/z3
synced 2025-04-29 03:45:51 +00:00
Polysat: check test results, forbidden intervals for coefficient -1 (#5241)
* Use scoped_ptr for condition * Check solver result in unit tests * Add test for unusual cjust * Add solver::get_value * Broken assertion * Support forbidden interval for coefficient -1
This commit is contained in:
parent
5791b41133
commit
fd1758ffab
8 changed files with 164 additions and 58 deletions
|
@ -68,8 +68,13 @@ namespace polysat {
|
|||
bool is_negative() const { return m_status == l_false; }
|
||||
bool is_undef() const { return m_status == l_undef; }
|
||||
|
||||
/** Precondition: all variables other than v are assigned. */
|
||||
virtual bool forbidden_interval(solver& s, pvar v, eval_interval& i, constraint*& neg_condition) { return false; }
|
||||
/** Precondition: all variables other than v are assigned.
|
||||
*
|
||||
* \param[out] out_interval The forbidden interval for this constraint
|
||||
* \param[out] out_neg_cond Negation of the side condition (the side condition is true when the forbidden interval is trivial). May be NULL if the condition is constant.
|
||||
* \returns True iff a forbidden interval exists and the output parameters were set.
|
||||
*/
|
||||
virtual bool forbidden_interval(solver& s, pvar v, eval_interval& out_interval, scoped_ptr<constraint>& out_neg_cond) { return false; }
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, constraint const& c) { return c.display(out); }
|
||||
|
|
|
@ -113,11 +113,17 @@ namespace polysat {
|
|||
*/
|
||||
|
||||
constraint* eq_constraint::eq_resolve(solver& s, pvar v) {
|
||||
SASSERT(is_currently_true(s));
|
||||
LOG("Resolve " << *this << " upon v" << v);
|
||||
if (s.m_conflict.size() != 1)
|
||||
return nullptr;
|
||||
constraint* c = s.m_conflict[0];
|
||||
SASSERT(c->is_currently_false(s));
|
||||
// 'c == this' can happen if propagation was from decide() with only one value left
|
||||
// (e.g., if there's an unsatisfiable clause and we try all values).
|
||||
// Resolution would give us '0 == 0' in this case, which is useless.
|
||||
if (c == this)
|
||||
return nullptr;
|
||||
SASSERT(is_currently_true(s)); // TODO: might not always hold (due to similar case as in comment above?)
|
||||
if (c->is_eq()) {
|
||||
pdd a = c->to_eq().p();
|
||||
pdd b = p();
|
||||
|
@ -146,7 +152,7 @@ namespace polysat {
|
|||
|
||||
|
||||
/// Compute forbidden interval for equality constraint by considering it as p <=u 0 (or p >u 0 for disequality)
|
||||
bool eq_constraint::forbidden_interval(solver& s, pvar v, eval_interval& i, constraint*& neg_condition)
|
||||
bool eq_constraint::forbidden_interval(solver& s, pvar v, eval_interval& out_interval, scoped_ptr<constraint>& out_neg_cond)
|
||||
{
|
||||
SASSERT(!is_undef());
|
||||
|
||||
|
@ -201,8 +207,8 @@ namespace polysat {
|
|||
swap(lo, hi);
|
||||
lo_val.swap(hi_val);
|
||||
}
|
||||
i = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||
neg_condition = nullptr;
|
||||
out_interval = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||
out_neg_cond = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ 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;
|
||||
bool forbidden_interval(solver& s, pvar v, eval_interval& out_interval, scoped_ptr<constraint>& out_neg_cond) override;
|
||||
|
||||
private:
|
||||
constraint* eq_resolve(solver& s, pvar v);
|
||||
|
|
|
@ -70,16 +70,14 @@ namespace polysat {
|
|||
rational longest_len;
|
||||
unsigned longest_i = UINT_MAX;
|
||||
for (constraint* c : conflict) {
|
||||
LOG("constraint: " << *c);
|
||||
LOG_H3("Computing forbidden interval for: " << *c);
|
||||
eval_interval interval = eval_interval::full();
|
||||
constraint* neg_cond = nullptr; // TODO: change to scoped_ptr
|
||||
scoped_ptr<constraint> neg_cond;
|
||||
if (c->forbidden_interval(s, v, interval, neg_cond)) {
|
||||
LOG("~> interval: " << interval);
|
||||
LOG(" neg_cond: " << show_deref(neg_cond));
|
||||
if (interval.is_currently_empty()) {
|
||||
dealloc(neg_cond);
|
||||
LOG("interval: " << interval);
|
||||
LOG("neg_cond: " << show_deref(neg_cond));
|
||||
if (interval.is_currently_empty())
|
||||
continue;
|
||||
}
|
||||
if (interval.is_full())
|
||||
has_full = true;
|
||||
else {
|
||||
|
@ -89,7 +87,7 @@ namespace polysat {
|
|||
longest_i = records.size();
|
||||
}
|
||||
}
|
||||
records.push_back({std::move(interval), neg_cond, c});
|
||||
records.push_back({std::move(interval), std::move(neg_cond), c});
|
||||
if (has_full)
|
||||
break;
|
||||
}
|
||||
|
@ -119,6 +117,7 @@ namespace polysat {
|
|||
return false;
|
||||
}
|
||||
LOG("seq: " << seq);
|
||||
SASSERT(seq.size() >= 2); // otherwise has_full should have been true
|
||||
|
||||
p_dependency* d = nullptr;
|
||||
unsigned lemma_lvl = 0;
|
||||
|
|
|
@ -272,6 +272,11 @@ namespace polysat {
|
|||
*/
|
||||
pdd var(pvar v) { return m_vars[v]; }
|
||||
|
||||
/**
|
||||
* Return value of v in the current model (only meaningful if check_sat() returned l_true).
|
||||
*/
|
||||
rational get_value(pvar v) const { SASSERT(!m_justification[v].is_unassigned()); return m_value[v]; }
|
||||
|
||||
/**
|
||||
* Create polynomial constraints (but do not activate them).
|
||||
* Each constraint is tracked by a dependency.
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace polysat {
|
|||
return p.is_val() && q.is_val() && p.val() > q.val();
|
||||
}
|
||||
|
||||
bool ule_constraint::forbidden_interval(solver& s, pvar v, eval_interval& i, constraint*& neg_condition)
|
||||
bool ule_constraint::forbidden_interval(solver& s, pvar v, eval_interval& out_interval, scoped_ptr<constraint>& out_neg_cond)
|
||||
{
|
||||
SASSERT(!is_undef());
|
||||
|
||||
|
@ -136,6 +136,8 @@ namespace polysat {
|
|||
|
||||
unsigned const sz = s.size(v);
|
||||
dd::pdd_manager& m = s.sz2pdd(sz);
|
||||
rational const pow2 = rational::power_of_two(sz);
|
||||
rational const minus_one = pow2 - 1;
|
||||
|
||||
pdd p1 = m.zero();
|
||||
pdd e1 = m.zero();
|
||||
|
@ -163,10 +165,10 @@ namespace polysat {
|
|||
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())
|
||||
// So currently we can only do it if the coefficient is 1 or -1.
|
||||
if (!a1.is_zero() && !a1.is_one() && a1 != minus_one)
|
||||
return false;
|
||||
if (!a2.is_zero() && !a2.is_one())
|
||||
if (!a2.is_zero() && !a2.is_one() && a2 != minus_one)
|
||||
return false;
|
||||
/*
|
||||
unsigned j1 = 0;
|
||||
|
@ -177,6 +179,9 @@ namespace polysat {
|
|||
return false;
|
||||
*/
|
||||
|
||||
rational const y_coeff = a1.is_zero() ? a2 : a1;
|
||||
SASSERT(!y_coeff.is_zero());
|
||||
|
||||
// Concrete values of evaluable terms
|
||||
auto e1s = e1.subst_val(s.m_search);
|
||||
auto e2s = e2.subst_val(s.m_search);
|
||||
|
@ -219,6 +224,7 @@ namespace polysat {
|
|||
else {
|
||||
SASSERT(!a1.is_zero());
|
||||
SASSERT(!a2.is_zero());
|
||||
SASSERT_EQ(a1, a2);
|
||||
// e1 + t <= e2 + t, with t = 2^j1*y = 2^j2*y
|
||||
// condition for empty/full: e1 == e2
|
||||
is_trivial = e1s.val() == e2s.val();
|
||||
|
@ -234,22 +240,44 @@ namespace polysat {
|
|||
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;
|
||||
out_neg_cond = nullptr;
|
||||
}
|
||||
else
|
||||
neg_condition = constraint::eq(level(), s.m_next_bvar++, is_trivial ? neg_t : pos_t, condition_body, m_dep);
|
||||
out_neg_cond = 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);
|
||||
// TODO: we cannot use empty intervals for interpolation. So we
|
||||
// can remove the empty case (make it represent 'full' instead),
|
||||
// and return 'false' here. Then we do not need the proper/full
|
||||
// tag on intervals.
|
||||
out_interval = eval_interval::empty(m);
|
||||
else
|
||||
i = eval_interval::full();
|
||||
out_interval = eval_interval::full();
|
||||
} else {
|
||||
if (y_coeff == minus_one) {
|
||||
// Transform according to: y \in [l;u[ <=> -y \in [1-u;1-l[
|
||||
// -y \in [1-u;1-l[
|
||||
// <=> -y - (1 - u) < (1 - l) - (1 - u) { by: y \in [l;u[ <=> y - l < u - l }
|
||||
// <=> u - y - 1 < u - l { simplified }
|
||||
// <=> (u-l) - (u-y-1) - 1 < u-l { by: a < b <=> b - a - 1 < b }
|
||||
// <=> y - l < u - l { simplified }
|
||||
// <=> y \in [l;u[.
|
||||
lo = 1 - lo;
|
||||
hi = 1 - hi;
|
||||
swap(lo, hi);
|
||||
lo_val = mod(1 - lo_val, pow2);
|
||||
hi_val = mod(1 - hi_val, pow2);
|
||||
lo_val.swap(hi_val);
|
||||
}
|
||||
else
|
||||
SASSERT(y_coeff.is_one());
|
||||
|
||||
if (is_negative()) {
|
||||
swap(lo, hi);
|
||||
lo_val.swap(hi_val);
|
||||
}
|
||||
i = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||
out_interval = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -38,8 +38,7 @@ 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;
|
||||
bool forbidden_interval(solver& s, pvar v, eval_interval& out_interval, scoped_ptr<constraint>& out_neg_cond) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue