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

add int-blast experiment

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2023-03-27 16:40:22 -07:00
parent 3796770e46
commit a82408e89b
2 changed files with 338 additions and 59 deletions

View file

@ -32,13 +32,70 @@ namespace polysat {
return deps;
}
bool univariate_solver::find_min(rational& val) {
val = model();
push();
// try reducing val by setting bits to 0, starting at the msb.
for (unsigned k = bit_width; k-- > 0; ) {
if (!val.get_bit(k)) {
add_bit0(k, null_dep);
continue;
}
// try decreasing k-th bit
push();
add_bit0(k, 0);
lbool result = check();
if (result == l_true) {
SASSERT(model() < val);
val = model();
}
pop(1);
if (result == l_true)
add_bit0(k, null_dep);
else if (result == l_false)
add_bit1(k, null_dep);
else
return false;
}
pop(1);
return true;
}
bool univariate_solver::find_max(rational& val) {
val = model();
push();
// try increasing val by setting bits to 1, starting at the msb.
for (unsigned k = bit_width; k-- > 0; ) {
if (val.get_bit(k)) {
add_bit1(k, 0);
continue;
}
// try increasing k-th bit
push();
add_bit1(k, 0);
lbool result = check();
if (result == l_true) {
SASSERT(model() > val);
val = model();
}
pop(1);
if (result == l_true)
add_bit1(k, null_dep);
else if (result == l_false)
add_bit0(k, null_dep);
else
return false;
}
pop(1);
return true;
}
class univariate_bitblast_solver : public univariate_solver {
// TODO: does it make sense to share m and bv between different solver instances?
// TODO: consider pooling solvers to save setup overhead, see if solver/solver_pool.h can be used
ast_manager m;
scoped_ptr<bv_util> bv;
scoped_ptr<solver> s;
unsigned bit_width;
unsigned m_scope_level = 0;
func_decl_ref x_decl;
expr_ref x;
@ -46,7 +103,7 @@ namespace polysat {
public:
univariate_bitblast_solver(solver_factory& mk_solver, unsigned bit_width) :
bit_width(bit_width),
univariate_solver(bit_width),
x_decl(m),
x(m) {
reg_decl_plugins(m);
@ -192,7 +249,7 @@ namespace polysat {
}
template <typename lhs_t, typename rhs_t>
void add_ule_impl(lhs_t const& lhs, rhs_t const& rhs, bool sign, dep_t dep) {
void add_ule_impl(lhs_t const& lhs, rhs_t const& rhs, bool sign, dep_t dep) {
if (is_zero(rhs))
add(m.mk_eq(mk_poly(lhs), mk_poly(rhs)), sign, dep);
else
@ -329,71 +386,289 @@ namespace polysat {
s->get_model(model);
SASSERT(model);
app* val = to_app(model->get_const_interp(x_decl));
SASSERT(val->get_decl_kind() == OP_BV_NUM);
SASSERT(val->get_num_parameters() == 2);
auto const& p = val->get_parameter(0);
SASSERT(p.is_rational());
cached_model = p.get_rational();
unsigned sz;
VERIFY(bv->is_numeral(val, cached_model, sz));
}
return cached_model;
}
bool find_min(rational& val) override {
val = model();
bool find_two(rational& out1, rational& out2) override {
out1 = model();
bool ok = true;
push();
// try reducing val by setting bits to 0, starting at the msb.
for (unsigned k = bit_width; k-- > 0; ) {
if (!val.get_bit(k)) {
add_bit0(k, null_dep);
continue;
}
// try decreasing k-th bit
push();
add_bit0(k, 0);
lbool result = check();
if (result == l_true) {
SASSERT(model() < val);
val = model();
}
pop(1);
if (result == l_true)
add_bit0(k, null_dep);
else if (result == l_false)
add_bit1(k, null_dep);
else
return false;
add(m.mk_eq(mk_numeral(out1), x), true, null_dep);
switch (check()) {
case l_true:
out2 = model();
break;
case l_false:
out2 = out1;
break;
default:
ok = false;
break;
}
pop(1);
IF_VERBOSE(10, verbose_stream() << "viable " << out1 << " " << out2 << "\n");
return ok;
}
std::ostream& display(std::ostream& out) const override {
return out << *s;
}
};
// stub for alternative int-blast solver.
class univariate_intblast_solver : public univariate_solver {
ast_manager m;
scoped_ptr<arith_util> a;
scoped_ptr<solver> s;
rational m_mod;
unsigned m_scope_level = 0;
func_decl_ref x_decl;
expr_ref x;
vector<rational> model_cache;
void add(expr* e, bool sign, dep_t dep) {
reset_cache();
if (sign)
e = m.mk_not(e);
if (dep == null_dep) {
s->assert_expr(e);
IF_VERBOSE(10, verbose_stream() << "(assert " << expr_ref(e, m) << ")\n");
}
else {
expr* a = m.mk_const(m.mk_const_decl(symbol(dep), m.mk_bool_sort()));
s->assert_expr(e, a);
IF_VERBOSE(10, verbose_stream() << "(assert (! " << expr_ref(e, m) << " :named " << expr_ref(a, m) << "))\n");
}
}
bool is_zero(univariate const& p) const {
for (auto n : p)
if (n != 0)
return false;
return true;
}
bool find_max(rational& val) override {
val = model();
push();
// try increasing val by setting bits to 1, starting at the msb.
for (unsigned k = bit_width; k-- > 0; ) {
if (val.get_bit(k)) {
add_bit1(k, 0);
continue;
bool is_zero(rational const& p) const {
return p.is_zero();
}
public:
univariate_intblast_solver(solver_factory& mk_solver, unsigned bit_width) :
univariate_solver(bit_width),
m_mod(rational::power_of_two(bit_width)),
x_decl(m),
x(m) {
reg_decl_plugins(m);
a = alloc(arith_util, m);
params_ref p;
p.set_bool("bv.polysat", false);
// p.set_bool("smt", true);
s = mk_solver(m, p, false, true, true, symbol::null);
x_decl = m.mk_const_decl("x", a->mk_int());
x = m.mk_const(x_decl);
model_cache.push_back(rational(-1));
s->assert_expr(a->mk_le(mk_numeral(0), x));
s->assert_expr(a->mk_lt(x, mk_numeral(m_mod)));
}
~univariate_intblast_solver() override = default;
void reset_cache() {
model_cache.back() = -1;
}
void push_cache() {
rational v = model_cache.back();
model_cache.push_back(v);
}
void pop_cache() {
model_cache.pop_back();
}
void push() override {
m_scope_level++;
push_cache();
s->push();
}
void pop(unsigned n) override {
SASSERT(scope_level() >= n);
m_scope_level -= n;
pop_cache();
s->pop(n);
}
unsigned scope_level() const override {
return m_scope_level;
}
expr* mk_numeral(rational const& r) const {
// assert 0 <= r < 2^bit-width
return a->mk_int(r);
}
expr* mk_numeral(uint64_t u) const {
// assert u < 2^bit-width
return a->mk_int(rational(u, rational::ui64()));
}
lbool check() override {
return s->check_sat();
}
expr_ref mk_poly(rational const& p) {
return {mk_numeral(p), m};
}
// 2^k*x --> x << k
// n*x --> n * x
expr* mk_poly_term(rational const& coeff, expr* xpow) const {
SASSERT(!coeff.is_zero());
if (coeff.is_one())
return xpow;
return a->mk_mul(mk_numeral(coeff), xpow);
}
// [d,c,b,a] --> d + c*x + b*(x*x) + a*(x*x*x)
expr_ref mk_poly(univariate const& p) {
expr_ref e(m);
if (p.empty())
e = mk_numeral(rational::zero());
else {
if (!p[0].is_zero())
e = mk_numeral(p[0]);
expr_ref xpow = x;
for (unsigned i = 1; i < p.size(); ++i) {
if (!p[i].is_zero()) {
expr* t = mk_poly_term(p[i], xpow);
e = e ? a->mk_add(e, t) : t;
}
if (i + 1 < p.size())
xpow = a->mk_mul(xpow, x);
}
// try increasing k-th bit
push();
add_bit1(k, 0);
lbool result = check();
if (result == l_true) {
SASSERT(model() > val);
val = model();
}
pop(1);
if (result == l_true)
add_bit1(k, null_dep);
else if (result == l_false)
add_bit0(k, null_dep);
else
return false;
if (!e)
e = mk_numeral(p[0]);
}
pop(1);
return true;
if (!a->is_numeral(e) && e != x)
e = a->mk_mod(e, mk_numeral(m_mod));
return e;
}
template <typename lhs_t, typename rhs_t>
void add_ule_impl(lhs_t const& lhs, rhs_t const& rhs, bool sign, dep_t dep) {
// todo: simplify x - k == 0 into x = k
// or ensure that bounds simplification tactic is enabled.
// without bounds simplification, int-blasting doesnt work.
if (is_zero(rhs))
add(m.mk_eq(mk_poly(lhs), mk_poly(rhs)), sign, dep);
else
add(a->mk_le(mk_poly(lhs), mk_poly(rhs)), sign, dep);
}
void add_ule(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override { add_ule_impl(lhs, rhs, sign, dep); }
void add_ule(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) override { add_ule_impl(lhs, rhs, sign, dep); }
void add_ule(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) override { add_ule_impl(lhs, rhs, sign, dep); }
void add_umul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override {
auto c = a->mk_mul(mk_poly(lhs), mk_poly(rhs));
if (sign) // or the other way around?
add(a->mk_lt(c, mk_numeral(m_mod)), sign, dep);
else
add(a->mk_ge(c, mk_numeral(m_mod)), sign, dep);
}
void add_smul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_smul_udfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_lshr(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_ashr(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_shl(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_and(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_or(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_xor(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_not(univariate const& in, univariate const& out, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_inv(univariate const& in, univariate const& out, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_udiv(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_urem(univariate const& in1, univariate const& in2, univariate const& out, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_ule_const(rational const& val, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_uge_const(rational const& val, bool sign, dep_t dep) override {
NOT_IMPLEMENTED_YET();
}
void add_bit(unsigned idx, bool sign, dep_t dep) override {
add(m.mk_eq(mk_numeral(m_mod/2), a->mk_mul(mk_numeral(rational::power_of_two(bit_width - idx - 1)), x)), sign, dep);
}
void unsat_core(dep_vector& deps) override {
deps.reset();
expr_ref_vector core(m);
s->get_unsat_core(core);
for (expr* a : core) {
unsigned dep = to_app(a)->get_decl()->get_name().get_num();
deps.push_back(dep);
}
IF_VERBOSE(10, verbose_stream() << "core " << deps << "\n");
SASSERT(deps.size() > 0);
}
rational model() override {
rational& cached_model = model_cache.back();
if (cached_model.is_neg()) {
model_ref model;
s->get_model(model);
SASSERT(model);
app* val = to_app(model->get_const_interp(x_decl));
VERIFY(a->is_numeral(val, cached_model));
}
return cached_model;
}
bool find_two(rational& out1, rational& out2) override {
@ -419,7 +694,7 @@ namespace polysat {
std::ostream& display(std::ostream& out) const override {
return out << *s;
}
}
};
class univariate_bitblast_factory : public univariate_solver_factory {

View file

@ -24,6 +24,8 @@ Author:
namespace polysat {
class univariate_solver {
protected:
unsigned bit_width;
public:
using dep_t = unsigned;
using dep_vector = svector<dep_t>;
@ -34,6 +36,8 @@ namespace polysat {
const dep_t null_dep = UINT_MAX;
univariate_solver(unsigned bit_width) : bit_width(bit_width) {}
virtual ~univariate_solver() = default;
virtual void push() = 0;
@ -59,7 +63,7 @@ namespace polysat {
* Precondition: check() returned l_true
* Returns: true on success, false on resource out.
*/
virtual bool find_min(rational& out_min) = 0;
bool find_min(rational& out_min);
/**
* Find maximal model.
@ -67,7 +71,7 @@ namespace polysat {
* Precondition: check() returned l_true
* Returns: true on success, false on resource out.
*/
virtual bool find_max(rational& out_max) = 0;
bool find_max(rational& out_max);
/**
* Find up to two viable values.