mirror of
https://github.com/Z3Prover/z3
synced 2025-06-24 23:03:41 +00:00
Update parity lemmas
p != 0 ==> odd(r) ... added premise p != 0 parity(p) < k ==> r <= 2^(N - k) - 1 ... do this also in the other branch (otherwise we hit the UNREACHABLE in bench3)
This commit is contained in:
parent
26e7d0d35a
commit
b6f8538d20
2 changed files with 64 additions and 48 deletions
|
@ -184,6 +184,7 @@ namespace polysat {
|
|||
break;
|
||||
case code::shl_op:
|
||||
// TODO: if shift amount is constant p << k, then add p << k == p*2^k
|
||||
// NOTE: we do that now as simplification in constraint_manager::shl
|
||||
break;
|
||||
case code::and_op:
|
||||
// handle masking of high order bits
|
||||
|
@ -294,9 +295,7 @@ namespace polysat {
|
|||
|
||||
if (p.is_val() && q.is_val() && r.is_val()) {
|
||||
SASSERT(q.val().is_unsigned()); // otherwise, previous condition should have been triggered
|
||||
// TODO: use right-shift operation instead of division
|
||||
auto divisor = rational::power_of_two(q.val().get_unsigned());
|
||||
return to_lbool(r.val() == div(p.val(), divisor));
|
||||
return to_lbool(r.val() == machine_div2k(p.val(), q.val().get_unsigned()));
|
||||
}
|
||||
|
||||
// TODO: other cases when we know lower bound of q,
|
||||
|
@ -616,11 +615,12 @@ namespace polysat {
|
|||
|
||||
/**
|
||||
* Produce lemmas for constraint: r == inv p
|
||||
* p = 0 => r = 0
|
||||
* r = 0 => p = 0
|
||||
* odd(r) -- for now we are looking for the smallest pseudo-inverse (there are 2^parity(p) of them)
|
||||
* parity(p) >= k && p * r < 2^k => p * r >= 2^k
|
||||
* parity(p) < k && p * r >= 2^k => p * r < 2^k
|
||||
* p = 0 ==> r = 0
|
||||
* r = 0 ==> p = 0
|
||||
* p != 0 ==> odd(r)
|
||||
* parity(p) >= k ==> p * r >= 2^k
|
||||
* parity(p) < k ==> p * r <= 2^k - 1
|
||||
* parity(p) < k ==> r <= 2^(N - k) - 1 (because r is the smallest pseudo-inverse)
|
||||
*/
|
||||
clause_ref op_constraint::lemma_inv(solver& s, assignment const& a) {
|
||||
auto& m = p().manager();
|
||||
|
@ -632,14 +632,15 @@ namespace polysat {
|
|||
|
||||
signed_constraint const invc(this, true);
|
||||
|
||||
// p = 0 => r = 0
|
||||
// p = 0 ==> r = 0
|
||||
if (pv.is_zero())
|
||||
return s.mk_clause(~invc, ~s.eq(p()), s.eq(r()), true);
|
||||
// r = 0 => p = 0
|
||||
// r = 0 ==> p = 0
|
||||
if (rv.is_zero())
|
||||
return s.mk_clause(~invc, ~s.eq(r()), s.eq(p()), true);
|
||||
|
||||
// p assigned => r = pseudo_inverse(eval(p))
|
||||
// p assigned ==> r = pseudo_inverse(eval(p))
|
||||
// TODO: (later) this should be propagated instead of adding a clause
|
||||
if (pv.is_val() && !rv.is_val())
|
||||
return s.mk_clause(~invc, ~s.eq(p(), pv), s.eq(r(), pv.val().pseudo_inverse(m.power_of_2())), true);
|
||||
|
||||
|
@ -649,15 +650,14 @@ namespace polysat {
|
|||
unsigned parity_pv = pv.val().trailing_zeros();
|
||||
unsigned parity_rv = rv.val().trailing_zeros();
|
||||
|
||||
// odd(r)
|
||||
// p != 0 ==> odd(r)
|
||||
if (parity_rv != 0)
|
||||
return s.mk_clause(~invc, s.odd(r()), true);
|
||||
// parity(p) >= k && p * r < 2^k => p * r >= 2^k
|
||||
// parity(p) < k && p * r >= 2^k => p * r < 2^k
|
||||
return s.mk_clause(~invc, ~s.eq(p()), s.odd(r()), true);
|
||||
|
||||
pdd prod = p() * r();
|
||||
rational prodv = (pv * rv).val();
|
||||
SASSERT(prodv != rational::power_of_two(parity_pv)); // Why did it evaluate to false in this case?
|
||||
unsigned lower = 0, upper = p().power_of_2();
|
||||
unsigned lower = 0, upper = m.power_of_2();
|
||||
// binary search for the parity (otw. we would have justifications like "parity_at_most(k) && parity_at_least(k)" for at most "k" widths
|
||||
while (lower + 1 < upper) {
|
||||
unsigned middle = (upper + lower) / 2;
|
||||
|
@ -665,18 +665,21 @@ namespace polysat {
|
|||
if (parity_pv >= middle) { // parity at least middle
|
||||
lower = middle;
|
||||
LOG("Its in [" << lower << "; " << upper << ")");
|
||||
if (prodv < rational::power_of_two(middle)) // product is for sure not 2^parity
|
||||
// parity(p) >= k ==> p * r >= 2^k
|
||||
if (prodv < rational::power_of_two(middle))
|
||||
return s.mk_clause(~invc, ~s.parity_at_least(p(), middle), s.uge(prod, rational::power_of_two(middle)), false);
|
||||
}
|
||||
else { // parity at most middle
|
||||
else { // parity less than middle
|
||||
upper = middle;
|
||||
LOG("Its in [" << lower << "; " << upper << ")");
|
||||
if (prodv > rational::power_of_two(middle)) // product is for sure not 2^parity
|
||||
return s.mk_clause(~invc, s.parity_at_least(p(), middle), s.ule(prod, rational::power_of_two(middle)), false);
|
||||
|
||||
if (rv.val() >= rational::power_of_two(p().power_of_2() - middle)) // enforce that pseudo-inverse is smaller than 2^k-parity (minimal pseudo-inverse)
|
||||
return s.mk_clause(~invc, s.parity_at_least(p(), middle), s.ule(r(), rational::power_of_two(p().power_of_2() - middle) - 1), false);
|
||||
// parity(p) < k ==> p * r <= 2^k - 1
|
||||
if (prodv > rational::power_of_two(middle))
|
||||
return s.mk_clause(~invc, s.parity_at_least(p(), middle), s.ule(prod, rational::power_of_two(middle) - 1), false);
|
||||
}
|
||||
// parity(p) < k ==> r <= 2^(N - k) - 1 (because r is the smallest pseudo-inverse)
|
||||
rational const max_rv = rational::power_of_two(m.power_of_2() - middle) - 1;
|
||||
if (rv.val() > max_rv)
|
||||
return s.mk_clause(~invc, s.parity_at_least(p(), middle), s.ule(r(), max_rv), false);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
|
@ -688,9 +691,9 @@ namespace polysat {
|
|||
return l_undef;
|
||||
|
||||
if (p.is_zero() || r.is_zero()) // the inverse of 0 is 0 (by arbitrary definition). Just to have some unique value
|
||||
return p.is_zero() && r.is_zero() ? l_true : l_false;
|
||||
return to_lbool(p.is_zero() && r.is_zero());
|
||||
|
||||
return p.val().pseudo_inverse(p.power_of_2()) == r.val() ? l_true : l_false;
|
||||
return to_lbool(p.val().pseudo_inverse(p.power_of_2()) == r.val());
|
||||
}
|
||||
|
||||
void op_constraint::add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
|
||||
|
|
|
@ -26,7 +26,20 @@ namespace polysat {
|
|||
|
||||
class op_constraint final : public constraint {
|
||||
public:
|
||||
enum class code { lshr_op, ashr_op, shl_op, and_op, inv_op };
|
||||
enum class code {
|
||||
/// r is the logical right shift of p by q
|
||||
lshr_op,
|
||||
/// r is the arithmetic right shift of p by q
|
||||
ashr_op,
|
||||
/// r is the left shift of p by q
|
||||
shl_op,
|
||||
/// r is the bit-wise 'and' of p and q
|
||||
and_op,
|
||||
/// 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
|
||||
};
|
||||
protected:
|
||||
friend class constraint_manager;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue