mirror of
https://github.com/Z3Prover/z3
synced 2025-08-27 21:48:56 +00:00
arrays (#4684)
* arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * arrays Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fill Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * update drat and fix euf bugs Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * const qualifiers Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * reorg ba Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * reorg Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * build warnings Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
d56dd1db7b
commit
796e2fd9eb
79 changed files with 2571 additions and 1850 deletions
|
@ -5,8 +5,12 @@ z3_add_component(sat_smt
|
|||
array_model.cpp
|
||||
array_solver.cpp
|
||||
atom2bool_var.cpp
|
||||
ba_card.cpp
|
||||
ba_constraint.cpp
|
||||
ba_internalize.cpp
|
||||
ba_pb.cpp
|
||||
ba_solver.cpp
|
||||
ba_xor.cpp
|
||||
bv_ackerman.cpp
|
||||
bv_internalize.cpp
|
||||
bv_solver.cpp
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace array {
|
|||
m_axiom_trail.push_back(r);
|
||||
if (m_axioms.contains(idx))
|
||||
m_axiom_trail.pop_back();
|
||||
else
|
||||
ctx.push(push_back_vector<euf::solver, svector<axiom_record>>(m_axiom_trail));
|
||||
}
|
||||
|
||||
bool solver::assert_axiom(unsigned idx) {
|
||||
|
@ -39,10 +41,16 @@ namespace array {
|
|||
app* select;
|
||||
switch (r.m_kind) {
|
||||
case axiom_record::kind_t::is_store:
|
||||
TRACE("array", tout << "store-axiom: " << mk_bounded_pp(child, m, 2) << "\n";);
|
||||
return assert_store_axiom(to_app(child));
|
||||
case axiom_record::kind_t::is_select:
|
||||
select = r.select->get_app();
|
||||
SASSERT(a.is_select(select));
|
||||
SASSERT(can_beta_reduce(r.n));
|
||||
TRACE("array", tout << "select-axiom: " << mk_bounded_pp(select, m, 2) << " " << mk_bounded_pp(child, m, 2) << "\n";);
|
||||
if (r.select->get_arg(0)->get_root() != r.n->get_root()) {
|
||||
IF_VERBOSE(0, verbose_stream() << "could delay " << mk_pp(select, m) << " " << mk_pp(child, m) << "\n");
|
||||
}
|
||||
if (a.is_const(child))
|
||||
return assert_select_const_axiom(select, to_app(child));
|
||||
else if (a.is_as_array(child))
|
||||
|
@ -57,20 +65,22 @@ namespace array {
|
|||
UNREACHABLE();
|
||||
break;
|
||||
case axiom_record::kind_t::is_default:
|
||||
SASSERT(can_beta_reduce(r.n));
|
||||
TRACE("array", tout << "default-axiom: " << mk_bounded_pp(child, m, 2) << "\n";);
|
||||
if (a.is_const(child))
|
||||
return assert_default_const_axiom(to_app(child));
|
||||
else if (a.is_store(child))
|
||||
return assert_default_store_axiom(to_app(child));
|
||||
else if (a.is_map(child))
|
||||
return assert_default_map_axiom(to_app(child));
|
||||
else if (a.is_as_array(child))
|
||||
return assert_default_as_array_axiom(to_app(child));
|
||||
else
|
||||
UNREACHABLE();
|
||||
return true;
|
||||
break;
|
||||
case axiom_record::kind_t::is_extensionality:
|
||||
TRACE("array", tout << "extensionality-axiom: " << mk_bounded_pp(child, m, 2) << "\n";);
|
||||
return assert_extensionality(r.n->get_arg(0)->get_expr(), r.n->get_arg(1)->get_expr());
|
||||
case axiom_record::kind_t::is_congruence:
|
||||
TRACE("array", tout << "congruence-axiom: " << mk_bounded_pp(child, m, 2) << " " << mk_bounded_pp(r.select->get_expr(), m, 2) << "\n";);
|
||||
return assert_congruent_axiom(child, r.select->get_expr());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
@ -86,7 +96,7 @@ namespace array {
|
|||
* n := store(a, i, v)
|
||||
*/
|
||||
bool solver::assert_store_axiom(app* e) {
|
||||
m_stats.m_num_store_axiom++;
|
||||
++m_stats.m_num_store_axiom;
|
||||
SASSERT(a.is_store(e));
|
||||
unsigned num_args = e->get_num_args();
|
||||
ptr_vector<expr> sel_args(num_args - 1, e->get_args());
|
||||
|
@ -104,7 +114,7 @@ namespace array {
|
|||
* where i = (i_1, ..., i_n), j = (j_1, .., j_n), k in 1..n
|
||||
*/
|
||||
bool solver::assert_select_store_axiom(app* select, app* store) {
|
||||
m_stats.m_num_select_store_axiom++;
|
||||
++m_stats.m_num_select_store_axiom;
|
||||
SASSERT(a.is_store(store));
|
||||
SASSERT(a.is_select(select));
|
||||
SASSERT(store->get_num_args() == 1 + select->get_num_args());
|
||||
|
@ -126,7 +136,10 @@ namespace array {
|
|||
if (s1->get_root() == s2->get_root())
|
||||
return false;
|
||||
sat::literal sel_eq = b_internalize(sel_eq_e);
|
||||
if (s().value(sel_eq) == l_true)
|
||||
return false;
|
||||
|
||||
bool new_prop = false;
|
||||
for (unsigned i = 1; i < num_args; i++) {
|
||||
expr* idx1 = store->get_arg(i);
|
||||
expr* idx2 = select->get_arg(i);
|
||||
|
@ -135,13 +148,15 @@ namespace array {
|
|||
if (r1 == r2)
|
||||
continue;
|
||||
if (m.are_distinct(r1->get_expr(), r2->get_expr())) {
|
||||
new_prop = true;
|
||||
add_clause(sel_eq);
|
||||
break;
|
||||
}
|
||||
sat::literal idx_eq = b_internalize(m.mk_eq(idx1, idx2));
|
||||
add_clause(idx_eq, sel_eq);
|
||||
if (add_clause(idx_eq, sel_eq))
|
||||
new_prop = true;
|
||||
}
|
||||
return true;
|
||||
return new_prop;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,7 +164,7 @@ namespace array {
|
|||
* select(const(v), i) = v
|
||||
*/
|
||||
bool solver::assert_select_const_axiom(app* select, app* cnst) {
|
||||
m_stats.m_num_select_const_axiom++;
|
||||
++m_stats.m_num_select_const_axiom;
|
||||
expr* val = nullptr;
|
||||
VERIFY(a.is_const(cnst, val));
|
||||
SASSERT(a.is_select(select));
|
||||
|
@ -167,7 +182,7 @@ namespace array {
|
|||
* e1 = e2 or select(e1, diff(e1,e2)) != select(e2, diff(e1, e2))
|
||||
*/
|
||||
bool solver::assert_extensionality(expr* e1, expr* e2) {
|
||||
m_stats.m_num_extensionality_axiom++;
|
||||
++m_stats.m_num_extensionality_axiom;
|
||||
func_decl_ref_vector* funcs = nullptr;
|
||||
VERIFY(m_sort2diff.find(m.get_sort(e1), funcs));
|
||||
expr_ref_vector args1(m), args2(m);
|
||||
|
@ -184,10 +199,7 @@ namespace array {
|
|||
expr_ref sel1_eq_sel2(m.mk_eq(sel1, sel2), m);
|
||||
literal lit1 = b_internalize(n1_eq_n2);
|
||||
literal lit2 = b_internalize(sel1_eq_sel2);
|
||||
if (s().value(lit1) == l_true || s().value(lit2) == l_false)
|
||||
return false;
|
||||
add_clause(lit1, ~lit2);
|
||||
return true;
|
||||
return add_clause(lit1, ~lit2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,17 +207,12 @@ namespace array {
|
|||
* select(map[f](a, ... d), i) = f(select(a,i),...,select(d,i))
|
||||
*/
|
||||
bool solver::assert_select_map_axiom(app* select, app* map) {
|
||||
m_stats.m_num_select_map_axiom++;
|
||||
++m_stats.m_num_select_map_axiom;
|
||||
SASSERT(a.is_map(map));
|
||||
SASSERT(a.is_select(select));
|
||||
SASSERT(map->get_num_args() > 0);
|
||||
func_decl* f = a.get_map_func_decl(map);
|
||||
|
||||
TRACE("array",
|
||||
tout << mk_bounded_pp(map, m) << "\n";
|
||||
tout << mk_bounded_pp(select, m) << "\n";);
|
||||
unsigned num_args = select->get_num_args();
|
||||
unsigned num_arrays = map->get_num_args();
|
||||
ptr_buffer<expr> args1, args2;
|
||||
vector<ptr_vector<expr> > args2l;
|
||||
args1.push_back(map);
|
||||
|
@ -238,7 +245,7 @@ namespace array {
|
|||
* select(as-array f, i_1, ..., i_n) = (f i_1 ... i_n)
|
||||
*/
|
||||
bool solver::assert_select_as_array_axiom(app* select, app* arr) {
|
||||
m_stats.m_num_select_as_array_axiom++;
|
||||
++m_stats.m_num_select_as_array_axiom;
|
||||
SASSERT(a.is_as_array(arr));
|
||||
SASSERT(a.is_select(select));
|
||||
unsigned num_args = select->get_num_args();
|
||||
|
@ -257,39 +264,31 @@ namespace array {
|
|||
* default(map[f](a,..,d)) = f(default(a),..,default(d))
|
||||
*/
|
||||
bool solver::assert_default_map_axiom(app* map) {
|
||||
m_stats.m_num_default_map_axiom++;
|
||||
++m_stats.m_num_default_map_axiom;
|
||||
SASSERT(a.is_map(map));
|
||||
func_decl* f = a.get_map_func_decl(map);
|
||||
SASSERT(map->get_num_args() == f->get_arity());
|
||||
ptr_buffer<expr> args2;
|
||||
expr_ref_vector args2(m);
|
||||
for (expr* arg : *map)
|
||||
args2.push_back(a.mk_default(arg));
|
||||
|
||||
expr_ref def1(a.mk_default(map), m);
|
||||
expr_ref def2(m.mk_app(f, args2), m);
|
||||
rewrite(def2);
|
||||
return ctx.propagate(e_internalize(def1), e_internalize(def2), array_axiom());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assert:
|
||||
* default(const(e)) = e
|
||||
*/
|
||||
bool solver::assert_default_const_axiom(app* cnst) {
|
||||
m_stats.m_num_default_const_axiom++;
|
||||
++m_stats.m_num_default_const_axiom;
|
||||
expr* val = nullptr;
|
||||
VERIFY(a.is_const(cnst, val));
|
||||
TRACE("array", tout << mk_bounded_pp(cnst, m) << "\n";);
|
||||
expr_ref def(a.mk_default(cnst), m);
|
||||
return ctx.propagate(expr2enode(val), e_internalize(def), array_axiom());
|
||||
}
|
||||
|
||||
bool solver::assert_default_as_array_axiom(app* as_array) {
|
||||
// no-op
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* let n := store(a, i, v)
|
||||
|
@ -303,19 +302,15 @@ namespace array {
|
|||
* default(n) = default(a)
|
||||
*/
|
||||
bool solver::assert_default_store_axiom(app* store) {
|
||||
m_stats.m_num_default_store_axiom++;
|
||||
++m_stats.m_num_default_store_axiom;
|
||||
SASSERT(a.is_store(store));
|
||||
SASSERT(store->get_num_args() >= 3);
|
||||
expr_ref def1(m), def2(m);
|
||||
bool prop = false;
|
||||
|
||||
unsigned num_args = store->get_num_args();
|
||||
|
||||
def1 = a.mk_default(store);
|
||||
def2 = a.mk_default(store->get_arg(0));
|
||||
|
||||
bool is_new = false;
|
||||
|
||||
if (has_unitary_domain(store)) {
|
||||
def2 = store->get_arg(num_args - 1);
|
||||
}
|
||||
|
@ -357,6 +352,7 @@ namespace array {
|
|||
* Assert select(lambda xs . M, N1,.., Nk) -> M[N1/x1, ..., Nk/xk]
|
||||
*/
|
||||
bool solver::assert_select_lambda_axiom(app* select, expr* lambda) {
|
||||
++m_stats.m_num_select_lambda_axiom;
|
||||
SASSERT(is_lambda(lambda));
|
||||
SASSERT(a.is_select(select));
|
||||
SASSERT(m.get_sort(lambda) == m.get_sort(select->get_arg(0)));
|
||||
|
@ -373,8 +369,8 @@ namespace array {
|
|||
*/
|
||||
bool solver::assert_congruent_axiom(expr* e1, expr* e2) {
|
||||
++m_stats.m_num_congruence_axiom;
|
||||
sort* s = m.get_sort(e1);
|
||||
unsigned dimension = get_array_arity(s);
|
||||
sort* srt = m.get_sort(e1);
|
||||
unsigned dimension = get_array_arity(srt);
|
||||
expr_ref n1_eq_n2(m.mk_eq(e1, e2), m);
|
||||
expr_ref_vector args1(m), args2(m);
|
||||
args1.push_back(e1);
|
||||
|
@ -382,10 +378,10 @@ namespace array {
|
|||
svector<symbol> names;
|
||||
sort_ref_vector sorts(m);
|
||||
for (unsigned i = 0; i < dimension; i++) {
|
||||
sort * srt = get_array_domain(s, i);
|
||||
sorts.push_back(srt);
|
||||
sort * asrt = get_array_domain(srt, i);
|
||||
sorts.push_back(asrt);
|
||||
names.push_back(symbol(i));
|
||||
expr * k = m.mk_var(dimension - i - 1, srt);
|
||||
expr * k = m.mk_var(dimension - i - 1, asrt);
|
||||
args1.push_back(k);
|
||||
args2.push_back(k);
|
||||
}
|
||||
|
@ -395,8 +391,8 @@ namespace array {
|
|||
expr_ref q(m.mk_forall(dimension, sorts.c_ptr(), names.c_ptr(), eq), m);
|
||||
rewrite(q);
|
||||
sat::literal fa_eq = b_internalize(q);
|
||||
add_clause(~b_internalize(n1_eq_n2), fa_eq);
|
||||
return true;
|
||||
sat::literal neq = b_internalize(n1_eq_n2);
|
||||
return add_clause(~neq, fa_eq);
|
||||
}
|
||||
|
||||
bool solver::has_unitary_domain(app* array_term) {
|
||||
|
@ -411,7 +407,7 @@ namespace array {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool solver::has_large_domain(app* array_term) {
|
||||
bool solver::has_large_domain(expr* array_term) {
|
||||
SASSERT(a.is_array(array_term));
|
||||
sort* s = m.get_sort(array_term);
|
||||
unsigned dim = get_array_arity(s);
|
||||
|
@ -429,7 +425,6 @@ namespace array {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::pair<app*, func_decl*> solver::mk_epsilon(sort* s) {
|
||||
app* eps = nullptr;
|
||||
func_decl* diag = nullptr;
|
||||
|
@ -444,24 +439,16 @@ namespace array {
|
|||
return std::make_pair(eps, diag);
|
||||
}
|
||||
|
||||
void solver::push_parent_select_store_axioms(theory_var v) {
|
||||
expr* e = var2expr(v);
|
||||
if (!a.is_array(e))
|
||||
return;
|
||||
auto& d = get_var_data(v);
|
||||
for (euf::enode* store : d.m_parents)
|
||||
if (a.is_store(store->get_expr()))
|
||||
for (euf::enode* sel : d.m_parents)
|
||||
if (a.is_select(sel->get_expr()))
|
||||
push_axiom(select_axiom(sel, store));
|
||||
}
|
||||
|
||||
bool solver::add_delayed_axioms() {
|
||||
if (!get_config().m_array_delay_exp_axiom)
|
||||
return false;
|
||||
unsigned num_vars = get_num_vars();
|
||||
for (unsigned v = 0; v < num_vars; v++)
|
||||
push_parent_select_store_axioms(v);
|
||||
for (unsigned v = 0; v < num_vars; v++) {
|
||||
propagate_parent_select_axioms(v);
|
||||
auto& d = get_var_data(v);
|
||||
if (d.m_prop_upward)
|
||||
propagate_parent_default(v);
|
||||
}
|
||||
return unit_propagate();
|
||||
}
|
||||
|
||||
|
@ -471,13 +458,15 @@ namespace array {
|
|||
bool prop = false;
|
||||
for (unsigned i = roots.size(); i-- > 0; ) {
|
||||
theory_var v1 = roots[i];
|
||||
euf::enode* n1 = var2enode(v1);
|
||||
expr* e1 = var2expr(v1);
|
||||
for (unsigned j = i; j-- > 0; ) {
|
||||
theory_var v2 = roots[j];
|
||||
euf::enode* n2 = var2enode(v2);
|
||||
if (m.get_sort(n1->get_expr()) != m.get_sort(n2->get_expr()))
|
||||
expr* e2 = var2expr(v2);
|
||||
if (m.get_sort(e1) != m.get_sort(e2))
|
||||
continue;
|
||||
expr_ref eq(m.mk_eq(n1->get_expr(), n2->get_expr()), m);
|
||||
if (have_different_model_values(v1, v2))
|
||||
continue;
|
||||
expr_ref eq(m.mk_eq(e1, e2), m);
|
||||
sat::literal lit = b_internalize(eq);
|
||||
if (s().value(lit) == l_undef)
|
||||
prop = true;
|
||||
|
@ -498,15 +487,10 @@ namespace array {
|
|||
if (r->is_marked1()) {
|
||||
continue;
|
||||
}
|
||||
// arrays used as indices in other arrays have to be treated as shared.
|
||||
// issue #3532, #3529
|
||||
//
|
||||
if (ctx.is_shared(r) || is_select_arg(r)) {
|
||||
TRACE("array", tout << "new shared var: #" << r->get_expr_id() << "\n";);
|
||||
theory_var r_th_var = r->get_th_var(get_id());
|
||||
SASSERT(r_th_var != euf::null_theory_var);
|
||||
roots.push_back(r_th_var);
|
||||
}
|
||||
// arrays used as indices in other arrays have to be treated as shared issue #3532, #3529
|
||||
if (ctx.is_shared(r) || is_select_arg(r))
|
||||
roots.push_back(r->get_th_var(get_id()));
|
||||
|
||||
r->mark1();
|
||||
to_unmark.push_back(r);
|
||||
}
|
||||
|
@ -516,6 +500,7 @@ namespace array {
|
|||
}
|
||||
|
||||
bool solver::is_select_arg(euf::enode* r) {
|
||||
SASSERT(r->is_root());
|
||||
for (euf::enode* n : euf::enode_parents(r))
|
||||
if (a.is_select(n->get_expr()))
|
||||
for (unsigned i = 1; i < n->num_args(); ++i)
|
||||
|
|
|
@ -20,13 +20,15 @@ Author:
|
|||
|
||||
namespace array {
|
||||
|
||||
sat::literal solver::internalize(expr* e, bool sign, bool root, bool learned) {
|
||||
// TODO
|
||||
return sat::null_literal;
|
||||
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
|
||||
SASSERT(m.is_bool(e));
|
||||
if (!visit_rec(m, e, sign, root, redundant))
|
||||
return sat::null_literal;
|
||||
return expr2literal(e);
|
||||
}
|
||||
|
||||
void solver::internalize(expr* e, bool redundant) {
|
||||
// TODO
|
||||
visit_rec(m, e, false, false, redundant);
|
||||
}
|
||||
|
||||
euf::theory_var solver::mk_var(euf::enode* n) {
|
||||
|
@ -39,8 +41,11 @@ namespace array {
|
|||
|
||||
void solver::ensure_var(euf::enode* n) {
|
||||
theory_var v = n->get_th_var(get_id());
|
||||
if (v == euf::null_theory_var)
|
||||
if (v == euf::null_theory_var) {
|
||||
mk_var(n);
|
||||
if (is_lambda(n->get_expr()))
|
||||
internalize_lambda(n);
|
||||
}
|
||||
}
|
||||
|
||||
void solver::apply_sort_cnstr(euf::enode * n, sort * s) {
|
||||
|
@ -48,19 +53,30 @@ namespace array {
|
|||
}
|
||||
|
||||
void solver::internalize_store(euf::enode* n) {
|
||||
if (get_config().m_array_laziness == 0)
|
||||
add_parent(n->get_arg(0), n);
|
||||
add_parent_lambda(n->get_arg(0)->get_th_var(get_id()), n);
|
||||
push_axiom(store_axiom(n));
|
||||
add_lambda(n->get_th_var(get_id()), n);
|
||||
SASSERT(!get_var_data(n->get_th_var(get_id())).m_prop_upward);
|
||||
}
|
||||
|
||||
void solver::internalize_map(euf::enode* n) {
|
||||
for (auto* arg : euf::enode_args(n)) {
|
||||
add_parent_lambda(arg->get_th_var(get_id()), n);
|
||||
set_prop_upward(arg);
|
||||
}
|
||||
push_axiom(default_axiom(n));
|
||||
add_lambda(n->get_th_var(get_id()), n);
|
||||
SASSERT(!get_var_data(n->get_th_var(get_id())).m_prop_upward);
|
||||
}
|
||||
|
||||
void solver::internalize_lambda(euf::enode* n) {
|
||||
set_prop_upward(n);
|
||||
push_axiom(default_axiom(n));
|
||||
add_lambda(n->get_th_var(get_id()), n);
|
||||
}
|
||||
|
||||
void solver::internalize_select(euf::enode* n) {
|
||||
if (get_config().m_array_laziness == 0)
|
||||
add_parent(n->get_arg(0), n);
|
||||
}
|
||||
|
||||
void solver::internalize_const(euf::enode* n) {
|
||||
push_axiom(default_axiom(n));
|
||||
set_prop_upward(n);
|
||||
add_parent_select(n->get_arg(0)->get_th_var(get_id()), n);
|
||||
}
|
||||
|
||||
void solver::internalize_ext(euf::enode* n) {
|
||||
|
@ -68,24 +84,10 @@ namespace array {
|
|||
}
|
||||
|
||||
void solver::internalize_default(euf::enode* n) {
|
||||
add_parent(n->get_arg(0), n);
|
||||
add_parent_default(n->get_arg(0)->get_th_var(get_id()), n);
|
||||
set_prop_upward(n);
|
||||
}
|
||||
|
||||
void solver::internalize_map(euf::enode* n) {
|
||||
for (auto* arg : euf::enode_args(n)) {
|
||||
add_parent(arg, n);
|
||||
set_prop_upward(arg);
|
||||
}
|
||||
push_axiom(default_axiom(n));
|
||||
}
|
||||
|
||||
void solver::internalize_as_array(euf::enode* n) {
|
||||
// TBD: delay verdict whether model is undetermined
|
||||
ctx.unhandled_function(n->get_decl());
|
||||
push_axiom(default_axiom(n));
|
||||
}
|
||||
|
||||
bool solver::visited(expr* e) {
|
||||
euf::enode* n = expr2enode(e);
|
||||
return n && n->is_attached_to(get_id());
|
||||
|
@ -94,7 +96,8 @@ namespace array {
|
|||
bool solver::visit(expr* e) {
|
||||
if (!is_app(e) || to_app(e)->get_family_id() != get_id()) {
|
||||
ctx.internalize(e, m_is_redundant);
|
||||
ensure_var(expr2enode(e));
|
||||
euf::enode* n = expr2enode(e);
|
||||
ensure_var(n);
|
||||
return true;
|
||||
}
|
||||
m_stack.push_back(sat::eframe(e));
|
||||
|
@ -108,7 +111,7 @@ namespace array {
|
|||
if (!n)
|
||||
n = mk_enode(e, false);
|
||||
SASSERT(!n->is_attached_to(get_id()));
|
||||
theory_var v = mk_var(n);
|
||||
mk_var(n);
|
||||
for (auto* arg : euf::enode_args(n))
|
||||
ensure_var(arg);
|
||||
switch (a->get_decl_kind()) {
|
||||
|
@ -118,8 +121,9 @@ namespace array {
|
|||
case OP_SELECT:
|
||||
internalize_select(n);
|
||||
break;
|
||||
case OP_AS_ARRAY:
|
||||
case OP_CONST_ARRAY:
|
||||
internalize_const(n);
|
||||
internalize_lambda(n);
|
||||
break;
|
||||
case OP_ARRAY_EXT:
|
||||
internalize_ext(n);
|
||||
|
@ -130,9 +134,6 @@ namespace array {
|
|||
case OP_ARRAY_MAP:
|
||||
internalize_map(n);
|
||||
break;
|
||||
case OP_AS_ARRAY:
|
||||
internalize_as_array(n);
|
||||
break;
|
||||
case OP_SET_UNION:
|
||||
case OP_SET_INTERSECT:
|
||||
case OP_SET_DIFFERENCE:
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace array {
|
|||
mdl.register_decl(f, fi);
|
||||
|
||||
for (euf::enode* p : euf::enode_parents(n)) {
|
||||
if (!a.is_select(p->get_expr()))
|
||||
if (!a.is_select(p->get_expr()) || p->get_arg(0)->get_root() != n->get_root())
|
||||
continue;
|
||||
args.reset();
|
||||
for (unsigned i = 1; i < p->num_args(); ++i)
|
||||
|
@ -74,4 +74,73 @@ namespace array {
|
|||
values.set(n->get_root_id(), m.mk_app(get_id(), OP_AS_ARRAY, 1, &p));
|
||||
}
|
||||
|
||||
|
||||
bool solver::have_different_model_values(theory_var v1, theory_var v2) {
|
||||
euf::enode* else1 = nullptr, * else2 = nullptr;
|
||||
euf::enode* n1 = var2enode(v1), *n2 = var2enode(v2);
|
||||
euf::enode* r1 = n1->get_root(), * r2 = n2->get_root();
|
||||
expr* e1 = n1->get_expr();
|
||||
expr* e;
|
||||
if (!a.is_array(e1))
|
||||
return true;
|
||||
auto find_else = [&](theory_var v, euf::enode* r) {
|
||||
var_data& d = get_var_data(find(v));
|
||||
for (euf::enode* c : d.m_lambdas)
|
||||
if (a.is_const(c->get_expr(), e))
|
||||
return expr2enode(e)->get_root();
|
||||
for (euf::enode* p : euf::enode_parents(r))
|
||||
for (euf::enode* pe : euf::enode_class(p))
|
||||
if (a.is_default(pe->get_expr()))
|
||||
return pe->get_root();
|
||||
return (euf::enode*)nullptr;
|
||||
};
|
||||
else1 = find_else(v1, r1);
|
||||
else2 = find_else(v2, r2);
|
||||
if (else1 && else2 && else1->get_root() != else2->get_root() && has_large_domain(e1))
|
||||
return true;
|
||||
struct eq {
|
||||
solver& s;
|
||||
eq(solver& s) :s(s) {}
|
||||
bool operator()(euf::enode* n1, euf::enode* n2) const {
|
||||
SASSERT(s.a.is_select(n1->get_expr()));
|
||||
SASSERT(s.a.is_select(n2->get_expr()));
|
||||
for (unsigned i = n1->num_args(); i-- > 1; )
|
||||
if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
struct hash {
|
||||
solver& s;
|
||||
hash(solver& s) :s(s) {}
|
||||
unsigned operator()(euf::enode* n) const {
|
||||
SASSERT(s.a.is_select(n->get_expr()));
|
||||
unsigned h = 33;
|
||||
for (unsigned i = n->num_args(); i-- > 1; )
|
||||
h = hash_u_u(h, n->get_arg(i)->get_root_id());
|
||||
return h;
|
||||
}
|
||||
};
|
||||
eq eq_proc(*this);
|
||||
hash hash_proc(*this);
|
||||
hashtable<euf::enode*, hash, eq> table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, hash_proc, eq_proc);
|
||||
euf::enode* p2 = nullptr;
|
||||
auto maps_diff = [&](euf::enode* p, euf::enode* else_, euf::enode* r) {
|
||||
return table.find(p, p2) ? p2->get_root() != r : (else_ && else_ != r);
|
||||
};
|
||||
auto table_diff = [&](euf::enode* r1, euf::enode* r2, euf::enode* else1) {
|
||||
table.reset();
|
||||
for (euf::enode* p : euf::enode_parents(r1))
|
||||
if (a.is_select(p->get_expr()) && r1 == p->get_arg(0)->get_root())
|
||||
table.insert(p);
|
||||
for (euf::enode* p : euf::enode_parents(r2))
|
||||
if (a.is_select(p->get_expr()) && r2 == p->get_arg(0)->get_root())
|
||||
if (maps_diff(p, else1, p->get_root()))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
return table_diff(r1, r2, else1) || table_diff(r2, r1, else2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,54 @@ Author:
|
|||
|
||||
Nikolaj Bjorner (nbjorner) 2020-09-08
|
||||
|
||||
Notes:
|
||||
|
||||
A node n has attribtes:
|
||||
|
||||
parent_selects: { A[i] | A ~ n }
|
||||
parent_lambdas: { store(A,i,v) | A ~ n } u { map(f, .., A, ..) | A ~ n }
|
||||
lambdas: { const(v) | const(v) ~ n }
|
||||
u { map(f,..) | map(f,..) ~ n }
|
||||
u { store(A,i,v) | store(A,i,v) ~ n }
|
||||
u { as-array(f) | as-array(f) ~ n }
|
||||
|
||||
The attributes are used for propagation.
|
||||
When n1 is merged with n2, and n1 is the new root, the attributes from n2 are added to n1.
|
||||
The merge also looks for new redexes.
|
||||
|
||||
Let A[j] in parent_selects(n2) :
|
||||
|
||||
lambda in parent_lambdas(n1)
|
||||
-------------------------------
|
||||
lambda[j] = beta-reduce(lambda[j])
|
||||
|
||||
lambda in lambdas(n1)
|
||||
-------------------------------
|
||||
lambda[j] = beta-reduce(lambda[j])
|
||||
|
||||
Beta reduction rules are:
|
||||
beta-reduce(store(A,j,v)[i]) = if(i = j, v, A[j])
|
||||
beta-reduce(map(f,A,B)[i]) = f(A[i],B[i])
|
||||
beta-reduce(as-array(f)[i]) = f(i)
|
||||
beta-reduce(const(v)[i]) = v
|
||||
beta-reduce((lambda x M[x])[i]) = M[i]
|
||||
|
||||
For enforcing
|
||||
store(A,j,v)[i] = beta-reduce(store(A,j,v)[i])
|
||||
|
||||
only the following axiom is instantiated:
|
||||
- i = j or store(A,j,v)[i] = A[i]
|
||||
|
||||
The other required axiom, store(A,j,v)[j] = v
|
||||
is added eagerly whenever store(A,j,v) is created.
|
||||
|
||||
Current setup: to enforce extensionality on lambdas,
|
||||
also currently, as a base-line it is eager:
|
||||
|
||||
A ~ B, A = lambda x. M[x]
|
||||
-------------------------------
|
||||
A = B => forall i . M[i] = B[i]
|
||||
|
||||
--*/
|
||||
|
||||
#include "ast/ast_ll_pp.h"
|
||||
|
@ -21,7 +69,7 @@ Author:
|
|||
|
||||
namespace array {
|
||||
|
||||
solver::solver(euf::solver& ctx, theory_id id):
|
||||
solver::solver(euf::solver& ctx, theory_id id) :
|
||||
th_euf_solver(ctx, id),
|
||||
a(m),
|
||||
m_sort2epsilon(m),
|
||||
|
@ -36,20 +84,16 @@ namespace array {
|
|||
}
|
||||
|
||||
sat::check_result solver::check() {
|
||||
flet<bool> _is_redundant(m_is_redundant, true);
|
||||
// flet<bool> _is_redundant(m_is_redundant, true);
|
||||
bool turn[2] = { false, false };
|
||||
turn[s().rand()(2)] = true;
|
||||
for (unsigned idx = 0; idx < 2; ++idx) {
|
||||
if (turn[idx]) {
|
||||
if (add_delayed_axioms())
|
||||
return sat::CR_CONTINUE;
|
||||
}
|
||||
else {
|
||||
if (add_interface_equalities())
|
||||
return sat::CR_CONTINUE;
|
||||
}
|
||||
if (turn[idx] && add_delayed_axioms())
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
else if (!turn[idx] && add_interface_equalities())
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
}
|
||||
return sat::CR_DONE;
|
||||
return sat::check_result::CR_DONE;
|
||||
}
|
||||
|
||||
void solver::push() {
|
||||
|
@ -57,38 +101,52 @@ namespace array {
|
|||
}
|
||||
|
||||
void solver::pop(unsigned n) {
|
||||
n = lazy_pop(n);
|
||||
n = lazy_pop(n);
|
||||
if (n == 0)
|
||||
return;
|
||||
m_var_data.resize(get_num_vars());
|
||||
}
|
||||
|
||||
std::ostream& solver::display(std::ostream& out) const {
|
||||
std::ostream& solver::display(std::ostream& out) const {
|
||||
for (unsigned i = 0; i < get_num_vars(); ++i) {
|
||||
auto& d = get_var_data(i);
|
||||
out << var2enode(i)->get_expr_id() << " " << mk_bounded_pp(var2expr(i), m, 2) << "\n";
|
||||
display_info(out, "parent beta", d.m_parent_lambdas);
|
||||
display_info(out, "parent select", d.m_parent_selects);
|
||||
display_info(out, "beta ", d.m_lambdas);
|
||||
}
|
||||
return out;
|
||||
return out;
|
||||
}
|
||||
std::ostream& solver::display_info(std::ostream& out, char const* id, euf::enode_vector const& v) const {
|
||||
if (v.empty())
|
||||
return out;
|
||||
out << id << ": ";
|
||||
for (euf::enode* p : v)
|
||||
out << mk_bounded_pp(p->get_expr(), m, 2) << " ";
|
||||
out << "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const { return out; }
|
||||
std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const { return out; }
|
||||
|
||||
void solver::collect_statistics(statistics& st) const {
|
||||
st.update("array store", m_stats.m_num_store_axiom);
|
||||
st.update("array sel/store", m_stats.m_num_select_store_axiom);
|
||||
st.update("array sel/const", m_stats.m_num_select_const_axiom);
|
||||
st.update("array sel/map", m_stats.m_num_select_map_axiom);
|
||||
st.update("array store", m_stats.m_num_store_axiom);
|
||||
st.update("array sel/store", m_stats.m_num_select_store_axiom);
|
||||
st.update("array sel/const", m_stats.m_num_select_const_axiom);
|
||||
st.update("array sel/map", m_stats.m_num_select_map_axiom);
|
||||
st.update("array sel/as array", m_stats.m_num_select_as_array_axiom);
|
||||
st.update("array def/map", m_stats.m_num_default_map_axiom);
|
||||
st.update("array def/const", m_stats.m_num_default_const_axiom);
|
||||
st.update("array def/store", m_stats.m_num_default_store_axiom);
|
||||
st.update("array ext ax", m_stats.m_num_extensionality_axiom);
|
||||
st.update("array cong ax", m_stats.m_num_congruence_axiom);
|
||||
st.update("array exp ax2", m_stats.m_num_select_store_axiom_delayed);
|
||||
st.update("array splits", m_stats.m_num_eq_splits);
|
||||
st.update("array sel/lambda", m_stats.m_num_select_lambda_axiom);
|
||||
st.update("array def/map", m_stats.m_num_default_map_axiom);
|
||||
st.update("array def/const", m_stats.m_num_default_const_axiom);
|
||||
st.update("array def/store", m_stats.m_num_default_store_axiom);
|
||||
st.update("array ext ax", m_stats.m_num_extensionality_axiom);
|
||||
st.update("array cong ax", m_stats.m_num_congruence_axiom);
|
||||
st.update("array exp ax2", m_stats.m_num_select_store_axiom_delayed);
|
||||
st.update("array splits", m_stats.m_num_eq_splits);
|
||||
}
|
||||
|
||||
euf::th_solver* solver::fresh(sat::solver* s, euf::solver& ctx) {
|
||||
euf::th_solver* solver::fresh(sat::solver* s, euf::solver& ctx) {
|
||||
auto* result = alloc(solver, ctx, get_id());
|
||||
ast_translation tr(m, ctx.get_manager());
|
||||
for (unsigned i = 0; i < get_num_vars(); ++i) {
|
||||
|
@ -97,21 +155,21 @@ namespace array {
|
|||
euf::enode* n = ctx.get_enode(e2);
|
||||
result->mk_var(n);
|
||||
}
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void solver::new_eq_eh(euf::th_eq const& eq) {
|
||||
m_find.merge(eq.m_v1, eq.m_v2);
|
||||
}
|
||||
|
||||
bool solver::unit_propagate() {
|
||||
bool solver::unit_propagate() {
|
||||
if (m_qhead == m_axiom_trail.size())
|
||||
return false;
|
||||
bool prop = false;
|
||||
ctx.push(value_trail<euf::solver, unsigned>(m_qhead));
|
||||
for (; m_qhead < m_axiom_trail.size() && !s().inconsistent(); ++m_qhead)
|
||||
for (; m_qhead < m_axiom_trail.size() && !s().inconsistent(); ++m_qhead)
|
||||
if (assert_axiom(m_qhead))
|
||||
prop = true;
|
||||
prop = true;
|
||||
return prop;
|
||||
}
|
||||
|
||||
|
@ -121,76 +179,97 @@ namespace array {
|
|||
SASSERT(n1->get_root() == n2->get_root());
|
||||
SASSERT(n1->is_root() || n2->is_root());
|
||||
SASSERT(v1 == find(v1));
|
||||
|
||||
expr* e1 = n1->get_expr();
|
||||
expr* e2 = n2->get_expr();
|
||||
auto& d1 = get_var_data(v1);
|
||||
auto& d2 = get_var_data(v2);
|
||||
if (d2.m_prop_upward && !d1.m_prop_upward)
|
||||
if (d2.m_prop_upward && !d1.m_prop_upward)
|
||||
set_prop_upward(v1);
|
||||
if (a.is_array(e1))
|
||||
for (euf::enode* parent : d2.m_parents) {
|
||||
add_parent(v1, parent);
|
||||
if (a.is_store(parent->get_expr()))
|
||||
add_store(v1, parent);
|
||||
}
|
||||
for (euf::enode* lambda : d2.m_lambdas)
|
||||
add_lambda(v1, lambda);
|
||||
for (euf::enode* lambda : d2.m_parent_lambdas)
|
||||
add_parent_lambda(v1, lambda);
|
||||
for (euf::enode* select : d2.m_parent_selects)
|
||||
add_parent_select(v1, select);
|
||||
if (is_lambda(e1) || is_lambda(e2))
|
||||
push_axiom(congruence_axiom(n1, n2));
|
||||
}
|
||||
|
||||
void solver::unmerge_eh(theory_var v1, theory_var v2) {
|
||||
auto& p1 = get_var_data(v1).m_parents;
|
||||
auto& p2 = get_var_data(v2).m_parents;
|
||||
p1.shrink(p1.size() - p2.size());
|
||||
void solver::tracked_push(euf::enode_vector& v, euf::enode* n) {
|
||||
v.push_back(n);
|
||||
ctx.push(push_back_trail<euf::solver, euf::enode*, false>(v));
|
||||
}
|
||||
|
||||
void solver::add_store(theory_var v, euf::enode* store) {
|
||||
SASSERT(a.is_store(store->get_expr()));
|
||||
auto& d = get_var_data(v);
|
||||
unsigned lambda_equiv_class_size = get_lambda_equiv_size(d);
|
||||
if (get_config().m_array_always_prop_upward || lambda_equiv_class_size >= 1)
|
||||
set_prop_upward(d);
|
||||
for (euf::enode* n : d.m_parents)
|
||||
if (a.is_select(n->get_expr()))
|
||||
push_axiom(select_axiom(n, store));
|
||||
if (get_config().m_array_always_prop_upward || lambda_equiv_class_size >= 1)
|
||||
set_prop_upward(store);
|
||||
}
|
||||
void solver::add_parent_select(theory_var v_child, euf::enode* select) {
|
||||
SASSERT(a.is_select(select->get_expr()));
|
||||
SASSERT(m.get_sort(select->get_arg(0)->get_expr()) == m.get_sort(var2expr(v_child)));
|
||||
|
||||
void solver::add_parent(theory_var v_child, euf::enode* parent) {
|
||||
SASSERT(parent->is_root());
|
||||
get_var_data(v_child).m_parents.push_back(parent);
|
||||
v_child = find(v_child);
|
||||
tracked_push(get_var_data(v_child).m_parent_selects, select);
|
||||
euf::enode* child = var2enode(v_child);
|
||||
euf::enode* r = child->get_root();
|
||||
expr* p = parent->get_expr();
|
||||
expr* c = child->get_expr();
|
||||
if (a.is_select(p) && parent->get_arg(0)->get_root() == r) {
|
||||
if (a.is_const(c) || a.is_as_array(c) || a.is_store(c) || is_lambda(c))
|
||||
push_axiom(select_axiom(parent, child));
|
||||
#if 0
|
||||
if (!get_config().m_array_delay_exp_axiom && d.m_prop_upward) {
|
||||
auto& d = get_var_data(v_child);
|
||||
for (euf::enode* p2 : d.m_parents)
|
||||
if (a.is_store(p2->get_expr()))
|
||||
push_axiom(select_axiom(parent, p2));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (a.mk_default(p)) {
|
||||
if (a.is_const(c) || a.is_store(c) || a.is_map(c) || a.is_as_array(c))
|
||||
push_axiom(default_axiom(child));
|
||||
if (can_beta_reduce(child))
|
||||
push_axiom(select_axiom(select, child));
|
||||
}
|
||||
|
||||
void solver::add_lambda(theory_var v, euf::enode* lambda) {
|
||||
SASSERT(can_beta_reduce(lambda));
|
||||
auto& d = get_var_data(find(v));
|
||||
if (should_set_prop_upward(d))
|
||||
set_prop_upward(d);
|
||||
tracked_push(d.m_lambdas, lambda);
|
||||
if (should_set_prop_upward(d)) {
|
||||
set_prop_upward(lambda);
|
||||
propagate_select_axioms(d, lambda);
|
||||
}
|
||||
}
|
||||
|
||||
void solver::add_parent_lambda(theory_var v_child, euf::enode* lambda) {
|
||||
SASSERT(can_beta_reduce(lambda));
|
||||
auto& d = get_var_data(find(v_child));
|
||||
tracked_push(d.m_parent_lambdas, lambda);
|
||||
if (should_set_prop_upward(d))
|
||||
propagate_select_axioms(d, lambda);
|
||||
}
|
||||
|
||||
void solver::add_parent_default(theory_var v, euf::enode* def) {
|
||||
SASSERT(a.is_default(def->get_expr()));
|
||||
auto& d = get_var_data(find(v));
|
||||
for (euf::enode* lambda : d.m_lambdas)
|
||||
push_axiom(default_axiom(lambda));
|
||||
if (should_prop_upward(d))
|
||||
propagate_parent_default(v);
|
||||
}
|
||||
|
||||
void solver::propagate_select_axioms(var_data const& d, euf::enode* lambda) {
|
||||
for (euf::enode* select : d.m_parent_selects)
|
||||
push_axiom(select_axiom(select, lambda));
|
||||
}
|
||||
|
||||
void solver::propagate_parent_default(theory_var v) {
|
||||
auto& d = get_var_data(find(v));
|
||||
for (euf::enode* lambda : d.m_parent_lambdas)
|
||||
push_axiom(default_axiom(lambda));
|
||||
}
|
||||
|
||||
void solver::propagate_parent_select_axioms(theory_var v) {
|
||||
v = find(v);
|
||||
expr* e = var2expr(v);
|
||||
if (!a.is_array(e))
|
||||
return;
|
||||
auto& d = get_var_data(v);
|
||||
for (euf::enode* lambda : d.m_parent_lambdas)
|
||||
propagate_select_axioms(d, lambda);
|
||||
}
|
||||
|
||||
void solver::set_prop_upward(theory_var v) {
|
||||
auto& d = get_var_data(find(v));
|
||||
if (!d.m_prop_upward) {
|
||||
ctx.push(reset_flag_trail<euf::solver>(d.m_prop_upward));
|
||||
d.m_prop_upward = true;
|
||||
if (!get_config().m_array_delay_exp_axiom)
|
||||
push_parent_select_store_axioms(v);
|
||||
set_prop_upward(d);
|
||||
}
|
||||
if (d.m_prop_upward)
|
||||
return;
|
||||
ctx.push(reset_flag_trail<euf::solver>(d.m_prop_upward));
|
||||
d.m_prop_upward = true;
|
||||
if (should_prop_upward(d))
|
||||
propagate_parent_select_axioms(v);
|
||||
set_prop_upward(d);
|
||||
}
|
||||
|
||||
void solver::set_prop_upward(euf::enode* n) {
|
||||
|
@ -199,22 +278,28 @@ namespace array {
|
|||
}
|
||||
|
||||
void solver::set_prop_upward(var_data& d) {
|
||||
for (auto* p : d.m_parents)
|
||||
for (auto* p : d.m_lambdas)
|
||||
set_prop_upward(p);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return the size of the equivalence class for array terms
|
||||
\brief Return the size of the equivalence class for array terms
|
||||
that can be expressed as \lambda i : Index . [.. (select a i) ..]
|
||||
*/
|
||||
unsigned solver::get_lambda_equiv_size(var_data const& d) {
|
||||
unsigned sz = 0;
|
||||
for (auto* p : d.m_parents)
|
||||
if (a.is_store(p->get_expr()))
|
||||
++sz;
|
||||
return sz;
|
||||
unsigned solver::get_lambda_equiv_size(var_data const& d) const {
|
||||
return d.m_parent_selects.size() + 2 * d.m_lambdas.size();
|
||||
}
|
||||
|
||||
bool solver::should_set_prop_upward(var_data const& d) const {
|
||||
return get_config().m_array_always_prop_upward || get_lambda_equiv_size(d) >= 1;
|
||||
}
|
||||
|
||||
bool solver::should_prop_upward(var_data const& d) const {
|
||||
return !get_config().m_array_delay_exp_axiom && d.m_prop_upward;
|
||||
}
|
||||
|
||||
bool solver::can_beta_reduce(euf::enode* n) const {
|
||||
expr* c = n->get_expr();
|
||||
return a.is_const(c) || a.is_as_array(c) || a.is_store(c) || is_lambda(c);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace array {
|
|||
unsigned m_num_select_const_axiom, m_num_select_store_axiom_delayed;
|
||||
unsigned m_num_default_store_axiom, m_num_default_map_axiom;
|
||||
unsigned m_num_default_const_axiom, m_num_default_as_array_axiom;
|
||||
unsigned m_num_select_lambda_axiom;
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
stats() { reset(); }
|
||||
};
|
||||
|
@ -49,10 +50,10 @@ namespace array {
|
|||
// void log_drat(array_justification const& c);
|
||||
|
||||
struct var_data {
|
||||
bool m_prop_upward{ false };
|
||||
bool m_is_array{ false };
|
||||
bool m_is_select{ false };
|
||||
ptr_vector<euf::enode> m_parents;
|
||||
bool m_prop_upward{ false };
|
||||
euf::enode_vector m_lambdas; // equivalent nodes that have beta reduction properties
|
||||
euf::enode_vector m_parent_lambdas; // parents that have beta reduction properties
|
||||
euf::enode_vector m_parent_selects; // parents that use array in select position
|
||||
var_data() {}
|
||||
};
|
||||
|
||||
|
@ -76,11 +77,10 @@ namespace array {
|
|||
void ensure_var(euf::enode* n);
|
||||
void internalize_store(euf::enode* n);
|
||||
void internalize_select(euf::enode* n);
|
||||
void internalize_const(euf::enode* n);
|
||||
void internalize_lambda(euf::enode* n);
|
||||
void internalize_ext(euf::enode* n);
|
||||
void internalize_default(euf::enode* n);
|
||||
void internalize_map(euf::enode* n);
|
||||
void internalize_as_array(euf::enode* n);
|
||||
|
||||
// axioms
|
||||
struct axiom_record {
|
||||
|
@ -144,33 +144,44 @@ namespace array {
|
|||
bool assert_default_map_axiom(app* map);
|
||||
bool assert_default_const_axiom(app* cnst);
|
||||
bool assert_default_store_axiom(app* store);
|
||||
bool assert_default_as_array_axiom(app* as_array);
|
||||
bool assert_congruent_axiom(expr* e1, expr* e2);
|
||||
bool add_delayed_axioms();
|
||||
|
||||
bool has_unitary_domain(app* array_term);
|
||||
bool has_large_domain(app* array_term);
|
||||
bool has_large_domain(expr* array_term);
|
||||
std::pair<app*, func_decl*> mk_epsilon(sort* s);
|
||||
void collect_shared_vars(sbuffer<theory_var>& roots);
|
||||
bool add_interface_equalities();
|
||||
bool is_select_arg(euf::enode* r);
|
||||
|
||||
// solving
|
||||
void add_parent(theory_var v_child, euf::enode* parent);
|
||||
void add_parent(euf::enode* child, euf::enode* parent) { add_parent(child->get_th_var(get_id()), parent); }
|
||||
void add_store(theory_var v, euf::enode* store);
|
||||
// solving
|
||||
void add_parent_select(theory_var v_child, euf::enode* select);
|
||||
void add_parent_default(theory_var v_child, euf::enode* def);
|
||||
void add_lambda(theory_var v, euf::enode* lambda);
|
||||
void add_parent_lambda(theory_var v_child, euf::enode* lambda);
|
||||
|
||||
void propagate_select_axioms(var_data const& d, euf::enode* a);
|
||||
void propagate_parent_select_axioms(theory_var v);
|
||||
void propagate_parent_default(theory_var v);
|
||||
|
||||
void set_prop_upward(theory_var v);
|
||||
void set_prop_upward(var_data& d);
|
||||
void set_prop_upward(euf::enode* n);
|
||||
void push_parent_select_store_axioms(theory_var v);
|
||||
unsigned get_lambda_equiv_size(var_data const& d);
|
||||
unsigned get_lambda_equiv_size(var_data const& d) const;
|
||||
bool should_set_prop_upward(var_data const& d) const;
|
||||
bool should_prop_upward(var_data const& d) const;
|
||||
bool can_beta_reduce(euf::enode* n) const;
|
||||
|
||||
var_data& get_var_data(euf::enode* n) { return get_var_data(n->get_th_var(get_id())); }
|
||||
var_data& get_var_data(theory_var v) { return *m_var_data[v]; }
|
||||
var_data const& get_var_data(theory_var v) const { return *m_var_data[v]; }
|
||||
|
||||
// models
|
||||
bool have_different_model_values(theory_var v1, theory_var v2);
|
||||
|
||||
// invariants
|
||||
// diagnostics
|
||||
|
||||
std::ostream& display_info(std::ostream& out, char const* id, euf::enode_vector const& v) const;
|
||||
public:
|
||||
solver(euf::solver& ctx, theory_id id);
|
||||
~solver() override {}
|
||||
|
@ -196,8 +207,10 @@ namespace array {
|
|||
euf::theory_var mk_var(euf::enode* n) override;
|
||||
void apply_sort_cnstr(euf::enode* n, sort* s) override;
|
||||
|
||||
void tracked_push(euf::enode_vector& v, euf::enode* n);
|
||||
|
||||
void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2);
|
||||
void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {}
|
||||
void unmerge_eh(theory_var v1, theory_var v2);
|
||||
void unmerge_eh(theory_var v1, theory_var v2) {}
|
||||
};
|
||||
}
|
||||
|
|
290
src/sat/smt/ba_card.cpp
Normal file
290
src/sat/smt/ba_card.cpp
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
ba_card.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Interface for Cardinality constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2017-01-30
|
||||
|
||||
--*/
|
||||
|
||||
#include "sat/smt/ba_card.h"
|
||||
#include "sat/smt/ba_solver.h"
|
||||
#include "sat/sat_simplifier.h"
|
||||
|
||||
namespace ba {
|
||||
|
||||
// -----------------------
|
||||
// pb_base
|
||||
|
||||
bool pb_base::well_formed() const {
|
||||
uint_set vars;
|
||||
if (lit() != sat::null_literal) vars.insert(lit().var());
|
||||
for (unsigned i = 0; i < size(); ++i) {
|
||||
bool_var v = get_lit(i).var();
|
||||
if (vars.contains(v)) return false;
|
||||
if (get_coeff(i) > k()) return false;
|
||||
vars.insert(v);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------
|
||||
// card
|
||||
|
||||
card::card(unsigned id, literal lit, literal_vector const& lits, unsigned k) :
|
||||
pb_base(tag_t::card_t, id, lit, lits.size(), get_obj_size(lits.size()), k) {
|
||||
for (unsigned i = 0; i < size(); ++i) {
|
||||
m_lits[i] = lits[i];
|
||||
}
|
||||
}
|
||||
|
||||
void card::negate() {
|
||||
m_lit.neg();
|
||||
for (unsigned i = 0; i < m_size; ++i) {
|
||||
m_lits[i].neg();
|
||||
}
|
||||
m_k = m_size - m_k + 1;
|
||||
SASSERT(m_size >= m_k && m_k > 0);
|
||||
}
|
||||
|
||||
bool card::is_watching(literal l) const {
|
||||
unsigned sz = std::min(k() + 1, size());
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if ((*this)[i] == l) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double card::get_reward(ba::solver_interface const& s, sat::literal_occs_fun& literal_occs) const {
|
||||
unsigned k = this->k(), slack = 0;
|
||||
bool do_add = s.get_config().m_lookahead_reward == sat::heule_schur_reward;
|
||||
double to_add = do_add ? 0 : 1;
|
||||
for (literal l : *this) {
|
||||
switch (s.value(l)) {
|
||||
case l_true: --k; if (k == 0) return 0;
|
||||
case l_undef:
|
||||
if (do_add) to_add += literal_occs(l);
|
||||
++slack; break;
|
||||
case l_false: break;
|
||||
}
|
||||
}
|
||||
if (k >= slack) return 1;
|
||||
return pow(0.5, slack - k + 1) * to_add;
|
||||
}
|
||||
|
||||
std::ostream& card::display(std::ostream& out) const {
|
||||
for (literal l : *this)
|
||||
out << l << " ";
|
||||
return out << " >= " << k();
|
||||
}
|
||||
|
||||
void constraint::display_lit(std::ostream& out, solver_interface const& s, literal lit, unsigned sz, bool values) const {
|
||||
if (lit != sat::null_literal) {
|
||||
if (values) {
|
||||
out << lit << "[" << sz << "]";
|
||||
out << "@(" << s.value(lit);
|
||||
if (s.value(lit) != l_undef) {
|
||||
out << ":" << s.lvl(lit);
|
||||
}
|
||||
out << "): ";
|
||||
}
|
||||
else {
|
||||
out << lit << " == ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& card::display(std::ostream& out, solver_interface const& s, bool values) const {
|
||||
auto const& c = *this;
|
||||
display_lit(out, s, c.lit(), c.size(), values);
|
||||
for (unsigned i = 0; i < c.size(); ++i) {
|
||||
literal l = c[i];
|
||||
out << l;
|
||||
if (values) {
|
||||
out << "@(" << s.value(l);
|
||||
if (s.value(l) != l_undef) {
|
||||
out << ":" << s.lvl(l);
|
||||
}
|
||||
out << ") ";
|
||||
}
|
||||
else {
|
||||
out << " ";
|
||||
}
|
||||
}
|
||||
return out << ">= " << c.k() << "\n";
|
||||
}
|
||||
|
||||
void card::clear_watch(solver_interface& s) {
|
||||
if (is_clear()) return;
|
||||
reset_watch();
|
||||
unsigned sz = std::min(k() + 1, size());
|
||||
for (unsigned i = 0; i < sz; ++i)
|
||||
unwatch_literal(s, (*this)[i]);
|
||||
}
|
||||
|
||||
bool card::init_watch(solver_interface& s) {
|
||||
auto& c = *this;
|
||||
literal root = c.lit();
|
||||
if (root != sat::null_literal && s.value(root) == l_false) {
|
||||
clear_watch(s);
|
||||
negate();
|
||||
root.neg();
|
||||
}
|
||||
if (root != sat::null_literal) {
|
||||
if (!is_watched(s, root)) watch_literal(s, root);
|
||||
if (!is_pure() && !is_watched(s, ~root)) watch_literal(s, ~root);
|
||||
}
|
||||
TRACE("ba", display(tout << "init watch: ", s, true););
|
||||
SASSERT(root == sat::null_literal || s.value(root) == l_true);
|
||||
unsigned j = 0, sz = c.size(), bound = c.k();
|
||||
// put the non-false literals into the head.
|
||||
|
||||
if (bound == sz) {
|
||||
for (literal l : c) s.assign(c, l);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (s.value(c[i]) != l_false) {
|
||||
if (j != i) {
|
||||
if (c.is_watched() && j <= bound && i > bound) {
|
||||
c.unwatch_literal(s, c[j]);
|
||||
c.watch_literal(s, c[i]);
|
||||
}
|
||||
c.swap(i, j);
|
||||
}
|
||||
++j;
|
||||
}
|
||||
}
|
||||
DEBUG_CODE(
|
||||
bool is_false = false;
|
||||
for (literal l : c) {
|
||||
SASSERT(!is_false || s.value(l) == l_false);
|
||||
is_false = s.value(l) == l_false;
|
||||
});
|
||||
|
||||
// j is the number of non-false, sz - j the number of false.
|
||||
|
||||
if (j < bound) {
|
||||
if (is_watched()) clear_watch(s);
|
||||
SASSERT(0 < bound && bound < sz);
|
||||
literal alit = c[j];
|
||||
|
||||
//
|
||||
// we need the assignment level of the asserting literal to be maximal.
|
||||
// such that conflict resolution can use the asserting literal as a starting
|
||||
// point.
|
||||
//
|
||||
|
||||
for (unsigned i = bound; i < sz; ++i) {
|
||||
if (s.lvl(alit) < s.lvl(c[i])) {
|
||||
c.swap(i, j);
|
||||
alit = c[j];
|
||||
}
|
||||
}
|
||||
s.set_conflict(c, alit);
|
||||
return false;
|
||||
}
|
||||
else if (j == bound) {
|
||||
for (unsigned i = 0; i < bound; ++i) {
|
||||
s.assign(c, c[i]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (c.is_watched()) return true;
|
||||
clear_watch(s);
|
||||
for (unsigned i = 0; i <= bound; ++i) {
|
||||
c.watch_literal(s, c[i]);
|
||||
}
|
||||
c.set_watch();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
card& constraint::to_card() {
|
||||
SASSERT(is_card());
|
||||
return static_cast<card&>(*this);
|
||||
}
|
||||
|
||||
card const& constraint::to_card() const {
|
||||
SASSERT(is_card());
|
||||
return static_cast<card const&>(*this);
|
||||
}
|
||||
|
||||
|
||||
bool card::is_extended_binary(literal_vector& r) const {
|
||||
auto const& ca = *this;
|
||||
if (ca.size() == ca.k() + 1 && ca.lit() == sat::null_literal) {
|
||||
r.reset();
|
||||
for (literal l : ca) r.push_back(l);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool card::validate_unit_propagation(solver_interface const& s, literal alit) const {
|
||||
(void) alit;
|
||||
if (lit() != sat::null_literal && s.value(lit()) != l_true)
|
||||
return false;
|
||||
for (unsigned i = k(); i < size(); ++i)
|
||||
if (s.value((*this)[i]) != l_false)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
lbool card::eval(solver_interface const& s) const {
|
||||
unsigned trues = 0, undefs = 0;
|
||||
for (literal l : *this) {
|
||||
switch (s.value(l)) {
|
||||
case l_true: trues++; break;
|
||||
case l_undef: undefs++; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (trues + undefs < k()) return l_false;
|
||||
if (trues >= k()) return l_true;
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
lbool card::eval(sat::model const& m) const {
|
||||
unsigned trues = 0, undefs = 0;
|
||||
for (literal l : *this) {
|
||||
switch (ba::value(m, l)) {
|
||||
case l_true: trues++; break;
|
||||
case l_undef: undefs++; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (trues + undefs < k()) return l_false;
|
||||
if (trues >= k()) return l_true;
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
void card::init_use_list(sat::ext_use_list& ul) const {
|
||||
auto idx = cindex();
|
||||
for (auto l : *this)
|
||||
ul.insert(l, idx);
|
||||
}
|
||||
|
||||
bool card::is_blocked(sat::simplifier& sim, literal lit) const {
|
||||
unsigned weight = 0;
|
||||
for (literal l2 : *this)
|
||||
if (sim.is_marked(~l2)) ++weight;
|
||||
|
||||
return weight >= k();
|
||||
}
|
||||
|
||||
}
|
70
src/sat/smt/ba_card.h
Normal file
70
src/sat/smt/ba_card.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
ba_card.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Interface for Cardinality constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2017-01-30
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sat/sat_types.h"
|
||||
#include "sat/smt/ba_constraint.h"
|
||||
|
||||
|
||||
namespace ba {
|
||||
|
||||
// base class for pb and cardinality constraints
|
||||
class pb_base : public constraint {
|
||||
protected:
|
||||
unsigned m_k;
|
||||
public:
|
||||
pb_base(ba::tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k) :
|
||||
constraint(t, id, l, sz, osz), m_k(k) {
|
||||
VERIFY(k < 4000000000);
|
||||
}
|
||||
virtual void set_k(unsigned k) { VERIFY(k < 4000000000); m_k = k; }
|
||||
virtual unsigned get_coeff(unsigned i) const { UNREACHABLE(); return 0; }
|
||||
unsigned k() const { return m_k; }
|
||||
bool well_formed() const override;
|
||||
};
|
||||
|
||||
class card : public pb_base {
|
||||
literal m_lits[0];
|
||||
public:
|
||||
static size_t get_obj_size(unsigned num_lits) { return sat::constraint_base::obj_size(sizeof(card) + num_lits * sizeof(literal)); }
|
||||
card(unsigned id, literal lit, literal_vector const& lits, unsigned k);
|
||||
literal operator[](unsigned i) const { return m_lits[i]; }
|
||||
literal& operator[](unsigned i) { return m_lits[i]; }
|
||||
literal const* begin() const { return m_lits; }
|
||||
literal const* end() const { return static_cast<literal const*>(m_lits) + m_size; }
|
||||
void negate() override;
|
||||
void swap(unsigned i, unsigned j) override { std::swap(m_lits[i], m_lits[j]); }
|
||||
literal_vector literals() const override { return literal_vector(m_size, m_lits); }
|
||||
bool is_watching(literal l) const override;
|
||||
literal get_lit(unsigned i) const override { return m_lits[i]; }
|
||||
void set_lit(unsigned i, literal l) override { m_lits[i] = l; }
|
||||
unsigned get_coeff(unsigned i) const override { return 1; }
|
||||
double get_reward(ba::solver_interface const& s, sat::literal_occs_fun& occs) const override;
|
||||
std::ostream& display(std::ostream& out) const override;
|
||||
std::ostream& display(std::ostream& out, solver_interface const& s, bool values) const override;
|
||||
void clear_watch(solver_interface& s) override;
|
||||
bool init_watch(solver_interface& s) override;
|
||||
bool is_extended_binary(literal_vector& r) const override;
|
||||
bool validate_unit_propagation(solver_interface const& s, literal alit) const override;
|
||||
lbool eval(sat::model const& m) const override;
|
||||
lbool eval(solver_interface const& s) const override;
|
||||
void init_use_list(sat::ext_use_list& ul) const override;
|
||||
bool is_blocked(sat::simplifier& s, literal lit) const override;
|
||||
|
||||
};
|
||||
}
|
58
src/sat/smt/ba_constraint.cpp
Normal file
58
src/sat/smt/ba_constraint.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
ba_constraint.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Interface for constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2017-01-30
|
||||
|
||||
--*/
|
||||
|
||||
#include "sat/smt/ba_constraint.h"
|
||||
|
||||
namespace ba {
|
||||
|
||||
unsigned constraint::fold_max_var(unsigned w) const {
|
||||
if (lit() != sat::null_literal) w = std::max(w, lit().var());
|
||||
for (unsigned i = 0; i < size(); ++i) w = std::max(w, get_lit(i).var());
|
||||
return w;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, constraint const& cnstr) {
|
||||
if (cnstr.lit() != sat::null_literal) out << cnstr.lit() << " == ";
|
||||
return cnstr.display(out);
|
||||
}
|
||||
|
||||
bool constraint::is_watched(solver_interface const& s, literal lit) const {
|
||||
return s.get_wlist(~lit).contains(sat::watched(cindex()));
|
||||
}
|
||||
|
||||
void constraint::unwatch_literal(solver_interface& s, literal lit) {
|
||||
sat::watched w(cindex());
|
||||
s.get_wlist(~lit).erase(w);
|
||||
SASSERT(!is_watched(s, lit));
|
||||
}
|
||||
|
||||
void constraint::watch_literal(solver_interface& s, literal lit) {
|
||||
if (is_pure() && lit == ~this->lit()) return;
|
||||
SASSERT(!is_watched(s, lit));
|
||||
sat::watched w(cindex());
|
||||
s.get_wlist(~lit).push_back(w);
|
||||
}
|
||||
|
||||
void constraint::nullify_tracking_literal(solver_interface& s) {
|
||||
if (lit() != sat::null_literal) {
|
||||
unwatch_literal(s, lit());
|
||||
unwatch_literal(s, ~lit());
|
||||
nullify_literal();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
143
src/sat/smt/ba_constraint.h
Normal file
143
src/sat/smt/ba_constraint.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
ba_constraint.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Interface for Boolean constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2017-01-30
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
#include "sat/smt/ba_solver_interface.h"
|
||||
|
||||
namespace ba {
|
||||
|
||||
enum class tag_t {
|
||||
card_t,
|
||||
pb_t,
|
||||
xr_t
|
||||
};
|
||||
|
||||
class card;
|
||||
class pb;
|
||||
class xr;
|
||||
class pb_base;
|
||||
|
||||
inline lbool value(sat::model const& m, literal l) { return l.sign() ? ~m[l.var()] : m[l.var()]; }
|
||||
|
||||
class constraint {
|
||||
protected:
|
||||
tag_t m_tag;
|
||||
bool m_removed;
|
||||
literal m_lit;
|
||||
literal m_watch;
|
||||
unsigned m_glue;
|
||||
unsigned m_psm;
|
||||
unsigned m_size;
|
||||
size_t m_obj_size;
|
||||
bool m_learned;
|
||||
unsigned m_id;
|
||||
bool m_pure; // is the constraint pure (only positive occurrences)
|
||||
|
||||
void display_lit(std::ostream& out, solver_interface const& s, literal lit, unsigned sz, bool values) const;
|
||||
public:
|
||||
constraint(tag_t t, unsigned id, literal l, unsigned sz, size_t osz):
|
||||
m_tag(t), m_removed(false), m_lit(l), m_watch(sat::null_literal), m_glue(0), m_psm(0), m_size(sz), m_obj_size(osz), m_learned(false), m_id(id), m_pure(false) {
|
||||
}
|
||||
sat::ext_constraint_idx cindex() const { return sat::constraint_base::mem2base(this); }
|
||||
void deallocate(small_object_allocator& a) { a.deallocate(obj_size(), sat::constraint_base::mem2base_ptr(this)); }
|
||||
unsigned id() const { return m_id; }
|
||||
tag_t tag() const { return m_tag; }
|
||||
literal lit() const { return m_lit; }
|
||||
unsigned size() const { return m_size; }
|
||||
void set_size(unsigned sz) { SASSERT(sz <= m_size); m_size = sz; }
|
||||
void update_literal(literal l) { m_lit = l; }
|
||||
bool was_removed() const { return m_removed; }
|
||||
void set_removed() { m_removed = true; }
|
||||
void nullify_literal() { m_lit = sat::null_literal; }
|
||||
unsigned glue() const { return m_glue; }
|
||||
void set_glue(unsigned g) { m_glue = g; }
|
||||
unsigned psm() const { return m_psm; }
|
||||
void set_psm(unsigned p) { m_psm = p; }
|
||||
void set_learned(bool f) { m_learned = f; }
|
||||
bool learned() const { return m_learned; }
|
||||
bool is_watched() const { return m_watch == m_lit && m_lit != sat::null_literal; }
|
||||
void set_watch() { m_watch = m_lit; }
|
||||
void reset_watch() { m_watch = sat::null_literal; }
|
||||
bool is_clear() const { return m_watch == sat::null_literal && m_lit != sat::null_literal; }
|
||||
bool is_pure() const { return m_pure; }
|
||||
void set_pure() { m_pure = true; }
|
||||
unsigned fold_max_var(unsigned w) const;
|
||||
|
||||
size_t obj_size() const { return m_obj_size; }
|
||||
card& to_card();
|
||||
pb& to_pb();
|
||||
xr& to_xr();
|
||||
card const& to_card() const;
|
||||
pb const& to_pb() const;
|
||||
xr const& to_xr() const;
|
||||
pb_base const& to_pb_base() const;
|
||||
bool is_card() const { return m_tag == tag_t::card_t; }
|
||||
bool is_pb() const { return m_tag == tag_t::pb_t; }
|
||||
bool is_xr() const { return m_tag == tag_t::xr_t; }
|
||||
|
||||
bool is_watched(solver_interface const& s, literal lit) const;
|
||||
void unwatch_literal(solver_interface& s, literal lit);
|
||||
void nullify_tracking_literal(solver_interface& s);
|
||||
void watch_literal(solver_interface& s, literal lit);
|
||||
virtual void clear_watch(solver_interface& s) = 0;
|
||||
virtual bool init_watch(solver_interface& s) = 0;
|
||||
virtual lbool eval(sat::model const& m) const = 0;
|
||||
virtual lbool eval(solver_interface const& s) const = 0;
|
||||
virtual bool is_blocked(sat::simplifier& s, literal lit) const = 0;
|
||||
|
||||
virtual bool validate_unit_propagation(solver_interface const& s, literal alit) const = 0;
|
||||
|
||||
virtual bool is_watching(literal l) const { UNREACHABLE(); return false; };
|
||||
virtual literal_vector literals() const { UNREACHABLE(); return literal_vector(); }
|
||||
virtual void swap(unsigned i, unsigned j) { UNREACHABLE(); }
|
||||
virtual literal get_lit(unsigned i) const { UNREACHABLE(); return sat::null_literal; }
|
||||
virtual void set_lit(unsigned i, literal l) { UNREACHABLE(); }
|
||||
virtual bool well_formed() const { return true; }
|
||||
virtual void negate() { UNREACHABLE(); }
|
||||
virtual bool is_extended_binary(literal_vector& r) const { return false; }
|
||||
|
||||
virtual double get_reward(solver_interface const& s, sat::literal_occs_fun& occs) const { return 0; }
|
||||
virtual std::ostream& display(std::ostream& out) const = 0;
|
||||
virtual std::ostream& display(std::ostream& out, solver_interface const& s, bool values) const = 0;
|
||||
virtual void init_use_list(sat::ext_use_list& ul) const = 0;
|
||||
|
||||
class iterator {
|
||||
constraint const& c;
|
||||
unsigned idx;
|
||||
public:
|
||||
iterator(constraint const& c, unsigned idx) : c(c), idx(idx) {}
|
||||
literal operator*() { return c.get_lit(idx); }
|
||||
iterator& operator++() { ++idx; return *this; }
|
||||
bool operator==(iterator const& other) const { SASSERT(&c == &other.c); return idx == other.idx; }
|
||||
bool operator!=(iterator const& other) const { SASSERT(&c == &other.c); return idx != other.idx; }
|
||||
};
|
||||
|
||||
class literal_iterator {
|
||||
constraint const& c;
|
||||
public:
|
||||
literal_iterator(constraint const& c):c(c) {}
|
||||
iterator begin() const { return iterator(c, 0); }
|
||||
iterator end() const { return iterator(c, c.size()); }
|
||||
};
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, constraint const& c);
|
||||
|
||||
|
||||
}
|
|
@ -284,7 +284,7 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
|
||||
expr_ref ba_solver::get_card(std::function<expr_ref(sat::literal)>& lit2expr, ba_solver::card const& c) {
|
||||
expr_ref ba_solver::get_card(std::function<expr_ref(sat::literal)>& lit2expr, ba::card const& c) {
|
||||
ptr_buffer<expr> lits;
|
||||
for (sat::literal l : c) {
|
||||
lits.push_back(lit2expr(l));
|
||||
|
@ -297,7 +297,7 @@ namespace sat {
|
|||
return fml;
|
||||
}
|
||||
|
||||
expr_ref ba_solver::get_pb(std::function<expr_ref(sat::literal)>& lit2expr, ba_solver::pb const& p) {
|
||||
expr_ref ba_solver::get_pb(std::function<expr_ref(sat::literal)>& lit2expr, pb const& p) {
|
||||
ptr_buffer<expr> lits;
|
||||
vector<rational> coeffs;
|
||||
for (auto const& wl : p) {
|
||||
|
@ -313,7 +313,7 @@ namespace sat {
|
|||
return fml;
|
||||
}
|
||||
|
||||
expr_ref ba_solver::get_xor(std::function<expr_ref(sat::literal)>& lit2expr, ba_solver::xr const& x) {
|
||||
expr_ref ba_solver::get_xor(std::function<expr_ref(sat::literal)>& lit2expr, xr const& x) {
|
||||
ptr_buffer<expr> lits;
|
||||
for (sat::literal l : x) {
|
||||
lits.push_back(lit2expr(l));
|
||||
|
@ -329,13 +329,13 @@ namespace sat {
|
|||
bool ba_solver::to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) {
|
||||
for (auto* c : constraints()) {
|
||||
switch (c->tag()) {
|
||||
case ba_solver::card_t:
|
||||
case ba::tag_t::card_t:
|
||||
fmls.push_back(get_card(l2e, c->to_card()));
|
||||
break;
|
||||
case ba_solver::pb_t:
|
||||
case ba::tag_t::pb_t:
|
||||
fmls.push_back(get_pb(l2e, c->to_pb()));
|
||||
break;
|
||||
case ba_solver::xr_t:
|
||||
case ba::tag_t::xr_t:
|
||||
fmls.push_back(get_xor(l2e, c->to_xr()));
|
||||
break;
|
||||
}
|
||||
|
|
308
src/sat/smt/ba_pb.cpp
Normal file
308
src/sat/smt/ba_pb.cpp
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
ba_pb.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Interface for PB constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2017-01-30
|
||||
|
||||
--*/
|
||||
|
||||
#include "sat/smt/ba_pb.h"
|
||||
|
||||
namespace ba {
|
||||
|
||||
pb& constraint::to_pb() {
|
||||
SASSERT(is_pb());
|
||||
return static_cast<pb&>(*this);
|
||||
}
|
||||
|
||||
pb const& constraint::to_pb() const {
|
||||
SASSERT(is_pb());
|
||||
return static_cast<pb const&>(*this);
|
||||
}
|
||||
|
||||
pb_base const& constraint::to_pb_base() const {
|
||||
SASSERT(is_pb() || is_card());
|
||||
return static_cast<pb_base const&>(*this);
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
// pb
|
||||
|
||||
pb::pb(unsigned id, literal lit, svector<wliteral> const& wlits, unsigned k) :
|
||||
pb_base(tag_t::pb_t, id, lit, wlits.size(), get_obj_size(wlits.size()), k),
|
||||
m_slack(0),
|
||||
m_num_watch(0),
|
||||
m_max_sum(0) {
|
||||
for (unsigned i = 0; i < size(); ++i) {
|
||||
m_wlits[i] = wlits[i];
|
||||
}
|
||||
update_max_sum();
|
||||
}
|
||||
|
||||
void pb::update_max_sum() {
|
||||
m_max_sum = 0;
|
||||
for (unsigned i = 0; i < size(); ++i) {
|
||||
m_wlits[i].first = std::min(k(), m_wlits[i].first);
|
||||
if (m_max_sum + m_wlits[i].first < m_max_sum) {
|
||||
throw default_exception("addition of pb coefficients overflows");
|
||||
}
|
||||
m_max_sum += m_wlits[i].first;
|
||||
}
|
||||
}
|
||||
|
||||
void pb::negate() {
|
||||
m_lit.neg();
|
||||
unsigned w = 0;
|
||||
for (unsigned i = 0; i < m_size; ++i) {
|
||||
m_wlits[i].second.neg();
|
||||
VERIFY(w + m_wlits[i].first >= w);
|
||||
w += m_wlits[i].first;
|
||||
}
|
||||
m_k = w - m_k + 1;
|
||||
VERIFY(w >= m_k && m_k > 0);
|
||||
}
|
||||
|
||||
bool pb::is_watching(literal l) const {
|
||||
for (unsigned i = 0; i < m_num_watch; ++i) {
|
||||
if ((*this)[i].second == l) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pb::is_cardinality() const {
|
||||
if (size() == 0) return false;
|
||||
unsigned w = (*this)[0].first;
|
||||
for (wliteral wl : *this) if (w != wl.first) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
double pb::get_reward(ba::solver_interface const& s, sat::literal_occs_fun& occs) const {
|
||||
unsigned k = this->k(), slack = 0;
|
||||
bool do_add = s.get_config().m_lookahead_reward == sat::heule_schur_reward;
|
||||
double to_add = do_add ? 0 : 1;
|
||||
double undefs = 0;
|
||||
for (wliteral wl : *this) {
|
||||
literal l = wl.second;
|
||||
unsigned w = wl.first;
|
||||
switch (s.value(l)) {
|
||||
case l_true: if (k <= w) return 0;
|
||||
case l_undef:
|
||||
if (do_add) to_add += occs(l);
|
||||
++undefs;
|
||||
slack += w;
|
||||
break; // TBD multiplier factor on this
|
||||
case l_false: break;
|
||||
}
|
||||
}
|
||||
if (k >= slack || 0 == undefs) return 0;
|
||||
double avg = slack / undefs;
|
||||
return pow(0.5, (slack - k + 1) / avg) * to_add;
|
||||
}
|
||||
|
||||
|
||||
void pb::clear_watch(solver_interface& s) {
|
||||
reset_watch();
|
||||
for (unsigned i = 0; i < num_watch(); ++i) {
|
||||
unwatch_literal(s, (*this)[i].second);
|
||||
}
|
||||
set_num_watch(0);
|
||||
DEBUG_CODE(for (wliteral wl : *this) VERIFY(!is_watched(s, wl.second)););
|
||||
}
|
||||
|
||||
|
||||
// watch a prefix of literals, such that the slack of these is >= k
|
||||
bool pb::init_watch(solver_interface& s) {
|
||||
auto& p = *this;
|
||||
clear_watch(s);
|
||||
if (lit() != sat::null_literal && s.value(p.lit()) == l_false) {
|
||||
negate();
|
||||
}
|
||||
|
||||
VERIFY(lit() == sat::null_literal || s.value(lit()) == l_true);
|
||||
unsigned sz = size(), bound = k();
|
||||
|
||||
// put the non-false literals into the head.
|
||||
unsigned slack = 0, slack1 = 0, num_watch = 0, j = 0;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (s.value(p[i].second) != l_false) {
|
||||
if (j != i) {
|
||||
swap(i, j);
|
||||
}
|
||||
if (slack <= bound) {
|
||||
slack += p[j].first;
|
||||
++num_watch;
|
||||
}
|
||||
else {
|
||||
slack1 += p[j].first;
|
||||
}
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_CODE(
|
||||
bool is_false = false;
|
||||
for (unsigned k = 0; k < sz; ++k) {
|
||||
SASSERT(!is_false || s.value(p[k].second) == l_false);
|
||||
SASSERT((k < j) == (s.value(p[k].second) != l_false));
|
||||
is_false = s.value(p[k].second) == l_false;
|
||||
});
|
||||
|
||||
if (slack < bound) {
|
||||
literal lit = p[j].second;
|
||||
VERIFY(s.value(lit) == l_false);
|
||||
for (unsigned i = j + 1; i < sz; ++i) {
|
||||
if (s.lvl(lit) < s.lvl(p[i].second)) {
|
||||
lit = p[i].second;
|
||||
}
|
||||
}
|
||||
s.set_conflict(p, lit);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
for (unsigned i = 0; i < num_watch; ++i) {
|
||||
p.watch_literal(s, p[i].second);
|
||||
}
|
||||
p.set_slack(slack);
|
||||
p.set_num_watch(num_watch);
|
||||
|
||||
// SASSERT(validate_watch(p, sat::null_literal));
|
||||
|
||||
TRACE("ba", display(tout << "init watch: ", s, true););
|
||||
|
||||
// slack is tight:
|
||||
if (slack + slack1 == bound) {
|
||||
SASSERT(slack1 == 0);
|
||||
SASSERT(j == num_watch);
|
||||
for (unsigned i = 0; i < j; ++i) {
|
||||
s.assign(p, p[i].second);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::ostream& pb::display(std::ostream& out) const {
|
||||
bool first = true;
|
||||
for (wliteral wl : *this) {
|
||||
if (!first) out << "+ ";
|
||||
if (wl.first != 1) out << wl.first << " * ";
|
||||
out << wl.second << " ";
|
||||
first = false;
|
||||
}
|
||||
return out << " >= " << k();
|
||||
}
|
||||
|
||||
std::ostream& pb::display(std::ostream& out, solver_interface const& s, bool values) const {
|
||||
auto const& p = *this;
|
||||
if (p.lit() != sat::null_literal) out << p.lit() << " == ";
|
||||
if (values) {
|
||||
out << "[watch: " << p.num_watch() << ", slack: " << p.slack() << "]";
|
||||
}
|
||||
if (p.lit() != sat::null_literal && values) {
|
||||
out << "@(" << s.value(p.lit());
|
||||
if (s.value(p.lit()) != l_undef) {
|
||||
out << ":" << s.lvl(p.lit());
|
||||
}
|
||||
out << "): ";
|
||||
}
|
||||
unsigned i = 0;
|
||||
for (wliteral wl : p) {
|
||||
literal l = wl.second;
|
||||
unsigned w = wl.first;
|
||||
if (i > 0) out << "+ ";
|
||||
if (i++ == p.num_watch()) out << " | ";
|
||||
if (w > 1) out << w << " * ";
|
||||
out << l;
|
||||
if (values) {
|
||||
out << "@(" << s.value(l);
|
||||
if (s.value(l) != l_undef) {
|
||||
out << ":" << s.lvl(l);
|
||||
}
|
||||
out << ") ";
|
||||
}
|
||||
else {
|
||||
out << " ";
|
||||
}
|
||||
}
|
||||
return out << ">= " << p.k() << "\n";
|
||||
}
|
||||
|
||||
bool pb::validate_unit_propagation(solver_interface const& s, literal alit) const {
|
||||
if (lit() != sat::null_literal && s.value(lit()) != l_true)
|
||||
return false;
|
||||
|
||||
unsigned sum = 0;
|
||||
TRACE("ba", display(tout << "validate: " << alit << "\n", s, true););
|
||||
for (wliteral wl : *this) {
|
||||
literal l = wl.second;
|
||||
lbool val = s.value(l);
|
||||
if (val != l_false && l != alit) {
|
||||
sum += wl.first;
|
||||
}
|
||||
}
|
||||
return sum < k();
|
||||
}
|
||||
|
||||
lbool pb::eval(sat::model const& m) const {
|
||||
auto const& p = *this;
|
||||
unsigned trues = 0, undefs = 0;
|
||||
for (wliteral wl : p) {
|
||||
switch (ba::value(m, wl.second)) {
|
||||
case l_true: trues += wl.first; break;
|
||||
case l_undef: undefs += wl.first; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (trues + undefs < p.k()) return l_false;
|
||||
if (trues >= p.k()) return l_true;
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
lbool pb::eval(solver_interface const& s) const {
|
||||
auto const& p = *this;
|
||||
unsigned trues = 0, undefs = 0;
|
||||
for (wliteral wl : p) {
|
||||
switch (s.value(wl.second)) {
|
||||
case l_true: trues += wl.first; break;
|
||||
case l_undef: undefs += wl.first; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (trues + undefs < p.k()) return l_false;
|
||||
if (trues >= p.k()) return l_true;
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
void pb::init_use_list(sat::ext_use_list& ul) const {
|
||||
auto idx = cindex();
|
||||
for (auto l : *this)
|
||||
ul.insert(l.second, idx);
|
||||
}
|
||||
|
||||
bool pb::is_blocked(sat::simplifier& sim, literal lit) const {
|
||||
unsigned weight = 0, offset = 0;
|
||||
for (wliteral l2 : *this) {
|
||||
if (~l2.second == lit) {
|
||||
offset = l2.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SASSERT(offset != 0);
|
||||
for (wliteral l2 : *this) {
|
||||
if (sim.is_marked(~l2.second)) {
|
||||
weight += std::min(offset, l2.first);
|
||||
}
|
||||
}
|
||||
return weight >= k();
|
||||
}
|
||||
}
|
67
src/sat/smt/ba_pb.h
Normal file
67
src/sat/smt/ba_pb.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
ba_pb.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Interface for PB constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2017-01-30
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sat/sat_types.h"
|
||||
#include "sat/smt/ba_card.h"
|
||||
|
||||
|
||||
namespace ba {
|
||||
|
||||
class pb : public pb_base {
|
||||
unsigned m_slack;
|
||||
unsigned m_num_watch;
|
||||
unsigned m_max_sum;
|
||||
wliteral m_wlits[0];
|
||||
public:
|
||||
static size_t get_obj_size(unsigned num_lits) { return sat::constraint_base::obj_size(sizeof(pb) + num_lits * sizeof(wliteral)); }
|
||||
pb(unsigned id, literal lit, svector<wliteral> const& wlits, unsigned k);
|
||||
literal lit() const { return m_lit; }
|
||||
wliteral operator[](unsigned i) const { return m_wlits[i]; }
|
||||
wliteral& operator[](unsigned i) { return m_wlits[i]; }
|
||||
wliteral const* begin() const { return m_wlits; }
|
||||
wliteral const* end() const { return begin() + m_size; }
|
||||
|
||||
unsigned slack() const { return m_slack; }
|
||||
void set_slack(unsigned s) { m_slack = s; }
|
||||
unsigned num_watch() const { return m_num_watch; }
|
||||
unsigned max_sum() const { return m_max_sum; }
|
||||
void update_max_sum();
|
||||
void set_num_watch(unsigned s) { m_num_watch = s; }
|
||||
bool is_cardinality() const;
|
||||
void negate() override;
|
||||
void set_k(unsigned k) override { m_k = k; VERIFY(k < 4000000000); update_max_sum(); }
|
||||
void swap(unsigned i, unsigned j) override { std::swap(m_wlits[i], m_wlits[j]); }
|
||||
literal_vector literals() const override { literal_vector lits; for (auto wl : *this) lits.push_back(wl.second); return lits; }
|
||||
bool is_watching(literal l) const override;
|
||||
literal get_lit(unsigned i) const override { return m_wlits[i].second; }
|
||||
void set_lit(unsigned i, literal l) override { m_wlits[i].second = l; }
|
||||
unsigned get_coeff(unsigned i) const override { return m_wlits[i].first; }
|
||||
double get_reward(ba::solver_interface const& s, sat::literal_occs_fun& occs) const override;
|
||||
void clear_watch(solver_interface& s) override;
|
||||
std::ostream& display(std::ostream& out) const override;
|
||||
std::ostream& display(std::ostream& out, solver_interface const& s, bool values) const override;
|
||||
bool init_watch(solver_interface& s) override;
|
||||
bool validate_unit_propagation(solver_interface const& s, literal alit) const override;
|
||||
lbool eval(sat::model const& m) const override;
|
||||
lbool eval(solver_interface const& s) const override;
|
||||
void init_use_list(sat::ext_use_list& ul) const override;
|
||||
bool is_blocked(sat::simplifier& s, literal lit) const override;
|
||||
};
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -26,6 +26,10 @@ Revision History:
|
|||
#include "sat/sat_big.h"
|
||||
#include "sat/smt/sat_smt.h"
|
||||
#include "sat/smt/sat_th.h"
|
||||
#include "sat/smt/ba_constraint.h"
|
||||
#include "sat/smt/ba_card.h"
|
||||
#include "sat/smt/ba_pb.h"
|
||||
#include "sat/smt/ba_xor.h"
|
||||
#include "util/small_object_allocator.h"
|
||||
#include "util/scoped_ptr_vector.h"
|
||||
#include "util/sorting_network.h"
|
||||
|
@ -33,9 +37,16 @@ Revision History:
|
|||
|
||||
namespace sat {
|
||||
|
||||
typedef ba::constraint constraint;
|
||||
typedef ba::wliteral wliteral;
|
||||
typedef ba::card card;
|
||||
typedef ba::xr xr;
|
||||
typedef ba::pb_base pb_base;
|
||||
typedef ba::pb pb;
|
||||
|
||||
class xor_finder;
|
||||
|
||||
class ba_solver : public euf::th_solver {
|
||||
class ba_solver : public euf::th_solver, public ba::solver_interface {
|
||||
|
||||
friend class local_search;
|
||||
|
||||
|
@ -54,167 +65,7 @@ namespace sat {
|
|||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
public:
|
||||
enum tag_t {
|
||||
card_t,
|
||||
pb_t,
|
||||
xr_t
|
||||
};
|
||||
|
||||
class card;
|
||||
class pb;
|
||||
class xr;
|
||||
class pb_base;
|
||||
|
||||
class constraint {
|
||||
protected:
|
||||
tag_t m_tag;
|
||||
bool m_removed;
|
||||
literal m_lit;
|
||||
literal m_watch;
|
||||
unsigned m_glue;
|
||||
unsigned m_psm;
|
||||
unsigned m_size;
|
||||
size_t m_obj_size;
|
||||
bool m_learned;
|
||||
unsigned m_id;
|
||||
bool m_pure; // is the constraint pure (only positive occurrences)
|
||||
public:
|
||||
constraint(tag_t t, unsigned id, literal l, unsigned sz, size_t osz):
|
||||
m_tag(t), m_removed(false), m_lit(l), m_watch(null_literal), m_glue(0), m_psm(0), m_size(sz), m_obj_size(osz), m_learned(false), m_id(id), m_pure(false) {
|
||||
}
|
||||
ext_constraint_idx cindex() const { return constraint_base::mem2base(this); }
|
||||
void deallocate(small_object_allocator& a) { a.deallocate(obj_size(), constraint_base::mem2base_ptr(this)); }
|
||||
unsigned id() const { return m_id; }
|
||||
tag_t tag() const { return m_tag; }
|
||||
literal lit() const { return m_lit; }
|
||||
unsigned size() const { return m_size; }
|
||||
void set_size(unsigned sz) { SASSERT(sz <= m_size); m_size = sz; }
|
||||
void update_literal(literal l) { m_lit = l; }
|
||||
bool was_removed() const { return m_removed; }
|
||||
void set_removed() { m_removed = true; }
|
||||
void nullify_literal() { m_lit = null_literal; }
|
||||
unsigned glue() const { return m_glue; }
|
||||
void set_glue(unsigned g) { m_glue = g; }
|
||||
unsigned psm() const { return m_psm; }
|
||||
void set_psm(unsigned p) { m_psm = p; }
|
||||
void set_learned(bool f) { m_learned = f; }
|
||||
bool learned() const { return m_learned; }
|
||||
bool is_watched() const { return m_watch == m_lit && m_lit != null_literal; }
|
||||
void set_watch() { m_watch = m_lit; }
|
||||
void clear_watch() { m_watch = null_literal; }
|
||||
bool is_clear() const { return m_watch == null_literal && m_lit != null_literal; }
|
||||
bool is_pure() const { return m_pure; }
|
||||
void set_pure() { m_pure = true; }
|
||||
unsigned fold_max_var(unsigned w) const;
|
||||
|
||||
size_t obj_size() const { return m_obj_size; }
|
||||
card& to_card();
|
||||
pb& to_pb();
|
||||
xr& to_xr();
|
||||
card const& to_card() const;
|
||||
pb const& to_pb() const;
|
||||
xr const& to_xr() const;
|
||||
pb_base const& to_pb_base() const;
|
||||
bool is_card() const { return m_tag == card_t; }
|
||||
bool is_pb() const { return m_tag == pb_t; }
|
||||
bool is_xr() const { return m_tag == xr_t; }
|
||||
|
||||
virtual bool is_watching(literal l) const { UNREACHABLE(); return false; };
|
||||
virtual literal_vector literals() const { UNREACHABLE(); return literal_vector(); }
|
||||
virtual void swap(unsigned i, unsigned j) { UNREACHABLE(); }
|
||||
virtual literal get_lit(unsigned i) const { UNREACHABLE(); return null_literal; }
|
||||
virtual void set_lit(unsigned i, literal l) { UNREACHABLE(); }
|
||||
virtual bool well_formed() const { return true; }
|
||||
virtual void negate() { UNREACHABLE(); }
|
||||
};
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, constraint const& c);
|
||||
|
||||
// base class for pb and cardinality constraints
|
||||
class pb_base : public constraint {
|
||||
protected:
|
||||
unsigned m_k;
|
||||
public:
|
||||
pb_base(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k):
|
||||
constraint(t, id, l, sz, osz), m_k(k) { VERIFY(k < 4000000000); }
|
||||
virtual void set_k(unsigned k) { VERIFY(k < 4000000000); m_k = k; }
|
||||
virtual unsigned get_coeff(unsigned i) const { UNREACHABLE(); return 0; }
|
||||
unsigned k() const { return m_k; }
|
||||
bool well_formed() const override;
|
||||
};
|
||||
|
||||
class card : public pb_base {
|
||||
literal m_lits[0];
|
||||
public:
|
||||
static size_t get_obj_size(unsigned num_lits) { return constraint_base::obj_size(sizeof(card) + num_lits * sizeof(literal)); }
|
||||
card(unsigned id, literal lit, literal_vector const& lits, unsigned k);
|
||||
literal operator[](unsigned i) const { return m_lits[i]; }
|
||||
literal& operator[](unsigned i) { return m_lits[i]; }
|
||||
literal const* begin() const { return m_lits; }
|
||||
literal const* end() const { return static_cast<literal const*>(m_lits) + m_size; }
|
||||
void negate() override;
|
||||
void swap(unsigned i, unsigned j) override { std::swap(m_lits[i], m_lits[j]); }
|
||||
literal_vector literals() const override { return literal_vector(m_size, m_lits); }
|
||||
bool is_watching(literal l) const override;
|
||||
literal get_lit(unsigned i) const override { return m_lits[i]; }
|
||||
void set_lit(unsigned i, literal l) override { m_lits[i] = l; }
|
||||
unsigned get_coeff(unsigned i) const override { return 1; }
|
||||
};
|
||||
|
||||
|
||||
typedef std::pair<unsigned, literal> wliteral;
|
||||
|
||||
class pb : public pb_base {
|
||||
unsigned m_slack;
|
||||
unsigned m_num_watch;
|
||||
unsigned m_max_sum;
|
||||
wliteral m_wlits[0];
|
||||
public:
|
||||
static size_t get_obj_size(unsigned num_lits) { return constraint_base::obj_size(sizeof(pb) + num_lits * sizeof(wliteral)); }
|
||||
pb(unsigned id, literal lit, svector<wliteral> const& wlits, unsigned k);
|
||||
literal lit() const { return m_lit; }
|
||||
wliteral operator[](unsigned i) const { return m_wlits[i]; }
|
||||
wliteral& operator[](unsigned i) { return m_wlits[i]; }
|
||||
wliteral const* begin() const { return m_wlits; }
|
||||
wliteral const* end() const { return begin() + m_size; }
|
||||
|
||||
unsigned slack() const { return m_slack; }
|
||||
void set_slack(unsigned s) { m_slack = s; }
|
||||
unsigned num_watch() const { return m_num_watch; }
|
||||
unsigned max_sum() const { return m_max_sum; }
|
||||
void update_max_sum();
|
||||
void set_num_watch(unsigned s) { m_num_watch = s; }
|
||||
bool is_cardinality() const;
|
||||
void negate() override;
|
||||
void set_k(unsigned k) override { m_k = k; VERIFY(k < 4000000000); update_max_sum(); }
|
||||
void swap(unsigned i, unsigned j) override { std::swap(m_wlits[i], m_wlits[j]); }
|
||||
literal_vector literals() const override { literal_vector lits; for (auto wl : *this) lits.push_back(wl.second); return lits; }
|
||||
bool is_watching(literal l) const override;
|
||||
literal get_lit(unsigned i) const override { return m_wlits[i].second; }
|
||||
void set_lit(unsigned i, literal l) override { m_wlits[i].second = l; }
|
||||
unsigned get_coeff(unsigned i) const override { return m_wlits[i].first; }
|
||||
};
|
||||
|
||||
class xr : public constraint {
|
||||
literal m_lits[0];
|
||||
public:
|
||||
static size_t get_obj_size(unsigned num_lits) { return constraint_base::obj_size(sizeof(xr) + num_lits * sizeof(literal)); }
|
||||
xr(unsigned id, literal_vector const& lits);
|
||||
literal operator[](unsigned i) const { return m_lits[i]; }
|
||||
literal const* begin() const { return m_lits; }
|
||||
literal const* end() const { return begin() + m_size; }
|
||||
void negate() override { m_lits[0].neg(); }
|
||||
void swap(unsigned i, unsigned j) override { std::swap(m_lits[i], m_lits[j]); }
|
||||
bool is_watching(literal l) const override;
|
||||
literal_vector literals() const override { return literal_vector(size(), begin()); }
|
||||
literal get_lit(unsigned i) const override { return m_lits[i]; }
|
||||
void set_lit(unsigned i, literal l) override { m_lits[i] = l; }
|
||||
bool well_formed() const override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
struct ineq {
|
||||
|
@ -235,29 +86,28 @@ namespace sat {
|
|||
sat_internalizer& si;
|
||||
pb_util m_pb;
|
||||
|
||||
solver* m_solver;
|
||||
lookahead* m_lookahead;
|
||||
solver* m_solver{ nullptr };
|
||||
lookahead* m_lookahead{ nullptr };
|
||||
stats m_stats;
|
||||
small_object_allocator m_allocator;
|
||||
|
||||
|
||||
ptr_vector<constraint> m_constraints;
|
||||
ptr_vector<constraint> m_learned;
|
||||
ptr_vector<constraint> m_constraint_to_reinit;
|
||||
ptr_vector<ba::constraint> m_constraints;
|
||||
ptr_vector<ba::constraint> m_learned;
|
||||
ptr_vector<ba::constraint> m_constraint_to_reinit;
|
||||
unsigned_vector m_constraint_to_reinit_lim;
|
||||
unsigned m_constraint_to_reinit_last_sz;
|
||||
unsigned m_constraint_id;
|
||||
unsigned m_constraint_to_reinit_last_sz{ 0 };
|
||||
unsigned m_constraint_id{ 0 };
|
||||
|
||||
// conflict resolution
|
||||
unsigned m_num_marks;
|
||||
unsigned m_conflict_lvl;
|
||||
unsigned m_num_marks{ 0 };
|
||||
unsigned m_conflict_lvl{ 0 };
|
||||
svector<int64_t> m_coeffs;
|
||||
svector<bool_var> m_active_vars;
|
||||
unsigned m_bound;
|
||||
unsigned m_bound{ 0 };
|
||||
tracked_uint_set m_active_var_set;
|
||||
literal_vector m_lemma;
|
||||
literal_vector m_skipped;
|
||||
unsigned m_num_propagations_since_pop;
|
||||
unsigned m_num_propagations_since_pop{ 0 };
|
||||
unsigned_vector m_parity_marks;
|
||||
literal_vector m_parity_trail;
|
||||
|
||||
|
@ -297,11 +147,11 @@ namespace sat {
|
|||
|
||||
vector<svector<constraint*>> m_cnstr_use_list;
|
||||
use_list m_clause_use_list;
|
||||
bool m_simplify_change;
|
||||
bool m_clause_removed;
|
||||
bool m_constraint_removed;
|
||||
bool m_simplify_change{ false };
|
||||
bool m_clause_removed{ false };
|
||||
bool m_constraint_removed{ false };
|
||||
literal_vector m_roots;
|
||||
bool_vector m_root_vars;
|
||||
bool_vector m_root_vars;
|
||||
unsigned_vector m_weights;
|
||||
svector<wliteral> m_wlits;
|
||||
|
||||
|
@ -324,9 +174,9 @@ namespace sat {
|
|||
unsigned elim_pure();
|
||||
bool elim_pure(literal lit);
|
||||
void unit_strengthen();
|
||||
void unit_strengthen(big& big, constraint& cs);
|
||||
void unit_strengthen(big& big, ba::constraint& cs);
|
||||
void unit_strengthen(big& big, pb_base& p);
|
||||
void subsumption(constraint& c1);
|
||||
void subsumption(ba::constraint& c1);
|
||||
void subsumption(card& c1);
|
||||
void gc_half(char const* _method);
|
||||
void update_psm(constraint& c) const;
|
||||
|
@ -345,10 +195,7 @@ namespace sat {
|
|||
// constraints
|
||||
constraint& index2constraint(size_t idx) const { return *reinterpret_cast<constraint*>(constraint_base::from_index(idx)->mem()); }
|
||||
void pop_constraint();
|
||||
void unwatch_literal(literal w, constraint& c);
|
||||
void watch_literal(literal w, constraint& c);
|
||||
void watch_literal(wliteral w, pb& p);
|
||||
bool is_watched(literal l, constraint const& c) const;
|
||||
// void watch_literal(wliteral w, pb& p);
|
||||
void add_constraint(constraint* c);
|
||||
bool init_watch(constraint& c);
|
||||
void init_watch(bool_var v);
|
||||
|
@ -357,9 +204,8 @@ namespace sat {
|
|||
bool incremental_mode() const;
|
||||
void simplify(constraint& c);
|
||||
void pre_simplify(xor_finder& xu, constraint& c);
|
||||
void nullify_tracking_literal(constraint& c);
|
||||
void set_conflict(constraint& c, literal lit);
|
||||
void assign(constraint& c, literal lit);
|
||||
void set_conflict(constraint& c, literal lit) override;
|
||||
void assign(constraint& c, literal lit) override;
|
||||
bool assigned_above(literal above, literal below);
|
||||
void get_antecedents(literal l, constraint const& c, literal_vector & r, bool probing);
|
||||
bool validate_conflict(constraint const& c) const;
|
||||
|
@ -377,12 +223,10 @@ namespace sat {
|
|||
void split_root(constraint& c);
|
||||
unsigned next_id() { return m_constraint_id++; }
|
||||
void set_non_learned(constraint& c);
|
||||
|
||||
double get_reward(literal l, ext_justification_idx idx, literal_occs_fun& occs) const override;
|
||||
|
||||
// cardinality
|
||||
bool init_watch(card& c);
|
||||
lbool add_assign(card& c, literal lit);
|
||||
void clear_watch(card& c);
|
||||
void reset_coeffs();
|
||||
void reset_marked_literals();
|
||||
void get_antecedents(literal l, card const& c, literal_vector & r);
|
||||
|
@ -392,13 +236,9 @@ namespace sat {
|
|||
bool clausify(literal lit, unsigned n, literal const* lits, unsigned k);
|
||||
lbool eval(card const& c) const;
|
||||
lbool eval(model const& m, card const& c) const;
|
||||
double get_reward(card const& c, literal_occs_fun& occs) const;
|
||||
|
||||
|
||||
// xr specific functionality
|
||||
void clear_watch(xr& x);
|
||||
bool init_watch(xr& x);
|
||||
bool parity(xr const& x, unsigned offset) const;
|
||||
lbool add_assign(xr& x, literal alit);
|
||||
void get_xr_antecedents(literal l, unsigned index, justification js, literal_vector& r);
|
||||
void get_antecedents(literal l, xr const& x, literal_vector & r);
|
||||
|
@ -411,11 +251,9 @@ namespace sat {
|
|||
lbool eval(model const& m, xr const& x) const;
|
||||
|
||||
// pb functionality
|
||||
unsigned m_a_max;
|
||||
bool init_watch(pb& p);
|
||||
unsigned m_a_max{ 0 };
|
||||
lbool add_assign(pb& p, literal alit);
|
||||
void add_index(pb& p, unsigned index, literal lit);
|
||||
void clear_watch(pb& p);
|
||||
void get_antecedents(literal l, pb const& p, literal_vector & r);
|
||||
void split_root(pb_base& p);
|
||||
void simplify(pb_base& p);
|
||||
|
@ -427,7 +265,6 @@ namespace sat {
|
|||
bool is_cardinality(pb const& p, literal_vector& lits);
|
||||
lbool eval(pb const& p) const;
|
||||
lbool eval(model const& m, pb const& p) const;
|
||||
double get_reward(pb const& p, literal_occs_fun& occs) const;
|
||||
|
||||
// RoundingPb conflict resolution
|
||||
lbool resolve_conflict_rs();
|
||||
|
@ -449,31 +286,32 @@ namespace sat {
|
|||
|
||||
|
||||
// access solver
|
||||
inline lbool value(bool_var v) const { return value(literal(v, false)); }
|
||||
inline lbool value(literal lit) const { return m_lookahead ? m_lookahead->value(lit) : m_solver->value(lit); }
|
||||
inline lbool value(model const& m, literal l) const { return l.sign() ? ~m[l.var()] : m[l.var()]; }
|
||||
inline bool is_false(literal lit) const { return l_false == value(lit); }
|
||||
inline lbool value(bool_var v) const override { return value(literal(v, false)); }
|
||||
inline lbool value(literal lit) const override { return m_lookahead ? m_lookahead->value(lit) : m_solver->value(lit); }
|
||||
inline bool is_false(literal lit) const override { return l_false == value(lit); }
|
||||
|
||||
inline unsigned lvl(literal lit) const { return m_lookahead ? 0 : m_solver->lvl(lit); }
|
||||
inline unsigned lvl(bool_var v) const { return m_lookahead ? 0 : m_solver->lvl(v); }
|
||||
inline bool inconsistent() const {
|
||||
inline unsigned lvl(literal lit) const override { return m_lookahead ? 0 : m_solver->lvl(lit); }
|
||||
inline unsigned lvl(bool_var v) const override { return m_lookahead ? 0 : m_solver->lvl(v); }
|
||||
inline bool inconsistent() const override {
|
||||
if (m_lookahead) return m_lookahead->inconsistent();
|
||||
return m_solver->inconsistent();
|
||||
}
|
||||
inline watch_list& get_wlist(literal l) { return m_lookahead ? m_lookahead->get_wlist(l) : m_solver->get_wlist(l); }
|
||||
inline watch_list const& get_wlist(literal l) const { return m_lookahead ? m_lookahead->get_wlist(l) : m_solver->get_wlist(l); }
|
||||
inline void assign(literal l, justification j) {
|
||||
inline watch_list& get_wlist(literal l) override { return m_lookahead ? m_lookahead->get_wlist(l) : m_solver->get_wlist(l); }
|
||||
inline watch_list const& get_wlist(literal l) const override { return m_lookahead ? m_lookahead->get_wlist(l) : m_solver->get_wlist(l); }
|
||||
inline void assign(literal l, justification j) override {
|
||||
if (m_lookahead) m_lookahead->assign(l);
|
||||
else m_solver->assign(l, j);
|
||||
}
|
||||
inline void set_conflict(justification j, literal l) {
|
||||
inline void set_conflict(justification j, literal l) override {
|
||||
if (m_lookahead) m_lookahead->set_conflict();
|
||||
else m_solver->set_conflict(j, l);
|
||||
}
|
||||
inline config const& get_config() const { return m_lookahead ? m_lookahead->get_config() : m_solver->get_config(); }
|
||||
inline config const& get_config() const override {
|
||||
return m_lookahead ? m_lookahead->get_config() : m_solver->get_config();
|
||||
}
|
||||
|
||||
|
||||
mutable bool m_overflow;
|
||||
mutable bool m_overflow{ false };
|
||||
void reset_active_var_set();
|
||||
bool test_and_set_active(bool_var v);
|
||||
void inc_coeff(literal l, unsigned offset);
|
||||
|
@ -499,10 +337,7 @@ namespace sat {
|
|||
bool validate_assign(literal_vector const& lits, literal lit);
|
||||
bool validate_lemma();
|
||||
bool validate_ineq(ineq const& ineq) const;
|
||||
bool validate_unit_propagation(card const& c, literal alit) const;
|
||||
bool validate_unit_propagation(pb const& p, literal alit) const;
|
||||
bool validate_unit_propagation(pb const& p, literal_vector const& r, literal alit) const;
|
||||
bool validate_unit_propagation(xr const& x, literal alit) const;
|
||||
bool validate_conflict(literal_vector const& lits, ineq& p);
|
||||
bool validate_watch_literals() const;
|
||||
bool validate_watch_literal(literal lit) const;
|
||||
|
@ -528,9 +363,6 @@ namespace sat {
|
|||
unsigned get_coeff(ineq const& pb, literal lit);
|
||||
|
||||
void display(std::ostream& out, ineq const& p, bool values = false) const;
|
||||
void display(std::ostream& out, card const& c, bool values) const;
|
||||
void display(std::ostream& out, pb const& p, bool values) const;
|
||||
void display(std::ostream& out, xr const& c, bool values) const;
|
||||
void display_lit(std::ostream& out, literal l, unsigned sz, bool values) const;
|
||||
|
||||
constraint* add_at_least(literal l, literal_vector const& lits, unsigned k, bool learned);
|
||||
|
@ -560,9 +392,9 @@ namespace sat {
|
|||
literal internalize_xor(expr* e, bool sign, bool root);
|
||||
|
||||
// Decompile
|
||||
expr_ref get_card(std::function<expr_ref(sat::literal)>& l2e, ba_solver::card const& c);
|
||||
expr_ref get_pb(std::function<expr_ref(sat::literal)>& l2e, ba_solver::pb const& p);
|
||||
expr_ref get_xor(std::function<expr_ref(sat::literal)>& l2e, ba_solver::xr const& x);
|
||||
expr_ref get_card(std::function<expr_ref(sat::literal)>& l2e, card const& c);
|
||||
expr_ref get_pb(std::function<expr_ref(sat::literal)>& l2e, pb const& p);
|
||||
expr_ref get_xor(std::function<expr_ref(sat::literal)>& l2e, xr const& x);
|
||||
|
||||
public:
|
||||
ba_solver(euf::solver& ctx, euf::theory_id id);
|
||||
|
@ -598,7 +430,6 @@ namespace sat {
|
|||
void pop_reinit() override;
|
||||
void gc() override;
|
||||
unsigned max_var(unsigned w) const override;
|
||||
double get_reward(literal l, ext_justification_idx idx, literal_occs_fun& occs) const override;
|
||||
bool is_extended_binary(ext_justification_idx idx, literal_vector & r) override;
|
||||
void init_use_list(ext_use_list& ul) override;
|
||||
bool is_blocked(literal l, ext_constraint_idx idx) override;
|
||||
|
|
52
src/sat/smt/ba_solver_interface.h
Normal file
52
src/sat/smt/ba_solver_interface.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
ba_solver_interface.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Abstract interface for a solver,
|
||||
covers functionality exposed by the sat and lookahead solvers.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2017-01-30
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sat/sat_types.h"
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/smt/sat_smt.h"
|
||||
|
||||
|
||||
namespace ba {
|
||||
|
||||
typedef sat::literal literal;
|
||||
typedef sat::bool_var bool_var;
|
||||
typedef sat::literal_vector literal_vector;
|
||||
typedef std::pair<unsigned, literal> wliteral;
|
||||
class constraint;
|
||||
|
||||
class solver_interface {
|
||||
public:
|
||||
virtual lbool value(bool_var v) const = 0;
|
||||
virtual lbool value(literal lit) const = 0;
|
||||
virtual bool is_false(literal lit) const = 0;
|
||||
virtual unsigned lvl(literal lit) const = 0;
|
||||
virtual unsigned lvl(bool_var v) const = 0;
|
||||
virtual bool inconsistent() const = 0;
|
||||
virtual sat::watch_list& get_wlist(literal l) = 0;
|
||||
virtual sat::watch_list const& get_wlist(literal l) const = 0;
|
||||
virtual void assign(literal l, sat::justification j) = 0;
|
||||
virtual void set_conflict(sat::justification j, literal l) = 0;
|
||||
virtual sat::config const& get_config() const = 0;
|
||||
virtual void assign(constraint& c, literal lit) = 0;
|
||||
virtual void set_conflict(constraint& c, literal lit) = 0;
|
||||
};
|
||||
}
|
192
src/sat/smt/ba_xor.cpp
Normal file
192
src/sat/smt/ba_xor.cpp
Normal file
|
@ -0,0 +1,192 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
ba_xor.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Interface for Xor constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2017-01-30
|
||||
|
||||
--*/
|
||||
|
||||
#include "sat/smt/ba_xor.h"
|
||||
#include "sat/smt/ba_solver.h"
|
||||
|
||||
namespace ba {
|
||||
|
||||
xr& constraint::to_xr() {
|
||||
SASSERT(is_xr());
|
||||
return static_cast<xr&>(*this);
|
||||
}
|
||||
|
||||
xr const& constraint::to_xr() const {
|
||||
SASSERT(is_xr());
|
||||
return static_cast<xr const&>(*this);
|
||||
}
|
||||
|
||||
xr::xr(unsigned id, literal_vector const& lits) :
|
||||
constraint(ba::tag_t::xr_t, id, sat::null_literal, lits.size(), get_obj_size(lits.size())) {
|
||||
for (unsigned i = 0; i < size(); ++i) {
|
||||
m_lits[i] = lits[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool xr::is_watching(literal l) const {
|
||||
return
|
||||
l == (*this)[0] || l == (*this)[1] ||
|
||||
~l == (*this)[0] || ~l == (*this)[1];
|
||||
}
|
||||
|
||||
bool xr::well_formed() const {
|
||||
uint_set vars;
|
||||
if (lit() != sat::null_literal) vars.insert(lit().var());
|
||||
for (literal l : *this) {
|
||||
bool_var v = l.var();
|
||||
if (vars.contains(v)) return false;
|
||||
vars.insert(v);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ostream& xr::display(std::ostream& out) const {
|
||||
for (unsigned i = 0; i < size(); ++i) {
|
||||
out << (*this)[i] << " ";
|
||||
if (i + 1 < size()) out << "x ";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void xr::clear_watch(solver_interface& s) {
|
||||
auto& x = *this;
|
||||
x.reset_watch();
|
||||
x.unwatch_literal(s, x[0]);
|
||||
x.unwatch_literal(s, x[1]);
|
||||
x.unwatch_literal(s, ~x[0]);
|
||||
x.unwatch_literal(s, ~x[1]);
|
||||
}
|
||||
|
||||
|
||||
bool xr::init_watch(solver_interface& s) {
|
||||
auto& x = *this;
|
||||
x.clear_watch(s);
|
||||
VERIFY(x.lit() == sat::null_literal);
|
||||
TRACE("ba", x.display(tout););
|
||||
unsigned sz = x.size();
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0; i < sz && j < 2; ++i) {
|
||||
if (s.value(x[i]) == l_undef) {
|
||||
x.swap(i, j);
|
||||
++j;
|
||||
}
|
||||
}
|
||||
switch (j) {
|
||||
case 0:
|
||||
if (!parity(s, 0)) {
|
||||
unsigned l = s.lvl(x[0]);
|
||||
j = 1;
|
||||
for (unsigned i = 1; i < sz; ++i) {
|
||||
if (s.lvl(x[i]) > l) {
|
||||
j = i;
|
||||
l = s.lvl(x[i]);
|
||||
}
|
||||
}
|
||||
s.set_conflict(x, x[j]);
|
||||
}
|
||||
return false;
|
||||
case 1:
|
||||
SASSERT(x.lit() == sat::null_literal || s.value(x.lit()) == l_true);
|
||||
s.assign(x, parity(s, 1) ? ~x[0] : x[0]);
|
||||
return false;
|
||||
default:
|
||||
SASSERT(j == 2);
|
||||
x.watch_literal(s, x[0]);
|
||||
x.watch_literal(s, x[1]);
|
||||
x.watch_literal(s, ~x[0]);
|
||||
x.watch_literal(s, ~x[1]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool xr::parity(solver_interface const& s, unsigned offset) const {
|
||||
auto const& x = *this;
|
||||
bool odd = false;
|
||||
unsigned sz = x.size();
|
||||
for (unsigned i = offset; i < sz; ++i) {
|
||||
SASSERT(s.value(x[i]) != l_undef);
|
||||
if (s.value(x[i]) == l_true) {
|
||||
odd = !odd;
|
||||
}
|
||||
}
|
||||
return odd;
|
||||
}
|
||||
|
||||
|
||||
std::ostream& xr::display(std::ostream& out, solver_interface const& s, bool values) const {
|
||||
auto const& x = *this;
|
||||
out << "xr: ";
|
||||
for (literal l : x) {
|
||||
out << l;
|
||||
if (values) {
|
||||
out << "@(" << s.value(l);
|
||||
if (s.value(l) != l_undef) {
|
||||
out << ":" << s.lvl(l);
|
||||
}
|
||||
out << ") ";
|
||||
}
|
||||
else {
|
||||
out << " ";
|
||||
}
|
||||
}
|
||||
return out << "\n";
|
||||
}
|
||||
|
||||
bool xr::validate_unit_propagation(solver_interface const& s, literal alit) const {
|
||||
if (s.value(lit()) != l_true) return false;
|
||||
for (unsigned i = 1; i < size(); ++i) {
|
||||
if (s.value((*this)[i]) == l_undef) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
lbool xr::eval(solver_interface const& s) const {
|
||||
auto const& x = *this;
|
||||
bool odd = false;
|
||||
for (auto l : x) {
|
||||
switch (s.value(l)) {
|
||||
case l_true: odd = !odd; break;
|
||||
case l_false: break;
|
||||
default: return l_undef;
|
||||
}
|
||||
}
|
||||
return odd ? l_true : l_false;
|
||||
}
|
||||
|
||||
lbool xr::eval(sat::model const& m) const {
|
||||
auto const& x = *this;
|
||||
bool odd = false;
|
||||
for (auto l : x) {
|
||||
switch (ba::value(m, l)) {
|
||||
case l_true: odd = !odd; break;
|
||||
case l_false: break;
|
||||
default: return l_undef;
|
||||
}
|
||||
}
|
||||
return odd ? l_true : l_false;
|
||||
}
|
||||
|
||||
void xr::init_use_list(sat::ext_use_list& ul) const {
|
||||
auto idx = cindex();
|
||||
for (auto l : *this) {
|
||||
ul.insert(l, idx);
|
||||
ul.insert(~l, idx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
53
src/sat/smt/ba_xor.h
Normal file
53
src/sat/smt/ba_xor.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
ba_xor.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Interface for Xor constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2017-01-30
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sat/sat_types.h"
|
||||
#include "sat/smt/ba_constraint.h"
|
||||
|
||||
|
||||
namespace ba {
|
||||
|
||||
class xr : public constraint {
|
||||
literal m_lits[0];
|
||||
public:
|
||||
static size_t get_obj_size(unsigned num_lits) { return sat::constraint_base::obj_size(sizeof(xr) + num_lits * sizeof(literal)); }
|
||||
xr(unsigned id, literal_vector const& lits);
|
||||
literal operator[](unsigned i) const { return m_lits[i]; }
|
||||
literal const* begin() const { return m_lits; }
|
||||
literal const* end() const { return begin() + m_size; }
|
||||
void negate() override { m_lits[0].neg(); }
|
||||
void swap(unsigned i, unsigned j) override { std::swap(m_lits[i], m_lits[j]); }
|
||||
bool is_watching(literal l) const override;
|
||||
literal_vector literals() const override { return literal_vector(size(), begin()); }
|
||||
literal get_lit(unsigned i) const override { return m_lits[i]; }
|
||||
void set_lit(unsigned i, literal l) override { m_lits[i] = l; }
|
||||
bool well_formed() const override;
|
||||
void clear_watch(solver_interface& s) override;
|
||||
bool init_watch(solver_interface& s) override;
|
||||
std::ostream& display(std::ostream& out) const override;
|
||||
std::ostream& display(std::ostream& out, solver_interface const& s, bool values) const override;
|
||||
|
||||
bool parity(solver_interface const& s, unsigned offset) const;
|
||||
bool validate_unit_propagation(solver_interface const& s, literal alit) const override;
|
||||
lbool eval(sat::model const& m) const override;
|
||||
lbool eval(solver_interface const& s) const override;
|
||||
void init_use_list(sat::ext_use_list& ul) const override;
|
||||
bool is_blocked(sat::simplifier& s, literal lit) const override { return false; }
|
||||
};
|
||||
}
|
|
@ -362,7 +362,7 @@ namespace bv {
|
|||
eq eq_proc(*this);
|
||||
hash hash_proc(*this);
|
||||
map<theory_var, theory_var, hash, eq> table(hash_proc, eq_proc);
|
||||
for (unsigned v = 0; v < get_num_vars(); ++v) {
|
||||
for (theory_var v = 0; v < static_cast<theory_var>(get_num_vars()); ++v) {
|
||||
if (!m_bits[v].empty()) {
|
||||
theory_var w = table.insert_if_not_there(v, v);
|
||||
if (v != w && m_find.find(v) != m_find.find(w))
|
||||
|
@ -424,7 +424,7 @@ namespace bv {
|
|||
result->m_bits[i].append(m_bits[i]);
|
||||
result->m_zero_one_bits[i].append(m_zero_one_bits[i]);
|
||||
}
|
||||
for (unsigned i = 0; i < get_num_vars(); ++i)
|
||||
for (theory_var i = 0; i < static_cast<theory_var>(get_num_vars()); ++i)
|
||||
if (find(i) != i)
|
||||
result->m_find.merge(i, find(i));
|
||||
result->m_prop_queue.append(m_prop_queue);
|
||||
|
|
|
@ -96,10 +96,12 @@ namespace euf {
|
|||
void ackerman::cg_conflict_eh(expr * n1, expr * n2) {
|
||||
if (!is_app(n1) || !is_app(n2))
|
||||
return;
|
||||
SASSERT(!s.m_drating);
|
||||
app* a = to_app(n1);
|
||||
app* b = to_app(n2);
|
||||
if (a->get_decl() != b->get_decl() || a->get_num_args() != b->get_num_args())
|
||||
return;
|
||||
TRACE("ack", tout << "conflict eh: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";);
|
||||
insert(a, b);
|
||||
gc();
|
||||
}
|
||||
|
@ -107,13 +109,19 @@ namespace euf {
|
|||
void ackerman::used_eq_eh(expr* a, expr* b, expr* c) {
|
||||
if (a == b || a == c || b == c)
|
||||
return;
|
||||
if (s.m_drating)
|
||||
return;
|
||||
TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";);
|
||||
insert(a, b, c);
|
||||
gc();
|
||||
}
|
||||
|
||||
void ackerman::used_cc_eh(app* a, app* b) {
|
||||
if (s.m_drating)
|
||||
return;
|
||||
TRACE("ack", tout << "used cc: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";);
|
||||
SASSERT(a->get_decl() == b->get_decl());
|
||||
SASSERT(a->get_num_args() == b->get_num_args());
|
||||
SASSERT(a->get_num_args() == b->get_num_args());
|
||||
insert(a, b);
|
||||
gc();
|
||||
}
|
||||
|
@ -153,15 +161,15 @@ namespace euf {
|
|||
}
|
||||
}
|
||||
|
||||
void ackerman::add_cc(expr* _a, expr* _b) {
|
||||
void ackerman::add_cc(expr* _a, expr* _b) {
|
||||
app* a = to_app(_a);
|
||||
app* b = to_app(_b);
|
||||
TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << "\n";);
|
||||
sat::literal_vector lits;
|
||||
unsigned sz = a->get_num_args();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr_ref eq(m.mk_eq(a->get_arg(i), b->get_arg(i)), m);
|
||||
sat::literal lit = s.internalize(eq, true, false, true);
|
||||
lits.push_back(~lit);
|
||||
lits.push_back(s.internalize(eq, true, false, true));
|
||||
}
|
||||
expr_ref eq(m.mk_eq(a, b), m);
|
||||
lits.push_back(s.internalize(eq, false, false, true));
|
||||
|
@ -173,6 +181,7 @@ namespace euf {
|
|||
expr_ref eq1(m.mk_eq(a, c), m);
|
||||
expr_ref eq2(m.mk_eq(b, c), m);
|
||||
expr_ref eq3(m.mk_eq(a, b), m);
|
||||
TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";);
|
||||
lits[0] = s.internalize(eq1, true, false, true);
|
||||
lits[1] = s.internalize(eq2, true, false, true);
|
||||
lits[2] = s.internalize(eq3, false, false, true);
|
||||
|
|
|
@ -84,7 +84,7 @@ namespace euf {
|
|||
void solver::attach_node(euf::enode* n) {
|
||||
expr* e = n->get_expr();
|
||||
if (!m.is_bool(e))
|
||||
log_node(e);
|
||||
drat_log_node(e);
|
||||
else
|
||||
attach_lit(literal(si.add_bool_var(e), false), e);
|
||||
|
||||
|
@ -98,20 +98,23 @@ namespace euf {
|
|||
}
|
||||
|
||||
sat::literal solver::attach_lit(literal lit, expr* e) {
|
||||
sat::bool_var v = lit.var();
|
||||
s().set_external(v);
|
||||
s().set_eliminated(v, false);
|
||||
|
||||
if (lit.sign()) {
|
||||
sat::bool_var v = si.add_bool_var(e);
|
||||
v = si.add_bool_var(e);
|
||||
s().set_external(v);
|
||||
s().set_eliminated(v, false);
|
||||
sat::literal lit2 = literal(v, false);
|
||||
s().mk_clause(~lit, lit2, sat::status::asserted());
|
||||
s().mk_clause(lit, ~lit2, sat::status::asserted());
|
||||
s().mk_clause(~lit, lit2, sat::status::th(m_is_redundant, m.get_basic_family_id()));
|
||||
s().mk_clause(lit, ~lit2, sat::status::th(m_is_redundant, m.get_basic_family_id()));
|
||||
lit = lit2;
|
||||
}
|
||||
sat::bool_var v = lit.var();
|
||||
m_var2expr.reserve(v + 1, nullptr);
|
||||
SASSERT(m_var2expr[v] == nullptr);
|
||||
m_var2expr[v] = e;
|
||||
m_var_trail.push_back(v);
|
||||
s().set_external(v);
|
||||
if (!m_egraph.find(e)) {
|
||||
enode* n = m_egraph.mk(e, 0, nullptr);
|
||||
m_egraph.set_merge_enabled(n, false);
|
||||
|
@ -215,14 +218,11 @@ namespace euf {
|
|||
void solver::axiomatize_basic(enode* n) {
|
||||
expr* e = n->get_expr();
|
||||
sat::status st = sat::status::th(m_is_redundant, m.get_basic_family_id());
|
||||
if (m.is_ite(e)) {
|
||||
expr* c = nullptr, * th = nullptr, * el = nullptr;
|
||||
if (!m.is_bool(e) && m.is_ite(e, c, th, el)) {
|
||||
app* a = to_app(e);
|
||||
expr* c = a->get_arg(0);
|
||||
expr* th = a->get_arg(1);
|
||||
expr* el = a->get_arg(2);
|
||||
sat::bool_var v = si.to_bool_var(c);
|
||||
SASSERT(v != sat::null_bool_var);
|
||||
SASSERT(!m.is_bool(e));
|
||||
expr_ref eq_th(m.mk_eq(a, th), m);
|
||||
expr_ref eq_el(m.mk_eq(a, el), m);
|
||||
sat::literal lit_th = internalize(eq_th, false, false, m_is_redundant);
|
||||
|
|
|
@ -44,5 +44,16 @@ namespace euf {
|
|||
}
|
||||
}
|
||||
|
||||
void solver::check_missing_eq_propagation() const {
|
||||
if (s().inconsistent())
|
||||
return;
|
||||
for (enode* n : m_egraph.nodes())
|
||||
if (m.is_false(n->get_root()->get_expr()) && m.is_eq(n->get_expr()) &&
|
||||
n->get_arg(0)->get_root() == n->get_arg(1)->get_root()) {
|
||||
TRACE("euf", display(tout << n->get_expr_id() << ": " << mk_pp(n->get_expr(), m) << "\n"););
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -15,24 +15,32 @@ Author:
|
|||
|
||||
--*/
|
||||
|
||||
#include "ast/ast_ll_pp.h"
|
||||
#include "sat/smt/euf_solver.h"
|
||||
|
||||
namespace euf {
|
||||
|
||||
void solver::init_drat() {
|
||||
if (!m_drat_initialized)
|
||||
get_drat().add_theory(m.get_basic_family_id(), symbol("euf"));
|
||||
if (!m_drat_initialized) {
|
||||
get_drat().add_theory(get_id(), symbol("euf"));
|
||||
get_drat().add_theory(m.get_basic_family_id(), symbol("bool"));
|
||||
}
|
||||
m_drat_initialized = true;
|
||||
}
|
||||
|
||||
void solver::log_node(expr* e) {
|
||||
void solver::drat_log_node(expr* e) {
|
||||
if (!use_drat())
|
||||
return;
|
||||
if (is_app(e)) {
|
||||
std::stringstream strm;
|
||||
strm << mk_ismt2_func(to_app(e)->get_decl(), m);
|
||||
get_drat().def_begin(e->get_id(), strm.str());
|
||||
for (expr* arg : *to_app(e))
|
||||
app* a = to_app(e);
|
||||
if (a->get_num_parameters() == 0)
|
||||
get_drat().def_begin(e->get_id(), a->get_decl()->get_name().str());
|
||||
else {
|
||||
std::stringstream strm;
|
||||
strm << mk_ismt2_func(a->get_decl(), m);
|
||||
get_drat().def_begin(e->get_id(), strm.str());
|
||||
}
|
||||
for (expr* arg : *a)
|
||||
get_drat().def_add_arg(arg->get_id());
|
||||
get_drat().def_end();
|
||||
}
|
||||
|
@ -57,7 +65,7 @@ namespace euf {
|
|||
for (literal lit : r) lits.push_back(~lit);
|
||||
if (l != sat::null_literal)
|
||||
lits.push_back(l);
|
||||
get_drat().add(lits, sat::status::th(true, m.get_basic_family_id()));
|
||||
get_drat().add(lits, sat::status::th(true, get_id()));
|
||||
}
|
||||
|
||||
void solver::log_antecedents(std::ostream& out, literal l, literal_vector const& r) {
|
||||
|
@ -65,14 +73,14 @@ namespace euf {
|
|||
expr* n = m_var2expr[l.var()];
|
||||
out << ~l << ": ";
|
||||
if (!l.sign()) out << "! ";
|
||||
out << mk_pp(n, m) << "\n";
|
||||
out << mk_bounded_pp(n, m) << "\n";
|
||||
SASSERT(s().value(l) == l_true);
|
||||
}
|
||||
if (l != sat::null_literal) {
|
||||
out << l << ": ";
|
||||
if (l.sign()) out << "! ";
|
||||
expr* n = m_var2expr[l.var()];
|
||||
out << mk_pp(n, m) << "\n";
|
||||
out << mk_bounded_pp(n, m) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,27 @@ Author:
|
|||
|
||||
namespace euf {
|
||||
|
||||
solver::solver(ast_manager& m, sat::sat_internalizer& si, params_ref const& p) :
|
||||
extension(m.mk_family_id("euf")),
|
||||
m(m),
|
||||
si(si),
|
||||
m_egraph(m),
|
||||
m_trail(*this),
|
||||
m_rewriter(m),
|
||||
m_unhandled_functions(m),
|
||||
m_solver(nullptr),
|
||||
m_lookahead(nullptr),
|
||||
m_to_m(&m),
|
||||
m_to_si(&si),
|
||||
m_reinit_exprs(m)
|
||||
{
|
||||
updt_params(p);
|
||||
|
||||
std::function<void(std::ostream&, void*)> disp =
|
||||
[&](std::ostream& out, void* j) { display_justification_ptr(out, reinterpret_cast<size_t*>(j)); };
|
||||
m_egraph.set_display_justification(disp);
|
||||
}
|
||||
|
||||
void solver::updt_params(params_ref const& p) {
|
||||
m_config.updt_params(p);
|
||||
}
|
||||
|
@ -129,7 +150,9 @@ namespace euf {
|
|||
ext->get_antecedents(lit, idx, r, probing);
|
||||
}
|
||||
}
|
||||
m_egraph.end_explain();
|
||||
m_egraph.end_explain();
|
||||
TRACE("euf", tout << "eplain " << l << " <- " << r << " " << probing << "\n";);
|
||||
DEBUG_CODE(for (auto lit : r) SASSERT(s().value(lit) == l_true););
|
||||
if (!probing)
|
||||
log_antecedents(l, r);
|
||||
}
|
||||
|
@ -150,7 +173,8 @@ namespace euf {
|
|||
expr* e = nullptr;
|
||||
euf::enode* n = nullptr;
|
||||
|
||||
init_ackerman();
|
||||
if (!probing && !m_drating)
|
||||
init_ackerman();
|
||||
|
||||
switch (j.kind()) {
|
||||
case constraint::kind_t::conflict:
|
||||
|
@ -185,7 +209,7 @@ namespace euf {
|
|||
}
|
||||
|
||||
bool sign = l.sign();
|
||||
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << " " << (sign ? "not ": " ") << e->get_id() << "\n";);
|
||||
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << "\n";);
|
||||
euf::enode* n = m_egraph.find(e);
|
||||
if (!n)
|
||||
return;
|
||||
|
@ -230,6 +254,7 @@ namespace euf {
|
|||
break;
|
||||
propagated = true;
|
||||
}
|
||||
DEBUG_CODE(if (!s().inconsistent()) check_missing_eq_propagation(););
|
||||
return propagated;
|
||||
}
|
||||
|
||||
|
@ -255,11 +280,19 @@ namespace euf {
|
|||
cnstr = lit_constraint().to_index();
|
||||
lit = literal(v, m.is_false(b));
|
||||
}
|
||||
unsigned lvl = s().scope_lvl();
|
||||
|
||||
CTRACE("euf", s().value(lit) != l_true, tout << lit << " " << s().value(lit) << "@" << lvl << " " << is_eq << " " << mk_bounded_pp(a, m) << " = " << mk_bounded_pp(b, m) << "\n";);
|
||||
if (s().value(lit) == l_false && m_ackerman)
|
||||
m_ackerman->cg_conflict_eh(a, b);
|
||||
unsigned lvl = s().scope_lvl();
|
||||
if (s().value(lit) != l_true)
|
||||
switch (s().value(lit)) {
|
||||
case l_true:
|
||||
break;
|
||||
case l_undef:
|
||||
case l_false:
|
||||
s().assign(lit, sat::justification::mk_ext_justification(lvl, cnstr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,15 +328,15 @@ namespace euf {
|
|||
bool cont = false;
|
||||
for (auto* e : m_solvers)
|
||||
switch (e->check()) {
|
||||
case sat::CR_CONTINUE: cont = true; break;
|
||||
case sat::CR_GIVEUP: give_up = true; break;
|
||||
case sat::check_result::CR_CONTINUE: cont = true; break;
|
||||
case sat::check_result::CR_GIVEUP: give_up = true; break;
|
||||
default: break;
|
||||
}
|
||||
if (cont)
|
||||
return sat::CR_CONTINUE;
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
if (give_up)
|
||||
return sat::CR_GIVEUP;
|
||||
return sat::CR_DONE;
|
||||
return sat::check_result::CR_GIVEUP;
|
||||
return sat::check_result::CR_DONE;
|
||||
}
|
||||
|
||||
void solver::push() {
|
||||
|
@ -329,6 +362,7 @@ namespace euf {
|
|||
m_trail.pop_scope(n);
|
||||
m_scopes.shrink(m_scopes.size() - n);
|
||||
si.pop(n);
|
||||
SASSERT(m_egraph.num_scopes() == m_scopes.size());
|
||||
}
|
||||
|
||||
void solver::start_reinit(unsigned n) {
|
||||
|
@ -356,8 +390,8 @@ namespace euf {
|
|||
return;
|
||||
si.set_expr2var_replay(&expr2var_replay);
|
||||
for (auto const& kv : expr2var_replay)
|
||||
si.internalize(kv.m_key, true);
|
||||
si.set_expr2var_replay(nullptr);
|
||||
attach_lit(si.internalize(kv.m_key, true), kv.m_key);
|
||||
si.set_expr2var_replay(nullptr);
|
||||
}
|
||||
|
||||
void solver::pre_simplify() {
|
||||
|
@ -397,6 +431,7 @@ namespace euf {
|
|||
if (n && n->merge_enabled())
|
||||
ok = false;
|
||||
}
|
||||
TRACE("euf", tout << ok << " " << l << " -> " << r << "\n";);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
@ -417,6 +452,13 @@ namespace euf {
|
|||
return out;
|
||||
}
|
||||
|
||||
std::ostream& solver::display_justification_ptr(std::ostream& out, size_t* j) const {
|
||||
if (is_literal(j))
|
||||
return out << get_literal(j) << " ";
|
||||
else
|
||||
return display_justification(out, get_justification(j)) << " ";
|
||||
}
|
||||
|
||||
std::ostream& solver::display_justification(std::ostream& out, ext_justification_idx idx) const {
|
||||
auto* ext = sat::constraint_base::to_extension(idx);
|
||||
if (ext != this)
|
||||
|
@ -480,6 +522,7 @@ namespace euf {
|
|||
return false;
|
||||
check_eqc_bool_assignment();
|
||||
check_missing_bool_enode_propagation();
|
||||
check_missing_eq_propagation();
|
||||
m_egraph.invariant();
|
||||
return true;
|
||||
}
|
||||
|
@ -531,7 +574,7 @@ namespace euf {
|
|||
void solver::init_ackerman() {
|
||||
if (m_ackerman)
|
||||
return;
|
||||
if (m_config.m_dack == DACK_DISABLED)
|
||||
if (m_config.m_dack == dyn_ack_strategy::DACK_DISABLED)
|
||||
return;
|
||||
m_ackerman = alloc(ackerman, *this, m);
|
||||
std::function<void(expr*,expr*,expr*)> used_eq = [&](expr* a, expr* b, expr* lca) {
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace euf {
|
|||
size_t* to_ptr(size_t jst) { return TAG(size_t*, reinterpret_cast<size_t*>(jst), 2); }
|
||||
bool is_literal(size_t* p) const { return GET_TAG(p) == 1; }
|
||||
bool is_justification(size_t* p) const { return GET_TAG(p) == 2; }
|
||||
sat::literal get_literal(size_t* p) {
|
||||
sat::literal get_literal(size_t* p) const {
|
||||
unsigned idx = static_cast<unsigned>(reinterpret_cast<size_t>(UNTAG(size_t*, p)));
|
||||
return sat::to_literal(idx >> 4);
|
||||
}
|
||||
|
@ -86,7 +86,6 @@ namespace euf {
|
|||
stats m_stats;
|
||||
th_rewriter m_rewriter;
|
||||
func_decl_ref_vector m_unhandled_functions;
|
||||
|
||||
|
||||
sat::solver* m_solver { nullptr };
|
||||
sat::lookahead* m_lookahead { nullptr };
|
||||
|
@ -148,13 +147,16 @@ namespace euf {
|
|||
// proofs
|
||||
void log_antecedents(std::ostream& out, literal l, literal_vector const& r);
|
||||
void log_antecedents(literal l, literal_vector const& r);
|
||||
void log_node(expr* n);
|
||||
|
||||
bool m_drat_initialized{ false };
|
||||
void init_drat();
|
||||
|
||||
// invariant
|
||||
void check_eqc_bool_assignment() const;
|
||||
void check_missing_bool_enode_propagation() const;
|
||||
void check_missing_bool_enode_propagation() const;
|
||||
void check_missing_eq_propagation() const;
|
||||
|
||||
std::ostream& display_justification_ptr(std::ostream& out, size_t* j) const;
|
||||
|
||||
constraint& mk_constraint(constraint*& c, constraint::kind_t k);
|
||||
constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); }
|
||||
|
@ -162,21 +164,7 @@ namespace euf {
|
|||
constraint& lit_constraint() { return mk_constraint(m_lit, constraint::kind_t::lit); }
|
||||
|
||||
public:
|
||||
solver(ast_manager& m, sat::sat_internalizer& si, params_ref const& p = params_ref()):
|
||||
m(m),
|
||||
si(si),
|
||||
m_egraph(m),
|
||||
m_trail(*this),
|
||||
m_rewriter(m),
|
||||
m_unhandled_functions(m),
|
||||
m_solver(nullptr),
|
||||
m_lookahead(nullptr),
|
||||
m_to_m(&m),
|
||||
m_to_si(&si),
|
||||
m_reinit_exprs(m)
|
||||
{
|
||||
updt_params(p);
|
||||
}
|
||||
solver(ast_manager& m, sat::sat_internalizer& si, params_ref const& p = params_ref());
|
||||
|
||||
~solver() override {
|
||||
if (m_conflict) dealloc(sat::constraint_base::mem2base_ptr(m_conflict));
|
||||
|
@ -267,6 +255,7 @@ namespace euf {
|
|||
void unhandled_function(func_decl* f);
|
||||
th_rewriter& get_rewriter() { return m_rewriter; }
|
||||
bool is_shared(euf::enode* n) const;
|
||||
void drat_log_node(expr* n);
|
||||
|
||||
void update_model(model_ref& mdl);
|
||||
|
||||
|
|
|
@ -31,17 +31,17 @@ namespace euf {
|
|||
loop:
|
||||
if (!m.inc())
|
||||
throw tactic_exception(m.limit().get_cancel_msg());
|
||||
sat::eframe& fr = m_stack.back();
|
||||
expr* e = fr.m_e;
|
||||
unsigned fsz = m_stack.size();
|
||||
expr* e = m_stack[fsz-1].m_e;
|
||||
if (visited(e)) {
|
||||
m_stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
unsigned num = is_app(e) ? to_app(e)->get_num_args() : 0;
|
||||
|
||||
while (fr.m_idx < num) {
|
||||
expr* arg = to_app(e)->get_arg(fr.m_idx);
|
||||
fr.m_idx++;
|
||||
while (m_stack[fsz - 1].m_idx < num) {
|
||||
expr* arg = to_app(e)->get_arg(m_stack[fsz - 1].m_idx);
|
||||
m_stack[fsz - 1].m_idx++;
|
||||
if (!visit(arg))
|
||||
goto loop;
|
||||
}
|
||||
|
@ -120,25 +120,27 @@ namespace euf {
|
|||
}
|
||||
}
|
||||
|
||||
void th_euf_solver::add_unit(sat::literal lit) {
|
||||
ctx.s().add_clause(1, &lit, sat::status::th(m_is_redundant, get_id()));
|
||||
bool th_euf_solver::add_unit(sat::literal lit) {
|
||||
return !is_true(lit) && (ctx.s().add_clause(1, &lit, sat::status::th(m_is_redundant, get_id())), true);
|
||||
}
|
||||
|
||||
void th_euf_solver::add_clause(sat::literal a, sat::literal b) {
|
||||
bool th_euf_solver::add_clause(sat::literal a, sat::literal b) {
|
||||
sat::literal lits[2] = { a, b };
|
||||
ctx.s().add_clause(2, lits, sat::status::th(m_is_redundant, get_id()));
|
||||
return !is_true(a, b) && (ctx.s().add_clause(2, lits, sat::status::th(m_is_redundant, get_id())), true);
|
||||
}
|
||||
|
||||
void th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) {
|
||||
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) {
|
||||
sat::literal lits[3] = { a, b, c };
|
||||
ctx.s().add_clause(3, lits, sat::status::th(m_is_redundant, get_id()));
|
||||
return !is_true(a, b, c) && (ctx.s().add_clause(3, lits, sat::status::th(m_is_redundant, get_id())), true);
|
||||
}
|
||||
|
||||
void th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d) {
|
||||
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d) {
|
||||
sat::literal lits[4] = { a, b, c, d };
|
||||
ctx.s().add_clause(4, lits, sat::status::th(m_is_redundant, get_id()));
|
||||
return !is_true(a, b, c, d) && (ctx.s().add_clause(4, lits, sat::status::th(m_is_redundant, get_id())), true);
|
||||
}
|
||||
|
||||
bool th_euf_solver::is_true(sat::literal lit) { return ctx.s().value(lit) == l_true; }
|
||||
|
||||
euf::enode* th_euf_solver::mk_enode(expr* e, bool suppress_args) {
|
||||
m_args.reset();
|
||||
if (!suppress_args)
|
||||
|
@ -146,15 +148,9 @@ namespace euf {
|
|||
m_args.push_back(expr2enode(arg));
|
||||
euf::enode* n = ctx.mk_enode(e, m_args.size(), m_args.c_ptr());
|
||||
ctx.attach_node(n);
|
||||
if (m.is_bool(e)) {
|
||||
sat::bool_var v = ctx.get_si().add_bool_var(e);
|
||||
NOT_IMPLEMENTED_YET();
|
||||
// TODO: ctx.attach_lit(literal(v, false), e);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void th_euf_solver::rewrite(expr_ref& a) {
|
||||
ctx.get_rewriter()(a);
|
||||
}
|
||||
|
|
|
@ -86,11 +86,8 @@ namespace euf {
|
|||
class th_solver : public sat::extension, public th_model_builder, public th_decompile, public th_internalizer {
|
||||
protected:
|
||||
ast_manager & m;
|
||||
theory_id m_id;
|
||||
public:
|
||||
th_solver(ast_manager& m, euf::theory_id id): m(m), m_id(id) {}
|
||||
|
||||
unsigned get_id() const override { return m_id; }
|
||||
th_solver(ast_manager& m, euf::theory_id id): extension(id), m(m) {}
|
||||
|
||||
virtual th_solver* fresh(sat::solver* s, euf::solver& ctx) = 0;
|
||||
|
||||
|
@ -115,11 +112,16 @@ namespace euf {
|
|||
region& get_region();
|
||||
|
||||
|
||||
void add_unit(sat::literal lit);
|
||||
void add_clause(sat::literal lit) { add_unit(lit); }
|
||||
void add_clause(sat::literal a, sat::literal b);
|
||||
void add_clause(sat::literal a, sat::literal b, sat::literal c);
|
||||
void add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d);
|
||||
bool add_unit(sat::literal lit);
|
||||
bool add_clause(sat::literal lit) { return add_unit(lit); }
|
||||
bool add_clause(sat::literal a, sat::literal b);
|
||||
bool add_clause(sat::literal a, sat::literal b, sat::literal c);
|
||||
bool add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d);
|
||||
|
||||
bool is_true(sat::literal lit);
|
||||
bool is_true(sat::literal a, sat::literal b) { return is_true(a) || is_true(b); }
|
||||
bool is_true(sat::literal a, sat::literal b, sat::literal c) { return is_true(a) || is_true(b, c); }
|
||||
bool is_true(sat::literal a, sat::literal b, sat::literal c, sat::literal d) { return is_true(a) || is_true(b, c, c); }
|
||||
|
||||
euf::enode* e_internalize(expr* e) { internalize(e, m_is_redundant); return expr2enode(e); }
|
||||
euf::enode* mk_enode(expr* e, bool suppress_args);
|
||||
|
|
|
@ -22,105 +22,12 @@ Revision History:
|
|||
#include "sat/sat_simplifier_params.hpp"
|
||||
#include "sat/sat_xor_finder.h"
|
||||
|
||||
|
||||
namespace sat {
|
||||
ba_solver::xr& ba_solver::constraint::to_xr() {
|
||||
SASSERT(is_xr());
|
||||
return static_cast<xr&>(*this);
|
||||
}
|
||||
|
||||
ba_solver::xr const& ba_solver::constraint::to_xr() const{
|
||||
SASSERT(is_xr());
|
||||
return static_cast<xr const&>(*this);
|
||||
}
|
||||
|
||||
ba_solver::xr::xr(unsigned id, literal_vector const& lits):
|
||||
constraint(xr_t, id, null_literal, lits.size(), get_obj_size(lits.size())) {
|
||||
for (unsigned i = 0; i < size(); ++i) {
|
||||
m_lits[i] = lits[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool ba_solver::xr::is_watching(literal l) const {
|
||||
return
|
||||
l == (*this)[0] || l == (*this)[1] ||
|
||||
~l == (*this)[0] || ~l == (*this)[1];
|
||||
}
|
||||
|
||||
bool ba_solver::xr::well_formed() const {
|
||||
uint_set vars;
|
||||
if (lit() != null_literal) vars.insert(lit().var());
|
||||
for (literal l : *this) {
|
||||
bool_var v = l.var();
|
||||
if (vars.contains(v)) return false;
|
||||
vars.insert(v);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------
|
||||
// xr:
|
||||
|
||||
void ba_solver::clear_watch(xr& x) {
|
||||
x.clear_watch();
|
||||
unwatch_literal(x[0], x);
|
||||
unwatch_literal(x[1], x);
|
||||
unwatch_literal(~x[0], x);
|
||||
unwatch_literal(~x[1], x);
|
||||
}
|
||||
|
||||
bool ba_solver::parity(xr const& x, unsigned offset) const {
|
||||
bool odd = false;
|
||||
unsigned sz = x.size();
|
||||
for (unsigned i = offset; i < sz; ++i) {
|
||||
SASSERT(value(x[i]) != l_undef);
|
||||
if (value(x[i]) == l_true) {
|
||||
odd = !odd;
|
||||
}
|
||||
}
|
||||
return odd;
|
||||
}
|
||||
|
||||
bool ba_solver::init_watch(xr& x) {
|
||||
clear_watch(x);
|
||||
VERIFY(x.lit() == null_literal);
|
||||
TRACE("ba", display(tout, x, true););
|
||||
unsigned sz = x.size();
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0; i < sz && j < 2; ++i) {
|
||||
if (value(x[i]) == l_undef) {
|
||||
x.swap(i, j);
|
||||
++j;
|
||||
}
|
||||
}
|
||||
switch (j) {
|
||||
case 0:
|
||||
if (!parity(x, 0)) {
|
||||
unsigned l = lvl(x[0]);
|
||||
j = 1;
|
||||
for (unsigned i = 1; i < sz; ++i) {
|
||||
if (lvl(x[i]) > l) {
|
||||
j = i;
|
||||
l = lvl(x[i]);
|
||||
}
|
||||
}
|
||||
set_conflict(x, x[j]);
|
||||
}
|
||||
return false;
|
||||
case 1:
|
||||
SASSERT(x.lit() == null_literal || value(x.lit()) == l_true);
|
||||
assign(x, parity(x, 1) ? ~x[0] : x[0]);
|
||||
return false;
|
||||
default:
|
||||
SASSERT(j == 2);
|
||||
watch_literal(x[0], x);
|
||||
watch_literal(x[1], x);
|
||||
watch_literal(~x[0], x);
|
||||
watch_literal(~x[1], x);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lbool ba_solver::add_assign(xr& x, literal alit) {
|
||||
// literal is assigned
|
||||
unsigned sz = x.size();
|
||||
|
@ -136,10 +43,10 @@ namespace sat {
|
|||
literal lit = x[i];
|
||||
if (value(lit) == l_undef) {
|
||||
x.swap(index, i);
|
||||
unwatch_literal(~alit, x);
|
||||
x.unwatch_literal(*this, ~alit);
|
||||
// alit gets unwatched by propagate_core because we return l_undef
|
||||
watch_literal(lit, x);
|
||||
watch_literal(~lit, x);
|
||||
x.watch_literal(*this, lit);
|
||||
x.watch_literal(*this, ~lit);
|
||||
TRACE("ba", tout << "swap in: " << lit << " " << x << "\n";);
|
||||
return l_undef;
|
||||
}
|
||||
|
@ -150,10 +57,10 @@ namespace sat {
|
|||
// alit resides at index 1.
|
||||
VERIFY(x[1].var() == alit.var());
|
||||
if (value(x[0]) == l_undef) {
|
||||
bool p = parity(x, 1);
|
||||
bool p = x.parity(*this, 1);
|
||||
assign(x, p ? ~x[0] : x[0]);
|
||||
}
|
||||
else if (!parity(x, 0)) {
|
||||
else if (!x.parity(*this, 0)) {
|
||||
set_conflict(x, ~x[1]);
|
||||
}
|
||||
return inconsistent() ? l_false : l_true;
|
||||
|
@ -232,7 +139,7 @@ namespace sat {
|
|||
}
|
||||
|
||||
|
||||
ba_solver::constraint* ba_solver::add_xr(literal_vector const& _lits, bool learned) {
|
||||
constraint* ba_solver::add_xr(literal_vector const& _lits, bool learned) {
|
||||
literal_vector lits;
|
||||
u_map<bool> var2sign;
|
||||
bool sign = false, odd = false;
|
||||
|
@ -400,31 +307,6 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
|
||||
lbool ba_solver::eval(xr const& x) const {
|
||||
bool odd = false;
|
||||
|
||||
for (auto l : x) {
|
||||
switch (value(l)) {
|
||||
case l_true: odd = !odd; break;
|
||||
case l_false: break;
|
||||
default: return l_undef;
|
||||
}
|
||||
}
|
||||
return odd ? l_true : l_false;
|
||||
}
|
||||
|
||||
lbool ba_solver::eval(model const& m, xr const& x) const {
|
||||
bool odd = false;
|
||||
|
||||
for (auto l : x) {
|
||||
switch (value(m, l)) {
|
||||
case l_true: odd = !odd; break;
|
||||
case l_false: break;
|
||||
default: return l_undef;
|
||||
}
|
||||
}
|
||||
return odd ? l_true : l_false;
|
||||
}
|
||||
|
||||
void ba_solver::pre_simplify(xor_finder& xf, constraint& c) {
|
||||
if (c.is_xr() && c.size() <= xf.max_xor_size()) {
|
||||
|
@ -532,30 +414,5 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
|
||||
void ba_solver::display(std::ostream& out, xr const& x, bool values) const {
|
||||
out << "xr: ";
|
||||
for (literal l : x) {
|
||||
out << l;
|
||||
if (values) {
|
||||
out << "@(" << value(l);
|
||||
if (value(l) != l_undef) {
|
||||
out << ":" << lvl(l);
|
||||
}
|
||||
out << ") ";
|
||||
}
|
||||
else {
|
||||
out << " ";
|
||||
}
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
bool ba_solver::validate_unit_propagation(xr const& x, literal alit) const {
|
||||
if (value(x.lit()) != l_true) return false;
|
||||
for (unsigned i = 1; i < x.size(); ++i) {
|
||||
if (value(x[i]) == l_undef) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue