3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 08:35:31 +00:00

add base support for signed multiplication over/under flow

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2024-01-30 12:47:28 -08:00
parent 2da3261d8a
commit 0dc204cd4a
5 changed files with 85 additions and 7 deletions

View file

@ -788,6 +788,32 @@ namespace intblast {
r = a.mk_lt(mul(umod(bv_expr, 0), umod(bv_expr, 1)), a.mk_int(bv_size(bv_expr)));
break;
}
case OP_BSMUL_NO_OVFL: {
bv_expr = e->get_arg(0);
expr* x = umod(bv_expr, 0), *y = umod(bv_expr, 1);
rational N = bv_size(bv_expr);
expr* lim = a.mk_int(N/2);
expr* signx = a.mk_ge(x, lim);
expr* signy = a.mk_ge(y, lim);
x = m.mk_ite(signx, a.mk_sub(a.mk_int(N), x), x);
y = m.mk_ite(signy, a.mk_sub(a.mk_int(N), y), y);
// signx != signy || x * y < N/2
r = m.mk_or(m.mk_not(m.mk_eq(signx, signy)), a.mk_lt(a.mk_mul(x, y), lim));
break;
}
case OP_BSMUL_NO_UDFL: {
bv_expr = e->get_arg(0);
expr* x = umod(bv_expr, 0), *y = umod(bv_expr, 1);
rational N = bv_size(bv_expr);
expr* lim = a.mk_int(N/2);
expr* signx = a.mk_ge(x, lim);
expr* signy = a.mk_ge(y, lim);
x = m.mk_ite(signx, a.mk_sub(a.mk_int(N), x), x);
y = m.mk_ite(signy, a.mk_sub(a.mk_int(N), y), y);
// signx == signy || x * y <= N/2
r = m.mk_or(m.mk_eq(signx, signy), a.mk_le(a.mk_mul(x, y), lim));
break;
}
case OP_BSHL: {
if (!a.is_numeral(arg(0)) && !a.is_numeral(arg(1)))
r = a.mk_shl(bv.get_bv_size(e), arg(0),arg(1));

View file

@ -110,8 +110,6 @@ namespace polysat {
signed_constraint ult(pdd const& p, pdd const& q) { return ~ule(q, p); }
signed_constraint slt(pdd const& p, pdd const& q) { return ~sle(q, p); }
signed_constraint umul_ovfl(pdd const& p, pdd const& q);
signed_constraint smul_ovfl(pdd const& p, pdd const& q) { throw default_exception("smul ovfl nyi"); }
signed_constraint smul_udfl(pdd const& p, pdd const& q) { throw default_exception("smult-udfl nyi"); }
signed_constraint bit(pdd const& p, unsigned i);
signed_constraint diseq(pdd const& p) { return ~eq(p); }

View file

@ -133,8 +133,6 @@ namespace polysat {
signed_constraint ule(pdd const& p, pdd const& q) { return m_constraints.ule(p, q); }
signed_constraint sle(pdd const& p, pdd const& q) { return m_constraints.sle(p, q); }
signed_constraint umul_ovfl(pdd const& p, pdd const& q) { return m_constraints.umul_ovfl(p, q); }
signed_constraint smul_ovfl(pdd const& p, pdd const& q) { return m_constraints.smul_ovfl(p, q); }
signed_constraint smul_udfl(pdd const& p, pdd const& q) { return m_constraints.smul_udfl(p, q); }
signed_constraint bit(pdd const& p, unsigned i) { return m_constraints.bit(p, i); }

View file

@ -107,9 +107,9 @@ namespace polysat {
case OP_SGT: internalize_le<true, false, true>(a); break;
case OP_BUMUL_NO_OVFL: internalize_binary_predicate(a, [&](pdd const& p, pdd const& q) { return ~m_core.umul_ovfl(p, q); }); break;
case OP_BSMUL_NO_OVFL: internalize_binary_predicate(a, [&](pdd const& p, pdd const& q) { return ~m_core.smul_ovfl(p, q); }); break;
case OP_BSMUL_NO_UDFL: internalize_binary_predicate(a, [&](pdd const& p, pdd const& q) { return ~m_core.smul_udfl(p, q); }); break;
case OP_BSMUL_NO_OVFL: internalize_smul_no_ovfl(a); break;
case OP_BSMUL_NO_UDFL: internalize_smul_no_udfl(a); break;
case OP_BUMUL_OVFL: internalize_binary_predicate(a, [&](pdd const& p, pdd const& q) { return m_core.umul_ovfl(p, q); }); break;
case OP_BSMUL_OVFL:
case OP_BSDIV_OVFL:
@ -509,6 +509,60 @@ namespace polysat {
m_var2pdd_valid.setx(v, false, false);
quot_rem(quot, rem, x, y);
}
void solver::internalize_smul_no_ovfl(app* e) {
auto x = e->get_arg(0);
auto y = e->get_arg(1);
auto sz = bv.get_bv_size(x);
auto lit = mk_literal(e);
// x' = ite(signx, -x, x)
// y' = ite(signy, -y, y)
// lit <=> signx != signy || (not umul_ovfl(x',y') && x'*y' < 2^{N-1})
// expands to:
// lit => signx != signy or not umul_ovfl(x',y')
// lit => signx != signy or not x'*y' < 2^{N-1}
// signx != signy => lit
// not umul_ovfl(x',y') & x'*y' < 2^{N-1} => lit
auto signx = bv.mk_slt(x, bv.mk_zero(sz));
auto signy = bv.mk_slt(y, bv.mk_zero(sz));
auto same_sign = eq_internalize(signx, signy);
auto x1 = m.mk_ite(signx, bv.mk_bv_neg(x), x);
auto y1 = m.mk_ite(signy, bv.mk_bv_neg(y), y);
auto umul_noovfl = mk_literal(bv.mk_bvumul_no_ovfl(x1, y1));
auto small = ~mk_literal(bv.mk_ule(bv.mk_numeral(rational::power_of_two(sz - 1), sz), bv.mk_bv_mul(x1, y1)));
add_axiom("smul_no_ovfl", { ~lit, ~same_sign, ~umul_noovfl }, false);
add_axiom("smul_no_ovfl", { ~lit, ~same_sign, small }, false);
add_axiom("smul_no_ovfl", { same_sign, lit }, false);
add_axiom("smul_no_ovfl", { umul_noovfl, ~small, lit }, false);
}
void solver::internalize_smul_no_udfl(app* e) {
auto x = e->get_arg(0);
auto y = e->get_arg(1);
auto sz = bv.get_bv_size(x);
auto lit = mk_literal(e);
// x' = ite(signx, -x, x)
// y' = ite(signy, -y, y)
// lit <=> signx = signy || (not umul_ovfl(x',y') && x'*y' <= 2^{N-1})
// expands to:
// lit => signx = signy or not umul_ovfl(x',y')
// lit => signx = signy or not x'*y' <= 2^{N-1}
// signx = signy => lit
// not umul_ovfl(x',y') & x'*y' <= 2^{N-1} => lit
auto signx = bv.mk_slt(x, bv.mk_zero(sz));
auto signy = bv.mk_slt(y, bv.mk_zero(sz));
auto same_sign = eq_internalize(signx, signy);
auto x1 = m.mk_ite(signx, bv.mk_bv_neg(x), x);
auto y1 = m.mk_ite(signy, bv.mk_bv_neg(y), y);
auto umul_noovfl = mk_literal(bv.mk_bvumul_no_ovfl(x1, y1));
auto small = mk_literal(bv.mk_ule(bv.mk_bv_mul(x1, y1), bv.mk_numeral(rational::power_of_two(sz - 1), sz)));
add_axiom("smul_no_udfl", { ~lit, same_sign, ~umul_noovfl }, false);
add_axiom("smul_no_udfl", { ~lit, same_sign, small }, false);
add_axiom("smul_no_udfl", { ~same_sign, lit }, false);
add_axiom("smul_no_udfl", { umul_noovfl, ~small, lit }, false);
}
void solver::quot_rem(expr* quot, expr* rem, expr* x, expr* y) {
pdd a = expr2pdd(x);

View file

@ -142,6 +142,8 @@ namespace polysat {
void internalize_binary(app* e, std::function<pdd(pdd, pdd)> const& fn);
void internalize_binary(app* e, std::function<expr*(expr*, expr*)> const& fn);
void internalize_binary_predicate(app* e, std::function<signed_constraint(pdd, pdd)> const& fn);
void internalize_smul_no_ovfl(app* e);
void internalize_smul_no_udfl(app* e);
void internalize_par_unary(app* e, std::function<pdd(pdd,unsigned)> const& fn);
void internalize_novfl(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref&)>& fn);
void internalize_interp(app* n, std::function<expr*(expr*, expr*)>& ibin, std::function<expr*(expr*)>& un);