3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 17:45:32 +00:00

Fix unsoundness in viable fallback

(the src constraint of forbidden intervals is not necessarily univariate)
This commit is contained in:
Jakob Rath 2022-12-19 15:37:49 +01:00
parent 868a3710e0
commit 86a36a524a
14 changed files with 100 additions and 38 deletions

View file

@ -1208,6 +1208,11 @@ namespace dd {
return true;
}
/** Return true iff p contains no variables other than v. */
bool pdd_manager::is_univariate_in(PDD p, unsigned v) {
return (is_val(p) || var(p) == v) && is_univariate(p);
}
/**
* Push coefficients of univariate polynomial in order of ascending degree.
* Example: a*x^2 + b*x + c ==> [ c, b, a ]

View file

@ -364,6 +364,7 @@ namespace dd {
bool is_monomial(PDD p);
bool is_univariate(PDD p);
bool is_univariate_in(PDD p, unsigned v);
void get_univariate_coefficients(PDD p, vector<rational>& coeff);
// create an spoly r if leading monomials of a and b overlap
@ -430,6 +431,7 @@ namespace dd {
bool is_binary() const { return m.is_binary(root); }
bool is_monomial() const { return m.is_monomial(root); }
bool is_univariate() const { return m.is_univariate(root); }
bool is_univariate_in(unsigned v) const { return m.is_univariate_in(root, v); }
void get_univariate_coefficients(vector<rational>& coeff) const { m.get_univariate_coefficients(root, coeff); }
vector<rational> get_univariate_coefficients() const { vector<rational> coeff; m.get_univariate_coefficients(root, coeff); return coeff; }
bool is_never_zero() const { return m.is_never_zero(root); }

View file

@ -114,9 +114,11 @@ namespace polysat {
bool is_pwatched() const { return m_is_pwatched; }
void set_pwatched(bool f) { m_is_pwatched = f; }
/// Assuming the constraint is univariate under the current assignment of 's',
/// adds the constraint to the univariate solver 'us'.
virtual void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const = 0;
/**
* If the constraint is univariate in variable 'v' under the current assignment of 's',
* add the constraint to the univariate solver 'us'.
*/
virtual void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const = 0;
};
inline std::ostream& operator<<(std::ostream& out, constraint const& c) { return c.display(out); }
@ -161,7 +163,7 @@ namespace polysat {
void narrow(solver& s, bool first) { get()->narrow(s, is_positive(), first); }
clause_ref produce_lemma(solver& s, assignment const& a) { return get()->produce_lemma(s, a, is_positive()); }
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep) const { get()->add_to_univariate_solver(s, us, dep, is_positive()); }
void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep) const { get()->add_to_univariate_solver(v, s, us, dep, is_positive()); }
unsigned_vector const& vars() const { return m_constraint->vars(); }
unsigned var(unsigned idx) const { return m_constraint->var(idx); }

View file

@ -448,19 +448,25 @@ namespace polysat {
return l_undef;
}
void op_constraint::add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
auto p_coeff = s.subst(p()).get_univariate_coefficients();
auto q_coeff = s.subst(q()).get_univariate_coefficients();
auto r_coeff = s.subst(r()).get_univariate_coefficients();
void op_constraint::add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
pdd pv = s.subst(p());
if (!pv.is_univariate_in(v))
return;
pdd qv = s.subst(q());
if (!qv.is_univariate_in(v))
return;
pdd rv = s.subst(r());
if (!rv.is_univariate_in(v))
return;
switch (m_op) {
case code::lshr_op:
us.add_lshr(p_coeff, q_coeff, r_coeff, !is_positive, dep);
us.add_lshr(pv.get_univariate_coefficients(), qv.get_univariate_coefficients(), rv.get_univariate_coefficients(), !is_positive, dep);
break;
case code::shl_op:
us.add_shl(p_coeff, q_coeff, r_coeff, !is_positive, dep);
us.add_shl(pv.get_univariate_coefficients(), qv.get_univariate_coefficients(), rv.get_univariate_coefficients(), !is_positive, dep);
break;
case code::and_op:
us.add_and(p_coeff, q_coeff, r_coeff, !is_positive, dep);
us.add_and(pv.get_univariate_coefficients(), qv.get_univariate_coefficients(), rv.get_univariate_coefficients(), !is_positive, dep);
break;
default:
NOT_IMPLEMENTED_YET();

View file

@ -69,7 +69,7 @@ namespace polysat {
bool operator==(constraint const& other) const override;
bool is_eq() const override { return false; }
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
};
struct op_constraint_args {

View file

@ -119,13 +119,17 @@ namespace polysat {
&& q() == other.to_smul_fl().q();
}
void smul_fl_constraint::add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
auto p_coeff = s.subst(p()).get_univariate_coefficients();
auto q_coeff = s.subst(q()).get_univariate_coefficients();
void smul_fl_constraint::add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
auto p1 = s.subst(p());
if (!p1.is_univariate_in(v))
return;
auto q1 = s.subst(q());
if (!q1.is_univariate_in(v))
return;
if (is_overflow())
us.add_smul_ovfl(p_coeff, q_coeff, !is_positive, dep);
us.add_smul_ovfl(p1.get_univariate_coefficients(), q1.get_univariate_coefficients(), !is_positive, dep);
else
us.add_smul_udfl(p_coeff, q_coeff, !is_positive, dep);
us.add_smul_udfl(p1.get_univariate_coefficients(), q1.get_univariate_coefficients(), !is_positive, dep);
}
}

View file

@ -42,7 +42,7 @@ namespace polysat {
bool operator==(constraint const& other) const override;
bool is_eq() const override { return false; }
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
};
}

View file

@ -245,9 +245,16 @@ namespace polysat {
return other.is_ule() && lhs() == other.to_ule().lhs() && rhs() == other.to_ule().rhs();
}
void ule_constraint::add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
auto p_coeff = s.subst(lhs()).get_univariate_coefficients();
auto q_coeff = s.subst(rhs()).get_univariate_coefficients();
us.add_ule(p_coeff, q_coeff, !is_positive, dep);
void ule_constraint::add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
pdd p = s.subst(lhs());
pdd q = s.subst(rhs());
bool p_ok = p.is_univariate_in(v);
bool q_ok = q.is_univariate_in(v);
if (!is_positive && !q_ok) // add p > 0
us.add_ugt(p.get_univariate_coefficients(), rational::zero(), false, dep);
if (!is_positive && !p_ok) // add -1 > q <==> q+1 > 0
us.add_ugt((q + 1).get_univariate_coefficients(), rational::zero(), false, dep);
if (p_ok && q_ok)
us.add_ule(p.get_univariate_coefficients(), q.get_univariate_coefficients(), !is_positive, dep);
}
}

View file

@ -44,7 +44,7 @@ namespace polysat {
unsigned hash() const override;
bool operator==(constraint const& other) const override;
bool is_eq() const override { return m_rhs.is_zero(); }
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
};
struct ule_pp {

View file

@ -25,8 +25,7 @@ namespace polysat {
}
void umul_ovfl_constraint::simplify() {
if (m_p.is_zero() || m_q.is_zero() ||
m_p.is_one() || m_q.is_one()) {
if (m_p.is_zero() || m_q.is_zero() || m_p.is_one() || m_q.is_one()) {
m_q = 0;
m_p = 0;
return;
@ -158,9 +157,13 @@ namespace polysat {
return other.is_umul_ovfl() && p() == other.to_umul_ovfl().p() && q() == other.to_umul_ovfl().q();
}
void umul_ovfl_constraint::add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
auto p_coeff = s.subst(p()).get_univariate_coefficients();
auto q_coeff = s.subst(q()).get_univariate_coefficients();
us.add_umul_ovfl(p_coeff, q_coeff, !is_positive, dep);
void umul_ovfl_constraint::add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
pdd p1 = s.subst(p());
if (!p1.is_univariate_in(v))
return;
pdd q1 = s.subst(q());
if (!q1.is_univariate_in(v))
return;
us.add_umul_ovfl(p1.get_univariate_coefficients(), q1.get_univariate_coefficients(), !is_positive, dep);
}
}

View file

@ -46,7 +46,7 @@ namespace polysat {
bool operator==(constraint const& other) const override;
bool is_eq() const override { return false; }
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
};
}

View file

@ -103,6 +103,10 @@ namespace polysat {
return true;
}
bool is_zero(rational const& p) const {
return p.is_zero();
}
#if 0
// [d,c,b,a] --> ((a*x + b)*x + c)*x + d
expr* mk_poly(univariate const& p) const {
@ -154,6 +158,10 @@ namespace polysat {
}
return e;
}
expr_ref mk_poly(rational const& p) {
return {mk_numeral(p), m};
}
#endif
void add(expr* e, bool sign, dep_t dep) {
@ -171,13 +179,18 @@ namespace polysat {
}
}
void add_ule(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override {
template <typename lhs_t, typename rhs_t>
void add_ule_impl(lhs_t const& lhs, rhs_t const& rhs, bool sign, dep_t dep) {
if (is_zero(rhs))
add(m.mk_eq(mk_poly(lhs), mk_poly(rhs)), sign, dep);
else
add(bv->mk_ule(mk_poly(lhs), mk_poly(rhs)), sign, dep);
}
void add_ule(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override { add_ule_impl(lhs, rhs, sign, dep); }
void add_ule(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) override { add_ule_impl(lhs, rhs, sign, dep); }
void add_ule(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) override { add_ule_impl(lhs, rhs, sign, dep); }
void add_umul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override {
add(bv->mk_bvumul_no_ovfl(mk_poly(lhs), mk_poly(rhs)), !sign, dep);
}
@ -220,13 +233,13 @@ namespace polysat {
void add_ule_const(rational const& val, bool sign, dep_t dep) override {
if (val == 0)
add(m.mk_eq(x, mk_numeral(val)), sign, dep);
add(m.mk_eq(x, mk_poly(val)), sign, dep);
else
add(bv->mk_ule(x, mk_numeral(val)), sign, dep);
add(bv->mk_ule(x, mk_poly(val)), sign, dep);
}
void add_uge_const(rational const& val, bool sign, dep_t dep) override {
add(bv->mk_ule(mk_numeral(val), x), sign, dep);
add(bv->mk_ule(mk_poly(val), x), sign, dep);
}
void add_bit(unsigned idx, bool sign, dep_t dep) override {

View file

@ -80,7 +80,23 @@ namespace polysat {
*/
virtual bool find_two(rational& out1, rational& out2) = 0;
/** lhs <= rhs */
virtual void add_ule(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
virtual void add_ule(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) = 0;
virtual void add_ule(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
/** lhs >= rhs */
void add_uge(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, sign, dep); }
void add_uge(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, sign, dep); }
void add_uge(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, sign, dep); }
/** lhs < rhs */
void add_ult(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, !sign, dep); }
void add_ult(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, !sign, dep); }
void add_ult(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, !sign, dep); }
/** lhs > rhs */
void add_ugt(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(lhs, rhs, !sign, dep); }
void add_ugt(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) { add_ule(lhs, rhs, !sign, dep); }
void add_ugt(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(lhs, rhs, !sign, dep); }
virtual void add_umul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
virtual void add_smul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
virtual void add_smul_udfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;

View file

@ -344,9 +344,9 @@ namespace polysat {
}
};
do {
LOG("refine-equal-lin for src: " << lit_pp(s, e->src));
rational coeff_val = mod(e->coeff * val, mod_value);
if (e->interval.currently_contains(coeff_val)) {
LOG("refine-equal-lin for src: " << lit_pp(s, e->src));
if (e->interval.lo_val().is_one() && e->interval.hi_val().is_zero() && e->coeff.is_odd()) {
rational lo(1);
@ -730,6 +730,7 @@ namespace polysat {
// 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.
LOG_H3("Checking looping univariate constraints for v" << v << "...");
entry const* first = m_units[v];
entry const* e = first;
do {
@ -740,7 +741,8 @@ namespace polysat {
sat::literal const lit = c.blit();
if (!added.contains(lit)) {
added.insert(lit);
c.add_to_univariate_solver(s, *us, lit.to_uint());
LOG("Adding " << lit_pp(s, lit));
c.add_to_univariate_solver(v, s, *us, lit.to_uint());
}
e = e->next();
}
@ -759,13 +761,15 @@ namespace polysat {
}
// Second step: looping constraints aren't UNSAT, so add the remaining relevant constraints
LOG_H3("Checking all univariate constraints for v" << v << "...");
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;
LOG("Adding " << lit_pp(s, lit));
added.insert(lit);
cs[i].add_to_univariate_solver(s, *us, lit.to_uint());
cs[i].add_to_univariate_solver(v, s, *us, lit.to_uint());
}
switch (us->check()) {
@ -1060,7 +1064,7 @@ namespace polysat {
for (unsigned i = cs.size(); i-- > 0; ) {
signed_constraint const c = cs[i];
LOG("Univariate constraint: " << c);
c.add_to_univariate_solver(s, *us, c.blit().to_uint());
c.add_to_univariate_solver(v, s, *us, c.blit().to_uint());
}
switch (us->check()) {