mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 17:45:32 +00:00
Draft: Made division/remainder to op_constraints (not yet used - old code still called)
This commit is contained in:
parent
e31eb9a6b1
commit
6b48b25beb
6 changed files with 219 additions and 6 deletions
|
@ -528,4 +528,49 @@ namespace polysat {
|
|||
return p.manager().mk_val(p.val().pseudo_inverse(p.power_of_2()));
|
||||
return mk_op_term(op_constraint::code::inv_op, p, p.manager().zero());
|
||||
}
|
||||
|
||||
pdd constraint_manager::udiv(pdd const& p, pdd const& q) {
|
||||
return div_rem_op_constraint(p, q).first;
|
||||
}
|
||||
|
||||
pdd constraint_manager::urem(pdd const& p, pdd const& q) {
|
||||
return div_rem_op_constraint(p, q).second;
|
||||
}
|
||||
|
||||
/** unsigned quotient/remainder */
|
||||
std::pair<pdd, pdd> constraint_manager::div_rem_op_constraint(pdd const& a, pdd const& b) {
|
||||
auto& m = a.manager();
|
||||
unsigned sz = m.power_of_2();
|
||||
if (b.is_zero()) {
|
||||
// By SMT-LIB specification, b = 0 ==> q = -1, r = a.
|
||||
return {m.mk_val(m.max_value()), a};
|
||||
}
|
||||
if (b.is_one()) {
|
||||
return {a, m.zero()};
|
||||
}
|
||||
if (a.is_val() && b.is_val()) {
|
||||
rational const av = a.val();
|
||||
rational const bv = b.val();
|
||||
SASSERT(!bv.is_zero());
|
||||
rational rv;
|
||||
rational qv = machine_div_rem(av, bv, rv);
|
||||
pdd q = m.mk_val(qv);
|
||||
pdd r = m.mk_val(rv);
|
||||
SASSERT_EQ(a, b * q + r);
|
||||
SASSERT(b.val()*q.val() + r.val() <= m.max_value());
|
||||
SASSERT(r.val() <= (b*q+r).val());
|
||||
SASSERT(r.val() < b.val());
|
||||
return {std::move(q), std::move(r)};
|
||||
}
|
||||
|
||||
pdd quot = mk_op_term(op_constraint::code::udiv_op, a, b);
|
||||
pdd rem = mk_op_term(op_constraint::code::urem_op, a, b);
|
||||
|
||||
op_constraint* op1 = (op_constraint*)mk_op_constraint(op_constraint::code::udiv_op, a, b, quot).get();
|
||||
op_constraint* op2 = (op_constraint*)mk_op_constraint(op_constraint::code::urem_op, a, b, quot).get();
|
||||
op1->m_linked = op2;
|
||||
op2->m_linked = op1;
|
||||
|
||||
return {std::move(quot), std::move(rem)};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,8 @@ namespace polysat {
|
|||
signed_constraint mk_op_constraint(op_constraint::code op, pdd const& p, pdd const& q, pdd const& r);
|
||||
pdd mk_op_term(op_constraint::code op, pdd const& p, pdd const& q);
|
||||
|
||||
std::pair<pdd, pdd> div_rem_op_constraint(const pdd& p, const pdd& q);
|
||||
|
||||
public:
|
||||
constraint_manager(solver& s);
|
||||
~constraint_manager();
|
||||
|
@ -131,6 +133,9 @@ namespace polysat {
|
|||
pdd bnand(pdd const& p, pdd const& q);
|
||||
pdd bnor(pdd const& p, pdd const& q);
|
||||
pdd pseudo_inv(pdd const& p);
|
||||
|
||||
pdd udiv(pdd const& a, pdd const& b);
|
||||
pdd urem(pdd const& a, pdd const& b);
|
||||
|
||||
constraint* const* begin() const { return m_constraints.data(); }
|
||||
constraint* const* end() const { return m_constraints.data() + m_constraints.size(); }
|
||||
|
|
|
@ -65,6 +65,10 @@ namespace polysat {
|
|||
return eval_and(p, q, r);
|
||||
case code::inv_op:
|
||||
return eval_inv(p, r);
|
||||
case code::udiv_op:
|
||||
return eval_udiv(p, q, r);
|
||||
case code::urem_op:
|
||||
return eval_urem(p, q, r);
|
||||
default:
|
||||
return l_undef;
|
||||
}
|
||||
|
@ -90,6 +94,10 @@ namespace polysat {
|
|||
return out << "&";
|
||||
case op_constraint::code::inv_op:
|
||||
return out << "inv";
|
||||
case op_constraint::code::udiv_op:
|
||||
return out << "/";
|
||||
case op_constraint::code::urem_op:
|
||||
return out << "%";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return out;
|
||||
|
@ -142,6 +150,10 @@ namespace polysat {
|
|||
return propagate_bits_shl(s, is_positive);
|
||||
case code::and_op:
|
||||
return propagate_bits_and(s, is_positive);
|
||||
case code::inv_op:
|
||||
case code::udiv_op:
|
||||
case code::urem_op:
|
||||
return false;
|
||||
default:
|
||||
NOT_IMPLEMENTED_YET();
|
||||
return false;
|
||||
|
@ -180,15 +192,12 @@ namespace polysat {
|
|||
|
||||
void op_constraint::activate(solver& s) {
|
||||
switch (m_op) {
|
||||
case code::lshr_op:
|
||||
break;
|
||||
case code::shl_op:
|
||||
break;
|
||||
case code::and_op:
|
||||
// handle masking of high order bits
|
||||
activate_and(s);
|
||||
break;
|
||||
case code::inv_op:
|
||||
case code::udiv_op: // division & remainder axioms (as they always occur as pairs; we do this only for divisions)
|
||||
activate_udiv(s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -699,6 +708,131 @@ namespace polysat {
|
|||
|
||||
return to_lbool(p.val().pseudo_inverse(p.power_of_2()) == r.val());
|
||||
}
|
||||
|
||||
void op_constraint::activate_udiv(solver& s) {
|
||||
// signed_constraint const udivc(this, true); Do we really need this premiss? We anyway assert these constraints as unit clauses
|
||||
|
||||
pdd const& quot = r();
|
||||
pdd const& rem = m_linked->r();
|
||||
|
||||
// Axioms for quotient/remainder:
|
||||
// a = b*q + r
|
||||
// multiplication does not overflow in b*q
|
||||
// addition does not overflow in (b*q) + r; for now expressed as: r <= bq+r
|
||||
// b ≠ 0 ==> r < b
|
||||
// b = 0 ==> q = -1
|
||||
// TODO: when a,b become evaluable, can we actually propagate q,r? doesn't seem like it.
|
||||
// Maybe we need something like an op_constraint for better propagation.
|
||||
s.add_clause(s.eq(q() * quot + rem - p()), false);
|
||||
s.add_clause(~s.umul_ovfl(q(), quot), false);
|
||||
// r <= b*q+r
|
||||
// { apply equivalence: p <= q <=> q-p <= -p-1 }
|
||||
// b*q <= -r-1
|
||||
s.add_clause(s.ule(q() * quot, -rem - 1), false);
|
||||
|
||||
auto c_eq = s.eq(q());
|
||||
s.add_clause(c_eq, s.ult(rem, q()), false);
|
||||
s.add_clause(~c_eq, s.eq(quot + 1), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce lemmas for constraint: r == p / q
|
||||
* q = 0 ==> r = max_value
|
||||
* p = 0 ==> r = 0 || r = max_value
|
||||
* q = 1 ==> r = p
|
||||
*/
|
||||
clause_ref op_constraint::lemma_udiv(solver& s, assignment const& a) {
|
||||
auto& m = p().manager();
|
||||
auto pv = a.apply_to(p());
|
||||
auto qv = a.apply_to(q());
|
||||
auto rv = a.apply_to(r());
|
||||
|
||||
if (eval_udiv(pv, qv, rv) == l_true)
|
||||
return {};
|
||||
|
||||
signed_constraint const udivc(this, true);
|
||||
|
||||
if (qv.is_zero() && !rv.is_val())
|
||||
return s.mk_clause(~udivc, ~s.eq(q()), s.eq(r(), r().manager().max_value()), true);
|
||||
if (pv.is_zero() && !rv.is_val())
|
||||
return s.mk_clause(~udivc, ~s.eq(p()), s.eq(r()), s.eq(r(), r().manager().max_value()), true);
|
||||
if (qv.is_one())
|
||||
return s.mk_clause(~udivc, ~s.eq(q(), 1), s.eq(r(), p()), true);
|
||||
|
||||
if (pv.is_val() && qv.is_val() && !rv.is_val()) {
|
||||
SASSERT(!qv.is_zero());
|
||||
// TODO: We could actually propagate an interval. Instead of p = 9 & q = 4 => r = 2 we could do p >= 8 && p < 12 && q = 4 => r = 2
|
||||
return s.mk_clause(~udivc, ~s.eq(p(), pv.val()), ~s.eq(q(), qv.val()), s.eq(r(), div(pv.val(), qv.val())), true);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Evaluate constraint: r == p / q */
|
||||
lbool op_constraint::eval_udiv(pdd const& p, pdd const& q, pdd const& r) {
|
||||
|
||||
if (q.is_zero() && r.is_val()) {
|
||||
return r.val() == r.manager().max_value() ? l_true : l_false;
|
||||
}
|
||||
if (q.is_one()) {
|
||||
if (r == p)
|
||||
return l_true;
|
||||
}
|
||||
|
||||
if (!p.is_val() || !q.is_val() || !r.is_val())
|
||||
return l_undef;
|
||||
|
||||
return r.val() == div(p.val(), q.val()) ? l_true : l_false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce lemmas for constraint: r == p % q
|
||||
* p = 0 ==> r = 0
|
||||
* q = 1 ==> r = 0
|
||||
* q = 0 ==> r = p
|
||||
*/
|
||||
clause_ref op_constraint::lemma_urem(solver& s, assignment const& a) {
|
||||
auto& m = p().manager();
|
||||
auto pv = a.apply_to(p());
|
||||
auto qv = a.apply_to(q());
|
||||
auto rv = a.apply_to(r());
|
||||
|
||||
if (eval_urem(pv, qv, rv) == l_true)
|
||||
return {};
|
||||
|
||||
signed_constraint const urem(this, true);
|
||||
|
||||
if (pv.is_zero() && !rv.is_val())
|
||||
return s.mk_clause(~urem, ~s.eq(p()), s.eq(r()), true);
|
||||
if (qv.is_one() && !rv.is_val())
|
||||
return s.mk_clause(~urem, ~s.eq(q(), 1), s.eq(r()), true);
|
||||
if (qv.is_zero())
|
||||
return s.mk_clause(~urem, ~s.eq(q()), s.eq(r(), p()), true);
|
||||
|
||||
if (pv.is_val() && qv.is_val() && !rv.is_val()) {
|
||||
SASSERT(!qv.is_zero());
|
||||
return s.mk_clause(~urem, ~s.eq(p(), pv.val()), ~s.eq(q(), qv.val()), s.eq(r(), mod(pv.val(), qv.val())), true);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Evaluate constraint: r == p % q */
|
||||
lbool op_constraint::eval_urem(pdd const& p, pdd const& q, pdd const& r) {
|
||||
|
||||
if (q.is_one() && r.is_val()) {
|
||||
return r.val().is_zero() ? l_true : l_false;
|
||||
}
|
||||
if (q.is_zero()) {
|
||||
if (r == p)
|
||||
return l_true;
|
||||
}
|
||||
|
||||
if (!p.is_val() || !q.is_val() || !r.is_val())
|
||||
return l_undef;
|
||||
|
||||
return r.val() == mod(p.val(), q.val()) ? l_true : l_false; // mod == rem as we know hat q > 0
|
||||
}
|
||||
|
||||
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());
|
||||
|
@ -723,6 +857,12 @@ namespace polysat {
|
|||
case code::inv_op:
|
||||
us.add_inv(pv.get_univariate_coefficients(), rv.get_univariate_coefficients(), !is_positive, dep);
|
||||
break;
|
||||
case code::udiv_op:
|
||||
us.add_udiv(pv.get_univariate_coefficients(), qv.get_univariate_coefficients(), rv.get_univariate_coefficients(), !is_positive, dep);
|
||||
break;
|
||||
case code::urem_op:
|
||||
us.add_urem(pv.get_univariate_coefficients(), qv.get_univariate_coefficients(), rv.get_univariate_coefficients(), !is_positive, dep);
|
||||
break;
|
||||
default:
|
||||
NOT_IMPLEMENTED_YET();
|
||||
break;
|
||||
|
|
|
@ -38,7 +38,11 @@ namespace polysat {
|
|||
/// r is the smallest multiplicative pseudo-inverse of p;
|
||||
/// by definition we set r == 0 when p == 0.
|
||||
/// Note that in general, there are 2^parity(p) many pseudo-inverses of p.
|
||||
inv_op
|
||||
inv_op,
|
||||
// r is the quotient of dividing p by q
|
||||
udiv_op,
|
||||
// r is the remainder of dividing p by q
|
||||
urem_op,
|
||||
};
|
||||
protected:
|
||||
friend class constraint_manager;
|
||||
|
@ -47,6 +51,8 @@ namespace polysat {
|
|||
pdd m_p; // operand1
|
||||
pdd m_q; // operand2
|
||||
pdd m_r; // result
|
||||
|
||||
op_constraint* m_linked; // for linking remainder/quotient
|
||||
|
||||
op_constraint(code c, pdd const& p, pdd const& q, pdd const& r);
|
||||
lbool eval(pdd const& p, pdd const& q, pdd const& r) const;
|
||||
|
@ -66,12 +72,19 @@ namespace polysat {
|
|||
|
||||
clause_ref lemma_inv(solver& s, assignment const& a);
|
||||
static lbool eval_inv(pdd const& p, pdd const& r);
|
||||
|
||||
clause_ref lemma_udiv(solver& s, assignment const& a);
|
||||
static lbool eval_udiv(pdd const& p, pdd const& q, pdd const& r);
|
||||
|
||||
clause_ref lemma_urem(solver& s, assignment const& a);
|
||||
static lbool eval_urem(pdd const& p, pdd const& q, pdd const& r);
|
||||
|
||||
std::ostream& display(std::ostream& out, char const* eq) const;
|
||||
|
||||
void activate(solver& s);
|
||||
|
||||
void activate_and(solver& s);
|
||||
void activate_udiv(solver& s);
|
||||
|
||||
public:
|
||||
~op_constraint() override {}
|
||||
|
|
|
@ -255,6 +255,14 @@ namespace polysat {
|
|||
add(bv->mk_ule(v_inv, v_inv_max), false, dep);
|
||||
}
|
||||
|
||||
void add_udiv(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) override {
|
||||
add(m.mk_eq(bv->mk_bv_udiv(mk_poly(in1), mk_poly(in2)), mk_poly(out)), sign, dep);
|
||||
}
|
||||
|
||||
void add_urem(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) override {
|
||||
add(m.mk_eq(bv->mk_bv_urem(mk_poly(in1), mk_poly(in2)), mk_poly(out)), sign, dep);
|
||||
}
|
||||
|
||||
void add_ule_const(rational const& val, bool sign, dep_t dep) override {
|
||||
if (val == 0)
|
||||
add(m.mk_eq(x, mk_poly(val)), sign, dep);
|
||||
|
|
|
@ -105,6 +105,8 @@ namespace polysat {
|
|||
virtual void add_xor(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) = 0;
|
||||
virtual void add_not(univariate const& in, univariate const& out, bool sign, dep_t dep) = 0;
|
||||
virtual void add_inv(univariate const& in, univariate const& out, bool sign, dep_t dep) = 0;
|
||||
virtual void add_udiv(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) = 0;
|
||||
virtual void add_urem(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) = 0;
|
||||
|
||||
/// Add x <= val or x > val, depending on sign
|
||||
virtual void add_ule_const(rational const& val, bool sign, dep_t dep) = 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue