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

adding model-based sls for datatatypes

This commit is contained in:
Nikolaj Bjorner 2024-10-20 10:20:38 -07:00
parent 3f33e2c098
commit a48044c6e0
7 changed files with 405 additions and 58 deletions

View file

@ -36,7 +36,7 @@ namespace sat {
}
lbool ddfw::check(unsigned sz, literal const* assumptions) {
init(sz, assumptions);
init(sz, assumptions);
if (m_plugin)
check_with_plugin();
else
@ -58,11 +58,12 @@ namespace sat {
void ddfw::check_with_plugin() {
m_plugin->init_search();
m_steps_since_progress = 0;
unsigned steps = 0;
save_best_values();
if (m_min_sz <= m_unsat.size())
save_best_values();
try {
while (m_min_sz != 0 && m_steps_since_progress++ <= 1500000 && m_limit.inc()) {
while (m_min_sz > 0 && m_limit.inc()) {
if (should_reinit_weights()) do_reinit_weights();
else if (steps % 5000 == 0) shift_weights(), m_plugin->on_rescale();
else if (should_restart()) do_restart(), m_plugin->on_restart();
@ -105,6 +106,7 @@ namespace sat {
bool ddfw::do_flip() {
double reward = 0;
bool_var v = pick_var<uses_plugin>(reward);
//verbose_stream() << "flip " << v << " " << reward << "\n";
return apply_flip<uses_plugin>(v, reward);
}
@ -125,46 +127,32 @@ namespace sat {
template<bool uses_plugin>
bool_var ddfw::pick_var(double& r) {
double sum_pos = 0;
unsigned n = 1, m = 1;
unsigned n = 1;
bool_var v0 = null_bool_var;
bool_var v1 = null_bool_var;
if (m_unsat_vars.empty())
return null_bool_var;
for (bool_var v : m_unsat_vars) {
r = reward(v);
if (r > 0.0)
sum_pos += score(r);
else if (r == 0.0 && sum_pos == 0 && (m_rand() % (n++)) == 0)
v0 = v;
else if (m_rand(m++) == 0)
v1 = v;
}
if (v0 != null_bool_var && m_rand(20) == 0)
return v0;
if (v1 != null_bool_var && m_rand(20) == 0)
return v1;
if (sum_pos > 0) {
double lim_pos = ((double) m_rand() / (1.0 + m_rand.max_value())) * sum_pos;
double lim_pos = ((double) m_rand() / (1.0 + m_rand.max_value())) * sum_pos;
for (bool_var v : m_unsat_vars) {
r = reward(v);
if (r > 0) {
lim_pos -= score(r);
if (lim_pos <= 0) {
return v;
}
if (lim_pos <= 0)
return v;
}
}
}
r = 0;
if (v0 == null_bool_var)
v0 = m_unsat_vars.elem_at(m_rand(m_unsat_vars.size()));
return v0;
if (v0 != null_bool_var)
return v0;
if (m_unsat_vars.empty())
return null_bool_var;
return m_unsat_vars.elem_at(m_rand(m_unsat_vars.size()));
}
void ddfw::add(unsigned n, literal const* c) {
@ -216,7 +204,6 @@ namespace sat {
m_assumptions.append(sz, assumptions);
add_assumptions();
for (unsigned v = 0; v < num_vars(); ++v) {
literal lit(v, false), nlit(v, true);
value(v) = (m_rand() % 2) == 0; // m_use_list[lit.index()].size() >= m_use_list[nlit.index()].size();
}
init_clause_data();
@ -238,6 +225,7 @@ namespace sat {
m_last_flips = 0;
m_shifts = 0;
m_stopwatch.start();
verbose_stream() << "unsat " << m_min_sz << "\n";
}
void ddfw::reinit() {
@ -373,7 +361,8 @@ namespace sat {
break;
}
}
save_best_values();
if (m_unsat.size() < m_min_sz)
save_best_values();
}
bool ddfw::should_restart() {
@ -422,15 +411,9 @@ namespace sat {
void ddfw::save_best_values() {
if (m_unsat.size() < m_min_sz || m_unsat.empty()) {
m_steps_since_progress = 0;
if (m_unsat.size() < 50 || m_min_sz * 10 > m_unsat.size() * 11)
save_model();
}
#if 0
if (m_unsat.size() <= m_min_sz) {
verbose_stream() << "unsat " << m_clauses[m_unsat[0]] << "\n";
}
#endif
if (m_unsat.size() < m_min_sz)
m_models.reset();

View file

@ -100,7 +100,7 @@ namespace sat {
unsigned m_restart_count = 0, m_reinit_count = 0;
uint64_t m_restart_next = 0, m_reinit_next = 0;
uint64_t m_flips = 0, m_last_flips = 0, m_shifts = 0;
unsigned m_min_sz = 0, m_steps_since_progress = 0;
unsigned m_min_sz = UINT_MAX;
u_map<unsigned> m_models;
stopwatch m_stopwatch;
unsigned_vector m_num_models;

View file

@ -561,10 +561,11 @@ namespace sls {
if (!m_repair_down.contains(e->get_id()))
m_repair_down.insert(e->get_id());
for (auto p : parents(e)) {
m_repair_up.reserve(p->get_id() + 1);
m_repair_down.reserve(p->get_id() + 1);
if (!m_repair_up.contains(p->get_id()))
m_repair_up.insert(p->get_id());
auto pid = p->get_id();
m_repair_up.reserve(pid + 1);
m_repair_down.reserve(pid + 1);
if (!m_repair_up.contains(pid))
m_repair_up.insert(pid);
}
}

View file

@ -77,6 +77,7 @@ Model-repair based:
#include "ast/sls/sls_datatype_plugin.h"
#include "ast/sls/sls_euf_plugin.h"
#include "ast/ast_pp.h"
#include "params/sls_params.hpp"
namespace sls {
@ -258,14 +259,20 @@ namespace sls {
}
void datatype_plugin::initialize() {
add_axioms();
sls_params sp(ctx.get_params());
m_axiomatic_mode = sp.dt_axiomatic();
if (m_axiomatic_mode)
add_axioms();
}
expr_ref datatype_plugin::get_value(expr* e) {
if (!dt.is_datatype(e))
return expr_ref(m);
init_values();
return expr_ref(m_values.get(g->find(e)->get_root_id()), m);
if (m_axiomatic_mode) {
init_values();
return expr_ref(m_values.get(g->find(e)->get_root_id()), m);
}
return expr_ref(m_eval.get(e->get_id()), m);
}
void datatype_plugin::init_values() {
@ -563,46 +570,380 @@ namespace sls {
}
void datatype_plugin::propagate_literal(sat::literal lit) {
euf.propagate_literal(lit);
if (m_axiomatic_mode)
euf.propagate_literal(lit);
else
propagate_literal_model_building(lit);
}
void datatype_plugin::propagate_literal_model_building(sat::literal lit) {
if (!ctx.is_true(lit))
return;
auto a = ctx.atom(lit.var());
if (!a || !is_app(a))
return;
repair_down(to_app(a));
}
bool datatype_plugin::is_sat() { return true; }
void datatype_plugin::register_term(expr* e) {}
void datatype_plugin::register_term(expr* e) {
expr* t = nullptr;
if (dt.is_accessor(e, t)) {
auto f = to_app(e)->get_decl();
m_occurs.insert_if_not_there(f, expr_set()).insert(e);
m_eval_accessor.insert_if_not_there(f, obj_map<expr, expr*>());
}
}
bool datatype_plugin::repair_down(app* e) {
verbose_stream() << "repair-down " << mk_bounded_pp(e, m) << "\n";
expr* t, * s;
auto v0 = eval0(e);
auto v1 = eval1(e);
if (v0 == v1)
return true;
IF_VERBOSE(2, verbose_stream() << "dt-repair-down " << mk_bounded_pp(e, m) << " " << v0 << " <- " << v1 << "\n");
if (dt.is_constructor(e))
repair_down_constructor(e, v0, v1);
else if (dt.is_accessor(e, t))
repair_down_accessor(e, t, v0);
else if (dt.is_recognizer(e, t))
repair_down_recognizer(e, t);
else if (m.is_eq(e, s, t))
repair_down_eq(e, s, t);
else if (m.is_distinct(e))
repair_down_distinct(e);
else {
UNREACHABLE();
}
return false;
}
//
// C(t) <- C(s) then repair t <- s
// C(t) <- D(s) then fail the repair.
//
void datatype_plugin::repair_down_constructor(app* e, expr* v0, expr* v1) {
SASSERT(dt.is_constructor(v0));
SASSERT(dt.is_constructor(v1));
SASSERT(e->get_decl() == to_app(v1)->get_decl());
if (e->get_decl() == to_app(v0)->get_decl()) {
for (unsigned i = 0; i < e->get_num_args(); ++i) {
auto w0 = to_app(v0)->get_arg(i);
auto w1 = to_app(v1)->get_arg(i);
if (w0 == w1)
continue;
expr* arg = e->get_arg(i);
set_eval0(arg, w0);
ctx.new_value_eh(arg);
}
}
}
//
// A_D(t) <- s, val(t) = D(..s'..) then update val(t) to agree with s
// A_D(t) <- s, val(t) = C(..) then set t to D(...s...)
// , eval(val(A_D(t))) = s' then update eval(val(A_D,(t))) to s'
void datatype_plugin::repair_down_accessor(app* e, expr* t, expr* v0) {
auto f = e->get_decl();
auto c = dt.get_accessor_constructor(f);
auto val_t = eval0(t);
SASSERT(dt.is_constructor(val_t));
expr_ref_vector args(m);
auto const& accs = *dt.get_constructor_accessors(c);
unsigned i;
for (i = 0; i < accs.size(); ++i) {
if (accs[i] == f)
break;
}
SASSERT(i < accs.size());
if (to_app(val_t)->get_decl() == c) {
if (to_app(val_t)->get_arg(i) == v0)
return;
args.append(accs.size(), to_app(val_t)->get_args());
args[i] = v0;
expr* new_val_t = m.mk_app(c, args);
set_eval0(t, new_val_t);
ctx.new_value_eh(t);
return;
}
if (ctx.rand(5) != 0) {
update_eval_accessor(e, val_t, v0);
return;
}
for (unsigned j = 0; j < accs.size(); ++j) {
if (i == j)
args[i] = v0;
else
args[j] = m_model->get_some_value(accs[j]->get_range());
}
expr* new_val_t = m.mk_app(c, args);
set_eval0(t, new_val_t);
ctx.new_value_eh(t);
}
void datatype_plugin::repair_down_recognizer(app* e, expr* t) {
auto bv = ctx.atom2bool_var(e);
auto is_true = ctx.is_true(bv);
auto c = dt.get_recognizer_constructor(e->get_decl());
auto val_t = eval0(t);
auto const& cons = *dt.get_datatype_constructors(t->get_sort());
auto set_to_instance = [&](func_decl* c) {
auto const& accs = *dt.get_constructor_accessors(c);
expr_ref_vector args(m);
for (auto a : accs)
args.push_back(m_model->get_some_value(a->get_range()));
set_eval0(t, m.mk_app(c, args));
ctx.new_value_eh(t);
};
auto different_constructor = [&](func_decl* c) {
unsigned i = 0;
func_decl* c_new = nullptr;
for (auto c2 : cons)
if (c2 != c && ctx.rand(++i) == 0)
c_new = c2;
return c_new;
};
SASSERT(dt.is_constructor(val_t));
if (c == to_app(val_t)->get_decl() && is_true)
return;
if (c != to_app(val_t)->get_decl() && !is_true)
return;
if (ctx.rand(10) == 0)
ctx.flip(bv);
else if (is_true)
set_to_instance(c);
else if (cons.size() == 1)
ctx.flip(bv);
else
set_to_instance(different_constructor(c));
}
void datatype_plugin::repair_down_eq(app* e, expr* s, expr* t) {
auto bv = ctx.atom2bool_var(e);
auto is_true = ctx.is_true(bv);
auto vs = eval0(s);
auto vt = eval0(t);
if (is_true && vs == vt)
return;
if (!is_true && vs != vt)
return;
if (is_true) {
auto coin = ctx.rand(5);
if (coin <= 1) {
set_eval0(s, vt);
ctx.new_value_eh(s);
return;
}
if (coin <= 3) {
set_eval0(t, vs);
ctx.new_value_eh(t);
}
if (true) {
auto new_v = m_model->get_some_value(s->get_sort());
set_eval0(s, new_v);
set_eval0(t, new_v);
ctx.new_value_eh(s);
ctx.new_value_eh(t);
return;
}
}
auto coin = ctx.rand(10);
if (coin <= 4) {
auto new_v = m_model->get_some_value(s->get_sort());
set_eval0(s, new_v);
ctx.new_value_eh(s);
return;
}
if (coin <= 9) {
auto new_v = m_model->get_some_value(s->get_sort());
set_eval0(t, new_v);
ctx.new_value_eh(t);
return;
}
}
void datatype_plugin::repair_down_distinct(app* e) {
auto bv = ctx.atom2bool_var(e);
auto is_true = ctx.is_true(bv);
unsigned sz = e->get_num_args();
for (unsigned i = 0; i < sz; ++i) {
auto val1 = eval0(e->get_arg(i));
for (unsigned j = i + 1; j < sz; ++j) {
auto val2 = eval0(e->get_arg(j));
if (val1 != val2)
continue;
if (!is_true)
return;
if (ctx.rand(2) == 0)
std::swap(i, j);
auto new_v = m_model->get_some_value(e->get_arg(i)->get_sort());
set_eval0(e->get_arg(i), new_v);
ctx.new_value_eh(e->get_arg(i));
return;
}
}
if (is_true)
return;
if (sz == 1) {
ctx.flip(bv);
return;
}
unsigned i = ctx.rand(sz);
unsigned j = ctx.rand(sz-1);
if (j == i)
++j;
if (ctx.rand(2) == 0)
std::swap(i, j);
set_eval0(e->get_arg(i), eval0(e->get_arg(j)));
}
void datatype_plugin::repair_up(app* e) {
verbose_stream() << "repair-up " << mk_bounded_pp(e, m) << "\n";
IF_VERBOSE(2, verbose_stream() << "dt-repair-up " << mk_bounded_pp(e, m) << "\n");
expr* t;
auto v0 = eval0(e);
auto v1 = eval1(e);
if (v0 == v1)
return;
if (dt.is_constructor(e))
set_eval0(e, v1);
else if (m.is_bool(e))
ctx.flip(ctx.atom2bool_var(e));
else if (dt.is_accessor(e, t))
repair_up_accessor(e, t, v1);
else {
UNREACHABLE();
}
}
void datatype_plugin::repair_up_accessor(app* e, expr* t, expr* v1) {
auto v_t = eval0(t);
auto f = e->get_decl();
SASSERT(dt.is_constructor(v_t));
auto c = dt.get_accessor_constructor(f);
if (to_app(v_t)->get_decl() != c)
update_eval_accessor(e, v_t, v1);
set_eval0(e, v1);
}
expr_ref datatype_plugin::eval1(expr* e) {
auto n = g->find(e)->get_root();
e = n->get_expr();
if (!dt.is_datatype(e))
return ctx.get_value(e);
auto con = get_constructor(n);
if (con) {
expr* s = nullptr, * t = nullptr;
if (m.is_eq(e, s, t))
return expr_ref(m.mk_bool_val(eval0rec(s) == eval0rec(t)), m);
if (m.is_distinct(e)) {
expr_ref_vector args(m);
for (auto arg : euf::enode_args(con))
for (auto arg : *to_app(e))
args.push_back(eval0(arg));
return expr_ref(m.mk_app(con->get_decl(), args), m);
bool d = true;
for (unsigned i = 0; i < args.size(); ++i)
for (unsigned j = i + 1; i < args.size(); ++j)
d &= args.get(i) != args.get(j);
return expr_ref(m.mk_bool_val(d), m);
}
return eval0(n);
if (dt.is_accessor(e, t)) {
auto f = to_app(e)->get_decl();
auto v = eval0rec(t);
return eval_accessor(f, v);
}
if (dt.is_constructor(e)) {
expr_ref_vector args(m);
for (auto arg : *to_app(e))
args.push_back(eval0rec(arg));
return expr_ref(m.mk_app(to_app(e)->get_decl(), args), m);
}
if (dt.is_recognizer(e, t)) {
auto v = eval0rec(t);
SASSERT(dt.is_constructor(v));
auto c = dt.get_recognizer_constructor(to_app(e)->get_decl());
return expr_ref(m.mk_bool_val(c == to_app(v)->get_decl()), m);
}
return eval0(e);
}
expr_ref datatype_plugin::eval0rec(expr* e) {
auto v = m_eval.get(e->get_id(), nullptr);
if (v)
return expr_ref(v, m);
if (!is_app(e) || to_app(e)->get_family_id() != m_fid)
return ctx.get_value(e);
auto w = eval1(e);
m_eval.set(e->get_id(), w);
return w;
}
expr_ref datatype_plugin::eval_accessor(func_decl* f, expr* t) {
auto& t2val = m_eval_accessor[f];
if (!t2val.contains(t)) {
auto val = m_model->get_some_value(f->get_range());
m.inc_ref(t);
m.inc_ref(val);
}
return expr_ref(t2val[t], m);
}
void datatype_plugin::update_eval_accessor(app* e, expr* t, expr* value) {
func_decl* f = e->get_decl();
auto& t2val = m_eval_accessor[f];
expr* old_value = nullptr;
t2val.find(t, old_value);
if (old_value == value)
;
else if (old_value) {
t2val[t] = value;
m.inc_ref(value);
m.dec_ref(old_value);
}
else {
m.inc_ref(t);
m.inc_ref(value);
t2val.insert(t, value);
}
for (expr* b : m_occurs[f]) {
if (b == e)
continue;
expr* a;
VERIFY(dt.is_accessor(b, a));
auto v_a = eval0(a);
if (v_a.get() == t) {
set_eval0(b, value);
ctx.new_value_eh(b);
}
}
}
void datatype_plugin::del_eval_accessor() {
ptr_vector<expr> kv;
for (auto& [f, t2val] : m_eval_accessor)
for (auto& [k, val] : t2val)
kv.push_back(k), kv.push_back(val);
for (auto k : kv)
m.dec_ref(k);
}
expr_ref datatype_plugin::eval0(expr* n) {
if (!dt.is_datatype(n->get_sort()))
return ctx.get_value(n);
if (!m_eval.get(n->get_id(), nullptr))
m_eval[n->get_id()] = m_model->get_some_value(n->get_sort());
auto v = m_eval.get(n->get_id(), nullptr);
if (v)
return expr_ref(v, m);
set_eval0(n, m_model->get_some_value(n->get_sort()));
return expr_ref(m_eval.get(n->get_id()), m);
}
void datatype_plugin::set_eval0(expr* e, expr* value) {
if (dt.is_datatype(e->get_sort()))
m_eval[e->get_id()] = value;
else
ctx.set_value(e, value);
}
expr_ref datatype_plugin::eval0(euf::enode* n) {
return eval0(n->get_root()->get_expr());
}

View file

@ -38,6 +38,7 @@ namespace sls {
obj_map<sort, ptr_vector<expr>> m_dts;
obj_map<expr, vector<parent_t>> m_parents;
bool m_axiomatic_mode = true;
mutable datatype_util dt;
expr_ref_vector m_axioms, m_values, m_eval;
model_ref m_model;
@ -54,10 +55,29 @@ namespace sls {
euf::enode* get_constructor(euf::enode* n) const;
// f -> v_t -> val
// e = A(t)
// val(t) <- val
//
typedef obj_hashtable<expr> expr_set;
obj_map<func_decl, obj_map<expr, expr*>> m_eval_accessor;
obj_map<func_decl, expr_set> m_occurs;
expr_ref eval1(expr* e);
expr_ref eval0(euf::enode* n);
expr_ref eval0(expr* n);
expr_ref eval0rec(expr* n);
expr_ref eval_accessor(func_decl* f, expr* t);
void update_eval_accessor(app* e, expr* t, expr* value);
void del_eval_accessor();
void set_eval0(expr* e, expr* val);
void repair_down_constructor(app* e, expr* v0, expr* v1);
void repair_down_accessor(app* e, expr* t, expr* v1);
void repair_down_recognizer(app* e, expr* t);
void repair_down_eq(app* e, expr* s, expr* t);
void repair_down_distinct(app* e);
void repair_up_accessor(app* e, expr* t, expr* v0);
void propagate_literal_model_building(sat::literal lit);
public:
datatype_plugin(context& c);

View file

@ -23,6 +23,7 @@ def_module_params('sls',
('random_offset', BOOL, 1, 'use random offset for candidate evaluation'),
('rescore', BOOL, 1, 'rescore/normalize top-level score every base restart interval'),
('euf_incremental', UINT, 0, '0 non-incremental, 1 incremental, 2 alternating EUF resolver'),
('dt_axiomatic', BOOL, True, 'use axiomatic mode or model reduction for datatype solver'),
('track_unsat', BOOL, 0, 'keep a list of unsat assertions as done in SAT - currently disabled internally'),
('random_seed', UINT, 0, 'random seed')
))

View file

@ -56,6 +56,7 @@ namespace sat {
}
void ddfw_wrapper::add(solver const& s) {
m_ddfw.set_seed(s.get_config().m_random_seed);
m_ddfw.m_clauses.reset();
m_ddfw.m_use_list.reset();
m_ddfw.m_num_non_binary_clauses = 0;