/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_bmc_engine.cpp Abstract: BMC engine for fixedpoint solver. Author: Nikolaj Bjorner (nbjorner) 2012-9-20 Revision History: --*/ #include "muz/base/dl_context.h" #include "muz/base/dl_rule_transformer.h" #include "muz/bmc/dl_bmc_engine.h" #include "muz/transforms/dl_mk_slice.h" #include "smt/smt_kernel.h" #include "ast/datatype_decl_plugin.h" #include "ast/dl_decl_plugin.h" #include "ast/rewriter/bool_rewriter.h" #include "model/model_smt2_pp.h" #include "ast/ast_smt_pp.h" #include "ast/well_sorted.h" #include "ast/rewriter/rewriter_def.h" #include "muz/transforms/dl_transforms.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "ast/scoped_proof.h" #include "muz/base/fp_params.hpp" namespace datalog { // --------------------------------------------------------------------------- // Basic linear BMC based on indexed variables using quantified bit-vector domains. class bmc::qlinear { bmc& b; ast_manager& m; bv_util m_bv; unsigned m_bit_width; public: qlinear(bmc& b): b(b), m(b.m), m_bv(m), m_bit_width(1) {} lbool check() { setup(); m_bit_width = 4; lbool res = l_false; while (res == l_false) { b.m_solver.push(); IF_VERBOSE(1, verbose_stream() << "bit_width: " << m_bit_width << "\n";); compile(); b.checkpoint(); func_decl_ref q = mk_q_func_decl(b.m_query_pred); expr* T = m.mk_const(symbol("T"), mk_index_sort()); expr_ref fml(m.mk_app(q, T), m); b.assert_expr(fml); res = b.m_solver.check(); if (res == l_true) { res = get_model(); } b.m_solver.pop(1); ++m_bit_width; } return res; } private: sort_ref mk_index_sort() { return sort_ref(m_bv.mk_sort(m_bit_width), m); } var_ref mk_index_var() { return var_ref(m.mk_var(0, mk_index_sort()), m); } void compile() { sort_ref index_sort = mk_index_sort(); var_ref var = mk_index_var(); sort* index_sorts[1] = { index_sort }; symbol tick("T"); rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: forall level . p(T) => body of rule i + equalities for head of rule i func_decl_ref pr = mk_q_func_decl(p); expr_ref pred = expr_ref(m.mk_app(pr, var.get()), m); expr_ref_vector rules(m), sub(m), conjs(m); expr_ref trm(m), rule_body(m), rule_i(m); for (unsigned i = 0; i < rls.size(); ++i) { sub.reset(); conjs.reset(); rule& r = *rls[i]; rule_i = m.mk_app(mk_q_rule(p, i), var.get()); rules.push_back(rule_i); mk_qrule_vars(r, i, sub); // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), trm); conjs.push_back(m.mk_eq(trm, mk_q_arg(p, k, true))); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), trm); conjs.push_back(m.mk_eq(trm, mk_q_arg(q, k, false))); } func_decl_ref qr = mk_q_func_decl(q); conjs.push_back(m.mk_app(qr, m_bv.mk_bv_sub(var, mk_q_one()))); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { vs(r.get_tail(j), sub.size(), sub.c_ptr(), trm); conjs.push_back(trm); } if (r.get_uninterpreted_tail_size() > 0) { conjs.push_back(m_bv.mk_ule(mk_q_one(), var)); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); trm = m.mk_implies(rule_i, rule_body); trm = m.mk_forall(1, index_sorts, &tick, trm, 1); b.assert_expr(trm); } bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), trm); trm = m.mk_implies(pred, trm); trm = m.mk_forall(1, index_sorts, &tick, trm, 1); SASSERT(is_well_sorted(m, trm)); b.assert_expr(trm); } } void setup() { b.m_fparams.m_relevancy_lvl = 2; b.m_fparams.m_model = true; b.m_fparams.m_model_compact = true; b.m_fparams.m_mbqi = true; b.m_rule_trace.reset(); } void mk_qrule_vars(datalog::rule const& r, unsigned rule_id, expr_ref_vector& sub) { ptr_vector sorts; r.get_vars(m, sorts); // populate substitution of bound variables. sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { expr* arg = r.get_head()->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_q_arg(r.get_decl(), k, true); } } } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { expr* arg = r.get_tail(j)->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_q_arg(q, k, false); } } } } for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { if (sorts[j] && !sub[j].get()) { sub[j] = mk_q_var(r.get_decl(), sorts[j], rule_id, idx++); } } } expr_ref mk_q_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx) { std::stringstream _name; _name << pred->get_name() << "#" << rule_id << "_" << idx; symbol nm(_name.str().c_str()); var_ref var = mk_index_var(); return expr_ref(m.mk_app(m.mk_func_decl(nm, mk_index_sort(), s), var), m); } expr_ref mk_q_arg(func_decl* pred, unsigned idx, bool is_current) { SASSERT(idx < pred->get_arity()); std::stringstream _name; _name << pred->get_name() << "#" << idx; symbol nm(_name.str().c_str()); expr_ref var(mk_index_var(), m); if (!is_current) { var = m_bv.mk_bv_sub(var, mk_q_one()); } return expr_ref(m.mk_app(m.mk_func_decl(nm, mk_index_sort(), pred->get_domain(idx)), var), m); } expr_ref mk_q_one() { return mk_q_num(1); } expr_ref mk_q_num(unsigned i) { return expr_ref(m_bv.mk_numeral(i, m_bit_width), m); } func_decl_ref mk_q_func_decl(func_decl* f) { std::stringstream _name; _name << f->get_name() << "#"; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, mk_index_sort(), f->get_range()), m); } func_decl_ref mk_q_rule(func_decl* f, unsigned rule_id) { std::stringstream _name; _name << f->get_name() << "#" << rule_id; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, mk_index_sort(), m.mk_bool_sort()), m); } expr_ref eval_q(model_ref& model, func_decl* f, unsigned i) { func_decl_ref fn = mk_q_func_decl(f); expr_ref t(m); t = m.mk_app(mk_q_func_decl(f).get(), mk_q_num(i)); return (*model)(t); } expr_ref eval_q(model_ref& model, expr* t, unsigned i) { expr_ref tmp(m), result(m), num(m); var_subst vs(m, false); num = mk_q_num(i); expr* nums[1] = { num }; vs(t, 1, nums, tmp); return (*model)(tmp); } lbool get_model() { rule_manager& rm = b.m_ctx.get_rule_manager(); func_decl_ref q = mk_q_func_decl(b.m_query_pred); expr_ref T(m), rule_i(m), vl(m); model_ref md; proof_ref pr(m); rule_unifier unifier(b.m_ctx); rational num; unsigned level, bv_size; b.m_solver.get_model(md); func_decl* pred = b.m_query_pred; dl_decl_util util(m); T = m.mk_const(symbol("T"), mk_index_sort()); vl = (*md)(T); VERIFY (m_bv.is_numeral(vl, num, bv_size)); SASSERT(num.is_unsigned()); level = num.get_unsigned(); SASSERT(m.is_true(eval_q(md, b.m_query_pred, level))); TRACE("bmc", model_smt2_pp(tout, m, *md, 0);); rule_ref r0(rm), r1(rm), r2(rm); while (true) { TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); expr_ref_vector sub(m); rule_vector const& rls = b.m_rules.get_predicate_rules(pred); rule* r = nullptr; unsigned i = 0; for (; i < rls.size(); ++i) { rule_i = m.mk_app(mk_q_rule(pred, i), mk_q_num(level).get()); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); if (m.is_true(eval_q(md, rule_i, level))) { r = rls[i]; break; } } SASSERT(r); mk_qrule_vars(*r, i, sub); b.m_rule_trace.push_back(r); // we have rule, we have variable names of rule. // extract values for the variables in the rule. for (unsigned j = 0; j < sub.size(); ++j) { expr_ref vl = eval_q(md, sub[j].get(), i); if (vl) { // vl can be 0 if the interpretation does not assign a value to it. sub[j] = vl; } else { sub[j] = m.mk_var(j, m.get_sort(sub[j].get())); } } svector > positions; vector substs; expr_ref fml(m), concl(m); rm.to_formula(*r, fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); proof_ref p(m); if (r0) { VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); rm.to_formula(*r1.get(), concl); scoped_proof _sp(m); p = r->get_proof(); if (!p) { p = m.mk_asserted(fml); } proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); substs.push_back(sub1); substs.push_back(sub); pr = m.mk_hyper_resolve(2, premises, concl, positions, substs); r0 = r1; } else { rm.to_formula(*r, concl); scoped_proof _sp(m); p = r->get_proof(); if (!p) { p = m.mk_asserted(fml); } if (sub.empty()) { pr = p; } else { substs.push_back(sub); proof* ps[1] = { p }; pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } if (level == 0) { SASSERT(r->get_uninterpreted_tail_size() == 0); break; } --level; SASSERT(r->get_uninterpreted_tail_size() == 1); pred = r->get_decl(0); } scoped_proof _sp(m); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; return l_true; } }; // -------------------------------------------------------------------------- // Basic non-linear BMC based on compiling into quantifiers. class bmc::nonlinear { bmc& b; ast_manager& m; public: nonlinear(bmc& b): b(b), m(b.m) {} lbool check() { setup(); for (unsigned i = 0; ; ++i) { IF_VERBOSE(1, verbose_stream() << "level: " << i << "\n";); b.checkpoint(); expr_ref_vector fmls(m); compile(b.m_rules, fmls, i); assert_fmls(fmls); lbool res = check(i); if (res == l_undef) { return res; } if (res == l_true) { get_model(i); return res; } } } expr_ref compile_query(func_decl* query_pred, unsigned level) { expr_ref_vector vars(m); func_decl_ref level_p = mk_level_predicate(query_pred, level); for (unsigned i = 0; i < level_p->get_arity(); ++i) { std::stringstream _name; _name << query_pred->get_name() << "#" << level << "_" << i; symbol nm(_name.str().c_str()); vars.push_back(m.mk_const(nm, level_p->get_domain(i))); } return expr_ref(m.mk_app(level_p, vars.size(), vars.c_ptr()), m); } void compile(rule_set const& rules, expr_ref_vector& result, unsigned level) { bool_rewriter br(m); rule_set::decl2rules::iterator it = rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: p_level(vars) => r1_level(vars) \/ r2_level(vars) \/ r3_level(vars) \/ ... // Assert: r_i_level(vars) => exists aux_vars . body of rule i for level func_decl_ref level_pred = mk_level_predicate(p, level); expr_ref_vector rules(m); expr_ref body(m), head(m); for (unsigned i = 0; i < rls.size(); ++i) { rule& r = *rls[i]; func_decl_ref rule_i = mk_level_rule(p, i, level); rules.push_back(apply_vars(rule_i)); ptr_vector rule_vars; expr_ref_vector args(m), conjs(m); r.get_vars(m, rule_vars); obj_hashtable used_vars; unsigned num_vars = 0; for (unsigned i = 0; i < r.get_decl()->get_arity(); ++i) { expr* arg = r.get_head()->get_arg(i); if (is_var(arg) && !used_vars.contains(arg)) { used_vars.insert(arg); args.push_back(arg); rule_vars[to_var(arg)->get_idx()] = 0; } else { sort* srt = m.get_sort(arg); args.push_back(m.mk_var(rule_vars.size()+num_vars, srt)); conjs.push_back(m.mk_eq(args.back(), arg)); ++num_vars; } } head = m.mk_app(rule_i, args.size(), args.c_ptr()); for (unsigned i = 0; i < r.get_tail_size(); ++i) { conjs.push_back(r.get_tail(i)); } br.mk_and(conjs.size(), conjs.c_ptr(), body); replace_by_level_predicates(level, body); body = skolemize_vars(r, args, rule_vars, body); body = m.mk_implies(head, body); body = bind_vars(body, head); result.push_back(body); } br.mk_or(rules.size(), rules.c_ptr(), body); head = apply_vars(level_pred); body = m.mk_implies(head, body); body = bind_vars(body, head); result.push_back(body); } } private: void assert_fmls(expr_ref_vector const& fmls) { for (unsigned i = 0; i < fmls.size(); ++i) { b.assert_expr(fmls[i]); } } void setup() { b.m_fparams.m_model = true; b.m_fparams.m_model_compact = true; // b.m_fparams.m_mbqi = true; b.m_fparams.m_relevancy_lvl = 2; b.m_rule_trace.reset(); } lbool check(unsigned level) { expr_ref p = compile_query(b.m_query_pred, level); expr_ref q(m), q_at_level(m); q = m.mk_fresh_const("q",m.mk_bool_sort()); q_at_level = m.mk_implies(q, p); b.assert_expr(q_at_level); expr* qr = q.get(); return b.m_solver.check(1, &qr); } proof_ref get_proof(model_ref& md, func_decl* pred, app* prop, unsigned level) { if (m.canceled()) { return proof_ref(nullptr, m); } TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); rule_manager& rm = b.m_ctx.get_rule_manager(); expr_ref prop_r(m), prop_v(m), fml(m), prop_body(m), tmp(m), body(m); expr_ref_vector args(m); proof_ref_vector prs(m); proof_ref pr(m); // find the rule that was triggered by evaluating the arguments to prop. rule_vector const& rls = b.m_rules.get_predicate_rules(pred); rule* r = nullptr; for (unsigned i = 0; i < rls.size(); ++i) { func_decl_ref rule_i = mk_level_rule(pred, i, level); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); prop_r = m.mk_app(rule_i, prop->get_num_args(), prop->get_args()); if (md->is_true(prop_r)) { r = rls[i]; break; } } SASSERT(r); b.m_rule_trace.push_back(r); rm.to_formula(*r, fml); IF_VERBOSE(1, verbose_stream() << mk_pp(fml, m) << "\n";); prs.push_back(r->get_proof()); unsigned sz = r->get_uninterpreted_tail_size(); ptr_vector rule_vars; r->get_vars(m, rule_vars); args.append(prop->get_num_args(), prop->get_args()); expr_ref_vector sub = mk_skolem_binding(*r, rule_vars, args); if (sub.empty() && sz == 0) { pr = prs[0].get(); return pr; } for (unsigned j = 0; j < sub.size(); ++j) { sub[j] = (*md)(sub[j].get()); } svector > positions; vector substs; var_subst vs(m, false); substs.push_back(sub); for (unsigned j = 0; j < sz; ++j) { func_decl* head_j = r->get_decl(j); app* body_j = r->get_tail(j); vs(body_j, sub.size(), sub.c_ptr(), prop_body); prs.push_back(get_proof(md, head_j, to_app(prop_body), level-1)); positions.push_back(std::make_pair(j+1,0)); substs.push_back(expr_ref_vector(m)); } pr = m.mk_hyper_resolve(sz+1, prs.c_ptr(), prop, positions, substs); return pr; } void get_model(unsigned level) { scoped_proof _sp(m); expr_ref level_query = compile_query(b.m_query_pred, level); model_ref md; b.m_solver.get_model(md); IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); proof_ref pr(m); pr = get_proof(md, b.m_query_pred, to_app(level_query), level); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } func_decl_ref mk_level_predicate(func_decl* p, unsigned level) { std::stringstream _name; _name << p->get_name() << "#" << level; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, p->get_arity(), p->get_domain(), m.mk_bool_sort()), m); } func_decl_ref mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level) { std::stringstream _name; _name << "rule:" << p->get_name() << "#" << level << "_" << rule_idx; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, p->get_arity(), p->get_domain(), m.mk_bool_sort()), m); } expr_ref apply_vars(func_decl* p) { expr_ref_vector vars(m); for (unsigned i = 0; i < p->get_arity(); ++i) { vars.push_back(m.mk_var(i, p->get_domain(i))); } return expr_ref(m.mk_app(p, vars.size(), vars.c_ptr()), m); } // remove variables from dst that are in src. void subtract_vars(ptr_vector& dst, ptr_vector const& src) { for (unsigned i = 0; i < dst.size(); ++i) { if (i >= src.size()) { break; } if (src[i]) { dst[i] = 0; } } } expr_ref_vector mk_skolem_binding(rule& r, ptr_vector const& vars, expr_ref_vector const& args) { expr_ref_vector binding(m); ptr_vector arg_sorts; for (unsigned i = 0; i < args.size(); ++i) { arg_sorts.push_back(m.get_sort(args[i])); } for (unsigned i = 0; i < vars.size(); ++i) { if (vars[i]) { func_decl_ref f = mk_body_func(r, arg_sorts, i, vars[i]); binding.push_back(m.mk_app(f, args.size(), args.c_ptr())); } else { binding.push_back(nullptr); } } return binding; } expr_ref skolemize_vars(rule& r, expr_ref_vector const& args, ptr_vector const& vars, expr* e) { expr_ref result(m); expr_ref_vector binding = mk_skolem_binding(r, vars, args); var_subst vs(m, false); vs(e, binding.size(), binding.c_ptr(), result); return result; } func_decl_ref mk_body_func(rule& r, ptr_vector const& args, unsigned index, sort* s) { std::stringstream _name; _name << r.get_decl()->get_name() << "@" << index; symbol name(_name.str().c_str()); func_decl* f = m.mk_func_decl(name, args.size(), args.c_ptr(), s); return func_decl_ref(f, m); } expr_ref bind_vars(expr* e, expr* pat) { ptr_vector sorts; svector names; expr_ref_vector binding(m), patterns(m); expr_ref tmp(m), head(m); expr_free_vars vars; vars(e); for (unsigned i = 0; i < vars.size(); ++i) { if (vars[i]) { binding.push_back(m.mk_var(sorts.size(), vars[i])); sorts.push_back(vars[i]); names.push_back(symbol(i)); } else { binding.push_back(nullptr); } } sorts.reverse(); if (sorts.empty()) { return expr_ref(e, m); } var_subst vs(m, false); vs(e, binding.size(), binding.c_ptr(), tmp); vs(pat, binding.size(), binding.c_ptr(), head); patterns.push_back(m.mk_pattern(to_app(head))); symbol qid, skid; return expr_ref(m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp, 1, qid, skid, 1, patterns.c_ptr()), m); } public: class level_replacer { nonlinear& n; unsigned m_level; bool is_predicate(func_decl* f) { return n.b.m_ctx.is_predicate(f); } public: level_replacer(nonlinear& n, unsigned level): n(n), m_level(level) {} br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (is_predicate(f)) { if (m_level > 0) { result = n.m.mk_app(n.mk_level_predicate(f, m_level-1), num_args, args); } else { result = n.m.mk_false(); } return BR_DONE; } return BR_FAILED; } bool reduce_quantifier(quantifier* old_q, expr* new_body, expr_ref& result) { if (is_ground(new_body)) { result = new_body; } else { expr * const * no_pats = &new_body; result = n.m.update_quantifier(old_q, 0, nullptr, 1, no_pats, new_body); } return true; } }; struct level_replacer_cfg : public default_rewriter_cfg { level_replacer m_r; level_replacer_cfg(nonlinear& nl, unsigned level): m_r(nl, level) {} bool rewrite_patterns() const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { return m_r.mk_app_core(f, num, args, result); } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { return m_r.reduce_quantifier(old_q, new_body, result); } }; class level_replacer_star : public rewriter_tpl { level_replacer_cfg m_cfg; public: level_replacer_star(nonlinear& nl, unsigned level): rewriter_tpl(nl.m, false, m_cfg), m_cfg(nl, level) {} }; private: void replace_by_level_predicates(unsigned level, expr_ref& fml) { level_replacer_star rep(*this, level); expr_ref tmp(m); rep(fml, tmp); fml = tmp; } }; // -------------------------------------------------------------------------- // Basic non-linear BMC based on compiling into data-types (it is inefficient) class bmc::nonlinear_dt { bmc& b; ast_manager& m; ast_ref_vector m_pinned; sort_ref m_path_sort; obj_map m_pred2sort; obj_map m_sort2pred; public: nonlinear_dt(bmc& b): b(b), m(b.m), m_pinned(m), m_path_sort(m) {} lbool check() { setup(); declare_datatypes(); compile(); return check_query(); } private: void setup() { m_pred2sort.reset(); m_pinned.reset(); m_sort2pred.reset(); b.m_fparams.m_relevancy_lvl = 0; b.m_fparams.m_model = true; b.m_fparams.m_model_compact = true; b.m_fparams.m_mbqi = false; b.m_fparams.m_relevancy_lvl = 2; b.m_rule_trace.reset(); } func_decl_ref mk_predicate(func_decl* pred) { std::stringstream _name; _name << pred->get_name() << "#"; symbol nm(_name.str().c_str()); sort* pred_trace_sort = m_pred2sort.find(pred); return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m); } func_decl_ref mk_rule(func_decl* p, unsigned rule_idx) { std::stringstream _name; _name << "rule:" << p->get_name() << "#" << rule_idx; symbol nm(_name.str().c_str()); sort* pred_trace_sort = m_pred2sort.find(p); return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m); } expr_ref mk_var(func_decl* pred, sort*s, unsigned idx, expr* path_arg, expr* trace_arg) { std::stringstream _name; _name << pred->get_name() << "#V_" << idx; symbol nm(_name.str().c_str()); func_decl_ref fn(m); fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, s); return expr_ref(m.mk_app(fn, trace_arg, path_arg), m); } expr_ref mk_arg(func_decl* pred, unsigned idx, expr* path_arg, expr* trace_arg) { SASSERT(idx < pred->get_arity()); std::stringstream _name; _name << pred->get_name() << "#X_" << idx; symbol nm(_name.str().c_str()); func_decl_ref fn(m); fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, pred->get_domain(idx)); return expr_ref(m.mk_app(fn, trace_arg, path_arg), m); } void mk_subst(datalog::rule& r, expr* path, app* trace, expr_ref_vector& sub) { datatype_util dtu(m); ptr_vector sorts; func_decl* p = r.get_decl(); ptr_vector const& succs = *dtu.get_datatype_constructors(m.get_sort(path)); // populate substitution of bound variables. r.get_vars(m, sorts); sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { expr* arg = r.get_head()->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_arg(p, k, path, trace); } } } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { func_decl* q = r.get_decl(j); expr_ref path_arg(m); if (j == 0) { path_arg = path; } else { path_arg = m.mk_app(succs[j], path); } for (unsigned k = 0; k < q->get_arity(); ++k) { expr* arg = r.get_tail(j)->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_arg(q, k, path_arg, trace->get_arg(j)); } } } } for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { if (sorts[j] && !sub[j].get()) { sub[j] = mk_var(r.get_decl(), sorts[j], idx++, path, trace); } } } /** \brief compile Horn rule into co-Horn implication. forall args . R(path_var, rule_i(trace_vars)) => Body[X(path_var, rule_i(trace_vars)), Y(S_j(path_var), trace_vars_j)] */ void compile() { datatype_util dtu(m); rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ... // where: r_i_level = body of rule i for level + equalities for head of rule i expr_ref rule_body(m), tmp(m), pred(m), trace_arg(m), fml(m); var_ref path_var(m), trace_var(m); expr_ref_vector rules(m), sub(m), conjs(m), vars(m), patterns(m); sort* pred_sort = m_pred2sort.find(p); path_var = m.mk_var(0, m_path_sort); trace_var = m.mk_var(1, pred_sort); // sort* sorts[2] = { pred_sort, m_path_sort }; ptr_vector const& cnstrs = *dtu.get_datatype_constructors(pred_sort); ptr_vector const& succs = *dtu.get_datatype_constructors(m_path_sort); SASSERT(cnstrs.size() == rls.size()); pred = m.mk_app(mk_predicate(p), trace_var.get(), path_var.get()); for (unsigned i = 0; i < rls.size(); ++i) { sub.reset(); conjs.reset(); vars.reset(); rule& r = *rls[i]; func_decl_ref rule_pred_i = mk_rule(p, i); // Create cnstr_rule_i(Vars) func_decl* cnstr = cnstrs[i]; rules.push_back(m.mk_app(rule_pred_i, trace_var.get(), path_var.get())); unsigned arity = cnstr->get_arity(); for (unsigned j = 0; j < arity; ++j) { vars.push_back(m.mk_var(arity-j,cnstr->get_domain(j))); } trace_arg = m.mk_app(cnstr, vars.size(), vars.c_ptr()); mk_subst(r, path_var, to_app(trace_arg), sub); // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp); expr_ref arg = mk_arg(p, k, path_var, trace_arg); conjs.push_back(m.mk_eq(tmp, arg)); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { expr_ref path_arg(m); if (j == 0) { path_arg = path_var.get(); } else { path_arg = m.mk_app(succs[j], path_var.get()); } func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp); expr_ref arg = mk_arg(q, k, path_arg, vars[j].get()); conjs.push_back(m.mk_eq(tmp, arg)); } func_decl_ref q_pred = mk_predicate(q); conjs.push_back(m.mk_app(q_pred, vars[j].get(), path_arg)); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); ptr_vector q_sorts; vector names; for (unsigned i = 0; i < vars.size(); ++i) { q_sorts.push_back(m.get_sort(vars[i].get())); names.push_back(symbol(i+1)); } vars.push_back(path_var); q_sorts.push_back(m.get_sort(path_var)); names.push_back(symbol("path")); SASSERT(names.size() == q_sorts.size()); SASSERT(vars.size() == names.size()); symbol qid = r.name(), skid; tmp = m.mk_app(mk_predicate(p), trace_arg.get(), path_var.get()); patterns.reset(); patterns.push_back(m.mk_pattern(to_app(tmp))); fml = m.mk_implies(tmp, rule_body); fml = m.mk_forall(vars.size(), q_sorts.c_ptr(), names.c_ptr(), fml, 1, qid, skid, 1, patterns.c_ptr()); b.assert_expr(fml); } } } void declare_datatypes() { rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); datatype_util dtu(m); ptr_vector dts; obj_map pred_idx; for (unsigned i = 0; it != end; ++it, ++i) { pred_idx.insert(it->m_key, i); } it = b.m_rules.begin_grouped_rules(); for (; it != end; ++it) { rule_vector const& rls = *it->m_value; func_decl* pred = it->m_key; ptr_vector cnstrs; for (unsigned i = 0; i < rls.size(); ++i) { rule* r = rls[i]; ptr_vector accs; for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { func_decl* q = r->get_decl(j); unsigned idx = pred_idx.find(q); std::stringstream _name; _name << pred->get_name() << "_" << q->get_name() << j; symbol name(_name.str().c_str()); type_ref tr(idx); accs.push_back(mk_accessor_decl(m, name, tr)); } std::stringstream _name; _name << pred->get_name() << "_" << i; symbol name(_name.str().c_str()); _name << "?"; symbol is_name(_name.str().c_str()); cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); } dts.push_back(mk_datatype_decl(dtu, pred->get_name(), 0, nullptr, cnstrs.size(), cnstrs.c_ptr())); } sort_ref_vector new_sorts(m); family_id dfid = m.mk_family_id("datatype"); datatype_decl_plugin* dtp = static_cast(m.get_plugin(dfid)); VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), 0, nullptr, new_sorts)); it = b.m_rules.begin_grouped_rules(); for (unsigned i = 0; it != end; ++it, ++i) { m_pred2sort.insert(it->m_key, new_sorts[i].get()); m_sort2pred.insert(new_sorts[i].get(), it->m_key); m_pinned.push_back(new_sorts[i].get()); } if (new_sorts.size() > 0) { TRACE("bmc", dtu.display_datatype(new_sorts[0].get(), tout);); } del_datatype_decls(dts.size(), dts.c_ptr()); // declare path data-type. { new_sorts.reset(); dts.reset(); ptr_vector cnstrs; unsigned max_arity = 0; rule_set::iterator it = b.m_rules.begin(); rule_set::iterator end = b.m_rules.end(); for (; it != end; ++it) { rule* r = *it; unsigned sz = r->get_uninterpreted_tail_size(); max_arity = std::max(sz, max_arity); } cnstrs.push_back(mk_constructor_decl(symbol("Z#"), symbol("Z#?"), 0, nullptr)); for (unsigned i = 0; i + 1 < max_arity; ++i) { std::stringstream _name; _name << "succ#" << i; symbol name(_name.str().c_str()); _name << "?"; symbol is_name(_name.str().c_str()); std::stringstream _name2; _name2 << "get_succ#" << i; ptr_vector accs; type_ref tr(0); accs.push_back(mk_accessor_decl(m, name, tr)); cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); } dts.push_back(mk_datatype_decl(dtu, symbol("Path"), 0, nullptr, cnstrs.size(), cnstrs.c_ptr())); VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), 0, nullptr, new_sorts)); m_path_sort = new_sorts[0].get(); } } proof_ref get_proof(model_ref& md, app* trace, app* path) { datatype_util dtu(m); rule_manager& rm = b.m_ctx.get_rule_manager(); sort* trace_sort = m.get_sort(trace); func_decl* p = m_sort2pred.find(trace_sort); datalog::rule_vector const& rules = b.m_rules.get_predicate_rules(p); ptr_vector const& cnstrs = *dtu.get_datatype_constructors(trace_sort); ptr_vector const& succs = *dtu.get_datatype_constructors(m_path_sort); for (unsigned i = 0; i < cnstrs.size(); ++i) { if (trace->get_decl() == cnstrs[i]) { svector > positions; scoped_proof _sc(m); proof_ref_vector prs(m); expr_ref_vector sub(m); vector substs; proof_ref pr(m); expr_ref fml(m), head(m), tmp(m); app_ref path1(m); var_subst vs(m, false); mk_subst(*rules[i], path, trace, sub); rm.to_formula(*rules[i], fml); prs.push_back(rules[i]->get_proof()); unsigned sz = trace->get_num_args(); if (sub.empty() && sz == 0) { pr = prs[0].get(); return pr; } for (unsigned j = 0; j < sub.size(); ++j) { sub[j] = (*md)(sub.get(j)); } rule_ref rl(b.m_ctx.get_rule_manager()); rl = rules[i]; b.m_ctx.get_rule_manager().substitute(rl, sub.size(), sub.c_ptr()); substs.push_back(sub); for (unsigned j = 0; j < sz; ++j) { if (j == 0) { path1 = path; } else { path1 = m.mk_app(succs[j], path); } prs.push_back(get_proof(md, to_app(trace->get_arg(j)), path1)); positions.push_back(std::make_pair(j+1,0)); substs.push_back(expr_ref_vector(m)); } head = rl->get_head(); pr = m.mk_hyper_resolve(sz+1, prs.c_ptr(), head, positions, substs); b.m_rule_trace.push_back(rl.get()); return pr; } } UNREACHABLE(); return proof_ref(nullptr, m); } // instantiation of algebraic data-types takes care of the rest. lbool check_query() { sort* trace_sort = m_pred2sort.find(b.m_query_pred); func_decl_ref q = mk_predicate(b.m_query_pred); expr_ref trace(m), path(m), fml(m); trace = m.mk_const(symbol("trace"), trace_sort); path = m.mk_const(symbol("path"), m_path_sort); fml = m.mk_app(q, trace.get(), path.get()); b.assert_expr(fml); while (true) { lbool is_sat = b.m_solver.check(); model_ref md; if (is_sat == l_false) { return is_sat; } b.m_solver.get_model(md); mk_answer(md, trace, path); return l_true; } } bool check_model(model_ref& md, expr* trace) { expr_ref trace_val(m), eq(m); trace_val = (*md)(trace); eq = m.mk_eq(trace, trace_val); b.m_solver.push(); b.m_solver.assert_expr(eq); lbool is_sat = b.m_solver.check(); if (is_sat != l_false) { b.m_solver.get_model(md); } b.m_solver.pop(1); if (is_sat == l_false) { IF_VERBOSE(1, verbose_stream() << "infeasible trace " << mk_pp(trace_val, m) << "\n";); eq = m.mk_not(eq); b.assert_expr(eq); } return is_sat != l_false; } void mk_answer(model_ref& md, expr_ref& trace, expr_ref& path) { IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); trace = (*md)(trace); path = (*md)(path); IF_VERBOSE(2, verbose_stream() << mk_pp(trace, m) << "\n"; for (unsigned i = 0; i < b.m_solver.size(); ++i) { verbose_stream() << mk_pp(b.m_solver.get_formula(i), m) << "\n"; }); scoped_proof _sp(m); proof_ref pr(m); pr = get_proof(md, to_app(trace), to_app(path)); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } }; // -------------------------------------------------------------------------- // Basic linear BMC based on incrementally unfolding the transition relation. class bmc::linear { bmc& b; ast_manager& m; public: linear(bmc& b): b(b), m(b.m) {} lbool check() { setup(); unsigned max_depth = b.m_ctx.get_params().bmc_linear_unrolling_depth(); for (unsigned i = 0; i < max_depth; ++i) { IF_VERBOSE(1, verbose_stream() << "level: " << i << "\n";); b.checkpoint(); compile(i); lbool res = check(i); if (res == l_undef) { return res; } if (res == l_true) { get_model(i); return res; } } return l_undef; } private: void get_model(unsigned level) { if (m.canceled()) { return; } rule_manager& rm = b.m_ctx.get_rule_manager(); expr_ref level_query = mk_level_predicate(b.m_query_pred, level); model_ref md; proof_ref pr(m); rule_unifier unifier(b.m_ctx); b.m_solver.get_model(md); func_decl* pred = b.m_query_pred; SASSERT(m.is_true(md->get_const_interp(to_app(level_query)->get_decl()))); TRACE("bmc", model_smt2_pp(tout, m, *md, 0);); rule_ref r0(rm), r1(rm), r2(rm); while (true) { TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); expr_ref_vector sub(m); rule_vector const& rls = b.m_rules.get_predicate_rules(pred); rule* r = nullptr; unsigned i = 0; for (; i < rls.size(); ++i) { expr_ref rule_i = mk_level_rule(pred, i, level); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); if (m.is_true(md->get_const_interp(to_app(rule_i)->get_decl()))) { r = rls[i]; break; } } SASSERT(r); b.m_rule_trace.push_back(r); mk_rule_vars(*r, level, i, sub); // we have rule, we have variable names of rule. // extract values for the variables in the rule. for (unsigned j = 0; j < sub.size(); ++j) { expr* vl = md->get_const_interp(to_app(sub[j].get())->get_decl()); if (vl) { // vl can be 0 if the interpretation does not assign a value to it. sub[j] = vl; } else { sub[j] = m.mk_var(j, m.get_sort(sub[j].get())); } } svector > positions; vector substs; expr_ref fml(m), concl(m); rm.to_formula(*r, fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); proof_ref p(m); { scoped_proof _sp(m); p = r->get_proof(); if (!p) { p = m.mk_asserted(fml); } } if (r0) { VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); rm.to_formula(*r1.get(), concl); scoped_proof _sp(m); proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); substs.push_back(sub1); substs.push_back(sub); pr = m.mk_hyper_resolve(2, premises, concl, positions, substs); r0 = r1; } else { rm.to_formula(*r2.get(), concl); scoped_proof _sp(m); if (sub.empty()) { pr = p; } else { substs.push_back(sub); proof * ps[1] = { p }; pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } if (level == 0) { SASSERT(r->get_uninterpreted_tail_size() == 0); break; } --level; SASSERT(r->get_uninterpreted_tail_size() == 1); pred = r->get_decl(0); } scoped_proof _sp(m); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } void setup() { b.m_fparams.m_relevancy_lvl = 0; b.m_fparams.m_model = true; b.m_fparams.m_model_compact = true; b.m_fparams.m_mbqi = false; // m_fparams.m_auto_config = false; b.m_rule_trace.reset(); } lbool check(unsigned level) { expr_ref level_query = mk_level_predicate(b.m_query_pred, level); expr* q = level_query.get(); return b.m_solver.check(1, &q); } expr_ref mk_level_predicate(func_decl* p, unsigned level) { return mk_level_predicate(p->get_name(), level); } expr_ref mk_level_predicate(symbol const& name, unsigned level) { std::stringstream _name; _name << name << "#" << level; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m); } expr_ref mk_level_arg(func_decl* pred, unsigned idx, unsigned level) { SASSERT(idx < pred->get_arity()); std::stringstream _name; _name << pred->get_name() << "#" << level << "_" << idx; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, pred->get_domain(idx)), m); } expr_ref mk_level_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx, unsigned level) { std::stringstream _name; _name << pred->get_name() << "#" << level << "_" << rule_id << "_" << idx; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, s), m); } expr_ref mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level) { std::stringstream _name; _name << "rule:" << p->get_name() << "#" << level << "_" << rule_idx; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m); } void mk_rule_vars(rule& r, unsigned level, unsigned rule_id, expr_ref_vector& sub) { ptr_vector sorts; r.get_vars(m, sorts); // populate substitution of bound variables. sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { expr* arg = r.get_head()->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_level_arg(r.get_decl(), k, level); } } } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { SASSERT(level > 0); func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { expr* arg = r.get_tail(j)->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_level_arg(q, k, level-1); } } } } for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { if (sorts[j] && !sub[j].get()) { sub[j] = mk_level_var(r.get_decl(), sorts[j], rule_id, idx++, level); } } } void compile(unsigned level) { rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ... // Assert: r_i_level => body of rule i for level + equalities for head of rule i expr_ref level_pred = mk_level_predicate(p, level); expr_ref_vector rules(m), sub(m), conjs(m); expr_ref rule_body(m), tmp(m); for (unsigned i = 0; i < rls.size(); ++i) { sub.reset(); conjs.reset(); rule& r = *rls[i]; expr_ref rule_i = mk_level_rule(p, i, level); rules.push_back(rule_i); if (level == 0 && r.get_uninterpreted_tail_size() > 0) { tmp = m.mk_not(rule_i); b.assert_expr(tmp); continue; } mk_rule_vars(r, level, i, sub); // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp); conjs.push_back(m.mk_eq(tmp, mk_level_arg(p, k, level))); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { SASSERT(level > 0); func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp); conjs.push_back(m.mk_eq(tmp, mk_level_arg(q, k, level-1))); } conjs.push_back(mk_level_predicate(q, level-1)); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); rule_body = m.mk_implies(rule_i, rule_body); b.assert_expr(rule_body); } bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), tmp); tmp = m.mk_implies(level_pred, tmp); b.assert_expr(tmp); } } }; bmc::bmc(context& ctx): engine_base(ctx.get_manager(), "bmc"), m_ctx(ctx), m(ctx.get_manager()), m_solver(m, m_fparams), m_rules(ctx), m_query_pred(m), m_answer(m), m_rule_trace(ctx.get_rule_manager()) { } bmc::~bmc() {} lbool bmc::query(expr* query) { m_solver.reset(); m_answer = nullptr; m_ctx.ensure_opened(); m_rules.reset(); datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); rule_set& rules0 = m_ctx.get_rules(); datalog::rule_set old_rules(rules0); rule_manager.mk_query(query, rules0); expr_ref bg_assertion = m_ctx.get_background_assertion(); apply_default_transformation(m_ctx); if (m_ctx.xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); } const rule_set& rules = m_ctx.get_rules(); if (rules.get_output_predicates().empty()) { return l_false; } m_query_pred = rules.get_output_predicate(); m_rules.replace_rules(rules); m_rules.close(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); checkpoint(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); if (m_rules.get_num_rules() == 0) { return l_false; } if (m_rules.get_predicate_rules(m_query_pred).empty()) { return l_false; } if (is_linear()) { if (m_ctx.get_engine() == QBMC_ENGINE) { qlinear ql(*this); return ql.check(); } else { linear lin(*this); return lin.check(); } } else { IF_VERBOSE(0, verbose_stream() << "WARNING: non-linear BMC is highly inefficient\n";); nonlinear nl(*this); return nl.check(); } } void bmc::assert_expr(expr* e) { TRACE("bmc", tout << mk_pp(e, m) << "\n";); m_solver.assert_expr(e); } bool bmc::is_linear() const { unsigned sz = m_rules.get_num_rules(); for (unsigned i = 0; i < sz; ++i) { if (m_rules.get_rule(i)->get_uninterpreted_tail_size() > 1) { return false; } if (m_rules.get_rule_manager().has_quantifiers(*m_rules.get_rule(i))) { return false; } } return true; } void bmc::checkpoint() { if (m.canceled()) { throw default_exception(Z3_CANCELED_MSG); } } void bmc::display_certificate(std::ostream& out) const { out << mk_pp(m_answer, m) << "\n"; } void bmc::collect_statistics(statistics& st) const { m_solver.collect_statistics(st); } void bmc::reset_statistics() { m_solver.reset_statistics(); } expr_ref bmc::get_answer() { return m_answer; } void bmc::get_rules_along_trace(datalog::rule_ref_vector& rules) { rules.append(m_rule_trace); } void bmc::compile(rule_set const& rules, expr_ref_vector& fmls, unsigned level) { nonlinear nl(*this); nl.compile(rules, fmls, level); } expr_ref bmc::compile_query(func_decl* query_pred, unsigned level) { nonlinear nl(*this); return nl.compile_query(query_pred, level); } }; template class rewriter_tpl;