3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-04-27 06:13:35 +00:00

bugbash bit-wise operations

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2024-01-10 16:05:55 -08:00
parent 33f17215f7
commit 33c37cfdf0
4 changed files with 71 additions and 71 deletions

View file

@ -99,10 +99,17 @@ namespace polysat {
auto M = m.max_value();
auto N = M + 1;
if (p.val() >= N / 2) {
if (q.val() >= m.power_of_2())
return to_lbool(r.val() == M);
if (q.val() >= m.power_of_2()) {
if (p.is_zero())
return to_lbool(r.val() == 0);
else
return to_lbool(r.val() == M);
}
unsigned k = q.val().get_unsigned();
return to_lbool(r.val() == p.val() - rational::power_of_two(k));
// p >>a q = p / 2^k - 2^{N-k}
auto pdiv2k = machine_div2k(p.val(), k);
auto pNk = rational::power_of_two(m.power_of_2() - k);
return to_lbool(r.val() == pdiv2k + N - pNk);
}
else
return eval_lshr(p, q, r);
@ -244,6 +251,7 @@ namespace polysat {
c.add_axiom("q >= N -> p >>l q = 0", { ~C.uge(q, N), C.eq(r) }, false);
c.add_axiom("q = 0 -> p >>l q = p", { ~C.eq(q), C.eq(r, p) }, false);
c.add_axiom("p = 0 -> p >>l q = p", { ~C.eq(p), C.eq(r, p) }, false);
c.add_axiom("p >>l q <= p", { C.ule(r, p) }, false);
break;
case code::shl_op:
c.add_axiom("q >= N -> p << q = 0", { ~C.uge(q, N), C.eq(r) }, false);
@ -288,15 +296,21 @@ namespace polysat {
}
void op_constraint::propagate(core& c, signed_constraint const& sc) {
bool op_constraint::propagate(core& c, signed_constraint const& sc) {
if (c.weak_eval(sc) != l_true)
return false;
c.propagate(sc, c.explain_weak_eval(sc));
return true;
}
void op_constraint::add_conflict(core& c, char const* ax, constraint_or_dependency_list const& cs) {
bool op_constraint::add_conflict(core& c, char const* ax, constraint_or_dependency_list const& cs) {
for (auto sc : cs)
if (std::holds_alternative<signed_constraint>(sc))
propagate(c, ~*std::get_if<signed_constraint>(&sc));
if (std::holds_alternative<signed_constraint>(sc) &&
!propagate(c, ~*std::get_if<signed_constraint>(&sc)))
return false;
c.add_axiom(ax, cs, true);
return true;
}
/**
* Enforce basic axioms for r == p >> q:
@ -314,7 +328,7 @@ namespace polysat {
* when q is a constant, several axioms can be enforced at activation time.
*
*/
void op_constraint::propagate_lshr(core& c) {
bool op_constraint::propagate_lshr(core& c) {
auto& m = p.manager();
auto const pv = c.subst(p);
auto const qv = c.subst(q);
@ -323,45 +337,23 @@ namespace polysat {
auto& C = c.cs();
if (pv.is_val() && rv.is_val() && rv.val() > pv.val())
return add_conflict(c, "lshr 1", { C.ule(r, p) });
if (!pv.is_val() || !qv.is_val() || !rv.is_val())
return false;
else if (qv.is_val() && qv.val() >= N && rv.is_val() && !rv.is_zero())
// TODO: instead of rv.is_val() && !rv.is_zero(), we should use !is_forced_zero(r) which checks whether eval(r) = 0 or bvalue(r=0) = true; see saturation.cpp
c.add_axiom("q >= N -> r = 0", { ~C.ule(N, q), C.eq(r) }, true);
else if (qv.is_zero() && pv.is_val() && rv.is_val() && pv != rv)
c.add_axiom("q = 0 -> p = r", { ~C.eq(q), C.eq(p, r) }, true);
else if (qv.is_val() && !qv.is_zero() && pv.is_val() && rv.is_val() && !pv.is_zero() && rv.val() >= pv.val())
c.add_axiom("q != 0 & p > 0 -> r < p", { C.eq(q), C.ule(p, 0), C.ult(r, p) }, true);
else if (qv.is_val() && !qv.is_zero() && qv.val() < N && rv.is_val() && rv.val() > rational::power_of_two(N - qv.val().get_unsigned()) - 1)
c.add_axiom("q >= k -> r <= 2^{N-k} - 1", { ~C.ule(qv.val(), q), C.ule(r, rational::power_of_two(N - qv.val().get_unsigned()) - 1) }, true);
else if (pv.is_val() && rv.is_val() && qv.is_val() && !qv.is_zero()) {
unsigned k = qv.val().get_unsigned();
for (unsigned i = 0; i < N - k; ++i) {
if (rv.val().get_bit(i) && !pv.val().get_bit(i + k))
c.add_axiom("q = k -> r[i] = p[i+k] for 0 <= i < N - k", { ~C.eq(q, k), ~C.bit(r, i), C.bit(p, i + k) }, true);
SASSERT(!qv.is_zero());
SASSERT(qv.val() < N);
if (!rv.val().get_bit(i) && pv.val().get_bit(i + k))
c.add_axiom("q = k -> r[i] = p[i+k] for 0 <= i < N - k", { ~C.eq(q, k), C.bit(r, i), ~C.bit(p, i + k) }, true);
}
}
else {
// forward propagation
SASSERT(!(pv.is_val() && qv.is_val() && rv.is_val()));
// LOG(p << " = " << pv << " and " << q << " = " << qv << " yields [>>] " << r << " = " << (qv.val().is_unsigned() ? machine_div2k(pv.val(), qv.val().get_unsigned()) : rational::zero()));
if (qv.is_val() && !rv.is_val()) {
rational const& q_val = qv.val();
if (q_val >= N)
// q >= N ==> r = 0
c.add_axiom("q >= N ==> r = 0", { ~C.ule(N, q), C.eq(r) }, true);
else if (pv.is_val()) {
SASSERT(q_val.is_unsigned());
//
rational const r_val = machine_div2k(pv.val(), q_val.get_unsigned());
c.add_axiom("p = p_val & q = q_val ==> r = p_val / 2^q_val", { ~C.eq(p, pv), ~C.eq(q, qv), C.eq(r, r_val) }, true);
}
}
}
auto k = qv.val().get_unsigned();
// q = k & 0 < k < N => 2^k r <= p <= 2^k*x + 2^k - 1
auto twoK = rational::power_of_two(k);
auto twoNK = rational::power_of_two(N - k);
bool c1 = add_conflict(c, "q = k & 0 < k < N -> 2^k (p >>l q) <= p", { ~C.eq(q, k), C.ule(r * twoK, p)});
bool c2 = add_conflict(c, "q = k & 0 < k < N -> p <= 2^k*(p >>l q) + 2^k - 1", { ~C.eq(q, k), C.ule(p, r * twoK + twoK - 1) });
bool c3 = add_conflict(c, "q = k & 0 < k < N -> (p >>l q) < 2^{N-k}", { ~C.eq(q, k), C.ult(r, twoNK) });
VERIFY(c1 || c2 || c3);
return true;
}
void op_constraint::propagate_ashr(core& c) {
@ -397,10 +389,12 @@ namespace polysat {
rational twoN = rational::power_of_two(N);
rational twoK = rational::power_of_two(k);
rational twoNk = rational::power_of_two(N - k);
auto eqK = C.eq(q, k);
c.add_axiom("q = k -> r*2^k + p < 2^k", { ~eqK, C.ult(p - r * twoK, twoK) }, true);
c.add_axiom("q = k & p >= 0 -> r < 2^{N-k}", { ~eqK, ~C.ule(0, p), C.ult(r, twoNk) }, true);
c.add_axiom("q = k & p < 0 -> r >= 2^N - 2^{N-k}", { ~eqK, ~C.slt(p, 0), C.uge(r, twoN - twoNk) }, true);
bool c1 = add_conflict(c, "q = k & 0 < k < N -> 2^k r <= p", { ~C.eq(q, k), C.ule(r * twoK, p) });
bool c2 = add_conflict(c, "q = k & 0 < k < N -> p <= 2^k*x + 2^k - 1", { ~C.eq(q, k), C.ule(p, r * twoK + twoK - 1) });
bool c3 = add_conflict(c, "p < 0 & q = k -> r >= 2^N - 2^{N-k}", { ~C.eq(q, k), ~C.slt(p, 0), C.uge(r, twoN - twoNk) });
bool c4 = add_conflict(c, "p >= 0 & q = k -> r < 2^{N-k}", { ~C.eq(q, k), C.slt(p, 0), C.ult(r, twoNk)});
VERIFY(c1 || c2 || c3 || c4);
}
}
@ -432,10 +426,6 @@ namespace polysat {
auto twoK = rational::power_of_two(k);
#if 0
if (rv.is_val() && rv.val() < twoK && !rv.is_zero())
return add_conflict(c, "q >= k -> r = 0 or r >= 2^k", { ~C.uge(q, k), C.eq(r), C.uge(r, twoK) });
#endif
add_conflict(c, "p << k = 2^k*p", { ~C.eq(q, k), C.eq(r, p * twoK) });
}
@ -487,7 +477,7 @@ namespace polysat {
}
}
void op_constraint::propagate_or(core& c) {
bool op_constraint::propagate_or(core& c) {
auto& m = p.manager();
auto pv = c.subst(p);
auto qv = c.subst(q);
@ -495,7 +485,7 @@ namespace polysat {
auto& C = c.cs();
if (!pv.is_val() || !qv.is_val() || !rv.is_val())
return;
return false;
if (pv.is_max() && pv != rv)
return add_conflict(c, "p = -1 => p & q = p", { ~C.eq(p, m.max_value()), C.eq(p, r)});
@ -518,8 +508,9 @@ namespace polysat {
add_conflict(c, "(p|q)[i] => p[i] or q[i]", { C.bit(p, i), C.bit(q, i), ~C.bit(r, i) });
else
UNREACHABLE();
return;
return true;
}
return false;
}
bool op_constraint::propagate_mask(core& c, pdd const& p, pdd const& q, pdd const& r, rational const& pv, rational const& qv, rational const& rv) {