3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-06-29 11:58:51 +00:00

Merge branch 'master' into c3

This commit is contained in:
CEisenhofer 2026-06-03 17:33:26 +02:00
commit 043c6c0ad1
259 changed files with 18907 additions and 3725 deletions

View file

@ -87,8 +87,8 @@ namespace smt {
tout << mk_ismt2_pp(n1->get_expr(), m) << "\n" << mk_ismt2_pp(n2->get_expr(), m) << "\n";);
if (n1->get_owner_id() > n2->get_owner_id())
std::swap(n1, n2);
app * t1 = n1->get_expr();
app * t2 = n2->get_expr();
expr * t1 = n1->get_expr();
expr * t2 = n2->get_expr();
if (m.are_distinct(t1, t2)) {
expr_ref eq(m.mk_eq(t1, t2), m);
ctx.internalize(eq, true);
@ -233,7 +233,7 @@ namespace smt {
void arith_eq_adapter::new_eq_eh(theory_var v1, theory_var v2) {
TRACE(arith_eq_adapter, tout << "v" << v1 << " = v" << v2 << " #" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";);
TRACE(arith_eq_adapter_bug, tout << mk_bounded_pp(get_enode(v1)->get_expr(), get_manager()) << "\n" << mk_bounded_pp(get_enode(v2)->get_expr(), get_manager()) << "\n";);
TRACE(arith_eq_adapter_bug, tout << mk_bounded_pp(get_expr(v1), get_manager()) << "\n" << mk_bounded_pp(get_expr(v2), get_manager()) << "\n";);
mk_axioms(get_enode(v1), get_enode(v2));
}

View file

@ -70,6 +70,7 @@ namespace smt {
context & get_context() const { return m_owner.get_context(); }
ast_manager & get_manager() const { return m_owner.get_manager(); }
enode * get_enode(theory_var v) const { return m_owner.get_enode(v); }
expr * get_expr(theory_var v) const { return m_owner.get_expr(v); }
public:
arith_eq_adapter(theory & owner, arith_util & u):m_owner(owner), m_util(u) {}

View file

@ -53,10 +53,7 @@ void arith_eq_solver::prop_mod_const(expr * e, unsigned depth, numeral const& k,
numeral n;
bool is_int;
if (depth == 0) {
result = e;
}
else if (m_util.is_add(e) || m_util.is_mul(e)) {
if (depth != 0 && (m_util.is_add(e) || m_util.is_mul(e))) {
expr_ref_vector args(m);
expr_ref tmp(m);
app* a = to_app(e);
@ -66,7 +63,7 @@ void arith_eq_solver::prop_mod_const(expr * e, unsigned depth, numeral const& k,
}
m_arith_rewriter.mk_app(a->get_decl(), args.size(), args.data(), result);
}
else if (m_util.is_numeral(e, n, is_int) && is_int) {
else if (depth != 0 && m_util.is_numeral(e, n, is_int) && is_int) {
result = m_util.mk_numeral(mod(n, k), true);
}
else {

View file

@ -101,14 +101,14 @@ namespace smt {
};
class dyn_ack_eq_justification : public justification {
app * m_app1;
app * m_app2;
app * m_r;
expr * m_app1;
expr * m_app2;
expr * m_r;
app * m_eq1;
app * m_eq2;
app * m_eq3;
public:
dyn_ack_eq_justification(app * n1, app * n2, app* r, app* eq1, app* eq2, app* eq3):
dyn_ack_eq_justification(expr * n1, expr * n2, expr* r, app* eq1, app* eq2, app* eq3):
justification(false), // dyn_ack_cc_justifications are not stored in regions.
m_app1(n1),
m_app2(n2),
@ -167,7 +167,7 @@ namespace smt {
dyn_ack_manager::~dyn_ack_manager() {
reset_app_pairs();
reset_app_triples();
reset_expr_triples();
}
void dyn_ack_manager::reset_app_pairs() {
@ -189,7 +189,7 @@ namespace smt {
m_num_propagations_since_last_gc = 0;
m_triple.m_app2num_occs.reset();
reset_app_triples();
reset_expr_triples();
m_triple.m_to_instantiate.reset();
m_triple.m_qhead = 0;
}
@ -230,7 +230,7 @@ namespace smt {
}
}
void dyn_ack_manager::eq_eh(app * n1, app * n2, app* r) {
void dyn_ack_manager::eq_eh(expr * n1, expr * n2, expr* r) {
if (n1 == n2 || r == n1 || r == n2 || m.is_bool(n1)) {
return;
}
@ -238,7 +238,7 @@ namespace smt {
std::swap(n1,n2);
TRACE(dyn_ack,
tout << mk_pp(n1, m) << " = " << mk_pp(n2, m) << " = " << mk_pp(r, m) << "\n";);
app_triple tr(n1, n2, r);
expr_triple tr(n1, n2, r);
if (m_triple.m_instantiated.contains(tr)) {
return;
}
@ -361,7 +361,7 @@ namespace smt {
SASSERT(!m_app_pair2num_occs.contains(a1, a2));
return;
}
app_triple tr(0,0,0);
expr_triple tr(0,0,0);
if (m_triple.m_clause2apps.find(cls, tr)) {
[[maybe_unused]] auto [a1, a2, a3] = tr;
SASSERT(a1 && a2 && a3);
@ -451,9 +451,8 @@ namespace smt {
m_triple.m_clause2apps.reset();
}
void dyn_ack_manager::reset_app_triples() {
for (app_triple& p : m_triple.m_apps) {
auto [a1, a2, a3] = p;
void dyn_ack_manager::reset_expr_triples() {
for (auto &[a1,a2,a3] : m_triple.m_apps) {
m.dec_ref(a1);
m.dec_ref(a2);
m.dec_ref(a3);
@ -461,7 +460,7 @@ namespace smt {
m_triple.m_apps.reset();
}
void dyn_ack_manager::instantiate(app * n1, app * n2, app* r) {
void dyn_ack_manager::instantiate(expr * n1, expr * n2, expr* r) {
context& ctx = m_context;
SASSERT(m_params.m_dack != dyn_ack_strategy::DACK_DISABLED);
SASSERT(n1 != n2 && n1 != r && n2 != r);
@ -471,7 +470,7 @@ namespace smt {
<< mk_pp(n2, m) << "\n"
<< mk_pp(r, m) << "\n";
);
app_triple tr(n1, n2, r);
expr_triple tr(n1, n2, r);
SASSERT(m_triple.m_app2num_occs.contains(n1, n2, r));
m_triple.m_app2num_occs.erase(n1, n2, r);
// pair n1,n2 is still in m_triple.m_apps
@ -504,22 +503,22 @@ namespace smt {
}
struct app_triple_lt {
typedef triple<app *, app *, app*> app_triple;
typedef obj_triple_map<app, app, app, unsigned> app_triple2num_occs;
app_triple2num_occs & m_app_triple2num_occs;
struct expr_triple_lt {
typedef triple<expr *, expr *,expr *> expr_triple;
typedef obj_triple_map<expr, expr, expr, unsigned> expr_triple2num_occs;
expr_triple2num_occs & m_expr_triple2num_occs;
app_triple_lt(app_triple2num_occs & m):
m_app_triple2num_occs(m) {
expr_triple_lt(expr_triple2num_occs & m):
m_expr_triple2num_occs(m) {
}
bool operator()(app_triple const & p1, app_triple const & p2) const {
bool operator()(expr_triple const & p1, expr_triple const & p2) const {
auto [a1_1, a1_2, a1_3] = p1;
auto [a2_1, a2_2, a2_3] = p2;
unsigned n1 = 0;
unsigned n2 = 0;
m_app_triple2num_occs.find(a1_1, a1_2, a1_3, n1);
m_app_triple2num_occs.find(a2_1, a2_2, a2_3, n2);
m_expr_triple2num_occs.find(a1_1, a1_2, a1_3, n1);
m_expr_triple2num_occs.find(a2_1, a2_2, a2_3, n2);
SASSERT(n1 > 0);
SASSERT(n2 > 0);
return n1 > n2;
@ -530,11 +529,11 @@ namespace smt {
TRACE(dyn_ack, tout << "dyn_ack GC\n";);
m_triple.m_to_instantiate.reset();
m_triple.m_qhead = 0;
svector<app_triple>::iterator it = m_triple.m_apps.begin();
svector<app_triple>::iterator end = m_triple.m_apps.end();
svector<app_triple>::iterator it2 = it;
svector<expr_triple>::iterator it = m_triple.m_apps.begin();
svector<expr_triple>::iterator end = m_triple.m_apps.end();
svector<expr_triple>::iterator it2 = it;
for (; it != end; ++it) {
app_triple & p = *it;
expr_triple & p = *it;
auto [a1, a2, a3] = p;
if (m_triple.m_instantiated.contains(p)) {
TRACE(dyn_ack, tout << "1) erasing:\n" << mk_pp(a1, m) << "\n" << mk_pp(a2, m) << "\n";);
@ -548,7 +547,7 @@ namespace smt {
m_triple.m_app2num_occs.find(a1, a2, a3, num_occs);
// The following invariant is not true. a1 and
// a2 may have been instantiated, and removed from
// m_app_triple2num_occs, but not from m_app_triples.
// m_triple.m_app2num_occs, but not from m_triple.m_apps.
//
// SASSERT(num_occs > 0);
num_occs = static_cast<unsigned>(num_occs * m_params.m_dack_gc_inv_decay);
@ -568,8 +567,8 @@ namespace smt {
m_triple.m_to_instantiate.push_back(p);
}
m_triple.m_apps.set_end(it2);
app_triple_lt f(m_triple.m_app2num_occs);
// app_triple_lt is not a total order
expr_triple_lt f(m_triple.m_app2num_occs);
// expr_triple_lt is not a total order
std::stable_sort(m_triple.m_to_instantiate.begin(), m_triple.m_to_instantiate.end(), f);
}

View file

@ -36,11 +36,11 @@ namespace smt {
typedef obj_pair_hashtable<app, app> app_pair_set;
typedef obj_map<clause, app_pair> clause2app_pair;
typedef triple<app *, app *,app *> app_triple;
typedef obj_triple_map<app, app, app, unsigned> app_triple2num_occs;
typedef svector<app_triple> app_triple_vector;
typedef obj_triple_hashtable<app, app, app> app_triple_set;
typedef obj_map<clause, app_triple> clause2app_triple;
typedef triple<expr *, expr *,expr *> expr_triple;
typedef obj_triple_map<expr, expr, expr, unsigned> expr_triple2num_occs;
typedef svector<expr_triple> expr_triple_vector;
typedef obj_triple_hashtable<expr, expr, expr> expr_triple_set;
typedef obj_map<clause, expr_triple> clause2expr_triple;
context & m_context;
ast_manager & m;
@ -55,14 +55,14 @@ namespace smt {
clause2app_pair m_clause2app_pair;
struct _triple {
app_triple2num_occs m_app2num_occs;
app_triple_vector m_apps;
app_triple_vector m_to_instantiate;
expr_triple2num_occs m_app2num_occs;
expr_triple_vector m_apps;
expr_triple_vector m_to_instantiate;
unsigned m_qhead;
unsigned m_num_instances;
unsigned m_num_propagations_since_last_gc;
app_triple_set m_instantiated;
clause2app_triple m_clause2apps;
expr_triple_set m_instantiated;
clause2expr_triple m_clause2apps;
};
_triple m_triple;
@ -76,9 +76,9 @@ namespace smt {
literal mk_eq(expr * n1, expr * n2);
void cg_eh(app * n1, app * n2);
void eq_eh(app * n1, app * n2, app* r);
void instantiate(app * n1, app * n2, app* r);
void reset_app_triples();
void eq_eh(expr * n1, expr * n2, expr* r);
void instantiate(expr * n1, expr * n2, expr* r);
void reset_expr_triples();
void gc_triples();
public:
@ -112,7 +112,7 @@ namespace smt {
/**
\brief This method is invoked when equalities are used during conflict resolution.
*/
void used_eq_eh(app * n1, app * n2, app* r) {
void used_eq_eh(expr * n1, expr * n2, expr* r) {
if (m_params.m_dack_eq)
eq_eh(n1, n2, r);
}

View file

@ -401,10 +401,7 @@ void expr_strong_context_simplifier::simplify_basic(expr* fml, expr_ref& result)
args.push_back(arg);
}
}
else if (!m.is_bool(arg)) {
args.push_back(arg);
}
else if (!n2) {
else if (!n2 && m.is_bool(arg)) {
n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true));
todo.push_back(arg);
parent_ids.push_back(self_pos);
@ -677,10 +674,7 @@ void expr_strong_context_simplifier::simplify_model_based(expr* fml, expr_ref& r
args.push_back(arg);
}
}
else if (!m.is_bool(arg)) {
args.push_back(arg);
}
else if (!n2) {
else if (!n2 && m.is_bool(arg)) {
n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true));
todo.push_back(arg);
parent_ids.push_back(self_pos);

View file

@ -20,10 +20,9 @@ Revision History:
namespace smt {
fingerprint::fingerprint(region & r, void * d, unsigned d_h, expr* def, unsigned n, enode * const * args):
fingerprint::fingerprint(region & r, void * d, unsigned d_h, unsigned n, enode * const * args):
m_data(d),
m_data_hash(d_h),
m_def(def),
m_num_args(n),
m_args(nullptr) {
m_args = new (r) enode*[n];
@ -62,7 +61,7 @@ namespace smt {
}
fingerprint * fingerprint_set::insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def) {
fingerprint * fingerprint_set::insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args) {
struct arg_data {
unsigned data_hash;
@ -93,9 +92,8 @@ namespace smt {
return nullptr;
}
TRACE(fingerprint_bug, tout << "inserting @" << m_scopes.size() << " " << *d;);
fingerprint * f = new (m_region) fingerprint(m_region, data, data_hash, def, num_args, d->m_args);
fingerprint * f = new (m_region) fingerprint(m_region, data, data_hash, num_args, d->m_args);
m_fingerprints.push_back(f);
m_defs.push_back(def);
m_set.insert(f);
return f;
}
@ -114,7 +112,6 @@ namespace smt {
void fingerprint_set::reset() {
m_set.reset();
m_fingerprints.reset();
m_defs.reset();
}
void fingerprint_set::push_scope() {
@ -134,7 +131,6 @@ namespace smt {
m_set.erase(m_fingerprints[i]);
}
m_fingerprints.shrink(old_size);
m_defs.shrink(old_size);
m_scopes.shrink(new_lvl);
TRACE(fingerprint_bug, tout << "pop @" << m_scopes.size() << "\n";);
}

View file

@ -27,16 +27,14 @@ namespace smt {
protected:
void* m_data = nullptr;
unsigned m_data_hash = 0;
expr* m_def = nullptr;
unsigned m_num_args = 0;
enode** m_args = nullptr;
friend class fingerprint_set;
fingerprint() = default;
public:
fingerprint(region & r, void * d, unsigned d_hash, expr* def, unsigned n, enode * const * args);
fingerprint(region & r, void * d, unsigned d_hash, unsigned n, enode * const * args);
void * get_data() const { return m_data; }
expr * get_def() const { return m_def; }
unsigned get_data_hash() const { return m_data_hash; }
unsigned get_num_args() const { return m_num_args; }
enode * const * get_args() const { return m_args; }
@ -59,7 +57,6 @@ namespace smt {
region & m_region;
set m_set;
ptr_vector<fingerprint> m_fingerprints;
expr_ref_vector m_defs;
unsigned_vector m_scopes;
ptr_vector<enode> m_tmp;
fingerprint m_dummy;
@ -67,8 +64,8 @@ namespace smt {
fingerprint * mk_dummy(void * data, unsigned data_hash, unsigned num_args, enode * const * args);
public:
fingerprint_set(ast_manager& m, region & r): m_region(r), m_defs(m) {}
fingerprint * insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def);
fingerprint_set(ast_manager& m, region & r): m_region(r) {}
fingerprint * insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args);
unsigned size() const { return m_fingerprints.size(); }
bool contains(void * data, unsigned data_hash, unsigned num_args, enode * const * args);
void reset();

View file

@ -215,6 +215,7 @@ namespace {
unsigned short m_num_args;
unsigned m_ireg;
unsigned m_oreg;
unsigned m_curr_max_generation = 0;
};
struct get_cgr : public instruction {
@ -1114,8 +1115,9 @@ namespace {
best_j = j;
}
}
if (best == nullptr)
continue;
m_mp_already_processed[best_j] = true;
SASSERT(best != 0);
app * p = best;
func_decl * lbl = p->get_decl();
unsigned short num_args = p->get_num_args();
@ -1225,7 +1227,11 @@ namespace {
SASSERT(head->m_next == 0);
m_seq.push_back(m_ct_manager.mk_yield(m_qa, m_mp, m_qa->get_num_decls(), reinterpret_cast<unsigned*>(m_vars.begin())));
unsigned num_decls = m_qa->get_num_decls();
unsigned_vector var_regs(num_decls);
for (unsigned i = 0; i < num_decls; ++i)
var_regs[i] = static_cast<unsigned>(m_vars[i]);
m_seq.push_back(m_ct_manager.mk_yield(m_qa, m_mp, num_decls, var_regs.data()));
for (instruction* curr : m_seq) {
head->m_next = curr;
@ -1882,33 +1888,43 @@ namespace {
m_used_enodes.push_back(std::make_tuple(prev, n));
}
void get_f_app(func_decl* lbl, unsigned num_expected_args, enode* curr, enode*& matching_cgr, enode*& min_gen_match) {
if (curr->get_decl() == lbl && curr->get_num_args() == num_expected_args) {
if (curr->is_cgr() && !matching_cgr)
matching_cgr = curr;
if (!min_gen_match || min_gen_match->get_generation() > curr->get_generation()) {
min_gen_match = curr;
}
}
}
// We have to provide the number of expected arguments because we have flat-assoc applications such as +.
// Flat-assoc applications may have arbitrary number of arguments.
enode * get_first_f_app(func_decl * lbl, unsigned num_expected_args, enode * curr) {
enode * first = curr;
enode *matching_cgr = nullptr, *min_gen_match = nullptr;
do {
if (curr->get_decl() == lbl && curr->is_cgr() && curr->get_num_args() == num_expected_args) {
update_max_generation(curr, first);
return curr;
}
get_f_app(lbl, num_expected_args, curr, matching_cgr, min_gen_match);
curr = curr->get_next();
}
while (curr != first);
return nullptr;
if (matching_cgr)
update_max_generation(min_gen_match, first);
return matching_cgr;
}
enode * get_next_f_app(func_decl * lbl, unsigned num_expected_args, enode * first, enode * curr) {
curr = curr->get_next();
while (curr != first) {
if (curr->get_decl() == lbl && curr->is_cgr() && curr->get_num_args() == num_expected_args) {
update_max_generation(curr, first);
if (curr->get_decl() == lbl && curr->get_num_args() == num_expected_args && curr->is_cgr())
return curr;
}
curr = curr->get_next();
}
return nullptr;
}
/**
\brief Execute the is_cgr instruction.
Return true if succeeded, and false if backtracking is needed.
@ -2471,6 +2487,7 @@ namespace {
m_backtrack_stack[m_top].m_old_max_generation = m_curr_max_generation; \
m_backtrack_stack[m_top].m_old_used_enodes_size = m_curr_used_enodes_size; \
m_backtrack_stack[m_top].m_curr = m_app; \
const_cast<bind*>(static_cast<const bind*>(m_pc))->m_curr_max_generation = m_max_generation; \
m_top++;
BIND_COMMON();
@ -2738,7 +2755,8 @@ namespace {
#define BBIND_COMMON() m_b = static_cast<const bind*>(bp.m_instr); \
m_n1 = m_registers[m_b->m_ireg]; \
m_app = get_next_f_app(m_b->m_label, m_b->m_num_args, m_n1, bp.m_curr); \
if (m_app == 0) { \
m_max_generation = m_b->m_curr_max_generation; \
if (!m_app) { \
m_top--; \
goto backtrack; \
} \
@ -2909,6 +2927,8 @@ namespace {
SASSERT(m.is_pattern(mp));
SASSERT(first_idx < mp->get_num_args());
app * p = to_app(mp->get_arg(first_idx));
if (is_ground(p))
return;
func_decl * lbl = p->get_decl();
unsigned lbl_id = lbl->get_small_id();
m_trees.reserve(lbl_id+1, nullptr);
@ -3736,7 +3756,7 @@ namespace {
}
void match_new_patterns() {
TRACE(mam_new_pat, tout << "matching new patterns:\n";);
TRACE(mam, tout << "matching new patterns:\n";);
m_tmp_trees_to_delete.reset();
for (auto const& kv : m_new_patterns) {
if (m_context.get_cancel_flag()) {
@ -3782,8 +3802,14 @@ namespace {
for (unsigned i = 0; i < num_patterns; ++i) {
app * pat = to_app(mp->get_arg(i));
TRACE(mam_pat, tout << mk_ismt2_pp(qa, m) << "\npat:\n" << mk_ismt2_pp(pat, m) << "\n";);
SASSERT(!pat->is_ground());
todo.push_back(pat);
if (pat->is_ground()) {
enode * e = mk_enode(m_context, qa, pat);
m_context.mark_as_relevant(e);
m_context.push_trail(add_shared_enode_trail(*this, e));
m_shared_enodes.insert(e);
}
else
todo.push_back(pat);
}
while (!todo.empty()) {
app * n = todo.back();
@ -3834,10 +3860,10 @@ namespace {
// Ground patterns are discarded.
// However, the simplifier may turn a non-ground pattern into a ground one.
// So, we should check it again here.
unsigned num_patterns = mp->get_num_args();
for (unsigned i = 0; i < num_patterns; ++i)
if (is_ground(mp->get_arg(i)))
return; // ignore multi-pattern containing ground pattern.
if (all_of(*mp, [](expr *arg) { return is_ground(arg); }))
return; // ignore multi-pattern containing only ground pattern.
if (any_of(*mp, [](expr *arg) { return has_quantifiers(arg); }))
return; // patterns with quantifiers are not handled.
update_filters(qa, mp);
collect_ground_exprs(qa, mp);
m_new_patterns.push_back(qp_pair(qa, mp));
@ -3845,7 +3871,7 @@ namespace {
// e-matching. So, for a multi-pattern [ p_1, ..., p_n ],
// we have to make n insertions. In the i-th insertion,
// the pattern p_i is assumed to be the first one.
for (unsigned i = 0; i < num_patterns; ++i)
for (unsigned i = 0; i < mp->get_num_args(); ++i)
m_trees.add_pattern(qa, mp, i);
}
@ -3949,7 +3975,7 @@ namespace {
#endif
unsigned min_gen = 0, max_gen = 0;
m_interpreter.get_min_max_top_generation(min_gen, max_gen);
m_context.add_instance(qa, pat, num_bindings, bindings, nullptr, max_generation, min_gen, max_gen, used_enodes);
m_context.add_instance(qa, pat, num_bindings, bindings, max_generation, min_gen, max_gen, used_enodes);
}
bool is_shared(enode * n) const override {

View file

@ -140,7 +140,7 @@ namespace smt {
tout << "new instance of " << q->get_qid() << ", weight " << q->get_weight()
<< ", generation: " << generation << ", scope_level: " << m_context.get_scope_level() << ", cost: " << cost << "\n";
for (unsigned i = 0; i < f->get_num_args(); ++i) {
tout << "#" << f->get_arg(i)->get_expr_id() << " d:" << f->get_arg(i)->get_expr()->get_depth() << " ";
tout << "#" << f->get_arg(i)->get_expr_id() << " d:" << get_depth(f->get_arg(i)->get_expr()) << " ";
}
tout << "\n";);
TRACE(new_entries_bug, tout << "[qi:insert]\n";);
@ -331,9 +331,6 @@ namespace smt {
unsigned gen = get_new_gen(q, generation, ent.m_cost);
display_instance_profile(f, q, num_bindings, bindings, proof_id, gen);
m_context.internalize_instance(lemma, pr1, gen);
if (f->get_def()) {
m_context.internalize(f->get_def(), true);
}
TRACE_CODE({
static unsigned num_useless = 0;
if (m.is_or(lemma)) {

View file

@ -77,7 +77,7 @@ namespace smt {
}
bool almost_cg_table::cg_eq::operator()(enode * n1, enode * n2) const {
if (n1->get_expr()->get_decl() != n2->get_expr()->get_decl())
if (n1->get_decl() != n2->get_decl() || !n1->is_app())
return false;
unsigned num_args = n1->get_num_args();
if (num_args != n2->get_num_args())

View file

@ -1046,7 +1046,7 @@ namespace {
void operator()(expr * e) {
if (m_context.e_internalized(e)) {
enode * n = m_context.get_enode(e);
n->set_generation(m_context, m_generation);
n->set_generation(&m_context, m_generation);
}
}
};

View file

@ -22,23 +22,25 @@ Revision History:
namespace smt {
bool checker::all_args(app * a, bool is_true) {
bool checker::all_args(app *a, unsigned depth, bool is_true) {
for (expr* arg : *a) {
if (!check(arg, is_true))
if (!check(arg, depth + 1, is_true))
return false;
}
return true;
}
bool checker::any_arg(app * a, bool is_true) {
bool checker::any_arg(app *a, unsigned depth, bool is_true) {
for (expr* arg : *a) {
if (check(arg, is_true))
if (check(arg, depth + 1, is_true))
return true;
}
return false;
}
bool checker::check_core(expr * n, bool is_true) {
bool checker::check_core(expr *n, unsigned depth, bool is_true) {
if (depth > 600)
return false;
SASSERT(m_manager.is_bool(n));
if (m_context.b_internalized(n) && m_context.is_relevant(n)) {
lbool val = m_context.get_assignment(n);
@ -54,11 +56,11 @@ namespace smt {
case OP_FALSE:
return !is_true;
case OP_NOT:
return check(a->get_arg(0), !is_true);
return check(a->get_arg(0), depth + 1, !is_true);
case OP_OR:
return is_true ? any_arg(a, true) : all_args(a, false);
return is_true ? any_arg(a, depth, true) : all_args(a, depth, false);
case OP_AND:
return is_true ? all_args(a, true) : any_arg(a, false);
return is_true ? all_args(a, depth, true) : any_arg(a, depth, false);
case OP_EQ:
if (!m_manager.is_iff(a)) {
enode * lhs = get_enode_eq_to(a->get_arg(0));
@ -74,27 +76,27 @@ namespace smt {
}
else if (is_true) {
return
(check(a->get_arg(0), true) &&
check(a->get_arg(1), true)) ||
(check(a->get_arg(0), false) &&
check(a->get_arg(1), false));
(check(a->get_arg(0), depth + 1, true) &&
check(a->get_arg(1), depth + 1, true)) ||
(check(a->get_arg(0), depth + 1, false) &&
check(a->get_arg(1), depth + 1, false));
}
else {
return
(check(a->get_arg(0), true) &&
check(a->get_arg(1), false)) ||
(check(a->get_arg(0), false) &&
check(a->get_arg(1), true));
(check(a->get_arg(0), depth + 1, true) &&
check(a->get_arg(1), depth + 1, false)) ||
(check(a->get_arg(0), depth + 1, false) &&
check(a->get_arg(1), depth + 1, true));
}
case OP_ITE: {
if (m_context.lit_internalized(a->get_arg(0)) && m_context.is_relevant(a->get_arg(0))) {
switch (m_context.get_assignment(a->get_arg(0))) {
case l_false: return check(a->get_arg(2), is_true);
case l_false: return check(a->get_arg(2), depth + 1, is_true);
case l_undef: return false;
case l_true: return check(a->get_arg(1), is_true);
case l_true: return check(a->get_arg(1), depth + 1, is_true);
}
}
return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true);
return check(a->get_arg(1), depth + 1, is_true) && check(a->get_arg(2), depth + 1, is_true);
}
default:
break;
@ -108,11 +110,11 @@ namespace smt {
return false;
}
bool checker::check(expr * n, bool is_true) {
bool checker::check(expr *n, unsigned depth, bool is_true) {
bool r;
if (n->get_ref_count() > 1 && m_is_true_cache[is_true].find(n, r))
return r;
r = check_core(n, is_true);
r = check_core(n, depth, is_true);
if (n->get_ref_count() > 1)
m_is_true_cache[is_true].insert(n, r);
return r;
@ -156,7 +158,7 @@ namespace smt {
bool checker::is_sat(expr * n, unsigned num_bindings, enode * const * bindings) {
flet<unsigned> l1(m_num_bindings, num_bindings);
flet<enode * const *> l2(m_bindings, bindings);
bool r = check(n, true);
bool r = check(n, 0, true);
m_is_true_cache[0].reset();
m_is_true_cache[1].reset();
m_to_enode_cache.reset();
@ -166,7 +168,7 @@ namespace smt {
bool checker::is_unsat(expr * n, unsigned num_bindings, enode * const * bindings) {
flet<unsigned> l1(m_num_bindings, num_bindings);
flet<enode * const *> l2(m_bindings, bindings);
bool r = check(n, false);
bool r = check(n, 0,false);
m_is_true_cache[0].reset();
m_is_true_cache[1].reset();
m_to_enode_cache.reset();

View file

@ -37,10 +37,10 @@ namespace smt {
unsigned m_num_bindings;
enode * const * m_bindings;
bool all_args(app * a, bool is_true);
bool any_arg(app * a, bool is_true);
bool check_core(expr * n, bool is_true);
bool check(expr * n, bool is_true);
bool all_args(app *a, unsigned depth, bool is_true);
bool any_arg(app *a, unsigned depth, bool is_true);
bool check_core(expr * n, unsigned depth, bool is_true);
bool check(expr *n, unsigned depth, bool is_true);
enode * get_enode_eq_to_core(app * n);
enode * get_enode_eq_to(expr * n);

View file

@ -126,7 +126,7 @@ namespace smt {
break;
case eq_justification::CONGRUENCE: {
CTRACE(dyn_ack_target, !lhs->is_eq(), tout << "dyn_ack_target2: " << lhs->get_owner_id() << " " << rhs->get_owner_id() << "\n";);
m_dyn_ack_manager.used_cg_eh(lhs->get_expr(), rhs->get_expr());
m_dyn_ack_manager.used_cg_eh(lhs->get_app(), rhs->get_app());
unsigned num_args = lhs->get_num_args();
SASSERT(num_args == rhs->get_num_args());
if (js.used_commutativity()) {
@ -787,8 +787,8 @@ namespace smt {
SASSERT(m.has_fact(pr));
expr* f1 = nullptr, *f2 = nullptr;
app * fact = to_app(m.get_fact(pr));
app * n1_owner = n1->get_expr();
app * n2_owner = n2->get_expr();
expr * n1_owner = n1->get_expr();
expr * n2_owner = n2->get_expr();
bool is_eq = m.is_eq(fact, f1, f2);
if (is_eq && is_quantifier(f1)) {
f1 = m_ctx.get_enode(f1)->get_expr();
@ -855,7 +855,7 @@ namespace smt {
case eq_justification::CONGRUENCE:
num_args = n1->get_num_args();
SASSERT(num_args == n2->get_num_args());
SASSERT(n1->get_expr()->get_decl() == n2->get_expr()->get_decl());
SASSERT(n1->get_decl() == n2->get_decl());
if (js.used_commutativity()) {
bool visited = true;
SASSERT(num_args == 2);
@ -878,8 +878,8 @@ namespace smt {
}
if (!visited)
return nullptr;
app * e1 = n1->get_expr();
app * e2 = n2->get_expr();
app * e1 = n1->get_app();
app * e2 = n2->get_app();
app * e2_prime = m.mk_app(e2->get_decl(), e2->get_arg(1), e2->get_arg(0));
proof * pr1 = nullptr;
if (!prs.empty()) {
@ -910,7 +910,7 @@ namespace smt {
}
if (!visited)
return nullptr;
proof * pr = m.mk_congruence(n1->get_expr(), n2->get_expr(), prs.size(), prs.data());
proof * pr = m.mk_congruence(n1->get_app(), n2->get_app(), prs.size(), prs.data());
m_new_proofs.push_back(pr);
return pr;
}

View file

@ -70,7 +70,6 @@ namespace smt {
m_fingerprints(m, get_region()),
m_b_internalized_stack(m),
m_e_internalized_stack(m),
m_l_internalized_stack(m),
m_final_check_idx(0),
m_cg_table(m),
m_conflict(null_b_justification),
@ -82,7 +81,6 @@ namespace smt {
m_unsat_core(m),
m_mk_bool_var_trail(*this),
m_mk_enode_trail(*this),
m_mk_lambda_trail(*this),
m_lemma_visitor(m) {
SASSERT(m_scope_lvl == 0);
@ -217,7 +215,7 @@ namespace smt {
}
ast_translation tr(src_ctx.m, m, false);
for (unsigned i = 0; i < src_ctx.m_user_propagator->get_num_vars(); ++i) {
app* e = src_ctx.m_user_propagator->get_expr(i);
auto e = src_ctx.m_user_propagator->get_expr(i);
m_user_propagator->add_expr(tr(e), true);
}
}
@ -289,8 +287,13 @@ namespace smt {
if (!decision && d.m_phase == l.sign())
m_agility += (1.0 - m_fparams.m_agility_factor);
}
bool new_phase = !l.sign();
m_stats.m_num_assignments++;
if (d.m_phase_available && d.m_phase != new_phase)
m_birthdate[l.var()] = m_stats.m_num_assignments; // reset birthdate when phase changes
d.m_phase_available = true;
d.m_phase = !l.sign();
d.m_phase = new_phase;
TRACE(assign_core, tout << (decision?"decision: ":"propagating: ") << l << " ";
display_literal_smt2(tout, l) << "\n";
tout << "relevant: " << is_relevant_core(l) << " level: " << m_scope_lvl << " is atom " << d.is_atom() << "\n";
@ -649,7 +652,7 @@ namespace smt {
lbool val = get_assignment(v);
if (val != l_true) {
if (val == l_false && js.get_kind() == eq_justification::CONGRUENCE)
m_dyn_ack_manager.cg_conflict_eh(n1->get_expr(), n2->get_expr());
m_dyn_ack_manager.cg_conflict_eh(n1->get_app(), n2->get_app());
assign(literal(v), mk_justification(eq_propagation_justification(lhs, rhs)));
}
// It is not necessary to reinsert the equality to the congruence table
@ -915,7 +918,7 @@ namespace smt {
lbool val2 = get_assignment(v2);
if (val2 != val) {
if (val2 != l_undef && congruent(source, target) && source->get_num_args() > 0)
m_dyn_ack_manager.cg_conflict_eh(source->get_expr(), target->get_expr());
m_dyn_ack_manager.cg_conflict_eh(source->get_app(), target->get_app());
assign(literal(v2, sign), mk_justification(mp_iff_justification(source, target)));
}
target = target->get_next();
@ -1133,7 +1136,7 @@ namespace smt {
m.inc_ref(eq);
_this->m_is_diseq_tmp = enode::mk_dummy(m, m_app2enode, eq);
}
else if (m_is_diseq_tmp->get_expr()->get_arg(0)->get_sort() != n1->get_sort()) {
else if (m_is_diseq_tmp->get_app()->get_arg(0)->get_sort() != n1->get_sort()) {
m.dec_ref(m_is_diseq_tmp->get_expr());
app * eq = m.mk_eq(n1->get_expr(), n2->get_expr());
m.inc_ref(eq);
@ -1280,14 +1283,14 @@ namespace smt {
enode * r = m_cg_table.find(tmp);
#ifdef Z3DEBUG
if (r != nullptr) {
SASSERT(r->get_expr()->get_decl() == f);
SASSERT(r->get_decl() == f);
SASSERT(r->get_num_args() == num_args);
if (r->is_commutative()) {
// TODO
}
else {
for (unsigned i = 0; i < num_args; ++i) {
expr * arg = r->get_expr()->get_arg(i);
expr * arg = r->get_arg(i)->get_expr();
SASSERT(e_internalized(arg));
enode * _arg = get_enode(arg);
CTRACE(eq_to_bug, args[i]->get_root() != _arg->get_root(),
@ -1773,9 +1776,11 @@ namespace smt {
return m_fingerprints.contains(q, q->get_id(), num_bindings, bindings);
}
bool context::add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, expr* def, unsigned max_generation,
bool context::add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, //expr* def,
unsigned max_generation,
unsigned min_top_generation, unsigned max_top_generation, vector<std::tuple<enode *, enode *>> & used_enodes) {
return m_qmanager->add_instance(q, pat, num_bindings, bindings, def, max_generation, min_top_generation, max_top_generation, used_enodes);
return m_qmanager->add_instance(q, pat, num_bindings, bindings,
max_generation, min_top_generation, max_top_generation, used_enodes);
}
void context::rescale_bool_var_activity() {
@ -4188,9 +4193,17 @@ namespace smt {
return FC_CONTINUE;
}
if (m_final_check_idx == old_idx) {
if (level >= max_level || result == FC_DONE || can_propagate())
if (level >= max_level || result == FC_DONE || result == FC_CONTINUE || can_propagate())
break;
++level;
// Re-evaluate at the higher level: clear the give-up state
// accumulated at lower levels so a level that succeeds is
// not masked by a previous FC_GIVEUP. See e.g. theory_lra
// whose level 2 invokes the full nlsat (m_nra.check) that
// is skipped at level 1.
result = FC_DONE;
f = OK;
m_incomplete_theories.reset();
}
}
@ -4670,7 +4683,7 @@ namespace smt {
return false;
}
case 1: {
if (m_qmanager->is_shared(n) && !m.is_lambda_def(n->get_expr()) && !m_lambdas.contains(n))
if (m_qmanager->is_shared(n) && !m_lambdas.contains(n))
return true;
// the variable is shared if the equivalence class of n
@ -4680,8 +4693,8 @@ namespace smt {
theory_id th_id = l->get_id();
for (enode * parent : enode::parents(n)) {
app* p = parent->get_expr();
family_id fid = p->get_family_id();
auto p = parent->get_expr();
family_id fid = parent->get_family_id();
if (fid != th_id && fid != m.get_basic_family_id()) {
if (is_beta_redex(parent, n))
continue;
@ -4729,7 +4742,7 @@ namespace smt {
}
bool context::is_beta_redex(enode* p, enode* n) const {
family_id th_id = p->get_expr()->get_family_id();
family_id th_id = p->get_family_id();
theory * th = get_theory(th_id);
return th && th->is_beta_redex(p, n);
}

View file

@ -18,6 +18,7 @@ Revision History:
--*/
#pragma once
#include <atomic>
#include "ast/quantifier_stat.h"
#include "ast/simplifiers/dependent_expr_state.h"
#include "smt/smt_clause.h"
@ -123,7 +124,6 @@ namespace smt {
// enodes. Examples: boolean expression nested in an
// uninterpreted function.
expr_ref_vector m_e_internalized_stack; // stack of the expressions already internalized as enodes.
quantifier_ref_vector m_l_internalized_stack;
ptr_vector<justification> m_justifications;
@ -139,6 +139,7 @@ namespace smt {
scoped_ptr<base_dependent_expr_state> m_fmls;
svector<double> m_lit_scores[2];
svector<unsigned> m_birthdate;
// -----------------------------------
@ -620,8 +621,8 @@ namespace smt {
return m_asserted_formulas.has_quantifiers();
}
fingerprint * add_fingerprint(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def = nullptr) {
return m_fingerprints.insert(data, data_hash, num_args, args, def);
fingerprint * add_fingerprint(void * data, unsigned data_hash, unsigned num_args, enode * const * args) {
return m_fingerprints.insert(data, data_hash, num_args, args);
}
theory_id get_var_theory(bool_var v) const {
@ -785,6 +786,13 @@ namespace smt {
return get_bdata(get_bool_var(n));
}
void update_generation(enode * n);
void update_generation(expr * e) {
if (is_app(e) && e_internalized(e))
update_generation(get_enode(to_app(e)));
}
typedef std::pair<expr *, bool> expr_bool_pair;
void ts_visit_child(expr * n, bool gate_ctx, svector<expr_bool_pair> & todo, bool & visited);
@ -863,16 +871,6 @@ namespace smt {
mk_enode_trail m_mk_enode_trail;
void undo_mk_enode();
friend class mk_lambda_trail;
class mk_lambda_trail : public trail {
context& ctx;
public:
mk_lambda_trail(context& ctx) :ctx(ctx) {}
void undo() override { ctx.undo_mk_lambda(); }
};
mk_lambda_trail m_mk_lambda_trail;
void undo_mk_lambda();
void apply_sort_cnstr(app * term, enode * e);
@ -1018,7 +1016,7 @@ namespace smt {
bool_var mk_bool_var(expr * n);
enode * mk_enode(app * n, bool suppress_args, bool merge_tf, bool cgc_enabled);
enode * mk_enode(expr * n, bool suppress_args, bool merge_tf, bool cgc_enabled);
void attach_th_var(enode * n, theory * th, theory_var v);
@ -1106,8 +1104,8 @@ namespace smt {
bool contains_instance(quantifier * q, unsigned num_bindings, enode * const * bindings);
bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, expr* def, unsigned max_generation,
unsigned min_top_generation, unsigned max_top_generation, vector<std::tuple<enode *, enode*>> & used_enodes /*gives the equalities used for the pattern match, see mam.cpp for more info*/);
bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings,
unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, vector<std::tuple<enode *, enode*>> & used_enodes /*gives the equalities used for the pattern match, see mam.cpp for more info*/);
void set_global_generation(unsigned generation) { m_generation = generation; }
@ -1147,7 +1145,7 @@ namespace smt {
void push_eq(enode * lhs, enode * rhs, eq_justification const & js) {
if (lhs->get_root() != rhs->get_root()) {
SASSERT(lhs->get_expr()->get_sort() == rhs->get_expr()->get_sort());
SASSERT(lhs->get_sort() == rhs->get_sort());
m_eq_propagation_queue.push_back(new_eq(lhs, rhs, js));
}
}

View file

@ -423,6 +423,7 @@ namespace smt {
st.update("minimized lits", m_stats.m_num_minimized_lits);
st.update("num checks", m_stats.m_num_checks);
st.update("mk bool var", m_stats.m_num_mk_bool_var ? m_stats.m_num_mk_bool_var - 1 : 0);
st.update("random seed", m_fparams.m_random_seed);
m_qmanager->collect_statistics(st);
m_asserted_formulas.collect_statistics(st);
for (theory* th : m_theory_set) {
@ -544,14 +545,14 @@ namespace smt {
out << std::left << n->get_owner_id() << " #";
out.width(5);
out << n->get_root()->get_owner_id() << " := " << std::right;
unsigned num = n->get_expr()->get_num_args();
unsigned num = n->get_num_args();
if (num > 0)
out << "(";
out << n->get_decl()->get_name();
if (!n->get_decl()->private_parameters())
display_parameters(out, n->get_decl()->get_num_parameters(), n->get_decl()->get_parameters());
for (unsigned i = 0; i < num; ++i) {
expr * arg = n->get_expr()->get_arg(i);
expr * arg = n->get_arg(i)->get_expr();
if (e_internalized(arg)) {
enode * n = get_enode(arg)->get_root();
out << " #" << n->get_owner_id();

View file

@ -25,7 +25,7 @@ namespace smt {
/**
\brief Initialize an enode in the given memory position.
*/
enode * enode::init(ast_manager & m, void * mem, app2enode_t const & app2enode, app * owner,
enode * enode::init(ast_manager & m, void * mem, app2enode_t const & app2enode, expr * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent) {
SASSERT(m.is_bool(owner) || !merge_tf);
@ -42,7 +42,7 @@ namespace smt {
n->m_interpreted = false;
n->m_suppress_args = suppress_args;
n->m_eq = m.is_eq(owner);
n->m_commutative = n->get_num_args() == 2 && owner->get_decl()->is_commutative();
n->m_commutative = n->get_num_args() == 2 && n->get_decl()->is_commutative();
n->m_bool = m.is_bool(owner);
n->m_merge_tf = merge_tf;
n->m_cgc_enabled = cgc_enabled;
@ -52,7 +52,7 @@ namespace smt {
n->m_is_shared = 2;
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; ++i) {
enode * arg = app2enode[owner->get_arg(i)->get_id()];
enode * arg = app2enode[to_app(owner)->get_arg(i)->get_id()];
n->m_args[i] = arg;
arg->get_root()->m_is_shared = 2;
SASSERT(n->get_arg(i) == arg);
@ -64,11 +64,11 @@ namespace smt {
return n;
}
enode * enode::mk(ast_manager & m, region & r, app2enode_t const & app2enode, app * owner,
enode * enode::mk(ast_manager & m, region & r, app2enode_t const & app2enode, expr * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent) {
SASSERT(m.is_bool(owner) || !merge_tf);
unsigned sz = get_enode_size(suppress_args ? 0 : owner->get_num_args());
unsigned sz = get_enode_size(suppress_args || !::is_app(owner) ? 0 : to_app(owner)->get_num_args());
void * mem = r.allocate(sz);
return init(m, mem, app2enode, owner, generation, suppress_args, merge_tf, iscope_lvl, cgc_enabled, update_children_parent);
}
@ -136,10 +136,11 @@ namespace smt {
\brief Push old value of generation on the context trail stack
and update the generation.
*/
void enode::set_generation(context & ctx, unsigned generation) {
void enode::set_generation(context * ctx, unsigned generation) {
if (m_generation == generation)
return;
ctx.push_trail(value_trail<unsigned>(m_generation));
if (ctx)
ctx->push_trail(value_trail<unsigned>(m_generation));
m_generation = generation;
}
@ -279,7 +280,7 @@ namespace smt {
bool congruent(enode * n1, enode * n2, bool & comm) {
comm = false;
if (n1->get_expr()->get_decl() != n2->get_expr()->get_decl())
if (!n1->is_app() || n1->get_decl() != n2->get_decl())
return false;
unsigned num_args = n1->get_num_args();
if (num_args != n2->get_num_args())

View file

@ -59,7 +59,7 @@ namespace smt {
equality propagation, and the theory central bus of equalities.
*/
class enode {
app * m_owner; //!< The application that 'owns' this enode.
expr * m_owner; //!< The application that 'owns' this enode.
enode * m_root; //!< Representative of the equivalence class
enode * m_next; //!< Next element in the equivalence class.
enode * m_cg;
@ -132,7 +132,7 @@ namespace smt {
friend class tmp_enode;
static enode * init(ast_manager & m, void * mem, app2enode_t const & app2enode, app * owner,
static enode * init(ast_manager & m, void * mem, app2enode_t const & app2enode, expr * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent);
public:
@ -141,7 +141,7 @@ namespace smt {
return sizeof(enode) + num_args * sizeof(enode*);
}
static enode * mk(ast_manager & m, region & r, app2enode_t const & app2enode, app * owner,
static enode * mk(ast_manager & m, region & r, app2enode_t const & app2enode, expr * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent);
@ -166,16 +166,28 @@ namespace smt {
void del_eh(ast_manager & m, bool update_children_parent = true);
app * get_expr() const { return m_owner; }
app * get_app() const { SASSERT(is_app()); return to_app(m_owner); }
expr *get_expr() const {
return m_owner;
}
bool is_app() const {
return ::is_app(m_owner);
}
unsigned get_owner_id() const { return m_owner->get_id(); }
unsigned get_expr_id() const { return m_owner->get_id(); }
func_decl * get_decl() const { return m_owner->get_decl(); }
unsigned get_decl_id() const { return m_owner->get_decl()->get_small_id(); }
func_decl * get_decl() const { return is_app() ? to_app(m_owner)->get_decl() : nullptr; }
unsigned get_decl_id() const { return is_app() ? to_app(m_owner)->get_decl()->get_small_id() : 43; }
sort* get_sort() const { return m_owner->get_sort(); }
family_id get_family_id() const {
return is_app() ? to_app(m_owner)->get_family_id() : basic_family_id;
}
unsigned hash() const {
return m_owner->hash();
}
@ -213,7 +225,7 @@ namespace smt {
}
unsigned get_num_args() const {
return m_suppress_args ? 0 : m_owner->get_num_args();
return m_suppress_args || !is_app() ? 0 : to_app(m_owner)->get_num_args();
}
enode * get_arg(unsigned idx) const {
@ -386,7 +398,7 @@ namespace smt {
return m_generation;
}
void set_generation(context & ctx, unsigned generation);
void set_generation(context * ctx, unsigned generation);
/**
\brief Return the enode n that is in the eqc of *this, and has the minimal generation.

View file

@ -101,6 +101,11 @@ namespace smt {
}
}
void context::update_generation(enode * e) {
if (0 < m_generation && m_generation < e->get_generation())
e->set_generation(nullptr, m_generation);
}
void context::ts_visit_child(expr * n, bool gate_ctx, svector<expr_bool_pair> & todo, bool & visited) {
if (get_color(tcolors, fcolors, n, gate_ctx) == White) {
todo.push_back(expr_bool_pair(n, gate_ctx));
@ -115,12 +120,16 @@ namespace smt {
return true;
SASSERT(is_app(n));
if (m.is_bool(n)) {
if (b_internalized(n))
if (b_internalized(n)) {
update_generation(n);
return true;
}
}
else {
if (e_internalized(n))
update_generation(n);
if (e_internalized(n))
return true;
}
bool visited = true;
@ -404,6 +413,8 @@ namespace smt {
bool_var v = get_bool_var(n);
TRACE(internalize_bug, tout << "#" << n->get_id() << " already has bool_var v" << v << "\n";);
update_generation(n);
// n was already internalized as boolean, but an enode was
// not associated with it. So, an enode is necessary, if
// n is not in the context of a gate and is an application.
@ -586,31 +597,9 @@ namespace smt {
SASSERT(is_lambda(q));
if (e_internalized(q))
return;
app_ref lam_name(m.mk_fresh_const("lambda", q->get_sort()), m);
app_ref eq(m), lam_app(m);
expr_ref_vector vars(m);
vars.push_back(lam_name);
unsigned sz = q->get_num_decls();
for (unsigned i = 0; i < sz; ++i)
vars.push_back(m.mk_var(sz - i - 1, q->get_decl_sort(i)));
array_util autil(m);
lam_app = autil.mk_select(vars.size(), vars.data());
eq = m.mk_eq(lam_app, q->get_expr());
quantifier_ref fa(m);
expr * patterns[1] = { m.mk_pattern(lam_app) };
fa = m.mk_forall(sz, q->get_decl_sorts(), q->get_decl_names(), eq, 0, m.lambda_def_qid(), symbol::null, 1, patterns);
internalize_quantifier(fa, true);
if (!e_internalized(lam_name))
internalize_uninterpreted(lam_name);
enode* lam_node = get_enode(lam_name);
push_trail(insert_obj_map<enode, quantifier*>(m_lambdas, lam_node));
m_lambdas.insert(lam_node, q);
m_app2enode.setx(q->get_id(), lam_node, nullptr);
m_l_internalized_stack.push_back(q);
m_trail_stack.push_ptr(&m_mk_lambda_trail);
bool_var bv = get_bool_var(fa);
assign(literal(bv, false), nullptr);
mark_as_relevant(bv);
mk_enode(q, true, /* do suppress args */
false, /* it is a term, so it should not be merged with true/false */
true);
}
bool context::has_lambda() {
@ -810,6 +799,8 @@ namespace smt {
*/
void context::internalize_term(app * n) {
if (e_internalized(n)) {
enode * e = get_enode(n);
update_generation(e);
theory * th = m_theories.get_plugin(n->get_family_id());
if (th != nullptr) {
// This code is necessary because some theories may decide
@ -822,7 +813,6 @@ namespace smt {
// Later, the core tries to internalize (f (* 2 x)).
// Now, (* 2 x) is not internal to arithmetic anymore,
// and a theory variable must be created for it.
enode * e = get_enode(n);
if (!th->is_attached_to_var(e))
th->internalize_term(n);
}
@ -935,6 +925,8 @@ namespace smt {
m_lit_scores[0].reserve(v + 1);
m_lit_scores[1].reserve(v + 1);
m_lit_scores[0][v] = m_lit_scores[1][v] = 0.0;
m_birthdate.reserve(v+1);
m_birthdate[v] = 0;
literal l(v, false);
literal not_l(v, true);
@ -997,7 +989,7 @@ namespace smt {
\remark If suppress_args is true, then the enode is viewed as a constant
in the egraph.
*/
enode * context::mk_enode(app * n, bool suppress_args, bool merge_tf, bool cgc_enabled) {
enode * context::mk_enode(expr * n, bool suppress_args, bool merge_tf, bool cgc_enabled) {
TRACE(mk_enode_detail, tout << mk_pp(n, m) << "\nsuppress_args: " << suppress_args << ", merge_tf: " <<
merge_tf << ", cgc_enabled: " << cgc_enabled << "\n";);
SASSERT(!e_internalized(n));
@ -1043,7 +1035,7 @@ namespace smt {
}
}
if (!e->is_eq()) {
unsigned decl_id = n->get_decl()->get_small_id();
unsigned decl_id = e->get_decl_id();
if (decl_id >= m_decl2enodes.size())
m_decl2enodes.resize(decl_id+1);
m_decl2enodes[decl_id].push_back(e);
@ -1059,19 +1051,11 @@ namespace smt {
SCTRACE(causality, m_coming_from_quant, tout << "EN: #" << e->get_owner_id() << "\n";);
if (m.has_trace_stream())
m.trace_stream() << "[attach-enode] #" << n->get_id() << " " << m_generation << "\n";
m.trace_stream() << "[attach-enode] #" << n->get_id() << " " << generation << "\n";
return e;
}
void context::undo_mk_lambda() {
SASSERT(!m_l_internalized_stack.empty());
m_stats.m_num_del_enode++;
quantifier * n = m_l_internalized_stack.back();
m_app2enode[n->get_id()] = nullptr;
m_l_internalized_stack.pop_back();
}
void context::undo_mk_enode() {
SASSERT(!m_e_internalized_stack.empty());
m_stats.m_num_del_enode++;
@ -1079,7 +1063,6 @@ namespace smt {
TRACE(undo_mk_enode, tout << "undo_enode: #" << n->get_id() << "\n" << mk_pp(n, m) << "\n";);
TRACE(mk_var_bug, tout << "undo_mk_enode: " << n->get_id() << "\n";);
unsigned n_id = n->get_id();
SASSERT(is_app(n));
enode * e = m_app2enode[n_id];
m_app2enode[n_id] = nullptr;
if (e->is_cgr() && !e->is_true_eq() && e->is_cgc_enabled()) {
@ -1087,7 +1070,7 @@ namespace smt {
m_cg_table.erase(e);
}
if (e->get_num_args() > 0 && !e->is_eq()) {
unsigned decl_id = to_app(n)->get_decl()->get_small_id();
unsigned decl_id = e->get_decl_id();
SASSERT(decl_id < m_decl2enodes.size());
SASSERT(m_decl2enodes[decl_id].back() == e);
m_decl2enodes[decl_id].pop_back();
@ -1884,4 +1867,3 @@ namespace smt {
SASSERT(th->is_attached_to_var(n));
}
};

View file

@ -203,7 +203,7 @@ namespace smt {
unsigned num_decls = q->get_num_decls();
// Remark: sks were created for the flat version of q.
SASSERT(sks.size() >= num_decls);
expr_ref_vector bindings(m), defs(m);
expr_ref_vector bindings(m);
expr_ref def(m);
bindings.resize(num_decls);
unsigned max_generation = 0;
@ -249,6 +249,7 @@ namespace smt {
sk_value = get_type_compatible_term(sk_value);
}
func_decl * f = nullptr;
expr_ref sk_term(sk_value, m);
if (autil.is_as_array(sk_value, f) && cex->get_func_interp(f) && cex->get_func_interp(f)->get_interp()) {
expr_ref body(cex->get_func_interp(f)->get_interp(), m);
if (contains_model_value(body))
@ -260,27 +261,23 @@ namespace smt {
defined_names dn(m);
body = replace_value_from_ctx(body);
body = m.mk_lambda(sorts.size(), sorts.data(), names.data(), body);
// sk_value = m.mk_fresh_const(0, m.get_sort(sk_value)); // get rid of as-array
body = dn.mk_definition(body, to_app(sk_value));
defs.push_back(body);
sk_term = body;
}
bindings.set(num_decls - i - 1, sk_value);
bindings.set(num_decls - i - 1, sk_term);
}
TRACE(model_checker, tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance: " << bindings << "\ndefs:\n" << defs << "\n";);
if (!defs.empty()) def = mk_and(defs);
TRACE(model_checker, tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance: " << bindings << "\n");
max_generation = std::max(m_qm->get_generation(q), max_generation);
add_instance(q, bindings, max_generation, def.get());
add_instance(q, bindings, max_generation);
return true;
}
void model_checker::add_instance(quantifier* q, expr_ref_vector const& bindings, unsigned max_generation, expr* def) {
void model_checker::add_instance(quantifier* q, expr_ref_vector const& bindings, unsigned max_generation) {
SASSERT(q->get_num_decls() == bindings.size());
unsigned offset = m_pinned_exprs.size();
m_pinned_exprs.append(bindings);
m_pinned_exprs.push_back(q);
m_pinned_exprs.push_back(def);
m_new_instances.push_back(instance(q, offset, def, max_generation));
m_new_instances.push_back(instance(q, offset, max_generation));
}
void model_checker::operator()(expr *n) {
@ -457,12 +454,6 @@ namespace smt {
TRACE(model_checker, tout << "MODEL_CHECKER INVOKED\n";
tout << "model:\n"; model_pp(tout, *m_curr_model););
for (quantifier* q : *m_qm)
if (m.is_lambda_def(q)) {
md->add_lambda_defs();
break;
}
md->compress();
@ -518,8 +509,7 @@ namespace smt {
for (quantifier * q : *m_qm) {
if (!(m_qm->mbqi_enabled(q) &&
m_context->is_relevant(q) &&
m_context->get_assignment(q) == l_true &&
(!m_context->get_fparams().m_ematching || !m.is_lambda_def(q)))) {
m_context->get_assignment(q) == l_true)) {
if (!m_qm->mbqi_enabled(q))
++num_failures;
continue;
@ -588,27 +578,11 @@ namespace smt {
bindings.push_back(m_context->get_enode(b));
}
if (inst.m_def) {
unsigned n = 1;
expr* const* args = &inst.m_def;
if (m.is_and(inst.m_def)) {
n = to_app(inst.m_def)->get_num_args();
args = to_app(inst.m_def)->get_args();
}
for (unsigned i = 0; i < n; ++i) {
proof* pr = nullptr;
expr* arg = args[i];
if (m.proofs_enabled())
pr = m.mk_def_intro(arg);
m_context->internalize_assertion(arg, pr, gen);
}
}
TRACE(model_checker_bug_detail, tout << "instantiating... q:\n" << mk_pp(q, m) << "\n";
tout << "inconsistent: " << m_context->inconsistent() << "\n";
tout << "bindings:\n" << expr_ref_vector(m, num_decls, m_pinned_exprs.data() + offset) << "\n";
tout << "def " << mk_pp(inst.m_def, m) << "\n";);
m_context->add_instance(q, nullptr, num_decls, bindings.data(), inst.m_def, gen, gen, gen, dummy);
);
m_context->add_instance(q, nullptr, num_decls, bindings.data(), gen, gen, gen, dummy);
TRACE(model_checker_bug_detail, tout << "after instantiating, inconsistent: " << m_context->inconsistent() << "\n";);
}
}

View file

@ -70,9 +70,8 @@ namespace smt {
struct instance {
quantifier * m_q;
unsigned m_generation;
expr * m_def;
unsigned m_bindings_offset;
instance(quantifier * q, unsigned offset, expr* def, unsigned gen):m_q(q), m_generation(gen), m_def(def), m_bindings_offset(offset) {}
instance(quantifier * q, unsigned offset, unsigned gen):m_q(q), m_generation(gen), m_bindings_offset(offset) {}
};
svector<instance> m_new_instances;
@ -86,7 +85,7 @@ namespace smt {
struct is_model_value {};
expr_mark m_visited;
bool contains_model_value(expr * e);
void add_instance(quantifier * q, expr_ref_vector const & bindings, unsigned max_generation, expr * def);
void add_instance(quantifier * q, expr_ref_vector const & bindings, unsigned max_generation);
bool is_safe_for_mbqi(quantifier * q) const;
public:

View file

@ -291,8 +291,8 @@ namespace smt {
}
void insert(expr* n, unsigned generation) {
SASSERT(is_ground(n));
get_root()->m_set->insert(n, generation);
if (is_ground(n))
get_root()->m_set->insert(n, generation);
}
void display(std::ostream& out, ast_manager& m) const {
@ -1378,7 +1378,7 @@ namespace smt {
Store in arrays, all enodes that match the pattern
*/
void get_auf_arrays(app* auf_arr, context* ctx, ptr_buffer<enode>& arrays) {
void get_auf_arrays(expr* auf_arr, context* ctx, ptr_buffer<enode>& arrays) {
if (is_ground(auf_arr)) {
if (ctx->e_internalized(auf_arr)) {
enode* e = ctx->get_enode(auf_arr);
@ -1387,8 +1387,8 @@ namespace smt {
}
}
}
else {
app* nested_array = to_app(auf_arr->get_arg(0));
else if (is_app(auf_arr)) {
app* nested_array = to_app(to_app(auf_arr)->get_arg(0));
ptr_buffer<enode> nested_arrays;
get_auf_arrays(nested_array, ctx, nested_arrays);
for (enode* curr : nested_arrays) {
@ -1396,7 +1396,7 @@ namespace smt {
enode_vector::iterator end2 = curr->end_parents();
for (; it2 != end2; ++it2) {
enode* p = *it2;
if (ctx->is_relevant(p) && p->get_expr()->get_decl() == auf_arr->get_decl()) {
if (ctx->is_relevant(p) && p->get_decl() == to_app(auf_arr)->get_decl()) {
arrays.push_back(p);
}
}
@ -1411,9 +1411,9 @@ namespace smt {
unsigned m_arg_i;
unsigned m_var_j;
app* get_array() const { return to_app(m_select->get_arg(0)); }
expr* get_array() const { return m_select->get_arg(0); }
func_decl* get_array_func_decl(app* ground_array, auf_solver& s) {
func_decl* get_array_func_decl(expr* ground_array, auf_solver& s) {
TRACE(model_evaluator, tout << expr_ref(ground_array, m) << "\n";);
expr* ground_array_interp = s.eval(ground_array, false);
if (ground_array_interp && m_array.is_as_array(ground_array_interp))
@ -1449,7 +1449,7 @@ namespace smt {
});
node* n1 = s.get_uvar(q, m_var_j);
for (enode* n : arrays) {
app* ground_array = n->get_expr();
auto ground_array = n->get_expr();
func_decl* f = get_array_func_decl(ground_array, s);
if (f) {
SASSERT(m_arg_i >= 1);
@ -1463,7 +1463,7 @@ namespace smt {
ptr_buffer<enode> arrays;
get_auf_arrays(get_array(), ctx, arrays);
for (enode* curr : arrays) {
app* ground_array = curr->get_expr();
auto ground_array = curr->get_expr();
func_decl* f = get_array_func_decl(ground_array, s);
if (f) {
node* A_f_i = s.get_A_f_i(f, m_arg_i - 1);
@ -1471,8 +1471,8 @@ namespace smt {
enode_vector::iterator end2 = curr->end_parents();
for (; it2 != end2; ++it2) {
enode* p = *it2;
if (ctx->is_relevant(p) && p->get_expr()->get_decl() == m_select->get_decl()) {
SASSERT(m_arg_i < p->get_expr()->get_num_args());
if (ctx->is_relevant(p) && p->get_decl() == m_select->get_decl()) {
SASSERT(m_arg_i < p->get_num_args());
enode* e_arg = p->get_arg(m_arg_i);
A_f_i->insert(e_arg->get_expr(), e_arg->get_generation());
}
@ -1690,7 +1690,7 @@ namespace smt {
typedef ptr_vector<cond_macro>::const_iterator macro_iterator;
static quantifier_ref mk_flat(ast_manager& m, quantifier* q) {
if (has_quantifiers(q->get_expr()) && !m.is_lambda_def(q)) {
if (has_quantifiers(q->get_expr())) {
proof_ref pr(m);
expr_ref new_q(m);
pull_quant pull(m);
@ -2279,7 +2279,6 @@ namespace smt {
void operator()(quantifier_info* d) {
m_info = d;
quantifier* q = d->get_flat_q();
if (m.is_lambda_def(q)) return;
expr* e = q->get_expr();
reset_cache();
if (!m.inc()) return;

View file

@ -105,7 +105,7 @@ namespace smt {
proc = alloc(expr_wrapper_proc, m.mk_false());
}
else if (m.is_model_value(r->get_expr()))
proc = alloc(expr_wrapper_proc, r->get_expr());
proc = alloc(expr_wrapper_proc, r->get_app());
else {
family_id fid = s->get_family_id();
theory * th = m_context->get_theory(fid);
@ -386,7 +386,7 @@ namespace smt {
// send model
for (enode * n : m_context->enodes()) {
if (is_uninterp_const(n->get_expr()) && m_context->is_relevant(n)) {
func_decl * d = n->get_expr()->get_decl();
func_decl * d = n->get_decl();
TRACE(mg_top_sort, tout << d->get_name() << " " << (m_hidden_ufs.contains(d)?"hidden":"visible") << "\n";);
if (m_hidden_ufs.contains(d)) continue;
expr * val = get_value(n);
@ -430,6 +430,8 @@ namespace smt {
if (!m_context->is_relevant(t))
continue;
enode * n = m_context->get_enode(t);
if (!n->is_app())
continue;
unsigned num_args = n->get_num_args();
func_decl * f = n->get_decl();
if (num_args == 0 && include_func_interp(f)) {

File diff suppressed because it is too large Load diff

View file

@ -21,8 +21,10 @@ Revision History:
#include "smt/smt_context.h"
#include "util/search_tree.h"
#include "ast/sls/sls_smt_solver.h"
#include <atomic>
#include <thread>
#include <mutex>
#include <condition_variable>
namespace smt {
@ -35,14 +37,37 @@ namespace smt {
class parallel {
context& ctx;
unsigned num_threads;
bool m_should_run_sls = false;
class core_minimizer_worker;
using node = search_tree::node<cube_config>;
struct shared_clause {
unsigned source_worker_id;
expr_ref clause;
};
struct bb_candidate {
expr_ref lit;
double age;
unsigned hits; // how many cubes reported it
bb_candidate(ast_manager& m, expr* e, double s, unsigned h) : lit(e, m), age(s), hits(h) {}
};
using bb_candidates = vector<bb_candidate>;
struct node_lease {
node* leased_node = nullptr;
// Cancellation generation counter for this node/subtree.
// Incremented when the node is closed; used to signal that all
// workers holding leases on this node (or its descendants)
// must abandon work immediately.
unsigned cancel_epoch = 0;
// Guards against multiple inc_cancel() calls for the same lease.
// Set when cancel_lease() is signaled; cleared when a new lease is assigned.
bool cancel_signaled = false;
};
class batch_manager {
enum state {
@ -56,22 +81,50 @@ namespace smt {
struct stats {
unsigned m_max_cube_depth = 0;
unsigned m_num_cubes = 0;
unsigned m_backbones_found = 0;
unsigned m_core_min_jobs_enqueued = 0;
unsigned m_core_min_jobs_published = 0;
unsigned m_core_min_jobs_skipped = 0;
unsigned m_core_min_global_unsat = 0;
};
struct core_min_job {
node* source = nullptr;
expr_ref_vector core;
core_min_job(ast_manager& m, node* source) : source(source), core(m) {}
};
ast_manager& m;
parallel& p;
std::mutex mux;
state m_state = state::is_running;
stats m_stats;
using node = search_tree::node<cube_config>;
search_tree::tree<cube_config> m_search_tree;
vector<node_lease> m_worker_leases;
unsigned m_exception_code = 0;
std::string m_exception_msg;
vector<shared_clause> shared_clause_trail; // store all shared clauses with worker IDs
obj_hashtable<expr> shared_clause_set; // for duplicate filtering on per-thread clause expressions
bb_candidates m_bb_candidates;
unsigned m_max_global_bb_candidates = 100;
unsigned m_bb_batch_size = 150;
obj_hashtable<expr> m_global_backbones;
std::atomic<unsigned> m_bb_candidate_epoch = 0;
// Backbone job queue
std::condition_variable m_bb_cv;
bb_candidates m_bb_current_batch;
unsigned m_bb_batch_id = 0;
unsigned m_num_global_bb_threads = 0;
unsigned_vector m_bb_last_batch_processed;
unsigned m_bb_cancel_epoch = 0; // When a backbone worker finishes early, it increments m_bb_cancel_epoch and notifies all
// Core minimization job queue
std::condition_variable m_core_min_cv;
scoped_ptr_vector<core_min_job> m_core_min_jobs;
bool m_ablate_backtracking = false;
// called from batch manager to cancel other workers if we've reached a verdict
void cancel_workers() {
IF_VERBOSE(1, verbose_stream() << "Canceling workers\n");
@ -86,17 +139,51 @@ namespace smt {
p.m_sls_worker->cancel();
}
void cancel_background_threads() {
cancel_workers();
cancel_sls_worker();
void cancel_backbones_worker() {
IF_VERBOSE(1, verbose_stream() << "Canceling backbones workers\n");
for (auto* w : p.m_global_backbones_workers)
w->cancel();
}
void init_parameters_state();
void cancel_background_threads() {
cancel_workers();
cancel_sls_worker();
if (!p.m_global_backbones_workers.empty()) {
cancel_backbones_worker();
m_bb_cv.notify_all();
}
if (p.m_core_minimizer_worker) {
p.m_core_minimizer_worker->cancel();
m_core_min_cv.notify_all();
}
}
// to avoid deadlock
bool is_global_backbone_unlocked(ast_translation& l2g, expr* bb_cand) {
expr_ref cand(l2g(bb_cand), l2g.to());
return m_global_backbones.contains(cand.get());
}
bool is_global_backbone_or_negation_unlocked(ast_translation& l2g, expr* bb_cand) {
expr_ref cand(l2g(bb_cand), l2g.to());
expr_ref neg_cand(mk_not(l2g.to(), cand), l2g.to());
return m_global_backbones.contains(cand.get()) || m_global_backbones.contains(neg_cand.get());
}
void backtrack_unlocked(ast_translation& l2g, unsigned worker_id, expr_ref_vector const& core,
node_lease const* lease = nullptr, vector<node_lease> const* targets = nullptr);
void collect_clause_unlocked(ast_translation &l2g, unsigned source_worker_id, expr *clause);
void release_lease_unlocked(unsigned worker_id, node* n);
void cancel_closed_leases_unlocked(unsigned source_worker_id);
void collect_matching_targets_unlocked(node* source, expr* lit, vector<cube_config::literal> const& core,
vector<node_lease>& targets);
node* find_core_source_unlocked(ast_translation& l2g, node* source, expr_ref_vector const& core);
unsigned select_best_core_min_job_unlocked() const;
public:
batch_manager(ast_manager& m, parallel& p) : m(m), p(p), m_search_tree(expr_ref(m)) { }
void initialize();
void initialize(unsigned num_global_bb_threads, unsigned initial_max_thread_conflicts = 1000); // TODO: pass in from worker config
void set_unsat(ast_translation& l2g, expr_ref_vector const& unsat_core);
void set_sat(ast_translation& l2g, model& m);
@ -104,14 +191,57 @@ namespace smt {
void set_exception(unsigned error_code);
void collect_statistics(::statistics& st) const;
bool get_cube(ast_translation& g2l, unsigned id, expr_ref_vector& cube, node*& n);
void backtrack(ast_translation& l2g, expr_ref_vector const& core, node* n);
void split(ast_translation& l2g, unsigned id, node* n, expr* atom);
void collect_backbone_candidates(ast_translation& l2g, bb_candidates& bb_candidates);
void collect_backbone_evidence(ast_translation& l2g, expr* lit, double delta);
bool collect_global_backbone(ast_translation& l2g, expr_ref const& backbone, unsigned source_worker_id = UINT_MAX);
bool wait_for_backbone_job(unsigned bb_thread_id, ast_translation& g2l, vector<parallel::bb_candidate>& out, reslimit& lim);
bool has_new_backbone_candidates(unsigned epoch) {
return m_bb_candidate_epoch.load(std::memory_order_acquire) != epoch;
}
unsigned get_bb_candidate_epoch() const {
return m_bb_candidate_epoch.load(std::memory_order_acquire);
}
expr_ref_vector get_global_backbones_snapshot(ast_translation& g2l) {
std::scoped_lock lock(mux);
expr_ref_vector snapshot(g2l.to());
for (expr* gb : m_global_backbones)
snapshot.push_back(g2l(gb));
return snapshot;
}
bool get_cube(ast_translation& g2l, unsigned id, expr_ref_vector& cube, bool is_first_run, node_lease& lease);
void backtrack(ast_translation& l2g, unsigned worker_id, expr_ref_vector const& core, node_lease const& lease);
void enqueue_core_minimization(ast_translation& l2g, node* source, expr_ref_vector const& core);
bool wait_for_core_min_job(ast_translation& g2l, node*& source,
expr_ref_vector& core, reslimit& lim);
void publish_minimized_core(ast_translation& l2g, expr_ref_vector const& asms, node* source,
unsigned original_core_size, expr_ref_vector const& minimized_core);
void try_split(ast_translation& l2g, unsigned worker_id, node_lease const& lease, expr* atom, unsigned effort);
void release_lease(unsigned worker_id, node_lease const& lease);
bool lease_canceled(node_lease const& lease);
void collect_clause(ast_translation& l2g, unsigned source_worker_id, expr* clause);
expr_ref_vector return_shared_clauses(ast_translation& g2l, unsigned& worker_limit, unsigned worker_id);
lbool get_result() const;
bool is_global_backbone_or_negation(ast_translation& l2g, expr* bb_cand) {
std::scoped_lock lock(mux);
return is_global_backbone_or_negation_unlocked(l2g, bb_cand);
}
void cancel_current_backbone_batch() {
std::scoped_lock lock(mux);
m_bb_cancel_epoch++;
m_bb_cv.notify_all();
}
unsigned get_cancel_epoch() {
std::scoped_lock lock(mux);
return m_bb_cancel_epoch;
}
lbool check(expr_ref_vector const &asms, context &ctx);
};
class worker {
@ -123,14 +253,16 @@ namespace smt {
bool m_share_units_initial_only = true;
double m_max_conflict_mul = 1.5;
bool m_inprocessing = false;
bool m_global_backbones = false;
bool m_local_backbones = false;
bool m_sls = false;
unsigned m_inprocessing_delay = 1;
unsigned m_max_cube_depth = 20;
unsigned m_max_conflicts = UINT_MAX;
bool m_core_minimize = false;
bool m_ablate_backtracking = false;
};
using node = search_tree::node<cube_config>;
unsigned id; // unique identifier for the worker
parallel& p;
batch_manager& b;
@ -141,8 +273,9 @@ namespace smt {
random_gen m_rand;
scoped_ptr<context> ctx;
ast_translation m_g2l, m_l2g;
uint_set m_known_units;
unsigned m_num_shared_units = 0;
unsigned m_shared_units_prefix = 0;
unsigned m_num_initial_atoms = 0;
unsigned m_shared_clause_limit = 0; // remembers the index into shared_clause_trail marking the boundary between "old" and "new" clauses to share
@ -152,10 +285,12 @@ namespace smt {
void share_units();
void update_max_thread_conflicts() {
// allow for backoff scheme of conflicts within the thread for cube timeouts.
m_config.m_threads_max_conflicts = (unsigned)(m_config.m_max_conflict_mul * m_config.m_threads_max_conflicts);
} // allow for backoff scheme of conflicts within the thread for cube timeouts.
}
void simplify();
bb_candidates find_backbone_candidates(unsigned k = 10);
public:
worker(unsigned id, parallel& p, expr_ref_vector const& _asms);
@ -164,6 +299,7 @@ namespace smt {
void collect_shared_clauses();
void cancel();
void cancel_lease();
void collect_statistics(::statistics& st) const;
reslimit& limit() {
@ -191,16 +327,93 @@ namespace smt {
}
};
class core_minimizer_worker {
batch_manager &b;
ast_manager m;
expr_ref_vector asms;
smt_params m_smt_params;
scoped_ptr<context> ctx;
ast_translation m_g2l, m_l2g;
unsigned m_num_core_minimize_calls = 0;
unsigned m_num_core_minimize_undef = 0;
unsigned m_num_core_minimize_refined = 0;
unsigned m_num_core_minimize_lits_removed = 0;
unsigned m_num_core_minimize_found_sat = 0;
unsigned m_core_minimize_conflict_budget = 5000;
unsigned m_shared_clause_limit = 0;
void minimize_unsat_core(expr_ref_vector& core);
void collect_shared_clauses();
public:
core_minimizer_worker(parallel& p, expr_ref_vector const& _asms);
void run();
void cancel();
void collect_statistics(::statistics& st) const;
reslimit& limit() { return m.limit(); }
};
class backbones_worker {
struct stats {
unsigned m_batches_total = 0;
unsigned m_candidates_total = 0;
unsigned m_singleton_backbones = 0;
unsigned m_backbones_detected = 0;
unsigned m_internal_backbones_found = 0;
unsigned m_retry_backbones_found = 0;
unsigned m_bb_retries = 0;
unsigned m_fallback_singleton_checks = 0;
unsigned m_fallback_reason_chunk_exhausted = 0;
unsigned m_fallback_reason_undef = 0;
unsigned m_core_refinement_rounds = 0;
unsigned m_lits_removed_by_core = 0;
unsigned m_num_chunk_increases = 0;
};
enum bb_mode {
bb_negated,
bb_positive
};
unsigned id; // unique identifier for the worker
batch_manager& b;
ast_manager m;
expr_ref_vector asms;
smt_params m_smt_params;
scoped_ptr<context> ctx;
ast_translation m_g2l, m_l2g;
unsigned m_bb_chunk_size = 20;
unsigned m_bb_conflicts_per_chunk = 1000;
uint_set m_known_units;
bool m_use_failed_literal_test;
stats m_stats;
bb_mode m_mode;
unsigned m_shared_clause_limit = 0; // remembers the index into shared_clause_trail marking the boundary between "old" and "new" clauses to share
unsigned m_shared_units_prefix = 0;
unsigned m_num_initial_atoms = 0;
bool try_get_unit_backbone(expr* candidate, expr_ref& backbone);
void run_batch_mode();
void run_failed_literal_mode();
lbool probe_literal(bool_var v, expr *e, bool is_retry);
public:
backbones_worker(unsigned id, parallel &p, expr_ref_vector const &_asms);
void cancel();
void collect_statistics(::statistics& st) const;
void run();
void collect_shared_clauses();
reslimit &limit() { return m.limit(); }
};
batch_manager m_batch_manager;
scoped_ptr_vector<worker> m_workers;
scoped_ptr<sls_worker> m_sls_worker;
scoped_ptr<core_minimizer_worker> m_core_minimizer_worker;
scoped_ptr_vector<backbones_worker> m_global_backbones_workers;
public:
parallel(context& ctx) :
ctx(ctx),
num_threads(std::min(
(unsigned)std::thread::hardware_concurrency(),
ctx.get_fparams().m_threads)),
m_batch_manager(ctx.m, *this) {}
lbool operator()(expr_ref_vector const& asms);

View file

@ -19,6 +19,8 @@ Revision History:
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/quantifier_stat.h"
#include "ast/euf/ho_matcher.h"
#include "ast/rewriter/var_subst.h"
#include "smt/smt_quantifier.h"
#include "smt/smt_context.h"
#include "smt/smt_model_finder.h"
@ -154,7 +156,8 @@ namespace smt {
}
unsigned get_generation(quantifier * q) const {
return get_stat(q)->get_generation();
auto* s = m_quantifier_stat.find_core(q);
return s ? s->get_data().get_value()->get_generation() : 0;
}
void add(quantifier * q, unsigned generation) {
@ -289,16 +292,24 @@ namespace smt {
bool add_instance(quantifier * q, app * pat,
unsigned num_bindings,
enode * const * bindings,
expr* def,
unsigned max_generation,
unsigned min_top_generation,
unsigned max_top_generation,
vector<std::tuple<enode *, enode *>> & used_enodes) {
// Try higher-order refinement first
if (pat && m_plugin->refine_instance(q, pat, num_bindings, bindings, max_generation, min_top_generation, max_top_generation, used_enodes))
return true;
if (!m_quantifier_stat.contains(q)) {
IF_VERBOSE(2, verbose_stream() << "add_instance: quantifier not in stat map: " << mk_pp(q, m()) << "\n");
return false;
}
max_generation = std::max(max_generation, get_generation(q));
get_stat(q)->update_max_generation(max_generation);
fingerprint * f = m_context.add_fingerprint(q, q->get_id(), num_bindings, bindings, def);
fingerprint * f = m_context.add_fingerprint(q, q->get_id(), num_bindings, bindings);
if (f) {
if (is_trace_enabled(TraceTag::causality)) {
log_causality(f,pat,used_enodes);
@ -472,17 +483,17 @@ namespace smt {
bool quantifier_manager::add_instance(quantifier * q, app * pat,
unsigned num_bindings,
enode * const * bindings,
expr* def,
unsigned max_generation,
unsigned min_top_generation,
unsigned max_top_generation,
vector<std::tuple<enode *, enode *>> & used_enodes) {
return m_imp->add_instance(q, pat, num_bindings, bindings, def, max_generation, min_top_generation, max_generation, used_enodes);
return m_imp->add_instance(q, pat, num_bindings, bindings, max_generation, min_top_generation, max_top_generation, used_enodes);
}
bool quantifier_manager::add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, expr* def, unsigned generation) {
bool quantifier_manager::add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation) {
vector<std::tuple<enode *, enode *>> tmp;
return add_instance(q, nullptr, num_bindings, bindings, def, generation, generation, generation, tmp);
return add_instance(q, nullptr, num_bindings, bindings,
generation, generation, generation, tmp);
}
void quantifier_manager::init_search_eh() {
@ -599,9 +610,23 @@ namespace smt {
scoped_ptr<mam> m_lazy_mam;
scoped_ptr<model_finder> m_model_finder;
scoped_ptr<model_checker> m_model_checker;
scoped_ptr<euf::ho_matcher> m_ho_matcher;
unsigned m_new_enode_qhead;
unsigned m_lazy_matching_idx;
bool m_active;
// State for higher-order match refinement callback
struct ho_match_state {
quantifier* m_q = nullptr;
app* m_pat = nullptr;
unsigned m_num_bindings = 0;
enode* const* m_bindings = nullptr;
unsigned m_max_generation = 0;
unsigned m_min_top_generation = 0;
unsigned m_max_top_generation = 0;
vector<std::tuple<enode*, enode*>>* m_used_enodes = nullptr;
};
ho_match_state m_ho_state;
public:
default_qm_plugin():
m_qm(nullptr),
@ -625,10 +650,110 @@ namespace smt {
m_model_finder->set_context(m_context);
m_model_checker->set_qm(qm);
if (m_fparams->m_ho_matching) {
m_ho_matcher = alloc(euf::ho_matcher, m, m_context->get_trail_stack());
std::function<void(euf::ho_subst&)> on_match = [&](euf::ho_subst& s) {
on_ho_match(s);
};
m_ho_matcher->set_on_match(on_match);
}
}
quantifier_manager_plugin * mk_fresh() override { return alloc(default_qm_plugin); }
void on_ho_match(euf::ho_subst& s) {
ast_manager& m = m_context->get_manager();
auto& st = m_ho_state;
auto* hoq = st.m_q;
auto* q = m_ho_matcher->hoq2q(hoq);
expr_ref_vector binding(m);
for (unsigned i = 0; i < s.size(); ++i)
binding.push_back(s.get(i));
// Shrink binding to original quantifier's num_decls
// The HO quantifier has extra vars at higher indices; drop them.
// Binding is indexed by var index: binding[i] = value for var i.
// First substitute any remaining vars, then keep only original vars.
TRACE(ho_matching, tout << "num bound variables " << q->get_num_decls() << " for " << mk_bounded_pp(q, m)
<< "\n"
<< binding << "\n";);
if (binding.size() > q->get_num_decls()) {
var_subst sub(m);
bool change = true;
while (change) {
change = false;
for (unsigned i = 1; i < binding.size(); ++i) {
if (!binding.get(i)) continue;
auto r = sub(binding.get(i), binding);
change |= r != binding.get(i);
binding[i] = r;
}
}
binding.shrink(q->get_num_decls());
}
if (binding.size() < q->get_num_decls())
return;
binding.reverse();
// Create enodes for the refined bindings and add instance
ptr_buffer<enode> new_bindings;
unsigned max_gen = st.m_max_generation;
for (expr* e : binding) {
if (!e)
return; // incomplete binding
if (!m_context->e_internalized(e)) {
m_context->internalize(e, false);
}
enode* n = m_context->get_enode(e);
new_bindings.push_back(n);
if (n->get_generation() > max_gen)
max_gen = n->get_generation();
}
TRACE(ho_matching,
tout << "ho_match refined for " << mk_pp(q, m) << "\n";
for (unsigned i = 0; i < new_bindings.size(); ++i)
tout << " binding[" << i << "] = " << mk_bounded_pp(new_bindings[i]->get_expr(), m) << "\n";);
vector<std::tuple<enode*, enode*>> used_enodes;
m_context->add_instance(q, nullptr, new_bindings.size(), new_bindings.data(),
max_gen, st.m_min_top_generation, st.m_max_top_generation, used_enodes);
}
bool try_ho_refine(quantifier* qa, app* pat, unsigned num_bindings, enode* const* bindings,
unsigned max_generation, unsigned min_top_gen, unsigned max_top_gen,
vector<std::tuple<enode*, enode*>>& used_enodes) {
if (!m_ho_matcher || !m_ho_matcher->is_ho_pattern(pat))
return false;
ast_manager& m = m_context->get_manager();
expr_ref_vector s(m);
// With var_subst(std_order=true): var idx maps to s[s.size()-idx-1]
// SMT MAM bindings: bindings[i] = var at index (num_bindings-1-i)
// So bindings[i] corresponds to s[i] with std_order
for (unsigned i = 0; i < num_bindings; ++i)
s.push_back(bindings[i]->get_expr());
m_ho_state.m_q = qa;
m_ho_state.m_pat = pat;
m_ho_state.m_num_bindings = num_bindings;
m_ho_state.m_bindings = bindings;
m_ho_state.m_max_generation = max_generation;
m_ho_state.m_min_top_generation = min_top_gen;
m_ho_state.m_max_top_generation = max_top_gen;
m_ho_state.m_used_enodes = &used_enodes;
IF_VERBOSE(10, verbose_stream() << "try_ho_refine: q=" << mk_pp(qa, m) << "\n pat=" << mk_pp(pat, m) << "\n";
for (unsigned i = 0; i < num_bindings; ++i)
verbose_stream() << " s[" << i << "] = " << mk_pp(s.get(i), m) << " sort=" << mk_pp(s.get(i)->get_sort(), m) << "\n";);
m_ho_matcher->refine_ho_match(pat, s);
return true;
}
bool model_based() const override { return m_fparams->m_mbqi; }
bool mbqi_enabled(quantifier *q) const override {
@ -656,13 +781,13 @@ namespace smt {
void push() override {
m_mam->push_scope();
m_lazy_mam->push_scope();
m_model_finder->push_scope();
m_model_finder->push_scope();
}
void pop(unsigned num_scopes) override {
m_mam->pop_scope(num_scopes);
m_lazy_mam->pop_scope(num_scopes);
m_model_finder->pop_scope(num_scopes);
m_model_finder->pop_scope(num_scopes);
}
void init_search_eh() override {
@ -704,6 +829,19 @@ namespace smt {
TRACE(quantifier, tout << "adding:\n" << expr_ref(mp, m) << "\n";);
m_mam->add_pattern(q, mp);
}
// Compile HO pattern and also register the compiled version with MAM
if (m_ho_matcher) {
auto [q1, p1] = m_ho_matcher->compile_ho_pattern(q, mp);
IF_VERBOSE(10, verbose_stream() << "ho_matching: q=" << q->get_qid()
<< " compiled=" << (p1 != mp)
<< " p1=" << mk_pp(p1, m) << "\n");
if (p1 != mp) {
if (!unary && j >= num_eager_multi_patterns)
m_lazy_mam->add_pattern(q1, p1);
else
m_mam->add_pattern(q1, p1);
}
}
if (!unary)
j++;
}
@ -713,6 +851,13 @@ namespace smt {
return m_fparams->m_ematching && !m_qm->empty();
}
bool refine_instance(quantifier* q, app* pat, unsigned num_bindings, enode* const* bindings,
unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation,
vector<std::tuple<enode*, enode*>>& used_enodes) override {
return try_ho_refine(q, pat, num_bindings, bindings, max_generation, min_top_generation, max_top_generation, used_enodes);
}
void add_eq_eh(enode * e1, enode * e2) override {
if (use_ematching())
m_mam->add_eq_eh(e1, e2);
@ -726,7 +871,9 @@ namespace smt {
}
bool can_propagate() const override {
return m_active && m_mam->has_work();
bool r = m_active && m_mam->has_work();
IF_VERBOSE(11, if (r) verbose_stream() << "ho_matching: can_propagate=true\n");
return r;
}
void restart_eh() override {
@ -750,6 +897,7 @@ namespace smt {
void propagate() override {
if (!m_active)
return;
IF_VERBOSE(10, verbose_stream() << "ho_matching: propagate(), mam.has_work=" << m_mam->has_work() << "\n");
m_mam->match();
if (!m_context->relevancy() && use_ematching()) {
ptr_vector<enode>::const_iterator it = m_context->begin_enodes();

View file

@ -60,12 +60,11 @@ namespace smt {
bool add_instance(quantifier * q, app * pat,
unsigned num_bindings,
enode * const * bindings,
expr* def,
unsigned max_generation,
unsigned min_top_generation,
unsigned max_top_generation,
vector<std::tuple<enode *, enode *>> & used_enodes /*gives the equalities used for the pattern match, see mam.cpp for more info*/);
bool add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, expr* def, unsigned generation = 0);
bool add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation = 0);
void init_search_eh();
void assign_eh(quantifier * q);
@ -178,8 +177,14 @@ namespace smt {
virtual void push() = 0;
virtual void pop(unsigned num_scopes) = 0;
/**
\brief Try to refine a match using higher-order matching.
Returns true if the pattern was an HO pattern and refinement was attempted.
In that case, the plugin handles adding instances via the refined bindings.
*/
virtual bool refine_instance(quantifier* q, app* pat, unsigned num_bindings, enode* const* bindings,
unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation,
vector<std::tuple<enode*, enode*>>& used_enodes) { return false; }
};
};

View file

@ -236,7 +236,7 @@ namespace smt {
TRACE(quick_checker_sizes, tout << "found new candidate\n";
for (unsigned i = 0; i < m_num_bindings; ++i) tout << "#" << m_bindings[i]->get_owner_id() << " "; tout << "\n";);
unsigned max_generation = get_max_generation(m_num_bindings, m_bindings.data());
if (m_context.add_instance(q, nullptr /* no pattern was used */, m_num_bindings, m_bindings.data(), nullptr,
if (m_context.add_instance(q, nullptr /* no pattern was used */, m_num_bindings, m_bindings.data(),
max_generation,
0, // min_top_generation is only available for instances created by the MAM
0, // max_top_generation is only available for instances created by the MAM

View file

@ -45,6 +45,7 @@ namespace smt {
unsigned m_num_checks;
unsigned m_num_simplifications;
unsigned m_num_del_clauses;
unsigned m_num_assignments;
statistics() {
reset();
}

View file

@ -204,7 +204,7 @@ namespace smt {
log_axiom_instantiation(mk_or(fmls));
}
void theory::log_axiom_instantiation(app * r, unsigned axiom_id, unsigned num_bindings, app * const * bindings, unsigned pattern_id, const vector<std::tuple<enode *, enode *>> & used_enodes) {
void theory::log_axiom_instantiation(app * r, unsigned axiom_id, unsigned num_bindings, expr * const * bindings, unsigned pattern_id, const vector<std::tuple<enode *, enode *>> & used_enodes) {
ast_manager & m = get_manager();
SASSERT(r->get_ref_count() > 0);
std::ostream& out = m.trace_stream();

View file

@ -259,7 +259,7 @@ namespace smt {
\brief This method is invoked when the theory application n
is marked as relevant.
*/
virtual void relevant_eh(app * n) {
virtual void relevant_eh(expr * n) {
}
/**
@ -435,7 +435,7 @@ namespace smt {
return m_var2enode[v];
}
app * get_expr(theory_var v) const {
expr * get_expr(theory_var v) const {
return get_enode(v)->get_expr();
}
@ -482,11 +482,11 @@ namespace smt {
protected:
void log_axiom_instantiation(app * r, unsigned axiom_id = UINT_MAX, unsigned num_bindings = 0,
app * const * bindings = nullptr, unsigned pattern_id = UINT_MAX,
expr * const * bindings = nullptr, unsigned pattern_id = UINT_MAX,
const vector<std::tuple<enode *, enode *>> & used_enodes = vector<std::tuple<enode *, enode*>>());
void log_axiom_instantiation(expr * r, unsigned axiom_id = UINT_MAX, unsigned num_bindings = 0,
app * const * bindings = nullptr, unsigned pattern_id = UINT_MAX,
expr * const * bindings = nullptr, unsigned pattern_id = UINT_MAX,
const vector<std::tuple<enode *, enode *>> & used_enodes = vector<std::tuple<enode *, enode*>>()) {
log_axiom_instantiation(to_app(r), axiom_id, num_bindings, bindings, pattern_id, used_enodes);
}

View file

@ -31,6 +31,7 @@ Notes:
#include "solver/solver.h"
#include "solver/mus.h"
#include "solver/parallel_tactical.h"
#include "solver/parallel_tactical2.h"
#include "solver/parallel_params.hpp"
#include <mutex>
@ -429,18 +430,30 @@ static tactic * mk_seq_smt_tactic(ast_manager& m, params_ref const & p) {
tactic * mk_parallel_smt_tactic(ast_manager& m, params_ref const& p) {
parallel_params pp(p);
if (pp.enable2())
return mk_parallel_tactic2(mk_smt_solver(m, p, symbol::null), p);
return mk_parallel_tactic(mk_smt_solver(m, p, symbol::null), p);
}
tactic * mk_smt_tactic_core(ast_manager& m, params_ref const& p, symbol const& logic) {
parallel_params pp(p);
return pp.enable() ? mk_parallel_tactic(mk_smt_solver(m, p, logic), p) : mk_seq_smt_tactic(m, p);
if (pp.enable())
return mk_parallel_tactic(mk_smt_solver(m, p, logic), p);
if (pp.enable2())
return mk_parallel_tactic2(mk_smt_solver(m, p, logic), p);
return mk_seq_smt_tactic(m, p);
}
tactic * mk_smt_tactic_core_using(ast_manager& m, bool auto_config, params_ref const& _p) {
parallel_params pp(_p);
params_ref p = _p;
p.set_bool("auto_config", auto_config);
return using_params(pp.enable() ? mk_parallel_smt_tactic(m, p) : mk_seq_smt_tactic(m, p), p);
tactic *t = nullptr;
if (pp.enable() || pp.enable2())
t = mk_parallel_smt_tactic(m, p);
else
t = mk_seq_smt_tactic(m, p);
return using_params(t, p);
}

View file

@ -524,7 +524,7 @@ namespace smt {
bool has_var(expr * v) const { return get_context().e_internalized(v) && get_context().get_enode(v)->get_th_var(get_id()) != null_theory_var; }
theory_var expr2var(expr * v) const { SASSERT(get_context().e_internalized(v)); return get_context().get_enode(v)->get_th_var(get_id()); }
expr * var2expr(theory_var v) const { return get_enode(v)->get_expr(); }
expr * var2expr(theory_var v) const { return get_expr(v); }
bool reflection_enabled() const;
bool reflect(app * n) const;
unsigned lazy_pivoting_lvl() const { return m_params.m_arith_lazy_pivoting_lvl; }
@ -656,7 +656,7 @@ namespace smt {
void push_scope_eh() override;
void pop_scope_eh(unsigned num_scopes) override;
void relevant_eh(app * n) override;
void relevant_eh(expr * n) override;
void restart_eh() override;
void init_search_eh() override;
@ -966,7 +966,7 @@ namespace smt {
\brief A monomial is 'pure' if does not have a numeric coefficient.
*/
bool is_pure_monomial(expr * m) const;
bool is_pure_monomial(theory_var v) const { return is_pure_monomial(get_enode(v)->get_expr()); }
bool is_pure_monomial(theory_var v) const { return is_pure_monomial(get_expr(v)); }
void mark_var(theory_var v, svector<theory_var> & vars, var_set & already_found);
void mark_dependents(theory_var v, svector<theory_var> & vars, var_set & already_found, row_set & already_visited_rows);
void get_non_linear_cluster(svector<theory_var> & vars);

View file

@ -1086,7 +1086,7 @@ namespace smt {
expr_ref theory_arith<Ext>::mk_gt(theory_var v) {
ast_manager& m = get_manager();
inf_numeral const& val = get_value(v);
expr* obj = get_enode(v)->get_expr();
expr* obj = get_expr(v);
expr_ref e(m);
rational r = val.get_rational();
if (m_util.is_int(obj->get_sort())) {
@ -1124,7 +1124,7 @@ namespace smt {
expr_ref theory_arith<Ext>::mk_ge(generic_model_converter& fm, theory_var v, inf_numeral const& val) {
ast_manager& m = get_manager();
std::ostringstream strm;
strm << val << " <= " << mk_pp(get_enode(v)->get_expr(), get_manager());
strm << val << " <= " << mk_pp(get_expr(v), get_manager());
app* b = m.mk_const(symbol(strm.str()), m.mk_bool_sort());
expr_ref result(b, m);
TRACE(opt, tout << result << "\n";);
@ -1799,7 +1799,7 @@ namespace smt {
*/
template<typename Ext>
typename theory_arith<Ext>::max_min_t theory_arith<Ext>::max_min(theory_var v, bool max, bool maintain_integrality, bool& has_shared) {
expr* e = get_enode(v)->get_expr();
expr* e = get_expr(v);
(void)e;
SASSERT(!maintain_integrality || valid_assignment());
SASSERT(satisfy_bounds());
@ -2179,8 +2179,8 @@ namespace smt {
TRACE(shared, tout << ctx.get_scope_level() << " " << v << " " << r->get_num_parents() << "\n";);
for (; it != end; ++it) {
enode * parent = *it;
app * o = parent->get_expr();
if (o->get_family_id() == get_id()) {
app* o = parent->get_app();
if (parent->get_family_id() == get_id()) {
switch (o->get_decl_kind()) {
case OP_DIV:
case OP_IDIV:

View file

@ -1381,18 +1381,19 @@ namespace smt {
}
template<typename Ext>
void theory_arith<Ext>::relevant_eh(app * n) {
void theory_arith<Ext>::relevant_eh(expr * n) {
TRACE(arith_relevant_eh, tout << "relevant_eh: " << mk_pp(n, m) << "\n";);
if (m_util.is_mod(n))
mk_idiv_mod_axioms(n->get_arg(0), n->get_arg(1));
else if (m_util.is_rem(n))
mk_rem_axiom(n->get_arg(0), n->get_arg(1));
else if (m_util.is_div(n))
mk_div_axiom(n->get_arg(0), n->get_arg(1));
expr* x = nullptr, *y = nullptr;
if (m_util.is_mod(n, x, y))
mk_idiv_mod_axioms(x, y);
else if (m_util.is_rem(n, x, y))
mk_rem_axiom(x, y);
else if (m_util.is_div(n, x, y))
mk_div_axiom(x, y);
else if (m_util.is_to_int(n))
mk_to_int_axiom(n);
mk_to_int_axiom(to_app(n));
else if (m_util.is_is_int(n))
mk_is_int_axiom(n);
mk_is_int_axiom(to_app(n));
}
template<typename Ext>
@ -1451,8 +1452,8 @@ namespace smt {
template<typename Ext>
void theory_arith<Ext>::new_diseq_eh(theory_var v1, theory_var v2) {
TRACE(arith_new_diseq_eh, tout << mk_bounded_pp(get_enode(v1)->get_expr(), m) << "\n" <<
mk_bounded_pp(get_enode(v2)->get_expr(), m) << "\n";);
TRACE(arith_new_diseq_eh, tout << mk_bounded_pp(get_expr(v1), m) << "\n" <<
mk_bounded_pp(get_expr(v2), m) << "\n";);
m_stats.m_assert_diseq++;
m_arith_eq_adapter.new_diseq_eh(v1, v2);
}

View file

@ -215,7 +215,7 @@ namespace smt {
tout << "k = " << k << ", _k = "<< _k << std::endl;
);
expr_ref bound(m);
expr* e = get_enode(v)->get_expr();
expr* e = get_expr(v);
bound = m_util.mk_ge(e, m_util.mk_numeral(_k, m_util.is_int(e)));
context & ctx = get_context();
{
@ -413,7 +413,7 @@ namespace smt {
for (; it != end; ++it) {
if (!it->is_dead() && it->m_var != b && is_free(it->m_var)) {
theory_var v = it->m_var;
expr* e = get_enode(v)->get_expr();
expr* e = get_expr(v);
bool _is_int = m_util.is_int(e);
expr_ref bound(m_util.mk_ge(e, m_util.mk_numeral(rational::zero(), _is_int)), get_manager());
context & ctx = get_context();
@ -629,9 +629,9 @@ namespace smt {
}
rational _k = k.to_rational();
if (is_lower)
bound = m_util.mk_ge(get_enode(v)->get_expr(), m_util.mk_numeral(_k, is_int(v)));
bound = m_util.mk_ge(get_expr(v), m_util.mk_numeral(_k, is_int(v)));
else
bound = m_util.mk_le(get_enode(v)->get_expr(), m_util.mk_numeral(_k, is_int(v)));
bound = m_util.mk_le(get_expr(v), m_util.mk_numeral(_k, is_int(v)));
}
else {
if (num_ints > 0) {

View file

@ -653,7 +653,7 @@ theory_var theory_arith<Ext>::find_nl_var_for_branching() {
bool computed_epsilon = false;
bool r = check_monomial_assignment(v, computed_epsilon);
if (!r) {
expr * m = get_enode(v)->get_expr();
expr * m = get_expr(v);
SASSERT(is_pure_monomial(m));
for (expr * arg : *to_app(m)) {
theory_var curr = ctx.get_enode(arg)->get_th_var(get_id());

View file

@ -484,7 +484,7 @@ namespace smt {
pp.set_benchmark_name("lemma");
int n = get_num_vars();
for (theory_var v = 0; v < n; ++v) {
expr * n = get_enode(v)->get_expr();
expr * n = get_expr(v);
if (is_fixed(v)) {
inf_numeral k_inf = lower_bound(v);
rational k = k_inf.get_rational().to_rational();

View file

@ -42,7 +42,7 @@ namespace smt {
// v1 is the new root
TRACE(array,
tout << "merging v" << v1 << " v" << v2 << "\n"; display_var(tout, v1);
tout << mk_pp(get_enode(v1)->get_expr(), m) << " <- " << mk_pp(get_enode(v2)->get_expr(), m) << "\n";);
tout << mk_pp(get_expr(v1), m) << " <- " << mk_pp(get_expr(v2), m) << "\n";);
SASSERT(v1 == find(v1));
var_data * d1 = m_var_data[v1];
var_data * d2 = m_var_data[v2];
@ -68,12 +68,12 @@ namespace smt {
m_var_data.push_back(alloc(var_data));
var_data * d = m_var_data[r];
TRACE(array, tout << mk_bounded_pp(n->get_expr(), m) << "\nis_array: " << is_array_sort(n) << ", is_select: " << is_select(n) <<
", is_store: " << is_store(n) << "\n";);
", is_store: " << is_store(n) << ", is_lambda: " << is_lambda(n->get_expr()) << "\n";);
d->m_is_array = is_array_sort(n);
if (d->m_is_array)
register_sort(n->get_expr()->get_sort());
d->m_is_select = is_select(n);
if (is_store(n))
if (is_store(n) || is_lambda(n->get_expr()))
d->m_stores.push_back(n);
ctx.attach_th_var(n, this, r);
if (laziness() <= 1 && is_store(n))
@ -88,14 +88,14 @@ namespace smt {
v = find(v);
var_data * d = m_var_data[v];
d->m_parent_selects.push_back(s);
TRACE(array, tout << v << " " << mk_pp(s->get_expr(), m) << " " << mk_pp(get_enode(v)->get_expr(), m) << "\n";);
TRACE(array, tout << v << " " << mk_pp(s->get_expr(), m) << " " << mk_pp(get_expr(v), m) << "\n";);
m_trail_stack.push(push_back_trail<enode *, false>(d->m_parent_selects));
for (enode* n : d->m_stores)
instantiate_axiom2a(s, n);
if (!m_params.m_array_delay_exp_axiom && d->m_prop_upward) {
for (enode* store : d->m_parent_stores) {
SASSERT(is_store(store));
SASSERT(is_store(store) || is_lambda(store->get_expr()));
if (!m_params.m_array_cg || store->is_cgr()) {
instantiate_axiom2b(s, store);
}
@ -106,7 +106,7 @@ namespace smt {
void theory_array::add_parent_store(theory_var v, enode * s) {
if (m_params.m_array_cg && !s->is_cgr())
return;
SASSERT(is_store(s));
SASSERT(is_store(s) || is_lambda(s->get_expr()));
v = find(v);
var_data * d = m_var_data[v];
d->m_parent_stores.push_back(s);
@ -177,7 +177,7 @@ namespace smt {
void theory_array::add_store(theory_var v, enode * s) {
if (m_params.m_array_cg && !s->is_cgr())
return;
SASSERT(is_store(s));
SASSERT(is_store(s) || is_lambda(s->get_expr()));
v = find(v);
var_data * d = m_var_data[v];
unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d);
@ -204,7 +204,7 @@ namespace smt {
void theory_array::instantiate_axiom2a(enode * select, enode * store) {
TRACE(array, tout << "axiom 2a: #" << select->get_owner_id() << " #" << store->get_owner_id() << "\n";);
SASSERT(is_select(select));
SASSERT(is_store(store));
SASSERT(is_store(store) || is_lambda(store->get_expr()));
if (assert_store_axiom2(store, select))
m_stats.m_num_axiom2a++;
}
@ -212,7 +212,7 @@ namespace smt {
bool theory_array::instantiate_axiom2b(enode * select, enode * store) {
TRACE(array_axiom2b, tout << "axiom 2b: #" << select->get_owner_id() << " #" << store->get_owner_id() << "\n";);
SASSERT(is_select(select));
SASSERT(is_store(store));
SASSERT(is_store(store) || is_lambda(store->get_expr()));
if (assert_store_axiom2(store, select)) {
m_stats.m_num_axiom2b++;
return true;
@ -261,7 +261,7 @@ namespace smt {
}
bool theory_array::internalize_term(app * n) {
if (!is_store(n) && !is_select(n)) {
if (!is_store(n) && !is_select(n) && !is_lambda(n)) {
if (!is_array_ext(n))
found_unsupported_op(n);
return false;
@ -282,7 +282,7 @@ namespace smt {
if (is_select(n)) {
add_parent_select(v_arg, ctx.get_enode(n));
}
else if (is_store(n)) {
else if (is_store(n) || is_lambda(n)) {
add_parent_store(v_arg, ctx.get_enode(n));
}
}
@ -298,11 +298,6 @@ namespace smt {
void theory_array::new_eq_eh(theory_var v1, theory_var v2) {
m_find.merge(v1, v2);
enode* n1 = get_enode(v1), *n2 = get_enode(v2);
if (n1->get_expr()->get_decl()->is_lambda() ||
n2->get_expr()->get_decl()->is_lambda()) {
assert_congruent(n1, n2);
}
}
void theory_array::new_diseq_eh(theory_var v1, theory_var v2) {
@ -310,8 +305,8 @@ namespace smt {
v2 = find(v2);
var_data * d1 = m_var_data[v1];
TRACE(ext, tout << "extensionality: " << d1->m_is_array << "\n"
<< mk_bounded_pp(get_enode(v1)->get_expr(), m, 5) << "\n"
<< mk_bounded_pp(get_enode(v2)->get_expr(), m, 5) << "\n";);
<< mk_bounded_pp(get_expr(v1), m, 5) << "\n"
<< mk_bounded_pp(get_expr(v2), m, 5) << "\n";);
if (d1->m_is_array) {
SASSERT(m_var_data[v2]->m_is_array);
@ -319,16 +314,18 @@ namespace smt {
}
}
void theory_array::relevant_eh(app * n) {
void theory_array::relevant_eh(expr * n) {
if (laziness() == 0)
return;
if (m.is_ite(n)) {
TRACE(array, tout << "relevant ite " << mk_pp(n, m) << "\n";);
}
if (!is_store(n) && !is_select(n))
if (!is_store(n) && !is_select(n) && !is_lambda(n))
return;
if (!ctx.e_internalized(n)) ctx.internalize(n, false);
enode * arg = ctx.get_enode(n->get_arg(0));
if (is_lambda(n))
return;
enode * arg = ctx.get_enode(to_app(n)->get_arg(0));
theory_var v_arg = arg->get_th_var(get_id());
SASSERT(v_arg != null_theory_var);

View file

@ -28,7 +28,7 @@ namespace smt {
unsigned m_num_axiom1, m_num_axiom2a, m_num_axiom2b, m_num_extensionality, m_num_eq_splits;
unsigned m_num_map_axiom, m_num_default_map_axiom;
unsigned m_num_select_const_axiom, m_num_default_store_axiom, m_num_default_const_axiom, m_num_default_as_array_axiom;
unsigned m_num_select_as_array_axiom, m_num_default_lambda_axiom;
unsigned m_num_select_as_array_axiom, m_num_default_lambda_axiom, m_num_choice_axiom;
void reset() { memset(this, 0, sizeof(theory_array_stats)); }
theory_array_stats() { reset(); }
};
@ -59,7 +59,7 @@ namespace smt {
void apply_sort_cnstr(enode * n, sort * s) override;
void new_eq_eh(theory_var v1, theory_var v2) override;
void new_diseq_eh(theory_var v1, theory_var v2) override;
void relevant_eh(app * n) override;
void relevant_eh(expr * n) override;
void push_scope_eh() override;
void pop_scope_eh(unsigned num_scopes) override;
final_check_status final_check_eh(unsigned) override;
@ -115,4 +115,3 @@ namespace smt {
};

View file

@ -108,7 +108,7 @@ namespace smt {
}
void theory_array_base::assert_store_axiom1_core(enode * e) {
app * n = e->get_expr();
app * n = e->get_app();
SASSERT(is_store(n));
ptr_buffer<expr> sel_args;
unsigned num_args = n->get_num_args();
@ -217,28 +217,48 @@ namespace smt {
if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n";
}
}
void theory_array_base::assert_lambda_axiom_core(enode* n, enode* select) {
SASSERT(is_lambda(n->get_expr()));
SASSERT(is_select(select));
expr *e = n->get_expr();
SASSERT(is_lambda(e));
app *s = select->get_app();
auto q = to_quantifier(e);
SASSERT(q);
SASSERT(q->get_num_decls() == s->get_num_args() - 1);
// do the same thing as in sat/smt/array_axioms:
ptr_vector<expr> args(s->get_num_args(), s->get_args());
args[0] = q;
array_util a(m);
expr_ref alpha(a.mk_select(args), m);
expr_ref beta(alpha);
ctx.get_rewriter()(beta);
TRACE(array, tout << alpha << " == " << beta << "\n";);
auto alpha_n = ensure_enode(alpha);
auto beta_n = ensure_enode(beta);
ctx.assign_eq(alpha_n, beta_n, eq_justification::mk_axiom());
}
bool theory_array_base::assert_store_axiom2(enode * store, enode * select) {
SASSERT(is_store(store) || is_lambda(store->get_expr()));
unsigned num_args = select->get_num_args();
unsigned i = 1;
for (; i < num_args; ++i)
if (store->get_arg(i)->get_root() != select->get_arg(i)->get_root())
if (is_store(store) && store->get_arg(i)->get_root() != select->get_arg(i)->get_root())
break;
if (i == num_args)
return false;
if (ctx.add_fingerprint(store, store->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) {
TRACE(array, tout << "adding axiom2 to todo queue\n";);
m_axiom2_todo.push_back(std::make_pair(store, select));
m_axiom2_todo.push_back({store, select});
return true;
}
TRACE(array, tout << "axiom already instantiated: #" << store->get_owner_id() << " #" << select->get_owner_id() << "\n";);
return false;
}
func_decl_ref_vector * theory_array_base::register_sort(sort * s_array) {
unsigned dimension = get_dimension(s_array);
func_decl_ref_vector * ext_skolems = nullptr;
@ -333,8 +353,8 @@ namespace smt {
void theory_array_base::assert_extensionality_core(enode * n1, enode * n2) {
app * e1 = n1->get_expr();
app * e2 = n2->get_expr();
expr * e1 = n1->get_expr();
expr * e2 = n2->get_expr();
func_decl_ref_vector * funcs = nullptr;
sort * s = e1->get_sort();
@ -371,15 +391,15 @@ namespace smt {
\brief assert n1 = n2 => forall vars . (n1 vars) = (n2 vars)
*/
void theory_array_base::assert_congruent_core(enode * n1, enode * n2) {
app * e1 = n1->get_expr();
app * e2 = n2->get_expr();
expr * e1 = n1->get_expr();
expr * e2 = n2->get_expr();
sort* s = e1->get_sort();
unsigned dimension = get_array_arity(s);
literal n1_eq_n2 = mk_eq(e1, e2, true);
ctx.mark_as_relevant(n1_eq_n2);
expr_ref_vector args1(m), args2(m);
args1.push_back(instantiate_lambda(e1));
args2.push_back(instantiate_lambda(e2));
args1.push_back(e1);
args2.push_back(e2);
svector<symbol> names;
sort_ref_vector sorts(m);
for (unsigned i = 0; i < dimension; ++i) {
@ -403,17 +423,6 @@ namespace smt {
assert_axiom(~n1_eq_n2, fa_eq);
}
expr_ref theory_array_base::instantiate_lambda(app* e) {
quantifier * q = m.is_lambda_def(e->get_decl());
expr_ref f(e, m);
if (q) {
// the variables in q are maybe not consecutive.
var_subst sub(m, false);
f = sub(q, e->get_num_args(), e->get_args());
}
return f;
}
bool theory_array_base::can_propagate() {
return
!m_axiom1_todo.empty() ||
@ -424,13 +433,16 @@ namespace smt {
}
void theory_array_base::propagate() {
while (can_propagate()) {
while (theory_array_base::can_propagate()) {
for (unsigned i = 0; i < m_axiom1_todo.size(); ++i)
assert_store_axiom1_core(m_axiom1_todo[i]);
m_axiom1_todo.reset();
for (unsigned i = 0; i < m_axiom2_todo.size(); ++i) {
auto [store, select] = m_axiom2_todo[i];
assert_store_axiom2_core(store, select);
if (is_store(store))
assert_store_axiom2_core(store, select);
else
assert_lambda_axiom_core(store, select);
}
m_axiom2_todo.reset();
for (unsigned i = 0; i < m_extensionality_todo.size(); ++i) {
@ -561,13 +573,13 @@ namespace smt {
TRACE(array_bug, tout << "mk_interface_eqs: processing: v" << *it1 << "\n";);
theory_var v1 = *it1;
enode * n1 = get_enode(v1);
sort * s1 = n1->get_expr()->get_sort();
sort * s1 = n1->get_sort();
sbuffer<theory_var>::iterator it2 = it1;
++it2;
for (; it2 != end1; ++it2) {
theory_var v2 = *it2;
enode * n2 = get_enode(v2);
sort * s2 = n2->get_expr()->get_sort();
sort * s2 = n2->get_sort();
if (s1 == s2 && !ctx.is_diseq(n1, n2)) {
app * eq = mk_eq_atom(n1->get_expr(), n2->get_expr());
if (!ctx.b_internalized(eq) || !ctx.is_relevant(eq)) {
@ -974,7 +986,7 @@ namespace smt {
model_value_proc * theory_array_base::mk_value(enode * n, model_generator & mg) {
theory_var v = n->get_th_var(get_id());
SASSERT(v != null_theory_var);
sort * s = n->get_expr()->get_sort();
sort * s = n->get_sort();
enode * else_val_n = get_default(v);
array_value_proc * result = nullptr;

View file

@ -34,25 +34,31 @@ namespace smt {
virtual void set_prop_upward(theory_var v) {}
void found_unsupported_op(expr * n);
void found_unsupported_op(enode* n) { found_unsupported_op(n->get_expr()); }
void found_unsupported_op(theory_var v) { found_unsupported_op(get_enode(v)->get_expr()); }
void found_unsupported_op(theory_var v) { found_unsupported_op(get_expr(v)); }
bool is_store(app const* n) const { return n->is_app_of(get_id(), OP_STORE); }
bool is_map(app const* n) const { return n->is_app_of(get_id(), OP_ARRAY_MAP); }
bool is_select(app const* n) const { return n->is_app_of(get_id(), OP_SELECT); }
bool is_default(app const* n) const { return n->is_app_of(get_id(), OP_ARRAY_DEFAULT); }
bool is_const(app const* n) const { return n->is_app_of(get_id(), OP_CONST_ARRAY); }
bool is_array_ext(app const * n) const { return n->is_app_of(get_id(), OP_ARRAY_EXT); }
bool is_as_array(app const * n) const { return n->is_app_of(get_id(), OP_AS_ARRAY); }
bool is_store(expr const* n) const { return is_app(n) && to_app(n)->is_app_of(get_id(), OP_STORE); }
bool is_map(expr const* n) const { return is_app(n) && to_app(n)->is_app_of(get_id(), OP_ARRAY_MAP); }
bool is_select(expr const* n) const { return is_app(n) && to_app(n)->is_app_of(get_id(), OP_SELECT); }
bool is_default(expr const* n) const { return is_app(n) && to_app(n)->is_app_of(get_id(), OP_ARRAY_DEFAULT); }
bool is_const(expr const* n) const { return is_app(n) && to_app(n)->is_app_of(get_id(), OP_CONST_ARRAY); }
bool is_array_ext(expr const * n) const { return is_app(n) && to_app(n)->is_app_of(get_id(), OP_ARRAY_EXT); }
bool is_as_array(expr const * n) const { return is_app(n) && to_app(n)->is_app_of(get_id(), OP_AS_ARRAY); }
bool is_choice(expr const* n) const { return is_app(n) && to_app(n)->is_app_of(get_id(), OP_CHOICE); }
bool is_array_sort(sort const* s) const { return s->is_sort_of(get_id(), ARRAY_SORT); }
bool is_array_sort(app const* n) const { return is_array_sort(n->get_sort()); }
bool is_array_sort(expr const* n) const { return is_array_sort(n->get_sort()); }
bool is_store(enode const * n) const { return is_store(n->get_expr()); }
bool is_map(enode const* n) const { return is_map(n->get_expr()); }
bool is_select(enode const* n) const { return is_select(n->get_expr()); }
bool is_const(enode const* n) const { return is_const(n->get_expr()); }
bool is_as_array(enode const * n) const { return is_as_array(n->get_expr()); }
bool is_choice(enode const* n) const { return is_choice(n->get_expr()); }
bool is_default(enode const* n) const { return is_default(n->get_expr()); }
bool is_array_sort(enode const* n) const { return is_array_sort(n->get_expr()); }
bool is_array_sort(enode const* n) const { return is_array_sort(n->get_sort()); }
bool is_select_arg(enode* r);
app * mk_select(unsigned num_args, expr * const * args);
@ -74,13 +80,14 @@ namespace smt {
void assert_axiom(literal l);
void assert_store_axiom1_core(enode * n);
void assert_store_axiom2_core(enode * store, enode * select);
void assert_lambda_axiom_core(enode *lambda, enode *select);
void assert_store_axiom1(enode * n) { m_axiom1_todo.push_back(n); }
bool assert_store_axiom2(enode * store, enode * select);
void assert_extensionality_core(enode * a1, enode * a2);
bool assert_extensionality(enode * a1, enode * a2);
expr_ref instantiate_lambda(app* e);
expr_ref instantiate_lambda(expr* e);
void assert_congruent_core(enode * a1, enode * a2);
void assert_congruent(enode * a1, enode * a2);
@ -208,4 +215,3 @@ namespace smt {
};

View file

@ -248,7 +248,7 @@ namespace smt {
instantiate_default_as_array_axiom(n);
d->m_as_arrays.push_back(n);
}
else if (m.is_lambda_def(n->get_decl())) {
else if (is_lambda(n->get_expr())) {
instantiate_default_lambda_def_axiom(n);
d->m_lambdas.push_back(n);
m_lambdas.push_back(n);
@ -271,7 +271,7 @@ namespace smt {
return theory_array::internalize_term(n);
}
if (!is_const(n) && !is_default(n) && !is_map(n) && !is_as_array(n)) {
if (!is_const(n) && !is_default(n) && !is_map(n) && !is_as_array(n) && !is_choice(n)) {
if (!is_array_ext(n))
found_unsupported_op(n);
return false;
@ -368,8 +368,8 @@ namespace smt {
TRACE(array, tout << "v" << v << " " << pp(get_enode(v), m) << " "
<< d->m_prop_upward << " " << m_params.m_array_delay_exp_axiom << "\n";);
for (enode * store : d->m_stores) {
SASSERT(is_store(store));
instantiate_default_store_axiom(store);
if (is_store(store))
instantiate_default_store_axiom(store);
}
if (!m_params.m_array_delay_exp_axiom && d->m_prop_upward) {
@ -403,22 +403,23 @@ namespace smt {
}
}
void theory_array_full::relevant_eh(app* n) {
void theory_array_full::relevant_eh(expr* n) {
TRACE(array, tout << mk_pp(n, m) << "\n";);
theory_array::relevant_eh(n);
if (!is_default(n) && !is_select(n) && !is_map(n) && !is_const(n) && !is_as_array(n)){
if (!is_default(n) && !is_select(n) && !is_map(n) &&
!is_const(n) && !is_as_array(n) && !is_choice(n)) {
return;
}
ctx.ensure_internalized(n);
enode* node = ctx.get_enode(n);
if (is_select(n)) {
enode * arg = ctx.get_enode(n->get_arg(0));
enode * arg = ctx.get_enode(to_app(n)->get_arg(0));
theory_var v = arg->get_th_var(get_id());
SASSERT(v != null_theory_var);
add_parent_select(find(v), node);
}
else if (is_default(n)) {
enode * arg = ctx.get_enode(n->get_arg(0));
enode * arg = ctx.get_enode(to_app(n)->get_arg(0));
theory_var v = arg->get_th_var(get_id());
SASSERT(v != null_theory_var);
set_prop_upward(v);
@ -431,7 +432,7 @@ namespace smt {
add_parent_default(find(v));
}
else if (is_map(n)) {
for (expr * e : *n) {
for (expr * e : *to_app(n)) {
enode* arg = ctx.get_enode(e);
theory_var v_arg = find(arg->get_th_var(get_id()));
add_parent_map(v_arg, node);
@ -442,6 +443,10 @@ namespace smt {
else if (is_as_array(n)) {
instantiate_default_as_array_axiom(node);
}
else if (is_choice(n)) {
m_choice_terms.push_back(node);
ctx.push_trail(push_back_vector(m_choice_terms));
}
}
bool theory_array_full::should_research(expr_ref_vector & unsat_core) {
@ -456,8 +461,8 @@ namespace smt {
// select(map[f](a, ... d), i) = f(select(a,i),...,select(d,i))
//
bool theory_array_full::instantiate_select_map_axiom(enode* sl, enode* mp) {
app* map = mp->get_expr();
app* select = sl->get_expr();
app* map = mp->get_app();
app* select = sl->get_app();
SASSERT(is_map(map));
SASSERT(is_select(select));
SASSERT(map->get_num_args() > 0);
@ -523,7 +528,7 @@ namespace smt {
bool theory_array_full::instantiate_default_map_axiom(enode* mp) {
SASSERT(is_map(mp));
app* map = mp->get_expr();
app* map = mp->get_app();
if (!ctx.add_fingerprint(this, m_default_map_fingerprint, 1, &mp)) {
return false;
}
@ -573,13 +578,12 @@ namespace smt {
if (!ctx.add_fingerprint(this, m_default_lambda_fingerprint, 1, &arr))
return false;
m_stats.m_num_default_lambda_axiom++;
expr* e = arr->get_expr();
expr_ref def(mk_default(e), m);
quantifier* lam = m.is_lambda_def(arr->get_decl());
TRACE(array, tout << mk_pp(lam, m) << "\n" << mk_pp(e, m) << "\n");
quantifier *lam = to_quantifier(arr->get_expr());
expr_ref def(mk_default(arr->get_expr()), m);
TRACE(array, tout << mk_pp(lam, m) << "\n");
expr_ref_vector args(m);
var_subst subst(m, false);
args.push_back(subst(lam, to_app(e)->get_num_args(), to_app(e)->get_args()));
args.push_back(lam);
for (unsigned i = 0; i < lam->get_num_decls(); ++i)
args.push_back(mk_epsilon(lam->get_decl_sort(i)).first);
expr_ref val(mk_select(args), m);
@ -596,6 +600,33 @@ namespace smt {
return try_assign_eq(val.get(), def);
}
bool theory_array_full::instantiate_choice_axiom(enode* ch) {
if (!ctx.add_fingerprint(this, m_choice_fingerprint, 1, &ch))
return false;
++m_stats.m_num_choice_axiom;
SASSERT(is_choice(ch));
app* choice_term = ch->get_app();
expr* pred = choice_term->get_arg(0);
sort* pred_sort = pred->get_sort();
SASSERT(is_array_sort(pred_sort));
SASSERT(get_array_arity(pred_sort) == 1);
SASSERT(m.is_bool(get_array_range(pred_sort)));
sort* x_sort = get_array_domain(pred_sort, 0);
expr_ref x(m.mk_var(0, x_sort), m);
expr* args1[2] = { pred, x };
expr_ref px(mk_select(2, args1), m);
expr* args2[2] = { pred, choice_term };
expr_ref pc(mk_select(2, args2), m);
expr_ref body(m.mk_implies(px, pc), m);
symbol x_name("x");
expr_ref q(m.mk_forall(1, &x_sort, &x_name, body), m);
ctx.get_rewriter()(q);
TRACE(array, tout << "choice " << q << "\n");
ctx.assert_expr(q);
ctx.internalize_assertions();
return true;
}
//
// Assert axiom:
// select(const v, i_1, ..., i_n) = v
@ -613,10 +644,10 @@ namespace smt {
ptr_buffer<expr> sel_args;
sel_args.push_back(cnst->get_expr());
for (unsigned short i = 1; i < num_args; ++i) {
sel_args.push_back(select->get_expr()->get_arg(i));
sel_args.push_back(select->get_app()->get_arg(i));
}
expr * sel = mk_select(sel_args.size(), sel_args.data());
expr * val = cnst->get_expr()->get_arg(0);
expr * val = cnst->get_app()->get_arg(0);
TRACE(array, tout << "new select-const axiom...\n";
tout << "const: " << mk_bounded_pp(cnst->get_expr(), m) << "\n";
tout << "select: " << mk_bounded_pp(select->get_expr(), m) << "\n";
@ -647,7 +678,7 @@ namespace smt {
ptr_buffer<expr> sel_args;
sel_args.push_back(arr->get_expr());
for (unsigned short i = 1; i < num_args; ++i) {
sel_args.push_back(select->get_expr()->get_arg(i));
sel_args.push_back(select->get_app()->get_arg(i));
}
expr * sel = mk_select(sel_args.size(), sel_args.data());
func_decl * f = array_util(m).get_as_array_func_decl(arr->get_expr());
@ -669,7 +700,7 @@ namespace smt {
bool theory_array_full::instantiate_default_store_axiom(enode* store) {
SASSERT(is_store(store));
SASSERT(store->get_num_args() >= 3);
app* store_app = store->get_expr();
app* store_app = store->get_app();
if (!ctx.add_fingerprint(this, m_default_store_fingerprint, store->get_num_args(), store->get_args())) {
return false;
}
@ -747,6 +778,17 @@ namespace smt {
return {eps, diag};
}
void theory_array_full::propagate() {
theory_array::propagate();
if (m_choice_qhead == m_choice_terms.size())
return;
ctx.push_trail(value_trail(m_choice_qhead));
for (; m_choice_qhead < m_choice_terms.size(); ++m_choice_qhead) {
enode *choice = m_choice_terms[m_choice_qhead];
instantiate_choice_axiom(choice);
}
}
final_check_status theory_array_full::assert_delayed_axioms() {
final_check_status r = FC_DONE;
if (!m_params.m_array_delay_exp_axiom) {
@ -839,5 +881,6 @@ namespace smt {
st.update("array def as-array", m_stats.m_num_default_as_array_axiom);
st.update("array sel as-array", m_stats.m_num_select_as_array_axiom);
st.update("array def lambda", m_stats.m_num_default_lambda_axiom);
st.update("array choice ax", m_stats.m_num_choice_axiom);
}
}

View file

@ -37,12 +37,15 @@ namespace smt {
ast2ast_trailmap<sort, app> m_sort2epsilon;
ast2ast_trailmap<sort, func_decl> m_sort2diag;
obj_pair_map<expr, expr, bool> m_eqs;
enode_vector m_choice_terms;
unsigned m_choice_qhead = 0;
static unsigned const m_default_map_fingerprint = UINT_MAX - 112;
static unsigned const m_default_store_fingerprint = UINT_MAX - 113;
static unsigned const m_default_const_fingerprint = UINT_MAX - 115;
static unsigned const m_default_as_array_fingerprint = UINT_MAX - 116;
static unsigned const m_default_lambda_fingerprint = UINT_MAX - 117;
static unsigned const m_choice_fingerprint = UINT_MAX - 118;
protected:
@ -59,7 +62,7 @@ namespace smt {
bool internalize_atom(app * atom, bool gate_ctx) override;
void pop_scope_eh(unsigned num_scopes) override;
theory_var mk_var(enode * n) override;
void relevant_eh(app * n) override;
void relevant_eh(expr * n) override;
bool should_research(expr_ref_vector & unsat_core) override;
void add_theory_assumptions(expr_ref_vector & assumptions) override;
@ -80,6 +83,8 @@ namespace smt {
bool instantiate_default_map_axiom(enode* map);
bool instantiate_default_as_array_axiom(enode* arr);
bool instantiate_default_lambda_def_axiom(enode* arr);
bool instantiate_select_lambda_axiom(enode *lambda);
bool instantiate_choice_axiom(enode* ch);
bool instantiate_parent_stores_default(theory_var v);
@ -108,8 +113,9 @@ namespace smt {
void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) override;
void display_var(std::ostream & out, theory_var v) const override;
void collect_statistics(::statistics & st) const override;
bool can_propagate() override { return theory_array::can_propagate() || m_choice_qhead < m_choice_terms.size(); }
void propagate() override;
};
};

View file

@ -38,7 +38,7 @@ namespace smt {
return r;
}
app * theory_bv::mk_bit2bool(app * bv, unsigned idx) {
app * theory_bv::mk_bit2bool(expr * bv, unsigned idx) {
parameter p(idx);
expr * args[1] = {bv};
return get_manager().mk_app(get_id(), OP_BIT2BOOL, 1, &p, 1, args);
@ -46,7 +46,7 @@ namespace smt {
void theory_bv::mk_bits(theory_var v) {
enode * n = get_enode(v);
app * owner = n->get_expr();
expr * owner = n->get_expr();
unsigned bv_size = get_bv_size(n);
bool is_relevant = ctx.is_relevant(n);
literal_vector & bits = m_bits[v];
@ -179,11 +179,15 @@ namespace smt {
if (params().m_bv_reflect) {
return n->get_arg(idx);
}
else {
app * arg = to_app(n->get_expr()->get_arg(idx));
else if (n->is_app()) {
app * arg = to_app(n->get_app()->get_arg(idx));
SASSERT(ctx.e_internalized(arg));
return ctx.get_enode(arg);
}
else {
UNREACHABLE();
return nullptr;
}
}
inline theory_var theory_bv::get_arg_var(enode * n, unsigned idx) {
@ -236,8 +240,8 @@ namespace smt {
TRACE(bv_diseq_axiom, tout << "found new diseq axiom\n"; display_var(tout, v1); display_var(tout, v2););
// found new disequality
m_stats.m_num_diseq_static++;
app * e1 = get_expr(v1);
app * e2 = get_expr(v2);
expr * e1 = get_expr(v1);
expr * e2 = get_expr(v2);
expr_ref eq(m.mk_eq(e1, e2), m);
literal l = ~mk_literal(eq);
std::function<expr*(void)> logfn = [&]() {
@ -438,8 +442,8 @@ namespace smt {
return;
}
++m_stats.m_num_eq_dynamic;
app* o1 = get_enode(v1)->get_expr();
app* o2 = get_enode(v2)->get_expr();
expr* o1 = get_expr(v1);
expr* o2 = get_expr(v2);
literal oeq = mk_eq(o1, o2, true);
ctx.mark_as_relevant(oeq);
@ -475,7 +479,7 @@ namespace smt {
VERIFY(get_fixed_value(v, val));
enode* n = get_enode(v);
if (ctx.watches_fixed(n)) {
expr_ref num(m_util.mk_numeral(val, n->get_expr()->get_sort()), m);
expr_ref num(m_util.mk_numeral(val, n->get_sort()), m);
literal_vector& lits = m_tmp_literals;
lits.reset();
for (literal b : m_bits[v]) {
@ -1124,15 +1128,18 @@ namespace smt {
// Determine whether bit-vector expression should be approximated
// based on the number of bits used by the arguments.
//
bool theory_bv::approximate_term(app* n) {
bool theory_bv::approximate_term(expr *e) {
if (params().m_bv_blast_max_size == INT_MAX) {
return false;
}
if (!is_app(e))
return false;
app *n = to_app(e);
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i <= num_args; ++i) {
expr* arg = (i == num_args)?n:n->get_arg(i);
sort* s = arg->get_sort();
if (m_util.is_bv_sort(s) && m_util.get_bv_size(arg) > params().m_bv_blast_max_size) {
expr *arg = (i == num_args) ? n : n->get_arg(i);
sort *s = arg->get_sort();
if (m_util.is_bv_sort(s) && m_util.get_bv_size(arg) > params().m_bv_blast_max_size) {
if (!m_approximates_large_bvs) {
TRACE(bv, tout << "found large size bit-vector:\n" << mk_pp(n, m) << "\n";);
ctx.push_trail(value_trail<bool>(m_approximates_large_bvs));
@ -1154,7 +1161,7 @@ namespace smt {
}
void theory_bv::new_eq_eh(theory_var v1, theory_var v2) {
TRACE(bv_eq, tout << "new_eq: " << mk_pp(get_enode(v1)->get_expr(), m) << " = " << mk_pp(get_enode(v2)->get_expr(), m) << "\n";);
TRACE(bv_eq, tout << "new_eq: " << mk_pp(get_expr(v1), m) << " = " << mk_pp(get_expr(v2), m) << "\n";);
TRACE(bv, tout << "new_eq_eh v" << v1 << " = v" << v2 << " @ " << ctx.get_scope_level() <<
" relevant1: " << ctx.is_relevant(get_enode(v1)) <<
" relevant2: " << ctx.is_relevant(get_enode(v2)) << "\n";);
@ -1218,7 +1225,7 @@ namespace smt {
literal_vector & lits = m_tmp_literals;
lits.reset();
literal eq = mk_eq(get_enode(v1)->get_expr(), get_enode(v2)->get_expr(), true);
literal eq = mk_eq(get_expr(v1), get_expr(v2), true);
lits.push_back(eq);
it1 = bits1.begin();
it2 = bits2.begin();
@ -1232,7 +1239,7 @@ namespace smt {
lits.push_back(arg);
}
TRACE(bv,
tout << mk_pp(get_enode(v1)->get_expr(), m) << " = " << mk_pp(get_enode(v2)->get_expr(), m) << " "
tout << mk_pp(get_expr(v1), m) << " = " << mk_pp(get_expr(v2), m) << " "
<< ctx.get_scope_level()
<< "\n";
ctx.display_literals_smt2(tout, lits););
@ -1385,10 +1392,12 @@ namespace smt {
}
}
void theory_bv::relevant_eh(app * n) {
void theory_bv::relevant_eh(expr * n) {
TRACE(arith, tout << "relevant: #" << n->get_id() << " " << ctx.e_internalized(n) << ": " << mk_bounded_pp(n, m) << "\n";);
TRACE(bv, tout << "relevant: #" << n->get_id() << " " << ctx.e_internalized(n) << ": " << mk_pp(n, m) << "\n";);
if (m.is_bool(n)) {
if (!ctx.b_internalized(n))
return;
bool_var v = ctx.get_bool_var(n);
atom * a = get_bv2a(v);
if (a && !a->is_bit()) {
@ -1401,18 +1410,18 @@ namespace smt {
}
}
else if (params().m_bv_enable_int2bv2int && m_util.is_ubv2int(n)) {
ctx.mark_as_relevant(n->get_arg(0));
assert_bv2int_axiom(n);
ctx.mark_as_relevant(to_app(n)->get_arg(0));
assert_bv2int_axiom(to_app(n));
}
else if (params().m_bv_enable_int2bv2int && m_util.is_int2bv(n)) {
ctx.mark_as_relevant(n->get_arg(0));
assert_int2bv_axiom(n);
ctx.mark_as_relevant(to_app(n)->get_arg(0));
assert_int2bv_axiom(to_app(n));
}
#if ENABLE_QUOT_REM_ENCODING
else if (m_util.is_bv_udivi(n)) {
ctx.mark_as_relevant(n->get_arg(0));
ctx.mark_as_relevant(n->get_arg(1));
assert_udiv_quot_rem_axiom(n);
ctx.mark_as_relevant(to_app(n)->get_arg(0));
ctx.mark_as_relevant(to_app(n)->get_arg(1));
assert_udiv_quot_rem_axiom(to_app(n));
}
#endif
else if (ctx.e_internalized(n)) {

View file

@ -144,13 +144,13 @@ namespace smt {
unsigned get_bv_size(app const * n) const { return m_util.get_bv_size(n); }
unsigned get_bv_size(enode const * n) const { return m_util.get_bv_size(n->get_expr()); }
unsigned get_bv_size(theory_var v) const { return get_bv_size(get_enode(v)); }
bool is_bv(app const* n) const { return m_util.is_bv_sort(n->get_sort()); }
bool is_bv(expr const* n) const { return m_util.is_bv_sort(n->get_sort()); }
bool is_bv(enode const* n) const { return is_bv(n->get_expr()); }
bool is_bv(theory_var v) const { return is_bv(get_enode(v)); }
region & get_region() { return m_trail_stack.get_region(); }
bool is_numeral(theory_var v) const { return m_util.is_numeral(get_enode(v)->get_expr()); }
app * mk_bit2bool(app * bv, unsigned idx);
bool is_numeral(theory_var v) const { return m_util.is_numeral(get_expr(v)); }
app * mk_bit2bool(expr * bv, unsigned idx);
void mk_bits(theory_var v);
friend class mk_atom_trail;
void mk_bit2bool(app * n);
@ -217,7 +217,7 @@ namespace smt {
void internalize_smul_no_overflow(app *n);
void internalize_smul_no_underflow(app *n);
bool approximate_term(app* n);
bool approximate_term(expr* e);
template<bool Signed>
void internalize_le(app * atom);
@ -240,7 +240,7 @@ namespace smt {
void new_diseq_eh(theory_var v1, theory_var v2) override;
virtual void expand_diseq(theory_var v1, theory_var v2);
void assign_eh(bool_var v, bool is_true) override;
void relevant_eh(app * n) override;
void relevant_eh(expr * n) override;
void push_scope_eh() override;
void pop_scope_eh(unsigned num_scopes) override;
final_check_status final_check_eh(unsigned) override;

View file

@ -138,7 +138,7 @@ namespace smt {
where acc_i are the accessors of constructor c.
*/
void theory_datatype::assert_is_constructor_axiom(enode * n, func_decl * c, literal antecedent) {
app* e = n->get_expr();
app* e = n->get_app();
TRACE(datatype_bug, tout << "creating axiom (= n (c (acc_1 n) ... (acc_m n))) for\n"
<< mk_pp(c, m) << " " << mk_pp(e, m) << "\n";);
m_stats.m_assert_cnstr++;
@ -171,7 +171,7 @@ namespace smt {
func_decl * d = n->get_decl();
ptr_vector<func_decl> const & accessors = *m_util.get_constructor_accessors(d);
SASSERT(n->get_num_args() == accessors.size());
app_ref_vector bindings(m);
expr_ref_vector bindings(m);
vector<std::tuple<enode *, enode *>> used_enodes;
used_enodes.push_back(std::make_tuple(nullptr, n));
for (unsigned i = 0; i < n->get_num_args(); ++i) {
@ -223,7 +223,7 @@ namespace smt {
void theory_datatype::assert_update_field_axioms(enode * n) {
m_stats.m_assert_update_field++;
SASSERT(is_update_field(n));
app* own = n->get_expr();
app* own = n->get_app();
expr* arg1 = own->get_arg(0);
func_decl * upd = n->get_decl();
func_decl * acc = to_func_decl(upd->get_parameter(0).get_ast());
@ -706,7 +706,7 @@ namespace smt {
return result;
}
void theory_datatype::relevant_eh(app * n) {
void theory_datatype::relevant_eh(expr * n) {
force_push();
TRACE(datatype, tout << "relevant_eh: " << mk_pp(n, m) << "\n";);
SASSERT(ctx.relevancy());
@ -1137,11 +1137,23 @@ namespace smt {
};
model_value_proc * theory_datatype::mk_value(enode * n, model_generator & mg) {
auto mk_fallback = [&]() -> model_value_proc * {
app* val = to_app(m_factory->get_some_value(n->get_sort()));
TRACE(datatype,
tout << "fallback datatype value for " << pp(n, m)
<< " = " << mk_pp(val, m) << "\n";);
return alloc(expr_wrapper_proc, val);
};
theory_var v = n->get_th_var(get_id());
// Guard before using union-find: null_theory_var is not a valid index for m_find.
if (v == null_theory_var)
return mk_fallback();
v = m_find.find(v);
SASSERT(v != null_theory_var);
if (v == null_theory_var || static_cast<unsigned>(v) >= m_var_data.size() || m_var_data[v] == nullptr)
return mk_fallback();
var_data * d = m_var_data[v];
SASSERT(d->m_constructor);
if (d->m_constructor == nullptr)
return mk_fallback();
func_decl * c_decl = d->m_constructor->get_decl();
datatype_value_proc * result = alloc(datatype_value_proc, c_decl);
for (enode* arg : enode::args(d->m_constructor))

View file

@ -68,17 +68,17 @@ namespace smt {
datatype_factory * m_factory;
stats m_stats;
bool is_constructor(app * f) const { return m_util.is_constructor(f); }
bool is_recognizer(app * f) const { return m_util.is_recognizer(f); }
bool is_subterm_predicate(app * f) const { return m_util.is_subterm_predicate(f); }
bool is_accessor(app * f) const { return m_util.is_accessor(f); }
bool is_update_field(app * f) const { return m_util.is_update_field(f); }
bool is_constructor(expr * f) const { return m_util.is_constructor(f); }
bool is_recognizer(expr * f) const { return m_util.is_recognizer(f); }
bool is_subterm_predicate(expr * f) const { return m_util.is_subterm_predicate(f); }
bool is_accessor(expr * f) const { return m_util.is_accessor(f); }
bool is_update_field(expr * f) const { return m_util.is_update_field(f); }
bool is_constructor(enode * n) const { return is_constructor(n->get_expr()); }
bool is_recognizer(enode * n) const { return is_recognizer(n->get_expr()); }
bool is_subterm_predicate(enode * n) const { return is_subterm_predicate(n->get_expr()); }
bool is_accessor(enode * n) const { return is_accessor(n->get_expr()); }
bool is_update_field(enode * n) const { return m_util.is_update_field(n->get_expr()); }
bool is_update_field(enode * n) const { return is_update_field(n->get_expr()); }
void assert_eq_axiom(enode * lhs, expr * rhs, literal antecedent);
void assert_is_constructor_axiom(enode * n, func_decl * c, literal antecedent);
@ -148,7 +148,7 @@ namespace smt {
bool use_diseqs() const override;
void new_diseq_eh(theory_var v1, theory_var v2) override;
void assign_eh(bool_var v, bool is_true) override;
void relevant_eh(app * n) override;
void relevant_eh(expr * n) override;
void push_scope_eh() override;
void pop_scope_eh(unsigned num_scopes) override;
final_check_status final_check_eh(unsigned) override;

View file

@ -721,7 +721,7 @@ namespace smt {
TRACE(ddl_model,
tout << "ddl model\n";
for (theory_var v = 0; v < num_vars; ++v) {
tout << "#" << mk_pp(get_enode(v)->get_expr(), m) << " = " << m_assignment[v] << "\n";
tout << "#" << mk_pp(get_expr(v), m) << " = " << m_assignment[v] << "\n";
});
}
@ -799,11 +799,11 @@ namespace smt {
enode * n = get_enode(v);
if (m_autil.is_zero(n->get_expr()) && !m_assignment[v].is_zero()) {
numeral val = m_assignment[v];
sort * s = n->get_expr()->get_sort();
sort * s = n->get_sort();
// adjust the value of all variables that have the same sort.
for (int v2 = 0; v2 < num_vars; ++v2) {
enode * n2 = get_enode(v2);
if (n2->get_expr()->get_sort() == s) {
if (n2->get_sort() == s) {
m_assignment[v2] -= val;
}
}
@ -813,7 +813,7 @@ namespace smt {
TRACE(ddl_model,
tout << "ddl model\n";
for (theory_var v = 0; v < num_vars; ++v) {
tout << "#" << mk_pp(get_enode(v)->get_expr(), m) << " = " << m_assignment[v] << "\n";
tout << "#" << mk_pp(get_expr(v), m) << " = " << m_assignment[v] << "\n";
});
}

View file

@ -263,7 +263,7 @@ namespace smt {
m_arith_eq_adapter.restart_eh();
}
void relevant_eh(app* e) override {}
void relevant_eh(expr* e) override {}
void init_search_eh() override {
m_arith_eq_adapter.init_search_eh();

View file

@ -384,7 +384,7 @@ final_check_status theory_diff_logic<Ext>::final_check_eh(unsigned level) {
}
for (enode* n : ctx.enodes()) {
family_id fid = n->get_expr()->get_family_id();
family_id fid = n->get_family_id();
if (fid != get_family_id() &&
fid != m.get_basic_family_id() &&
!is_uninterp_const(n->get_expr())) {
@ -974,10 +974,9 @@ theory_var theory_diff_logic<Ext>::expand(bool pos, theory_var v, rational & k)
enode* e = get_enode(v);
rational r;
for (;;) {
app* n = e->get_expr();
if (m_util.is_add(n) && n->get_num_args() == 2) {
app* x = to_app(n->get_arg(0));
app* y = to_app(n->get_arg(1));
expr *x = nullptr, *y = nullptr;
expr* n = e->get_expr();
if (m_util.is_add(n, x, y)) {
if (m_util.is_numeral(x, r)) {
e = ctx.get_enode(y);
}
@ -1024,8 +1023,8 @@ void theory_diff_logic<Ext>::new_eq_or_diseq(bool is_eq, theory_var v1, theory_v
app_ref eq(m), s2(m), t2(m);
app* s1 = get_enode(s)->get_expr();
app* t1 = get_enode(t)->get_expr();
expr* s1 = get_expr(s);
expr* t1 = get_expr(t);
s2 = m_util.mk_sub(t1, s1);
t2 = m_util.mk_numeral(k, s2->get_sort());
// t1 - s1 = k

View file

@ -166,20 +166,20 @@ namespace smt {
}
void apply_sort_cnstr(enode * n, sort * s) override {
app* term = n->get_expr();
auto term = n->get_expr();
if (u().is_finite_sort(term)) {
mk_rep(term);
}
}
void relevant_eh(app * n) override {
void relevant_eh(expr * n) override {
if (u().is_finite_sort(n)) {
sort* s = n->get_sort();
func_decl* r, *v;
get_rep(s, r, v);
if (n->get_decl() != v) {
if (is_app(n) && to_app(n)->get_decl() != v) {
expr* rep = m().mk_app(r, n);
uint64_t vl;
if (u().is_numeral_ext(n, vl)) {
@ -214,11 +214,12 @@ namespace smt {
}
}
bool mk_rep(app* n) {
unsigned num_args = n->get_num_args();
bool mk_rep(expr* n) {
enode * e = nullptr;
for (unsigned i = 0; i < num_args; ++i) {
ctx.internalize(n->get_arg(i), false);
if (is_app(n)) {
for (auto arg : *to_app(n))
ctx.internalize(arg, false);
}
if (ctx.e_internalized(n)) {
e = ctx.get_enode(n);

View file

@ -239,8 +239,9 @@ namespace smt {
return true;
}
void theory_finite_set::relevant_eh(app* t) {
add_immediate_axioms(t);
void theory_finite_set::relevant_eh(expr* t) {
if (is_app(t))
add_immediate_axioms(to_app(t));
}
void theory_finite_set::apply_sort_cnstr(enode* n, sort* s) {

View file

@ -155,7 +155,7 @@ namespace smt {
bool can_propagate() override;
void propagate() override;
void assign_eh(bool_var v, bool is_true) override;
void relevant_eh(app *n) override;
void relevant_eh(expr *n) override;
theory * mk_fresh(context * new_ctx) override;
char const * get_name() const override { return "finite_set"; }

View file

@ -298,9 +298,9 @@ namespace smt {
SASSERT(s->get_family_id() == get_family_id());
SASSERT(m_fpa_util.is_float(s) || m_fpa_util.is_rm(s));
SASSERT(m_fpa_util.is_float(n->get_expr()) || m_fpa_util.is_rm(n->get_expr()));
SASSERT(n->get_expr()->get_decl()->get_range() == s);
SASSERT(n->get_decl()->get_range() == s);
app * owner = n->get_expr();
expr * owner = n->get_expr();
if (!is_attached_to_var(n)) {
attach_new_th_var(n);
@ -437,7 +437,7 @@ namespace smt {
assert_cnstr(cnstr);
}
void theory_fpa::relevant_eh(app * n) {
void theory_fpa::relevant_eh(expr * n) {
TRACE(t_fpa, tout << "relevant_eh for: " << mk_ismt2_pp(n, m) << "\n";);
mpf_manager & mpfm = m_fpa_util.fm();
@ -472,10 +472,26 @@ namespace smt {
wu = m.mk_eq(m_converter.unwrap(wrapped, n->get_sort()), n);
TRACE(t_fpa, tout << "w/u eq: " << std::endl << mk_ismt2_pp(wu, m) << std::endl;);
assert_cnstr(wu);
// For non-FPA-family terms (e.g. datatype accessors like
// get-fp), mk_uf creates a separate BV UF that is not
// linked to bvwrap. Assert wrap(n) == concat(conv_components)
// to close the constraint gap (same pattern as numerals above).
if (!is_app(n) || to_app(n)->get_family_id() != get_family_id()) {
expr_ref conv_e = convert(n);
if (m_fpa_util.is_fp(conv_e) && to_app(conv_e)->get_num_args() == 3) {
app_ref conv_a(m);
conv_a = to_app(conv_e.get());
expr_ref cc(m);
cc = m_bv_util.mk_concat({conv_a->get_arg(0), conv_a->get_arg(1), conv_a->get_arg(2)});
assert_cnstr(m.mk_eq(wrapped, cc));
assert_cnstr(mk_side_conditions());
}
}
}
}
}
else if (n->get_family_id() == get_family_id()) {
else if (is_app(n) && to_app(n)->get_family_id() == get_family_id()) {
// These are the conversion functions fp.to_* */
SASSERT(!m_fpa_util.is_float(n) && !m_fpa_util.is_rm(n));
}

View file

@ -104,7 +104,7 @@ namespace smt {
model_value_proc * mk_value(enode * n, model_generator & mg) override;
void assign_eh(bool_var v, bool is_true) override;
void relevant_eh(app * n) override;
void relevant_eh(expr * n) override;
void init_model(model_generator & m) override;
void finalize_model(model_generator & mg) override;

View file

@ -153,7 +153,7 @@ namespace smt {
void theory_intblast::apply_sort_cnstr(enode* n, sort* s) {
SASSERT(bv.is_bv_sort(s));
if (!is_attached_to_var(n)) {
m_translator.internalize_bv(n->get_expr());
m_translator.internalize_bv(n->get_app());
auto v = mk_var(n);
ctx.attach_th_var(n, this, v);
}

View file

@ -228,7 +228,7 @@ class theory_lra::imp {
bool is_real(enode* n) const { return a.is_real(n->get_expr()); }
enode* get_enode(theory_var v) const { return th.get_enode(v); }
enode* get_enode(expr* e) const { return ctx().get_enode(e); }
expr* get_owner(theory_var v) const { return get_enode(v)->get_expr(); }
expr* get_expr(theory_var v) const { return get_enode(v)->get_expr(); }
enode_pp pp(enode* n) const { return enode_pp(n, ctx()); }
enode_pp pp(theory_var v) const { return pp(get_enode(v)); }
mk_bounded_pp bpp(expr* e) { return mk_bounded_pp(e, m); }
@ -449,25 +449,42 @@ class theory_lra::imp {
internalize_term(to_app(n));
internalize_term(to_app(n1));
internalize_term(to_app(n2));
internalize_term(to_app(mod));
theory_var q = mk_var(n);
theory_var x = mk_var(n1);
theory_var y = mk_var(n2);
m_nla->add_idivision(register_theory_var_in_lar_solver(q), register_theory_var_in_lar_solver(x), register_theory_var_in_lar_solver(y));
theory_var rv = mk_var(mod);
m_nla->add_idivision(register_theory_var_in_lar_solver(q), register_theory_var_in_lar_solver(x), register_theory_var_in_lar_solver(y), register_theory_var_in_lar_solver(rv));
}
if (a.is_numeral(n2) && a.is_bounded(n1)) {
ensure_nla();
internalize_term(to_app(n));
internalize_term(to_app(n1));
internalize_term(to_app(n2));
internalize_term(to_app(mod));
theory_var q = mk_var(n);
theory_var x = mk_var(n1);
theory_var y = mk_var(n2);
m_nla->add_bounded_division(register_theory_var_in_lar_solver(q), register_theory_var_in_lar_solver(x), register_theory_var_in_lar_solver(y));
theory_var rv = mk_var(mod);
m_nla->add_bounded_division(register_theory_var_in_lar_solver(q), register_theory_var_in_lar_solver(x), register_theory_var_in_lar_solver(y), register_theory_var_in_lar_solver(rv));
}
}
else if (a.is_mod(n, n1, n2)) {
if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n);
if (!ctx().relevancy()) mk_idiv_mod_axioms(n1, n2);
if (!ctx().relevancy()) mk_idiv_mod_axioms(n1, n2);
if (m_nla && a.is_numeral(n2) && !r.is_zero()) {
app_ref div(a.mk_idiv(n1, n2), m);
ctx().internalize(div, false);
internalize_term(to_app(div));
internalize_term(to_app(n1));
internalize_term(to_app(n2));
internalize_term(t);
theory_var q = mk_var(div);
theory_var x = mk_var(n1);
theory_var y = mk_var(n2);
theory_var rv = mk_var(n);
m_nla->add_bounded_division(register_theory_var_in_lar_solver(q), register_theory_var_in_lar_solver(x), register_theory_var_in_lar_solver(y), register_theory_var_in_lar_solver(rv));
}
}
else if (a.is_rem(n, n1, n2)) {
if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n);
@ -1101,7 +1118,7 @@ public:
m_nla->simplify();
}
void relevant_eh(app* n) {
void relevant_eh(expr* n) {
expr* n1, *n2;
if (a.is_mod(n, n1, n2))
mk_idiv_mod_axioms(n1, n2);
@ -1110,11 +1127,11 @@ public:
else if (a.is_div(n, n1, n2))
mk_div_axiom(n1, n2);
else if (a.is_to_int(n))
mk_to_int_axiom(n);
mk_to_int_axiom(to_app(n));
else if (a.is_is_int(n))
mk_is_int_axiom(n);
mk_is_int_axiom(to_app(n));
else if (m.is_ite(n))
mk_ite_axiom(n);
mk_ite_axiom(to_app(n));
else if (a.is_power(n, n1, n2))
mk_power_axiom(n, n1, n2);
}
@ -1235,9 +1252,9 @@ public:
/// abs(r) > r >= 0
void assert_idiv_mod_axioms(theory_var u, theory_var v, theory_var w, rational const& r) {
app_ref term(m);
term = a.mk_mul(a.mk_numeral(r, true), get_enode(w)->get_expr());
term = a.mk_add(get_enode(v)->get_expr(), term);
term = a.mk_sub(get_enode(u)->get_expr(), term);
term = a.mk_mul(a.mk_numeral(r, true), get_expr(w));
term = a.mk_add(get_expr(v), term);
term = a.mk_sub(get_expr(u), term);
theory_var z = internalize_def(term);
lpvar zi = register_theory_var_in_lar_solver(z);
lpvar vi = register_theory_var_in_lar_solver(v);
@ -1820,7 +1837,7 @@ public:
rational lc = denominator(k);
for (auto const& kv : coeffs) {
theory_var w = kv.m_key;
expr* o = get_enode(w)->get_expr();
expr* o = get_expr(w);
is_int = a.is_int(o);
if (!is_int) break;
lc = lcm(lc, denominator(kv.m_value));
@ -2109,6 +2126,14 @@ public:
m_explanation = l.expl();
literal_vector core;
SASSERT(!m_lemma.is_empty());
TRACE(nla_solver,
tout << "varmap:";
for (lpvar j : m_nla->get_core().collect_vars(l)) {
auto ext = lp().local_to_external(j);
if (ext != lp::null_lpvar && static_cast<unsigned>(ext) < th.get_num_vars())
tout << " " << lp().get_variable_name(j) << "=" << pp(ext);
}
tout << "\n";);
for (auto const& ineq : m_lemma.ineqs()) {
auto lit = mk_literal(ineq);
core.push_back(~lit);
@ -2487,7 +2512,7 @@ public:
lpvar vi = be.m_j;
if (lp().column_has_term(vi))
return;
expr_ref w(get_enode(v)->get_expr(), m);
expr_ref w(get_expr(v), m);
if (a.is_add(w) || a.is_numeral(w) || m.is_ite(w))
return;
literal bound = null_literal;
@ -3392,7 +3417,7 @@ public:
theory_var v = lp().local_to_external(vi);
rational val;
TRACE(arith, tout << lp().get_variable_name(vi) << " " << v << "\n";);
if (v != null_theory_var && a.is_numeral(get_owner(v), val) && bound == val) {
if (v != null_theory_var && a.is_numeral(get_expr(v), val) && bound == val) {
dep = nullptr;
return bound == val;
}
@ -4121,7 +4146,7 @@ public:
// Overload: create blocker from a saved impq value (used when x has been restored)
expr_ref mk_gt(theory_var v, lp::impq const& val) {
expr* obj = get_enode(v)->get_expr();
expr* obj = get_expr(v);
rational r = val.x;
expr_ref e(m);
if (a.is_int(obj->get_sort())) {
@ -4179,7 +4204,7 @@ public:
app_ref coeffs2app(u_map<rational> const& coeffs, rational const& offset, bool is_int) {
expr_ref_vector args(m);
for (auto const& [w, coeff] : coeffs) {
expr* o = get_enode(w)->get_expr();
expr* o = get_expr(w);
if (coeff.is_zero()) {
// continue
}
@ -4226,13 +4251,14 @@ public:
app_ref mk_obj(theory_var v) {
auto t = get_lpvar(v);
bool is_int = a.is_int(get_enode(v)->get_expr());
auto e = th.get_expr(v);
bool is_int = a.is_int(e);
if (lp().column_has_term(t)) {
return mk_term(lp().get_term(t), is_int);
}
else {
// theory_var w = lp().external_to_local(vi);
return app_ref(get_enode(v)->get_expr(), m);
return app_ref(to_app(e), m);
}
}
@ -4240,7 +4266,7 @@ public:
rational r = val.get_rational();
bool is_strict = val.get_infinitesimal().is_pos();
app_ref b(m);
bool is_int = a.is_int(get_enode(v)->get_expr());
bool is_int = a.is_int(get_expr(v));
TRACE(arith, display(tout << "v" << v << "\n"););
if (is_strict) {
b = a.mk_le(mk_obj(v), a.mk_numeral(r, is_int));
@ -4446,7 +4472,7 @@ void theory_lra::pop_scope_eh(unsigned num_scopes) {
void theory_lra::restart_eh() {
m_imp->restart_eh();
}
void theory_lra::relevant_eh(app* e) {
void theory_lra::relevant_eh(expr* e) {
m_imp->relevant_eh(e);
}
void theory_lra::init_search_eh() {

View file

@ -59,7 +59,7 @@ namespace smt {
void restart_eh() override;
void relevant_eh(app* e) override;
void relevant_eh(expr* e) override;
void init_search_eh() override;

View file

@ -2367,11 +2367,10 @@ namespace smt {
}
model_value_proc * theory_pb::mk_value(enode * n, model_generator & mg) {
app* a = n->get_expr();
auto a = n->get_app();
pb_model_value_proc* p = alloc(pb_model_value_proc, a);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
p->add(ctx.get_enode(a->get_arg(i)));
}
for (auto arg : *a)
p->add(ctx.get_enode(arg));
return p;
}

View file

@ -99,7 +99,7 @@ namespace smt {
* then case-expand `n`. If it's a macro we can also immediately
* body-expand it.
*/
void theory_recfun::relevant_eh(app * n) {
void theory_recfun::relevant_eh(expr * n) {
SASSERT(ctx.relevancy());
// TRACEFN("relevant_eh: (defined) " << u().is_defined(n) << " " << mk_pp(n, m));
if (u().is_defined(n) && u().has_defs())

View file

@ -61,8 +61,8 @@ namespace smt {
bool is_disabled_guard(expr* guard) { return m_disabled_guards.contains(guard); }
recfun::util & u() const { return m_util; }
bool is_defined(app * f) const { return u().is_defined(f); }
bool is_case_pred(app * f) const { return u().is_case_pred(f); }
bool is_defined(expr * f) const { return u().is_defined(f); }
bool is_case_pred(expr * f) const { return u().is_case_pred(f); }
bool is_defined(enode * e) const { return is_defined(e->get_expr()); }
bool is_case_pred(enode * e) const { return is_case_pred(e->get_expr()); }
@ -90,7 +90,7 @@ namespace smt {
bool internalize_atom(app * atom, bool gate_ctx) override;
bool internalize_term(app * term) override;
void reset_eh() override;
void relevant_eh(app * n) override;
void relevant_eh(expr * n) override;
char const * get_name() const override;
final_check_status final_check_eh(unsigned) override;
void assign_eh(bool_var v, bool is_true) override;

View file

@ -1485,8 +1485,7 @@ bool theory_seq::internalize_term(app* term) {
return true;
}
if (m.is_bool(term) &&
(m_util.str.is_in_re(term) || m_sk.is_skolem(term))) {
if (m.is_bool(term) && (m_util.str.is_in_re(term) || m_sk.is_skolem(term))) {
bool_var bv = ctx.mk_bool_var(term);
ctx.set_var_theory(bv, get_id());
ctx.mark_as_relevant(bv);
@ -2104,7 +2103,7 @@ app* theory_seq::get_ite_value(expr* e) {
}
model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) {
app* e = n->get_expr();
expr* e = n->get_expr();
TRACE(seq, tout << mk_pp(e, m) << "\n";);
// Shortcut for well-founded values to avoid some quadratic overhead
@ -2164,7 +2163,7 @@ model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) {
}
app* theory_seq::mk_value(app* e) {
app* theory_seq::mk_value(expr* e) {
expr_ref result(m);
e = get_ite_value(e);
result = m_rep.find(e);
@ -3287,7 +3286,10 @@ void theory_seq::pop_scope_eh(unsigned num_scopes) {
void theory_seq::restart_eh() {
}
void theory_seq::relevant_eh(app* n) {
void theory_seq::relevant_eh(expr* _n) {
if (!is_app(_n))
return;
app *n = to_app(_n);
if (m_util.str.is_index(n) ||
m_util.str.is_replace(n) ||
m_util.str.is_extract(n) ||

View file

@ -392,7 +392,7 @@ namespace smt {
void push_scope_eh() override;
void pop_scope_eh(unsigned num_scopes) override;
void restart_eh() override;
void relevant_eh(app* n) override;
void relevant_eh(expr* n) override;
bool should_research(expr_ref_vector &) override;
void add_theory_assumptions(expr_ref_vector & assumptions) override;
theory* mk_fresh(context* new_ctx) override { return alloc(theory_seq, *new_ctx); }
@ -629,7 +629,7 @@ namespace smt {
void init() override;
// model building
app* mk_value(app* a);
app* mk_value(expr* a);
trail_stack& get_trail_stack() { return m_trail_stack; }
void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2) {}

View file

@ -174,8 +174,8 @@ namespace smt {
}
void theory_special_relations::new_eq_eh(theory_var v1, theory_var v2) {
app* t1 = get_expr(v1);
app* t2 = get_expr(v2);
expr* t1 = get_expr(v1);
expr* t2 = get_expr(v2);
literal eq = mk_eq(t1, t2, false);
for (auto const& kv : m_relations) {
relation& r = *kv.m_value;

View file

@ -239,7 +239,7 @@ namespace smt {
m_arith_eq_adapter.restart_eh();
}
void relevant_eh(app* e) override {}
void relevant_eh(expr* e) override {}
void init_search_eh() override {
m_arith_eq_adapter.init_search_eh();
@ -323,7 +323,7 @@ namespace smt {
void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just);
bool is_int(theory_var v) const { return a.is_int(get_enode(v)->get_expr()); }
bool is_int(theory_var v) const { return a.is_int(get_expr(v)); }
th_var get_zero(sort* s) { return a.is_int(s) ? m_izero : m_rzero; }

View file

@ -170,8 +170,8 @@ namespace smt {
//
app_ref eq(m), s2(m), t2(m);
app* s1 = get_enode(s)->get_expr();
app* t1 = get_enode(t)->get_expr();
expr* s1 = get_expr(s);
expr* t1 = get_expr(t);
s2 = a.mk_sub(t1, s1);
t2 = a.mk_numeral(k, s2->get_sort());
eq = m.mk_eq(s2.get(), t2.get());
@ -588,7 +588,7 @@ namespace smt {
expr* x, *y;
rational r;
for (;;) {
app* n = e->get_expr();
auto n = e->get_expr();
if (a.is_add(n, x, y)) {
if (a.is_numeral(x, r)) {
e = ctx.get_enode(y);
@ -906,7 +906,7 @@ namespace smt {
num = num/rational(2);
SASSERT(!is_int || num.is_int());
TRACE(utvpi,
expr* n = get_enode(v)->get_expr();
expr* n = get_expr(v);
tout << mk_pp(n, m) << " |-> (" << val1 << " - " << val2 << ")/2 = " << num << "\n";);
return num;