3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-22 02:57:50 +00:00
z3/src/sat/smt/polysat/fixed_bits.cpp
Nikolaj Bjorner 20afc55b41 misc bugfixes
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2023-12-28 14:24:52 -08:00

276 lines
9 KiB
C++

/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
Extract fixed bits from constraints
Author:
Jakob Rath, Nikolaj Bjorner (nbjorner), Clemens Eisenhofer 2022-08-22
--*/
#include "sat/smt/polysat/fixed_bits.h"
#include "sat/smt/polysat/ule_constraint.h"
#include "sat/smt/polysat/core.h"
namespace polysat {
void fixed_bits::reset() {
m_fixed_slices.reset();
m_var = null_var;
m_fixed.reset();
m_bits.reset();
}
// reset with fixed bits information for variable v
void fixed_bits::reset(pvar v) {
m_fixed_slices.reset();
m_var = v;
m_fixed.reset();
m_fixed.resize(c.size(v), l_undef);
m_bits.reserve(c.size(v));
fixed_bits_vector fbs;
c.get_fixed_bits(v, fbs);
for (auto const& fb : fbs)
for (unsigned i = fb.lo; i <= fb.hi; ++i)
m_fixed[i] = to_lbool(fb.value.get_bit(i - fb.lo));
}
// find then next value >= val that agrees with fixed bits, or false if none exists within the maximal value for val.
// examples
// fixed bits: 1?0 (least significant bit is last)
// val: 101
// next: 110
// fixed bits ?1?0
// val 1011
// next 1100
// algorithm: Let i be the most significant index where fixed bits disagree with val.
// Set non-fixed values below i to 0.
// If m_fixed[i] == l_true; then updating val to mask by fixed bits sufficies.
// Otherwise, the range above the disagreement has to be incremented.
// Increment the non-fixed bits by 1
// The first non-fixed 0 position is set to 1, non-fixed positions below are set to 0.
// If there are none, then the value is maximal and we return false.
bool fixed_bits::next(rational& val) {
if (m_fixed_slices.empty())
return true;
unsigned sz = c.size(m_var);
for (unsigned i = 0; i < sz; ++i)
m_bits[i] = val.get_bit(i);
unsigned i = sz;
for (; i-- > 0; )
if (m_fixed[i] != l_undef && m_fixed[i] != to_lbool(m_bits[i]))
break;
if (i == 0)
return true;
for (unsigned j = 0; j < sz; ++j) {
if (m_fixed[j] != l_undef)
m_bits[j] = m_fixed[j] == l_true;
else if (j < i)
m_bits[j] = false;
}
if (m_fixed[i] == l_false) {
for (; i < sz; ++i) {
if (m_fixed[i] != l_undef)
continue;
if (m_bits[i])
m_bits[i] = false;
else {
m_bits[i] = true;
break;
}
}
CTRACE("bv", i == sz, display(tout << "overflow\n"));
// overflow
if (i == sz)
return false;
}
val = 0;
for (unsigned i = sz; i-- > 0;)
val = val * 2 + rational(m_bits[i]);
return true;
}
// explain the fixed bits ranges.
dependency_vector fixed_bits::explain() {
dependency_vector result;
for (auto const& slice : m_fixed_slices)
result.push_back(dependency({ m_var, slice }));
return result;
}
std::ostream& fixed_bits::display(std::ostream& out) const {
return out << "fixed bits: v" << m_var << " " << m_fixed << "\n";
}
/**
* 2^k * x = 2^k * b
* ==> x[N-k-1:0] = b[N-k-1:0]
*/
bool get_eq_fixed_lsb(pdd const& p, fixed_slice& out) {
SASSERT(!p.is_val());
unsigned const N = p.power_of_2();
// Recognize p = 2^k * a * x - 2^k * b
if (!p.hi().is_val())
return false;
if (!p.lo().is_val())
return false;
// p = c * x - d
rational const c = p.hi().val();
rational const d = (-p.lo()).val();
SASSERT(!c.is_zero());
#if 1
// NOTE: ule_constraint::simplify removes odd factors of the leading term
unsigned k;
VERIFY(c.is_power_of_two(k));
if (d.parity(N) < k)
return false;
rational const b = machine_div2k(d, k);
out = fixed_slice(N - k - 1, 0, b);
SASSERT_EQ(d, b * rational::power_of_two(k));
SASSERT_EQ(p, (p.manager().mk_var(p.var()) - out.value) * rational::power_of_two(k));
return true;
#else
// branch if we want to support non-simplifed constraints (not recommended)
//
// 2^k * a * x = 2^k * b
// ==> x[N-k-1:0] = a^-1 * b[N-k-1:0]
// for odd a
unsigned k = c.parity(N);
if (d.parity(N) < k)
return false;
rational const a = machine_div2k(c, k);
SASSERT(a.is_odd());
SASSERT(a.is_one()); // TODO: ule-simplify will multiply with a_inv already, so we can drop the check here.
rational a_inv;
VERIFY(a.mult_inverse(N, a_inv));
rational const b = machine_div2k(d, k);
out.hi = N - k - 1;
out.lo = 0;
out.value = a_inv * b;
SASSERT_EQ(p, (p.manager().mk_var(p.var()) - out.value) * a * rational::power_of_two(k));
return true;
#endif
}
bool get_eq_fixed_slice(pdd const& p, fixed_slice& out) {
if (get_eq_fixed_lsb(p, out))
return true;
return false;
}
/**
* Constraint lhs <= rhs.
*
* -2^(k - 2) * x > 2^(k - 1)
* <=> 2 + x[1:0] > 2 (mod 4)
* ==> x[1:0] = 1
* -- TODO: Generalize [the obvious solution does not work]
*/
bool get_ule_fixed_lsb(pdd const& lhs, pdd const& rhs, bool is_positive, fixed_slice& out) {
return false;
}
/**
* Constraint lhs <= rhs.
*
* x <= 2^k - 1 ==> x[N-1:k] = 0
* x < 2^k ==> x[N-1:k] = 0
*/
bool get_ule_fixed_msb(pdd const& p, pdd const& q, bool is_positive, fixed_slice& out) {
SASSERT(!q.is_zero()); // equalities are handled elsewhere
unsigned const N = p.power_of_2();
pdd const& lhs = is_positive ? p : q;
pdd const& rhs = is_positive ? q : p;
bool const is_strict = !is_positive;
if (lhs.is_var() && rhs.is_val()) {
// x <= c
// find smallest k such that c <= 2^k - 1, i.e., c+1 <= 2^k
// ==> x <= 2^k - 1 ==> x[N-1:k] = 0
//
// x < c
// find smallest k such that c <= 2^k
// ==> x < 2^k ==> x[N-1:k] = 0
rational const c = is_strict ? rhs.val() : (rhs.val() + 1);
unsigned const k = c.next_power_of_two();
if (k < N) {
out.hi = N - 1;
out.lo = k;
out.value = 0;
return true;
}
}
return false;
}
// 2^(N-1) <= 2^(N-1-i) * x
bool get_ule_fixed_bit(pdd const& p, pdd const& q, bool is_positive, fixed_slice& out) {
return false;
}
bool get_ule_fixed_slice(pdd const& lhs, pdd const& rhs, bool is_positive, fixed_slice& out) {
SASSERT(ule_constraint::is_simplified(lhs, rhs));
if (rhs.is_zero())
return is_positive ? get_eq_fixed_slice(lhs, out) : false;
if (get_ule_fixed_msb(lhs, rhs, is_positive, out))
return true;
if (get_ule_fixed_lsb(lhs, rhs, is_positive, out))
return true;
if (get_ule_fixed_bit(lhs, rhs, is_positive, out))
return true;
return false;
}
bool get_fixed_slice(signed_constraint c, fixed_slice& out) {
SASSERT_EQ(c.vars().size(), 1); // this only makes sense for univariate constraints
if (c.is_ule())
return get_ule_fixed_slice(c.to_ule().lhs(), c.to_ule().rhs(), c.is_positive(), out);
// if (c->is_op())
// ; // TODO: x & constant = constant ==> bitmask ... but we have trouble recognizing that because we introduce a new variable for '&' before we see the equality.
return false;
}
/*
// 2^(k - d) * x = m * 2^(k - d)
// Special case [still seems to occur frequently]: -2^(k - 2) * x > 2^(k - 1) - TODO: Generalize [the obvious solution does not work] => lsb(x, 2) = 1
bool get_lsb(pdd lhs, pdd rhs, pdd& p, trailing_bits& info, bool pos) {
SASSERT(lhs.is_univariate() && lhs.degree() <= 1);
SASSERT(rhs.is_univariate() && rhs.degree() <= 1);
else { // inequality - check for special case
if (pos || lhs.power_of_2() < 3)
return false;
auto it = lhs.begin();
if (it == lhs.end())
return false;
if (it->vars.size() != 1)
return false;
rational coeff = it->coeff;
it++;
if (it != lhs.end())
return false;
if ((mod2k(-coeff, lhs.power_of_2())) != rational::power_of_two(lhs.power_of_2() - 2))
return false;
p = lhs.div(coeff);
SASSERT(p.is_var());
info.bits = 1;
info.length = 2;
info.positive = true; // this is a conjunction
return true;
}
}
*/
} // namespace polysat