From 8f46179deffa7f671d5f9ec5cd92496c8bfc72f2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 8 Apr 2013 13:50:56 -0700 Subject: [PATCH 01/91] reorganization of rule_set structure Signed-off-by: Nikolaj Bjorner --- src/ast/dl_decl_plugin.cpp | 6 +- src/muz_qe/dl_base.h | 2 + src/muz_qe/dl_bmc_engine.cpp | 19 +- src/muz_qe/dl_cmds.cpp | 36 - src/muz_qe/dl_compiler.cpp | 8 +- src/muz_qe/dl_context.cpp | 121 +- src/muz_qe/dl_context.h | 30 +- src/muz_qe/dl_finite_product_relation.cpp | 4 +- src/muz_qe/dl_mk_array_blast.cpp | 8 +- src/muz_qe/dl_mk_bit_blast.cpp | 49 +- src/muz_qe/dl_mk_coalesce.cpp | 1 + src/muz_qe/dl_mk_coi_filter.cpp | 5 +- src/muz_qe/dl_mk_explanations.cpp | 200 +- src/muz_qe/dl_mk_explanations.h | 20 +- src/muz_qe/dl_mk_extract_quantifiers.cpp | 379 -- src/muz_qe/dl_mk_extract_quantifiers.h | 99 - src/muz_qe/dl_mk_filter_rules.cpp | 7 +- src/muz_qe/dl_mk_interp_tail_simplifier.cpp | 1 + src/muz_qe/dl_mk_karr_invariants.cpp | 13 +- src/muz_qe/dl_mk_loop_counter.cpp | 7 +- src/muz_qe/dl_mk_loop_counter.h | 1 + src/muz_qe/dl_mk_magic_sets.cpp | 177 +- src/muz_qe/dl_mk_magic_sets.h | 25 +- src/muz_qe/dl_mk_partial_equiv.cpp | 5 +- src/muz_qe/dl_mk_quantifier_abstraction.cpp | 22 +- src/muz_qe/dl_mk_quantifier_abstraction.h | 6 +- src/muz_qe/dl_mk_quantifier_instantiation.cpp | 13 +- src/muz_qe/dl_mk_rule_inliner.cpp | 27 +- src/muz_qe/dl_mk_rule_inliner.h | 4 +- src/muz_qe/dl_mk_similarity_compressor.cpp | 150 +- src/muz_qe/dl_mk_simple_joins.cpp | 7 +- src/muz_qe/dl_mk_slice.cpp | 8 +- src/muz_qe/dl_mk_slice.h | 2 +- src/muz_qe/dl_mk_subsumption_checker.cpp | 29 +- src/muz_qe/dl_mk_subsumption_checker.h | 4 +- src/muz_qe/dl_mk_unbound_compressor.cpp | 59 +- src/muz_qe/dl_mk_unbound_compressor.h | 14 +- src/muz_qe/dl_mk_unfold.cpp | 1 + src/muz_qe/dl_relation_manager.cpp | 12 +- src/muz_qe/dl_relation_manager.h | 6 +- src/muz_qe/dl_rule.cpp | 47 +- src/muz_qe/dl_rule.h | 16 +- src/muz_qe/dl_rule_set.cpp | 161 +- src/muz_qe/dl_rule_set.h | 31 +- src/muz_qe/dl_rule_transformer.cpp | 32 +- src/muz_qe/dl_skip_table.cpp | 622 --- src/muz_qe/dl_skip_table.h | 161 - src/muz_qe/dl_sparse_table.cpp | 3 + src/muz_qe/imdd.cpp | 3688 ----------------- src/muz_qe/imdd.h | 849 ---- src/muz_qe/interval_skip_list.h | 1876 --------- src/muz_qe/pdr_dl_interface.cpp | 52 +- src/muz_qe/pdr_manager.cpp | 2 +- src/muz_qe/pdr_quantifiers.cpp | 660 --- src/muz_qe/pdr_quantifiers.h | 117 - src/muz_qe/rel_context.cpp | 145 +- src/muz_qe/rel_context.h | 13 +- src/muz_qe/tab_context.cpp | 14 +- src/shell/datalog_frontend.cpp | 5 +- src/test/dl_query.cpp | 2 +- src/test/dl_table.cpp | 12 - src/test/imdd.cpp | 617 --- src/test/interval_skip_list.cpp | 716 ---- src/test/main.cpp | 13 +- src/util/mpz.cpp | 5 +- 65 files changed, 778 insertions(+), 10668 deletions(-) delete mode 100644 src/muz_qe/dl_mk_extract_quantifiers.cpp delete mode 100644 src/muz_qe/dl_mk_extract_quantifiers.h delete mode 100644 src/muz_qe/dl_skip_table.cpp delete mode 100644 src/muz_qe/dl_skip_table.h delete mode 100644 src/muz_qe/imdd.cpp delete mode 100644 src/muz_qe/imdd.h delete mode 100644 src/muz_qe/interval_skip_list.h delete mode 100644 src/muz_qe/pdr_quantifiers.cpp delete mode 100644 src/muz_qe/pdr_quantifiers.h delete mode 100644 src/test/imdd.cpp delete mode 100644 src/test/interval_skip_list.cpp diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index 8ac19c11c..1524760e5 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -636,9 +636,13 @@ namespace datalog { app* dl_decl_util::mk_numeral(uint64 value, sort* s) { if (is_finite_sort(s)) { + uint64 sz = 0; + if (try_get_size(s, sz) && sz <= value) { + m.raise_exception("value is out of bounds"); + } parameter params[2] = { parameter(rational(value, rational::ui64())), parameter(s) }; return m.mk_const(m.mk_func_decl(m_fid, OP_DL_CONSTANT, 2, params, 0, (sort*const*)0)); - } + } if (m_arith.is_int(s) || m_arith.is_real(s)) { return m_arith.mk_numeral(rational(value, rational::ui64()), s); } diff --git a/src/muz_qe/dl_base.h b/src/muz_qe/dl_base.h index 6c53a4b27..e7243cb70 100644 --- a/src/muz_qe/dl_base.h +++ b/src/muz_qe/dl_base.h @@ -37,6 +37,8 @@ namespace datalog { ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm); context & get_context_from_rel_manager(const relation_manager & rm); + typedef func_decl_set decl_set; + #if DL_LEAK_HUNTING void leak_guard_check(const symbol & s); #endif diff --git a/src/muz_qe/dl_bmc_engine.cpp b/src/muz_qe/dl_bmc_engine.cpp index 959a11605..15876b631 100644 --- a/src/muz_qe/dl_bmc_engine.cpp +++ b/src/muz_qe/dl_bmc_engine.cpp @@ -44,7 +44,6 @@ namespace datalog { public: qlinear(bmc& b): b(b), m(b.m), m_bv(m), m_bit_width(1) {} - lbool check() { setup(); m_bit_width = 4; @@ -1416,19 +1415,12 @@ namespace datalog { lbool bmc::query(expr* query) { m_solver.reset(); m_answer = 0; - m_ctx.ensure_opened(); m_rules.reset(); - datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); - datalog::rule_set old_rules(m_ctx.get_rules()); - datalog::rule_ref_vector query_rules(rule_manager); - datalog::rule_ref query_rule(rule_manager); - rule_manager.mk_query(query, m_query_pred, query_rules, query_rule); - m_ctx.add_rules(query_rules); - expr_ref bg_assertion = m_ctx.get_background_assertion(); - - m_ctx.set_output_predicate(m_query_pred); + datalog::rule_set old_rules(m_ctx.get_rules()); + rule_manager.mk_query(query, m_ctx.get_rules()); + expr_ref bg_assertion = m_ctx.get_background_assertion(); m_ctx.apply_default_transformation(); if (m_ctx.get_params().slice()) { @@ -1436,10 +1428,9 @@ namespace datalog { datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); - m_query_pred = slice->get_predicate(m_query_pred.get()); - m_ctx.set_output_predicate(m_query_pred); } - m_rules.add_rules(m_ctx.get_rules()); + m_query_pred = m_ctx.get_rules().get_output_predicate(); + m_rules.replace_rules(m_ctx.get_rules()); m_rules.close(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); diff --git a/src/muz_qe/dl_cmds.cpp b/src/muz_qe/dl_cmds.cpp index c1e3f85f9..b82225785 100644 --- a/src/muz_qe/dl_cmds.cpp +++ b/src/muz_qe/dl_cmds.cpp @@ -445,40 +445,8 @@ public: ctx.insert(var); m_dl_ctx->dlctx().register_variable(var); } - }; -class dl_push_cmd : public cmd { - ref m_ctx; -public: - dl_push_cmd(dl_context* ctx): - cmd("fixedpoint-push"), - m_ctx(ctx) - {} - - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "push context on the fixedpoint engine"; } - - virtual void execute(cmd_context& ctx) { - m_ctx->push(); - } -}; - -class dl_pop_cmd : public cmd { - ref m_ctx; -public: - dl_pop_cmd(dl_context* ctx): - cmd("fixedpoint-pop"), - m_ctx(ctx) - {} - - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "pop context on the fixedpoint engine"; } - - virtual void execute(cmd_context& ctx) { - m_ctx->pop(); - } -}; static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_cmds) { dl_context * dl_ctx = alloc(dl_context, ctx, collected_cmds); @@ -486,10 +454,6 @@ static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_c ctx.insert(alloc(dl_query_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_var_cmd, dl_ctx)); -#ifndef _EXTERNAL_RELEASE - ctx.insert(alloc(dl_push_cmd, dl_ctx)); // not exposed to keep command-extensions simple. - ctx.insert(alloc(dl_pop_cmd, dl_ctx)); -#endif } void install_dl_cmds(cmd_context & ctx) { diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index c898f7964..681dc696a 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -760,7 +760,7 @@ namespace datalog { typedef svector tail_delta_infos; unsigned rule_len = r->get_uninterpreted_tail_size(); - reg_idx head_reg = m_pred_regs.find(r->get_head()->get_decl()); + reg_idx head_reg = m_pred_regs.find(r->get_decl()); svector tail_regs; tail_delta_infos tail_deltas; @@ -884,7 +884,7 @@ namespace datalog { rule_vector::const_iterator rend = pred_rules.end(); for(; rit!=rend; ++rit) { rule * r = *rit; - SASSERT(head_pred==r->get_head()->get_decl()); + SASSERT(head_pred==r->get_decl()); compile_rule_evaluation(r, input_deltas, d_head_reg, widen_predicate_in_loop, acc); } @@ -1039,7 +1039,7 @@ namespace datalog { rule_vector::const_iterator end = rules.end(); for (; it != end; ++it) { rule * r = *it; - SASSERT(r->get_head()->get_decl()==head_pred); + SASSERT(r->get_decl()==head_pred); compile_rule_evaluation(r, input_deltas, output_delta, false, acc); } @@ -1112,7 +1112,7 @@ namespace datalog { //load predicate data for(unsigned i=0;iget_head()->get_decl(), acc); + ensure_predicate_loaded(r->get_decl(), acc); unsigned rule_len = r->get_uninterpreted_tail_size(); for(unsigned j=0;jget_name(), decl); + } } - m_pinned.push_back(decl); - m_preds.insert(decl); - if (named) { - m_preds_by_name.insert(decl->get_name(), decl); + } + + void context::restrict_predicates(func_decl_set const& preds) { + m_preds.reset(); + func_decl_set::iterator it = preds.begin(), end = preds.end(); + for (; it != end; ++it) { + m_preds.insert(*it); } } @@ -447,21 +446,6 @@ namespace datalog { return new_pred; } - void context::set_output_predicate(func_decl * pred) { - ensure_rel(); - m_rel->set_output_predicate(pred); - } - - bool context::is_output_predicate(func_decl * pred) { - ensure_rel(); - return m_rel->is_output_predicate(pred); - } - - const decl_set & context::get_output_predicates() { - ensure_rel(); - return m_rel->get_output_predicates(); - } - void context::add_rule(expr* rl, symbol const& name) { m_rule_fmls.push_back(rl); m_rule_names.push_back(name); @@ -469,14 +453,12 @@ namespace datalog { void context::flush_add_rules() { datalog::rule_manager& rm = get_rule_manager(); - datalog::rule_ref_vector rules(rm); scoped_proof_mode _scp(m, generate_proof_trace()?PGM_FINE:PGM_DISABLED); for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { expr* fml = m_rule_fmls[i].get(); proof* p = generate_proof_trace()?m.mk_asserted(fml):0; - rm.mk_rule(fml, p, rules, m_rule_names[i]); + rm.mk_rule(fml, p, m_rule_set, m_rule_names[i]); } - add_rules(rules); m_rule_fmls.reset(); m_rule_names.reset(); } @@ -487,25 +469,28 @@ namespace datalog { // void context::update_rule(expr* rl, symbol const& name) { datalog::rule_manager& rm = get_rule_manager(); - datalog::rule_ref_vector rules(rm); proof* p = 0; if (generate_proof_trace()) { p = m.mk_asserted(rl); } - rm.mk_rule(rl, p, rules, name); - if (rules.size() != 1) { + unsigned size_before = m_rule_set.get_num_rules(); + rm.mk_rule(rl, p, m_rule_set, name); + unsigned size_after = m_rule_set.get_num_rules(); + if (size_before + 1 != size_after) { std::stringstream strm; strm << "Rule " << name << " has a non-trivial body. It cannot be modified"; throw default_exception(strm.str()); } - rule_ref r(rules[0].get(), rm); + // The new rule is inserted last: + rule_ref r(m_rule_set.get_rule(size_before), rm); rule_ref_vector const& rls = m_rule_set.get_rules(); rule* old_rule = 0; - for (unsigned i = 0; i < rls.size(); ++i) { + for (unsigned i = 0; i < size_before; ++i) { if (rls[i]->name() == name) { if (old_rule) { std::stringstream strm; strm << "Rule " << name << " occurs twice. It cannot be modified"; + m_rule_set.del_rule(r); throw default_exception(strm.str()); } old_rule = rls[i]; @@ -518,11 +503,11 @@ namespace datalog { old_rule->display(*this, strm); strm << "does not subsume new rule "; r->display(*this, strm); + m_rule_set.del_rule(r); throw default_exception(strm.str()); } m_rule_set.del_rule(old_rule); } - m_rule_set.add_rule(r); } bool context::check_subsumes(rule const& stronger_rule, rule const& weaker_rule) { @@ -623,20 +608,20 @@ namespace datalog { } class context::contains_pred : public i_expr_pred { - rule_manager const& m; + context const& ctx; public: - contains_pred(rule_manager const& m): m(m) {} + contains_pred(context& ctx): ctx(ctx) {} virtual ~contains_pred() {} virtual bool operator()(expr* e) { - return is_app(e) && m.is_predicate(to_app(e)); + return ctx.is_predicate(e); } }; void context::check_existential_tail(rule_ref& r) { unsigned ut_size = r->get_uninterpreted_tail_size(); unsigned t_size = r->get_tail_size(); - contains_pred contains_p(get_rule_manager()); + contains_pred contains_p(*this); check_pred check_pred(contains_p, get_manager()); TRACE("dl", r->display_smt2(get_manager(), tout); tout << "\n";); @@ -663,7 +648,7 @@ namespace datalog { } ast_manager& m = get_manager(); datalog::rule_manager& rm = get_rule_manager(); - contains_pred contains_p(rm); + contains_pred contains_p(*this); check_pred check_pred(contains_p, get_manager()); for (unsigned i = ut_size; i < t_size; ++i) { @@ -676,7 +661,7 @@ namespace datalog { continue; } visited.mark(e, true); - if (rm.is_predicate(e)) { + if (is_predicate(e)) { } else if (m.is_and(e) || m.is_or(e)) { todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); @@ -749,14 +734,6 @@ namespace datalog { m_rule_set.add_rule(r); } - void context::add_rules(rule_ref_vector& rules) { - for (unsigned i = 0; i < rules.size(); ++i) { - rule_ref rule(rules[i].get(), rules.get_manager()); - add_rule(rule); - } - } - - void context::add_fact(func_decl * pred, const relation_fact & fact) { if (get_engine() == DATALOG_ENGINE) { ensure_rel(); @@ -771,10 +748,9 @@ namespace datalog { void context::add_fact(app * head) { SASSERT(is_fact(head)); - relation_fact fact(get_manager()); - unsigned n=head->get_num_args(); - for (unsigned i=0; iget_num_args(); + for (unsigned i = 0; i < n; i++) { fact.push_back(to_app(head->get_arg(i))); } add_fact(head->get_decl(), fact); @@ -849,6 +825,12 @@ namespace datalog { transform_rules(m_transf); } + + void context::transform_rules(rule_transformer::plugin* plugin) { + rule_transformer transformer(*this); + transformer.register_plugin(plugin); + transform_rules(transformer); + } void context::transform_rules(rule_transformer& transf) { SASSERT(m_closed); //we must finish adding rules before we start transforming them @@ -862,15 +844,16 @@ namespace datalog { } } - void context::replace_rules(rule_set & rs) { + void context::replace_rules(rule_set const & rs) { SASSERT(!m_closed); - m_rule_set.reset(); - m_rule_set.add_rules(rs); + m_rule_set.replace_rules(rs); + if (m_rel) { + m_rel->restrict_predicates(get_predicates()); + } } void context::record_transformed_rules() { - m_transformed_rule_set.reset(); - m_transformed_rule_set.add_rules(m_rule_set); + m_transformed_rule_set.replace_rules(m_rule_set); } void context::apply_default_transformation() { @@ -921,16 +904,6 @@ namespace datalog { if (m_pdr.get()) m_pdr->updt_params(); } - void context::collect_predicates(decl_set& res) { - ensure_rel(); - m_rel->collect_predicates(res); - } - - void context::restrict_predicates(decl_set const& res) { - ensure_rel(); - m_rel->restrict_predicates(res); - } - expr_ref context::get_background_assertion() { expr_ref result(m); switch (m_background.size()) { @@ -1269,14 +1242,13 @@ namespace datalog { void context::get_rules_as_formulas(expr_ref_vector& rules, svector& names) { expr_ref fml(m); datalog::rule_manager& rm = get_rule_manager(); - datalog::rule_ref_vector rule_refs(rm); // ensure that rules are all using bound variables. for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { ptr_vector sorts; get_free_vars(m_rule_fmls[i].get(), sorts); if (!sorts.empty()) { - rm.mk_rule(m_rule_fmls[i].get(), 0, rule_refs, m_rule_names[i]); + rm.mk_rule(m_rule_fmls[i].get(), 0, m_rule_set, m_rule_names[i]); m_rule_fmls[i] = m_rule_fmls.back(); m_rule_names[i] = m_rule_names.back(); m_rule_fmls.pop_back(); @@ -1284,7 +1256,6 @@ namespace datalog { --i; } } - add_rules(rule_refs); rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); for (; it != end; ++it) { (*it)->to_formula(fml); diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index cb432d4e9..62f1b83e1 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -185,7 +185,17 @@ namespace datalog { */ void register_predicate(func_decl * pred, bool named); - bool is_predicate(func_decl * pred) const; + /** + Restrict reltaions to set of predicates. + */ + void restrict_predicates(func_decl_set const& preds); + + /** + \brief Retrieve predicates + */ + func_decl_set const& get_predicates() const { return m_preds; } + bool is_predicate(func_decl* pred) const { return m_preds.contains(pred); } + bool is_predicate(expr * e) const { return is_app(e) && is_predicate(to_app(e)->get_decl()); } /** \brief If a predicate name has a \c func_decl object assigned, return pointer to it; @@ -238,11 +248,9 @@ namespace datalog { void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names); - void set_output_predicate(func_decl * pred); - bool is_output_predicate(func_decl * pred); - const decl_set & get_output_predicates(); + void set_output_predicate(func_decl * pred) { m_rule_set.set_output_predicate(pred); } - rule_set const & get_rules() { flush_add_rules(); return m_rule_set; } + rule_set & get_rules() { flush_add_rules(); return m_rule_set; } void get_rules_as_formulas(expr_ref_vector& fmls, svector& names); @@ -251,7 +259,6 @@ namespace datalog { void add_rule(rule_ref& r); - void add_rules(rule_ref_vector& rs); void assert_expr(expr* e); expr_ref get_background_assertion(); @@ -329,7 +336,8 @@ namespace datalog { void transform_rules(); void transform_rules(rule_transformer& transf); - void replace_rules(rule_set & rs); + void transform_rules(rule_transformer::plugin* plugin); + void replace_rules(rule_set const& rs); void record_transformed_rules(); void apply_default_transformation(); @@ -338,14 +346,6 @@ namespace datalog { void updt_params(params_ref const& p); - void collect_predicates(decl_set & res); - /** - \brief Restrict the set of used predicates to \c res. - - The function deallocates unsused relations, it does not deal with rules. - */ - void restrict_predicates(const decl_set & res); - void display_rules(std::ostream & out) const { m_rule_set.display(out); } diff --git a/src/muz_qe/dl_finite_product_relation.cpp b/src/muz_qe/dl_finite_product_relation.cpp index 3070475ac..4880e6068 100644 --- a/src/muz_qe/dl_finite_product_relation.cpp +++ b/src/muz_qe/dl_finite_product_relation.cpp @@ -129,7 +129,7 @@ namespace datalog { for(unsigned i=0; idisplay(m_ctx, tout << "new rule\n");); + TRACE("dl", new_rules.last()->display(m_ctx, tout << "new rule\n");); rule_ref new_rule(rm); - if (m_simplifier.transform_rule(new_rules[0].get(), new_rule)) { + if (m_simplifier.transform_rule(new_rules.last(), new_rule)) { rules.add_rule(new_rule.get()); rm.mk_rule_rewrite_proof(r, *new_rule.get()); } @@ -214,6 +213,7 @@ namespace datalog { rule_set * mk_array_blast::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); + rules->inherit_predicates(source); rule_set::iterator it = source.begin(), end = source.end(); bool change = false; for (; !m_ctx.canceled() && it != end; ++it) { diff --git a/src/muz_qe/dl_mk_bit_blast.cpp b/src/muz_qe/dl_mk_bit_blast.cpp index 3d6c35bb3..3f92bbce8 100644 --- a/src/muz_qe/dl_mk_bit_blast.cpp +++ b/src/muz_qe/dl_mk_bit_blast.cpp @@ -108,31 +108,35 @@ namespace datalog { class expand_mkbv_cfg : public default_rewriter_cfg { context& m_context; - rule_ref_vector& m_rules; ast_manager& m; bv_util m_util; expr_ref_vector m_args, m_f_vars, m_g_vars; func_decl_ref_vector m_old_funcs; func_decl_ref_vector m_new_funcs; + rule_set const* m_src; + rule_set* m_dst; obj_map m_pred2blast; public: - expand_mkbv_cfg(context& ctx, rule_ref_vector& rules): + expand_mkbv_cfg(context& ctx): m_context(ctx), - m_rules(rules), m(ctx.get_manager()), m_util(m), m_args(m), m_f_vars(m), m_g_vars(m), m_old_funcs(m), - m_new_funcs(m) + m_new_funcs(m), + m_src(0), + m_dst(0) {} ~expand_mkbv_cfg() {} + void set_src(rule_set const* src) { m_src = src; } + void set_dst(rule_set* dst) { m_dst = dst; } func_decl_ref_vector const& old_funcs() const { return m_old_funcs; } func_decl_ref_vector const& new_funcs() const { return m_new_funcs; } @@ -183,14 +187,7 @@ namespace datalog { m_new_funcs.push_back(g); m_pred2blast.insert(f, g); - // Create rule f(mk_mkbv(args)) :- g(args) - - fml = m.mk_implies(m.mk_app(g, m_g_vars.size(), m_g_vars.c_ptr()), m.mk_app(f, m_f_vars.size(), m_f_vars.c_ptr())); - proof_ref pr(m); - if (m_context.generate_proof_trace()) { - pr = m.mk_asserted(fml); // or a def? - } - rm.mk_rule(fml, pr, m_rules, g->get_name()); + m_dst->inherit_predicate(*m_src, f, g); } result = m.mk_app(g, m_args.size(), m_args.c_ptr()); result_pr = 0; @@ -200,9 +197,9 @@ namespace datalog { struct expand_mkbv : public rewriter_tpl { expand_mkbv_cfg m_cfg; - expand_mkbv(ast_manager& m, context& ctx, rule_ref_vector& rules): + expand_mkbv(ast_manager& m, context& ctx): rewriter_tpl(m, m.proofs_enabled(), m_cfg), - m_cfg(ctx, rules) { + m_cfg(ctx) { } }; @@ -212,7 +209,6 @@ namespace datalog { context & m_context; ast_manager & m; params_ref m_params; - rule_ref_vector m_rules; mk_interp_tail_simplifier m_simplifier; bit_blaster_rewriter m_blaster; expand_mkbv m_rewriter; @@ -239,19 +235,14 @@ namespace datalog { } } - void reset() { - m_rules.reset(); - } - public: impl(context& ctx): m_context(ctx), m(ctx.get_manager()), m_params(ctx.get_params().p), - m_rules(ctx.get_rule_manager()), m_simplifier(ctx), m_blaster(ctx.get_manager(), m_params), - m_rewriter(ctx.get_manager(), ctx, m_rules) { + m_rewriter(ctx.get_manager(), ctx) { m_params.set_bool("blast_full", true); m_params.set_bool("blast_quant", true); m_blaster.updt_params(m_params); @@ -265,8 +256,9 @@ namespace datalog { rule_manager& rm = m_context.get_rule_manager(); unsigned sz = source.get_num_rules(); expr_ref fml(m); - reset(); - rule_set * result = alloc(rule_set, m_context); + rule_set * result = alloc(rule_set, m_context); + m_rewriter.m_cfg.set_src(&source); + m_rewriter.m_cfg.set_dst(result); for (unsigned i = 0; !m_context.canceled() && i < sz; ++i) { rule * r = source.get_rule(i); r->to_formula(fml); @@ -275,17 +267,16 @@ namespace datalog { if (m_context.generate_proof_trace()) { pr = m.mk_asserted(fml); // loses original proof of r. } - rm.mk_rule(fml, pr, m_rules, r->name()); + // TODO add logic for pc: + // 1. replace fresh predicates by non-bit-blasted predicates + // 2. replace pr by the proof of r. + rm.mk_rule(fml, pr, *result, r->name()); } else { - m_rules.push_back(r); + result->add_rule(r); } } - for (unsigned i = 0; i < m_rules.size(); ++i) { - result->add_rule(m_rules.get(i)); - } - if (m_context.get_model_converter()) { filter_model_converter* fmc = alloc(filter_model_converter, m); bit_blast_model_converter* bvmc = alloc(bit_blast_model_converter, m); diff --git a/src/muz_qe/dl_mk_coalesce.cpp b/src/muz_qe/dl_mk_coalesce.cpp index 94c42e33b..8c7f1d5b4 100644 --- a/src/muz_qe/dl_mk_coalesce.cpp +++ b/src/muz_qe/dl_mk_coalesce.cpp @@ -173,6 +173,7 @@ namespace datalog { rule_set * mk_coalesce::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); + rules->inherit_predicates(source); rule_set::decl2rules::iterator it = source.begin_grouped_rules(), end = source.end_grouped_rules(); for (; it != end; ++it) { rule_ref_vector d_rules(rm); diff --git a/src/muz_qe/dl_mk_coi_filter.cpp b/src/muz_qe/dl_mk_coi_filter.cpp index 6aa688a3c..b253a0e20 100644 --- a/src/muz_qe/dl_mk_coi_filter.cpp +++ b/src/muz_qe/dl_mk_coi_filter.cpp @@ -35,7 +35,7 @@ namespace datalog { rule_set * mk_coi_filter::operator()(rule_set const & source) { - if (source.get_num_rules()==0) { + if (source.empty()) { return 0; } @@ -43,7 +43,7 @@ namespace datalog { decl_set pruned_preds; ptr_vector todo; { - const decl_set& output_preds = m_context.get_output_predicates(); + const decl_set& output_preds = source.get_output_predicates(); decl_set::iterator oend = output_preds.end(); for (decl_set::iterator it = output_preds.begin(); it!=oend; ++it) { todo.push_back(*it); @@ -70,6 +70,7 @@ namespace datalog { } scoped_ptr res = alloc(rule_set, m_context); + res->inherit_predicates(source); rule_set::iterator rend = source.end(); for (rule_set::iterator rit = source.begin(); rit!=rend; ++rit) { diff --git a/src/muz_qe/dl_mk_explanations.cpp b/src/muz_qe/dl_mk_explanations.cpp index 646a0bcbe..bf3548a7a 100644 --- a/src/muz_qe/dl_mk_explanations.cpp +++ b/src/muz_qe/dl_mk_explanations.cpp @@ -80,8 +80,8 @@ namespace datalog { virtual bool can_handle_signature(const relation_signature & s) { unsigned n=s.size(); - for(unsigned i=0; i(get_plugin().mk_empty(get_signature())); - if(empty()) { + if (empty()) { res->set_undefined(); } return res; } void display_explanation(app * expl, std::ostream & out) const { - if(expl) { + if (expl) { //TODO: some nice explanation output ast_smt_pp pp(get_plugin().get_ast_manager()); pp.display_expr_smt2(out, expl); @@ -249,13 +249,13 @@ namespace datalog { } virtual void display(std::ostream & out) const { - if(empty()) { + if (empty()) { out << "\n"; return; } unsigned sz = get_signature().size(); - for(unsigned i=0; i(plugin.mk_empty(get_result_signature())); - if(!r1.empty() && !r2.empty()) { + if (!r1.empty() && !r2.empty()) { res->m_empty = false; SASSERT(res->m_data.empty()); res->m_data.append(r1.m_data); @@ -317,10 +317,10 @@ namespace datalog { relation_join_fn * explanation_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if(&r1.get_plugin()!=this || &r2.get_plugin()!=this) { + if (&r1.get_plugin()!=this || &r2.get_plugin()!=this) { return 0; } - if(col_cnt!=0) { + if (col_cnt!=0) { return 0; } return alloc(join_fn, r1.get_signature(), r2.get_signature()); @@ -337,7 +337,7 @@ namespace datalog { explanation_relation_plugin & plugin = r.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); - if(!r.empty()) { + if (!r.empty()) { relation_fact proj_data = r.m_data; project_out_vector_columns(proj_data, m_removed_cols); res->assign_data(proj_data); @@ -348,7 +348,7 @@ namespace datalog { relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { - if(&r.get_plugin()!=this) { + if (&r.get_plugin()!=this) { return 0; } return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); @@ -365,7 +365,7 @@ namespace datalog { explanation_relation_plugin & plugin = r.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); - if(!r.empty()) { + if (!r.empty()) { relation_fact permutated_data = r.m_data; permutate_by_cycle(permutated_data, m_cycle); res->assign_data(permutated_data); @@ -389,16 +389,16 @@ namespace datalog { explanation_relation * delta = delta0 ? static_cast(delta0) : 0; explanation_relation_plugin & plugin = tgt.get_plugin(); - if(!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { + if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { UNREACHABLE(); } - if(src.empty()) { + if (src.empty()) { return; } - if(plugin.m_relation_level_explanations) { + if (plugin.m_relation_level_explanations) { tgt.unite_with_data(src.m_data); - if(delta) { - if(!m_delta_union_fun) { + if (delta) { + if (!m_delta_union_fun) { m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src); SASSERT(m_delta_union_fun); } @@ -406,9 +406,9 @@ namespace datalog { } } else { - if(tgt.empty()) { + if (tgt.empty()) { tgt.assign_data(src.m_data); - if(delta && delta->empty()) { + if (delta && delta->empty()) { delta->assign_data(src.m_data); } } @@ -423,11 +423,11 @@ namespace datalog { explanation_relation & tgt = static_cast(tgt0); explanation_relation * delta = delta0 ? static_cast(delta0) : 0; - if(src.empty()) { + if (src.empty()) { return; } tgt.set_undefined(); - if(delta) { + if (delta) { delta->set_undefined(); } } @@ -435,10 +435,10 @@ namespace datalog { relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { - if(!check_kind(tgt) || (delta && !check_kind(*delta))) { + if (!check_kind(tgt) || (delta && !check_kind(*delta))) { return 0; } - if(!check_kind(src)) { + if (!check_kind(src)) { //this is to handle the product relation return alloc(foreign_union_fn); } @@ -460,7 +460,7 @@ namespace datalog { virtual void operator()(relation_base & r0) { explanation_relation & r = static_cast(r0); - if(!r.is_undefined(m_col_idx)) { + if (!r.is_undefined(m_col_idx)) { UNREACHABLE(); } @@ -468,7 +468,7 @@ namespace datalog { ptr_vector subst_arg; subst_arg.resize(sz, 0); unsigned ofs = sz-1; - for(unsigned i=0; iget_arg(0); expr * arg2 = cond->get_arg(1); - if(is_var(arg2)) { + if (is_var(arg2)) { std::swap(arg1, arg2); } - if(!is_var(arg1) || !is_app(arg2)) { + if (!is_var(arg1) || !is_app(arg2)) { return 0; } var * col_var = to_var(arg1); app * new_rule = to_app(arg2); - if(!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { + if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { return 0; } unsigned col_idx = col_var->get_idx(); @@ -511,7 +511,7 @@ namespace datalog { class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { public: virtual void operator()(relation_base & r, const relation_base & neg) { - if(!neg.empty()) { + if (!neg.empty()) { r.reset(); } } @@ -520,7 +520,7 @@ namespace datalog { relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, const relation_base & neg, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { - if(&r.get_plugin()!=this || &neg.get_plugin()!=this) { + if (&r.get_plugin()!=this || &neg.get_plugin()!=this) { return 0; } return alloc(negation_filter_fn); @@ -537,26 +537,26 @@ namespace datalog { explanation_relation & tgt = static_cast(tgt0); const explanation_relation & src = static_cast(src0); - if(src.empty()) { + if (src.empty()) { tgt.reset(); return; } - if(tgt.empty()) { + if (tgt.empty()) { return; } unsigned sz = tgt.get_signature().size(); - for(unsigned i=0; iget_decl()==m_union_decl.get()) { - if(curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) { + if (curr_tgt->get_decl()==m_union_decl.get()) { + if (curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) { tgt.m_data.set(i, curr_src); continue; } @@ -570,18 +570,18 @@ namespace datalog { relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_intersection_fn( const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, const unsigned * tgt_cols, const unsigned * src_cols) { - if(&tgt.get_plugin()!=this || &src.get_plugin()!=this) { + if (&tgt.get_plugin()!=this || &src.get_plugin()!=this) { return 0; } //this checks the join is one to one on all columns - if(tgt.get_signature()!=src.get_signature() + if (tgt.get_signature()!=src.get_signature() || joined_col_cnt!=tgt.get_signature().size() || !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) { return 0; } counter ctr; ctr.count(joined_col_cnt, tgt_cols); - if(ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) { + if (ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) { return 0; } return alloc(intersection_filter_fn, *this); @@ -595,23 +595,23 @@ namespace datalog { // ----------------------------------- - mk_explanations::mk_explanations(context & ctx, bool relation_level) - : plugin(50000), - m_manager(ctx.get_manager()), - m_context(ctx), - m_decl_util(ctx.get_decl_util()), - m_relation_level(relation_level), - m_pinned(m_manager) { + mk_explanations::mk_explanations(context & ctx) + : plugin(50000), + m_manager(ctx.get_manager()), + m_context(ctx), + m_decl_util(ctx.get_decl_util()), + m_relation_level(ctx.explanations_on_relation_level()), + m_pinned(m_manager) { m_e_sort = m_decl_util.mk_rule_sort(); m_pinned.push_back(m_e_sort); relation_manager & rmgr = ctx.get_rel_context().get_rmanager(); - symbol er_symbol = explanation_relation_plugin::get_name(relation_level); + symbol er_symbol = explanation_relation_plugin::get_name(m_relation_level); m_er_plugin = static_cast(rmgr.get_relation_plugin(er_symbol)); - if(!m_er_plugin) { - m_er_plugin = alloc(explanation_relation_plugin, relation_level, rmgr); + if (!m_er_plugin) { + m_er_plugin = alloc(explanation_relation_plugin, m_relation_level, rmgr); rmgr.register_plugin(m_er_plugin); - if(!m_relation_level) { + if (!m_relation_level) { DEBUG_CODE( finite_product_relation_plugin * dummy; SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); @@ -620,7 +620,7 @@ namespace datalog { } } DEBUG_CODE( - if(!m_relation_level) { + if (!m_relation_level) { finite_product_relation_plugin * dummy; SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); } @@ -668,7 +668,7 @@ namespace datalog { func_decl * mk_explanations::get_e_decl(func_decl * orig_decl) { decl_map::obj_map_entry * e = m_e_decl_map.insert_if_not_there2(orig_decl, 0); - if(e->get_data().m_value==0) { + if (e->get_data().m_value==0) { relation_signature e_domain; e_domain.append(orig_decl->get_arity(), orig_decl->get_domain()); e_domain.push_back(m_e_sort); @@ -677,7 +677,7 @@ namespace datalog { m_pinned.push_back(new_decl); e->get_data().m_value = new_decl; - if(m_relation_level) { + if (m_relation_level) { assign_rel_level_kind(new_decl, orig_decl); } } @@ -716,13 +716,13 @@ namespace datalog { app_ref_vector e_tail(m_manager); svector neg_flags; unsigned pos_tail_sz = r->get_positive_tail_size(); - for(unsigned i=0; iget_tail(i), e_var)); neg_flags.push_back(false); } unsigned tail_sz = r->get_tail_size(); - for(unsigned i=pos_tail_sz; iget_tail(i)); neg_flags.push_back(r->is_neg_tail(i)); } @@ -730,9 +730,9 @@ namespace datalog { symbol rule_repr = get_rule_symbol(r); expr_ref_vector rule_expr_args(m_manager); - for(unsigned tail_idx=0; tail_idxget_arg(tail->get_num_args()-1)); } @@ -754,37 +754,32 @@ namespace datalog { return m_context.get_rule_manager().mk(e_head, e_tail.size(), e_tail.c_ptr(), neg_flags.c_ptr()); } - void mk_explanations::transform_rules(const rule_set & orig, rule_set & tgt) { - rule_set::iterator rit = orig.begin(); - rule_set::iterator rend = orig.end(); - for(; rit!=rend; ++rit) { + void mk_explanations::transform_rules(const rule_set & src, rule_set & dst) { + rule_set::iterator rit = src.begin(); + rule_set::iterator rend = src.end(); + for (; rit!=rend; ++rit) { rule * e_rule = get_e_rule(*rit); - tgt.add_rule(e_rule); + dst.add_rule(e_rule); } //add rules that will (for output predicates) copy facts from explained relations back to //the original ones expr_ref_vector lit_args(m_manager); - decl_set::iterator pit = m_original_preds.begin(); - decl_set::iterator pend = m_original_preds.end(); - for(; pit!=pend; ++pit) { + decl_set::iterator pit = src.get_output_predicates().begin(); + decl_set::iterator pend = src.get_output_predicates().end(); + for (; pit != pend; ++pit) { func_decl * orig_decl = *pit; - if(!m_context.is_output_predicate(orig_decl)) { - continue; - } - lit_args.reset(); unsigned arity = orig_decl->get_arity(); - for(unsigned i=0; iget_domain(i))); } app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager); app_ref e_lit(get_e_lit(orig_lit, arity), m_manager); app * tail[] = { e_lit.get() }; - tgt.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0)); + dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0)); } - } void mk_explanations::translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, @@ -799,7 +794,7 @@ namespace datalog { sieve_relation * srels[] = { static_cast(&prod_rel[0]), static_cast(&prod_rel[1]) }; - if(&srels[0]->get_inner().get_plugin()==m_er_plugin) { + if (&srels[0]->get_inner().get_plugin()==m_er_plugin) { std::swap(srels[0], srels[1]); } SASSERT(&srels[0]->get_inner().get_plugin()==&orig.get_plugin()); @@ -821,10 +816,9 @@ namespace datalog { } } - void mk_explanations::transform_facts(relation_manager & rmgr) { + void mk_explanations::transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst) { - - if(!m_e_fact_relation) { + if (!m_e_fact_relation) { relation_signature expl_singleton_sig; expl_singleton_sig.push_back(m_e_sort); @@ -836,29 +830,26 @@ namespace datalog { SASSERT(&expl_singleton->get_plugin()==m_er_plugin); m_e_fact_relation = static_cast(expl_singleton); } - - - - decl_set::iterator it = m_original_preds.begin(); - decl_set::iterator end = m_original_preds.end(); - for(; it!=end; ++it) { + func_decl_set const& predicates = m_context.get_predicates(); + decl_set::iterator it = predicates.begin(); + decl_set::iterator end = predicates.end(); + for (; it!=end; ++it) { func_decl * orig_decl = *it; func_decl * e_decl = get_e_decl(orig_decl); - if(m_context.is_output_predicate(orig_decl)) { - m_context.set_output_predicate(e_decl); - } - - if(!rmgr.try_get_relation(orig_decl)) { - //there are no facts for this predicate + if (!rmgr.try_get_relation(orig_decl) && + !src.contains(orig_decl)) { + // there are no facts or rules for this predicate continue; } + dst.inherit_predicate(src, orig_decl, e_decl); + relation_base & orig_rel = rmgr.get_relation(orig_decl); relation_base & e_rel = rmgr.get_relation(e_decl); SASSERT(e_rel.empty()); //the e_rel should be a new relation - - if(m_relation_level) { + + if (m_relation_level) { translate_rel_level_relation(rmgr, orig_rel, e_rel); } else { @@ -869,18 +860,19 @@ namespace datalog { SASSERT(union_fun); (*union_fun)(e_rel, *aux_extended_rel); } - } } rule_set * mk_explanations::operator()(rule_set const & source) { - if(source.get_num_rules()==0) { + if (source.empty()) { + return 0; + } + if (!m_context.generate_explanations()) { return 0; } - m_context.collect_predicates(m_original_preds); rule_set * res = alloc(rule_set, m_context); - transform_facts(m_context.get_rel_context().get_rmanager()); + transform_facts(m_context.get_rel_context().get_rmanager(), source, *res); transform_rules(source, *res); return res; } diff --git a/src/muz_qe/dl_mk_explanations.h b/src/muz_qe/dl_mk_explanations.h index 4e7e23e98..9e4d705c3 100644 --- a/src/muz_qe/dl_mk_explanations.h +++ b/src/muz_qe/dl_mk_explanations.h @@ -32,19 +32,13 @@ namespace datalog { typedef obj_map decl_map; - ast_manager & m_manager; - context & m_context; + ast_manager & m_manager; + context & m_context; dl_decl_util & m_decl_util; - - bool m_relation_level; - - decl_set m_original_preds; - + bool m_relation_level; ast_ref_vector m_pinned; - explanation_relation_plugin * m_er_plugin; - - sort * m_e_sort; + sort * m_e_sort; scoped_rel m_e_fact_relation; decl_map m_e_decl_map; @@ -62,15 +56,15 @@ namespace datalog { void assign_rel_level_kind(func_decl * e_decl, func_decl * orig); void translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel); - void transform_rules(const rule_set & orig, rule_set & tgt); + void transform_rules(const rule_set & src, rule_set & dst); - void transform_facts(relation_manager & rmgr); + void transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst); public: /** If relation_level is true, the explanation will not be stored for each fact, but we will rather store history of the whole relation. */ - mk_explanations(context & ctx, bool relation_level); + mk_explanations(context & ctx); /** \brief Return explanation predicate that corresponds to \c orig_decl. diff --git a/src/muz_qe/dl_mk_extract_quantifiers.cpp b/src/muz_qe/dl_mk_extract_quantifiers.cpp deleted file mode 100644 index 76e329d79..000000000 --- a/src/muz_qe/dl_mk_extract_quantifiers.cpp +++ /dev/null @@ -1,379 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_extract_quantifiers.cpp - -Abstract: - - Remove universal quantifiers over interpreted predicates in the body. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-11-21 - -Revision History: - ---*/ - -#include"dl_mk_extract_quantifiers.h" -#include"ast_pp.h" -#include"dl_bmc_engine.h" -#include"smt_quantifier.h" -#include"smt_context.h" -#include"for_each_expr.h" -#include "expr_abstract.h" - - -namespace datalog { - - - mk_extract_quantifiers::mk_extract_quantifiers(context & ctx) : - rule_transformer::plugin(101, false), - m_ctx(ctx), - m(ctx.get_manager()), - rm(ctx.get_rule_manager()), - m_query_pred(m) - {} - - mk_extract_quantifiers::~mk_extract_quantifiers() { - reset(); - } - - void mk_extract_quantifiers::set_query(func_decl* q) { - m_query_pred = q; - } - - void mk_extract_quantifiers::ensure_predicate(expr* e, unsigned& max_var, app_ref_vector& tail) { - SASSERT(is_app(e)); - SASSERT(to_app(e)->get_decl()->get_family_id() == null_family_id); - app* a = to_app(e); - expr_ref_vector args(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - expr* arg = a->get_arg(i); - if (is_var(arg) || m.is_value(arg)) { - args.push_back(arg); - } - else { - expr_ref new_var(m); - new_var = m.mk_var(++max_var, m.get_sort(arg)); - args.push_back(new_var); - tail.push_back(m.mk_eq(new_var, arg)); - } - } - tail.push_back(m.mk_app(a->get_decl(), args.size(), args.c_ptr())); - } - - class mk_extract_quantifiers::collect_insts { - ast_manager& m; - ptr_vector m_binding; - vector m_bindings; - ptr_vector m_quantifiers; - public: - collect_insts(ast_manager& m): m(m) { } - - void operator()(expr* n) { - expr* not_q_or_i, *e1, *e2, *e3; - if (m.is_quant_inst(n, not_q_or_i, m_binding)) { - VERIFY(m.is_or(not_q_or_i, e1, e2)); - VERIFY(m.is_not(e1, e3)); - SASSERT(is_quantifier(e3)); - m_quantifiers.push_back(to_quantifier(e3)); - m_bindings.push_back(expr_ref_vector(m,m_binding.size(), m_binding.c_ptr())); - m_binding.reset(); - } - else if ((m.is_rewrite(n, e1, e2) || - (m.is_rewrite_star(n) && - (e3 = to_app(n)->get_arg(to_app(n)->get_num_args()-1), - e1 = to_app(e3)->get_arg(0), - e2 = to_app(e3)->get_arg(1), - true))) && - is_quantifier(e1) && m.is_false(e2)) { - quantifier* q = to_quantifier(e1); - m_quantifiers.push_back(q); - m_bindings.push_back(expr_ref_vector(m)); - expr_ref_vector& b = m_bindings.back(); - for (unsigned i = 0; i < q->get_num_decls(); ++i) { - b.push_back(m.mk_fresh_const("V", q->get_decl_sort(i))); - } - } - } - - void reset() { - m_quantifiers.reset(); - m_bindings.reset(); - } - - unsigned size() const { return m_quantifiers.size(); } - ptr_vector const& quantifiers() const { return m_quantifiers; } - vector const& bindings() const { return m_bindings; } - }; - - - /* - * forall y . P1(x,y) & - * forall y . P2(x,y) & - * Body[x] & - * ~H[x] - * forall y . y != binding1 => ~ P1(x,y) - * forall y . y != binding2 => ~ P2(x,y) - */ - void mk_extract_quantifiers::extract(rule& r, rule_set& new_rules) { - unsigned utsz = r.get_uninterpreted_tail_size(); - expr_ref_vector conjs(m); - quantifier_ref_vector qs(m); - for (unsigned i = utsz; i < r.get_tail_size(); ++i) { - conjs.push_back(r.get_tail(i)); - } - datalog::flatten_and(conjs); - for (unsigned j = 0; j < conjs.size(); ++j) { - expr* e = conjs[j].get(); - quantifier* q; - if (rule_manager::is_forall(m, e, q)) { - qs.push_back(q); - conjs[j] = conjs.back(); - conjs.pop_back(); - --j; - } - } - if (qs.empty()) { - new_rules.add_rule(&r); - } - else { - expr_ref fml(m); - expr_ref_vector bindings(m); - obj_map insts; - for (unsigned i = 0; i < qs.size(); ++i) { - insts.insert(qs[i].get(), alloc(expr_ref_vector, m)); - } - - unsigned max_inst = 10; // TODO configuration. - - for (unsigned i = 0; i < max_inst; ++i) { - app_ref_vector sub(m); - rule2formula(r, insts, fml, sub); - bool new_binding = find_instantiations_proof_based(fml, sub, insts, bindings); - if (!new_binding) { - break; - } - } - - expr_ref_vector fmls(m); - for (unsigned i = 0; i < utsz; ++i) { - fmls.push_back(r.get_tail(i)); - } - fmls.append(bindings); - fmls.append(conjs); - fml = m.mk_implies(m.mk_and(fmls.size(), fmls.c_ptr()), r.get_head()); - TRACE("dl", tout << "new rule\n" << mk_pp(fml, m) << "\n";); - rule_ref_vector rules(rm); - proof_ref pr(m); - if (m_ctx.generate_proof_trace()) { - scoped_proof _scp(m); - expr_ref fml1(m); - r.to_formula(fml1); - pr = m.mk_rewrite(fml1, fml); - pr = m.mk_modus_ponens(r.get_proof(), pr); - } - rm.mk_rule(fml, pr, rules, r.name()); - for (unsigned i = 0; i < rules.size(); ++i) { - new_rules.add_rule(rules[i].get()); - m_quantifiers.insert(rules[i].get(), alloc(quantifier_ref_vector, qs)); - } - obj_map::iterator it = insts.begin(), end = insts.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - } - } - - void mk_extract_quantifiers::rule2formula( - rule& r, - obj_map const& insts, - expr_ref& fml, - app_ref_vector& sub) - { - expr_ref body(m); - expr_ref_vector fmls(m); - ptr_vector sorts; - var_subst vs(m, false); - obj_map::iterator it = insts.begin(), end = insts.end(); - for (; it != end; ++it) { - quantifier* q = it->m_key; - expr_ref_vector& eqs = *it->m_value; - expr_ref_vector disj(m); - disj.append(eqs); - disj.push_back(m.mk_not(q->get_expr())); - body = m.mk_or(disj.size(), disj.c_ptr()); - fml = m.update_quantifier(q, body); - fmls.push_back(fml); - } - fml = m.mk_or(fmls.size(), fmls.c_ptr()); - fmls.reset(); - fmls.push_back(fml); - for (unsigned i = 0; i < r.get_tail_size(); ++i) { - SASSERT(!r.is_neg_tail(i)); - fmls.push_back(r.get_tail(i)); - } - fmls.push_back(m.mk_not(r.get_head())); - fml = m.mk_and(fmls.size(), fmls.c_ptr()); - - get_free_vars(fml, sorts); - for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } - sub.push_back(m.mk_const(symbol(i), sorts[i])); - } - vs(fml, sub.size(), (expr*const*)sub.c_ptr(), fml); - } - - bool mk_extract_quantifiers::find_instantiations_proof_based( - expr* fml, - app_ref_vector const& var_inst, - obj_map& insts, - expr_ref_vector& bindings) - { - datalog::scoped_proof _scp(m); - smt_params fparams; - fparams.m_mbqi = true; // false - fparams.m_soft_timeout = 1000; - smt::kernel solver(m, fparams); - solver.assert_expr(fml); - IF_VERBOSE(1, verbose_stream() << "check\n";); - lbool result = solver.check(); - IF_VERBOSE(1, verbose_stream() << "checked\n";); - TRACE("dl", tout << result << "\n";); - if (result != l_false) { - return false; - } - - map qid_map; - quantifier* q; - - obj_map::iterator it = insts.begin(), end = insts.end(); - for (; it != end; ++it) { - q = it->m_key; - qid_map.insert(q->get_qid(), q); - } - - proof* p = solver.get_proof(); - TRACE("dl", tout << mk_pp(p, m) << "\n";); - collect_insts collector(m); - for_each_expr(collector, p); - ptr_vector const& quants = collector.quantifiers(); - - for (unsigned i = 0; i < collector.size(); ++i) { - symbol qid = quants[i]->get_qid(); - if (!qid_map.find(qid, q)) { - TRACE("dl", tout << "Could not find quantifier " << mk_pp(quants[i], m) << "\n";); - continue; - } - expr_ref_vector const& binding = collector.bindings()[i]; - - TRACE("dl", tout << "Instantiating:\n" << mk_pp(quants[i], m) << "\n"; - for (unsigned j = 0; j < binding.size(); ++j) { - tout << mk_pp(binding[j], m) << " "; - } - tout << "\n";); - - expr_ref_vector instantiation(m); - for (unsigned j = 0; j < binding.size(); ++j) { - instantiation.push_back(binding[j]); - } - add_binding(var_inst, bindings, q, instantiation, insts); - } - - return collector.size() > 0; - } - - void mk_extract_quantifiers::add_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts) - { - if (instantiation.size() == q->get_num_decls()) { - // Full binding. - apply_binding(var_inst, bindings, q, instantiation, insts); - } - } - - void mk_extract_quantifiers::apply_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts) - { - datalog::scoped_no_proof _scp(m); - expr_ref e(m); - expr_ref_vector eqs(m); - var_subst vs(m, false); - inv_var_shifter invsh(m); - vs(q->get_expr(), instantiation.size(), instantiation.c_ptr(), e); - invsh(e, q->get_num_decls(), e); - expr_ref_vector inst(m); - inst.append(var_inst.size(), (expr*const*)var_inst.c_ptr()); - inst.reverse(); - expr_abstract(m, 0, inst.size(), inst.c_ptr(), e, e); - bindings.push_back(e); - for (unsigned i = 0; i < instantiation.size(); ++i) { - e = instantiation[i]; - e = m.mk_eq(m.mk_var(i, q->get_decl_sort(i)), e); - eqs.push_back(e); - } - e = m.mk_and(eqs.size(), eqs.c_ptr()); - insts.find(q)->push_back(e); - - TRACE("dl", tout << mk_pp(q, m) << "\n"; - tout << "instantiation: "; - for (unsigned i = 0; i < instantiation.size(); ++i) { - tout << mk_pp(instantiation[i], m) << " "; - } - tout << "\n"; - tout << "inst: "; - for (unsigned i = 0; i < var_inst.size(); ++i) { - tout << mk_pp(var_inst[i], m) << " "; - } - tout << "\n"; - tout << mk_pp(bindings.back(), m) << "\n"; - tout << "eqs: " << mk_pp(e, m) << "\n"; - ); - } - - - void mk_extract_quantifiers::reset() { - obj_map::iterator it = m_quantifiers.begin(), - end = m_quantifiers.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - m_has_quantifiers = false; - m_quantifiers.reset(); - } - - rule_set * mk_extract_quantifiers::operator()(rule_set const & source) { - reset(); - rule_set::iterator it = source.begin(), end = source.end(); - for (; !m_has_quantifiers && it != end; ++it) { - m_has_quantifiers = (*it)->has_quantifiers(); - } - if (!m_has_quantifiers) { - return 0; - } - - rule_set* rules = alloc(rule_set, m_ctx); - it = source.begin(); - for (; it != end; ++it) { - extract(**it, *rules); - } - - return rules; - } - -}; - - diff --git a/src/muz_qe/dl_mk_extract_quantifiers.h b/src/muz_qe/dl_mk_extract_quantifiers.h deleted file mode 100644 index 27b13cd71..000000000 --- a/src/muz_qe/dl_mk_extract_quantifiers.h +++ /dev/null @@ -1,99 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_extract_quantifiers.h - -Abstract: - - Replace universal quantifiers over interpreted predicates in the body - by instantiations mined using bounded model checking search. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-11-21 - -Revision History: - ---*/ -#ifndef _DL_MK_EXTRACT_QUANTIFIERS_H_ -#define _DL_MK_EXTRACT_QUANTIFIERS_H_ - -#include"dl_context.h" -#include"dl_rule_set.h" -#include"dl_rule_transformer.h" -#include"obj_pair_hashtable.h" - -namespace datalog { - - /** - \brief Extract universal quantifiers from rules. - */ - class mk_extract_quantifiers : public rule_transformer::plugin { - - class collect_insts; - - context& m_ctx; - ast_manager& m; - rule_manager& rm; - func_decl_ref m_query_pred; - bool m_has_quantifiers; - obj_map m_quantifiers; - - void reset(); - - void extract(rule& r, rule_set& new_rules); - - void rule2formula( - rule& r, - obj_map const& insts, - expr_ref& fml, - app_ref_vector& sub); - - - void add_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts); - - void apply_binding( - app_ref_vector const& var_inst, - expr_ref_vector& bindings, - quantifier* q, - expr_ref_vector const& instantiation, - obj_map& insts); - - - public: - /** - \brief Create rule transformer that extracts universal quantifiers (over recursive predicates). - */ - mk_extract_quantifiers(context & ctx); - - virtual ~mk_extract_quantifiers(); - - void set_query(func_decl* q); - - rule_set * operator()(rule_set const & source); - - bool has_quantifiers() { return m_has_quantifiers; } - - obj_map& quantifiers() { return m_quantifiers; } - - void ensure_predicate(expr* e, unsigned& max_var, app_ref_vector& tail); - - bool find_instantiations_proof_based( - expr* fml, - app_ref_vector const& var_inst, - obj_map& insts, - expr_ref_vector& bindings); - - }; - -}; - -#endif /* _DL_MK_EXTRACT_QUANTIFIERS_H_ */ - diff --git a/src/muz_qe/dl_mk_filter_rules.cpp b/src/muz_qe/dl_mk_filter_rules.cpp index 62abd78c4..3f5ee0394 100644 --- a/src/muz_qe/dl_mk_filter_rules.cpp +++ b/src/muz_qe/dl_mk_filter_rules.cpp @@ -31,6 +31,7 @@ namespace datalog { m_result(0), m_pinned(m_manager) { } + mk_filter_rules::~mk_filter_rules() { ptr_vector to_dealloc; filter_cache::iterator it = m_tail2filter.begin(); @@ -50,7 +51,7 @@ namespace datalog { \brief Return true if \c pred is a cadidate for a "filter" rule. */ bool mk_filter_rules::is_candidate(app * pred) { - if (!m_context.get_rule_manager().is_predicate(pred)) { + if (!m_context.is_predicate(pred)) { TRACE("mk_filter_rules", tout << mk_pp(pred, m_manager) << "\nis not a candidate because it is interpreted.\n";); return false; } @@ -157,13 +158,13 @@ namespace datalog { m_modified = false; unsigned num_rules = source.get_num_rules(); for (unsigned i = 0; i < num_rules; i++) { - rule * r = source.get_rule(i); - process(r); + process(source.get_rule(i)); } if(!m_modified) { dealloc(m_result); return static_cast(0); } + m_result->inherit_predicates(source); return m_result; } diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp index 0d468f9ef..51bbd52df 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp @@ -587,6 +587,7 @@ namespace datalog { dealloc(res); res = 0; } + res->inherit_predicates(source); return res; } diff --git a/src/muz_qe/dl_mk_karr_invariants.cpp b/src/muz_qe/dl_mk_karr_invariants.cpp index c4a6a3cdb..c8e350eeb 100644 --- a/src/muz_qe/dl_mk_karr_invariants.cpp +++ b/src/muz_qe/dl_mk_karr_invariants.cpp @@ -199,15 +199,15 @@ namespace datalog { return 0; } } + m_inner_ctx.reset(); rel_context& rctx = m_inner_ctx.get_rel_context(); ptr_vector heads; - m_inner_ctx.ensure_opened(); - it = source.begin(); - for (; it != end; ++it) { - rule_ref r(*it, m_inner_ctx.get_rule_manager()); - m_inner_ctx.add_rule(r); - m_inner_ctx.register_predicate(r->get_decl(), false); + func_decl_set const& predicates = m_ctx.get_predicates(); + for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) { + m_inner_ctx.register_predicate(*fit, false); } + m_inner_ctx.ensure_opened(); + m_inner_ctx.replace_rules(source); m_inner_ctx.close(); rule_set::decl2rules::iterator dit = source.begin_grouped_rules(); rule_set::decl2rules::iterator dend = source.end_grouped_rules(); @@ -237,6 +237,7 @@ namespace datalog { m_ctx.add_model_converter(kmc); } TRACE("dl", rules->display(tout);); + rules->inherit_predicates(source); return rules; } diff --git a/src/muz_qe/dl_mk_loop_counter.cpp b/src/muz_qe/dl_mk_loop_counter.cpp index 6b95671f2..144826639 100644 --- a/src/muz_qe/dl_mk_loop_counter.cpp +++ b/src/muz_qe/dl_mk_loop_counter.cpp @@ -25,6 +25,7 @@ namespace datalog { mk_loop_counter::mk_loop_counter(context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), + m_ctx(ctx), a(m), m_refs(m) { } @@ -72,14 +73,14 @@ namespace datalog { for (unsigned j = 0; j < utsz; ++j, ++cnt) { tail.push_back(add_arg(r.get_tail(j), cnt)); neg.push_back(r.is_neg_tail(j)); - ctx.register_predicate(tail.back()->get_decl(), false); + m_ctx.register_predicate(tail.back()->get_decl(), false); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } head = add_arg(r.get_head(), cnt); - ctx.register_predicate(head->get_decl(), false); + m_ctx.register_predicate(head->get_decl(), false); // set the loop counter to be an increment of the previous bool found = false; unsigned last = head->get_num_args()-1; @@ -107,6 +108,8 @@ namespace datalog { // model converter: remove references to extra argument. // proof converter: remove references to extra argument as well. + result->inherit_predicates(source); + return result; } diff --git a/src/muz_qe/dl_mk_loop_counter.h b/src/muz_qe/dl_mk_loop_counter.h index 6601f837f..fc4d7e32f 100644 --- a/src/muz_qe/dl_mk_loop_counter.h +++ b/src/muz_qe/dl_mk_loop_counter.h @@ -26,6 +26,7 @@ namespace datalog { class mk_loop_counter : public rule_transformer::plugin { ast_manager& m; + context& m_ctx; arith_util a; func_decl_ref_vector m_refs; obj_map m_new2old; diff --git a/src/muz_qe/dl_mk_magic_sets.cpp b/src/muz_qe/dl_mk_magic_sets.cpp index 6885edc4e..27846410c 100644 --- a/src/muz_qe/dl_mk_magic_sets.cpp +++ b/src/muz_qe/dl_mk_magic_sets.cpp @@ -24,13 +24,12 @@ Revision History: namespace datalog { - mk_magic_sets::mk_magic_sets(context & ctx, rule * goal_rule) : + mk_magic_sets::mk_magic_sets(context & ctx, func_decl* goal) : plugin(10000, true), m_context(ctx), - m_manager(ctx.get_manager()), - m_rules(ctx.get_rule_manager()), - m_pinned(m_manager), - m_goal_rule(goal_rule, ctx.get_rule_manager()) { + m(ctx.get_manager()), + m_pinned(m), + m_goal(goal, m) { } void mk_magic_sets::reset() { @@ -39,14 +38,13 @@ namespace datalog { m_adorned_preds.reset(); m_adornments.reset(); m_magic_preds.reset(); - m_rules.reset(); m_pinned.reset(); } void mk_magic_sets::adornment::populate(app * lit, const var_idx_set & bound_vars) { SASSERT(empty()); unsigned arity = lit->get_num_args(); - for(unsigned i=0; iget_arg(i); bool bound = !is_var(arg) || bound_vars.contains(to_var(arg)->get_idx()); push_back(bound ? AD_BOUND : AD_FREE); @@ -57,17 +55,8 @@ namespace datalog { std::string res; const_iterator eit = begin(); const_iterator eend = end(); - for(; eit!=eend; ++eit) { - switch(*eit) { - case AD_BOUND: - res+='b'; - break; - case AD_FREE: - res+='f'; - break; - default: - UNREACHABLE(); - } + for (; eit != eend; ++eit) { + res += (*eit == AD_BOUND)?'b':'f'; } return res; } @@ -75,14 +64,13 @@ namespace datalog { unsigned get_bound_arg_count(app * lit, const var_idx_set & bound_vars) { unsigned res = 0; unsigned n = lit->get_num_args(); - for(unsigned i=0; iget_arg(i); - if(is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { - continue; + if (!is_var(arg) || bound_vars.contains(to_var(arg)->get_idx())) { + SASSERT(is_var(arg) || is_app(arg)); + SASSERT(!is_app(arg) || to_app(arg)->get_num_args()==0); + res++; } - SASSERT(is_var(arg) || is_app(arg)); - SASSERT(!is_app(arg) || to_app(arg)->get_num_args()==0); - res++; } return res; } @@ -91,10 +79,10 @@ namespace datalog { func_decl * pred = lit->get_decl(); float res = 1; unsigned n = lit->get_num_args(); - for(unsigned i=0; iget_arg(i); - if(is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { - res*=m_context.get_sort_size_estimate(pred->get_domain(i)); + if (is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { + res *= m_context.get_sort_size_estimate(pred->get_domain(i)); } //res-=1; } @@ -111,22 +99,22 @@ namespace datalog { float best_cost; int candidate_index = -1; unsigned n = cont.size(); - for(unsigned i=0; iget_tail(cont[i]); unsigned bound_cnt = get_bound_arg_count(lit, bound_vars); - if(bound_cnt==0) { + if (bound_cnt==0) { continue; } float cost = get_unbound_cost(lit, bound_vars); - if(candidate_index==-1 || cost(n-1)) { + if (candidate_index != static_cast(n-1)) { std::swap(cont[candidate_index], cont[n-1]); } unsigned res = cont.back(); @@ -137,12 +125,12 @@ namespace datalog { app * mk_magic_sets::adorn_literal(app * lit, const var_idx_set & bound_vars) { SASSERT(!m_extentional.contains(lit->get_decl())); func_decl * old_pred = lit->get_decl(); - SASSERT(m_manager.is_bool(old_pred->get_range())); + SASSERT(m.is_bool(old_pred->get_range())); adornment_desc adn(old_pred); adn.m_adornment.populate(lit, bound_vars); adornment_map::entry * e = m_adorned_preds.insert_if_not_there2(adn, 0); func_decl * new_pred = e->get_data().m_value; - if(new_pred==0) { + if (new_pred==0) { std::string suffix = "ad_"+adn.m_adornment.to_string(); new_pred = m_context.mk_fresh_head_predicate( old_pred->get_name(), symbol(suffix.c_str()), @@ -152,34 +140,34 @@ namespace datalog { m_todo.push_back(adn); m_adornments.insert(new_pred, adn.m_adornment); } - app * res = m_manager.mk_app(new_pred, lit->get_args()); + app * res = m.mk_app(new_pred, lit->get_args()); m_pinned.push_back(res); return res; } app * mk_magic_sets::create_magic_literal(app * l) { func_decl * l_pred = l->get_decl(); - SASSERT(m_manager.is_bool(l_pred->get_range())); + SASSERT(m.is_bool(l_pred->get_range())); pred_adornment_map::obj_map_entry * ae = m_adornments.find_core(l_pred); SASSERT(ae); const adornment & adn = ae->get_data().m_value; unsigned l_arity = l->get_num_args(); ptr_vector bound_args; - for(unsigned i=0; iget_arg(i)); } } pred2pred::obj_map_entry * e = m_magic_preds.insert_if_not_there2(l_pred, 0); func_decl * mag_pred = e->get_data().m_value; - if(mag_pred==0) { + if (mag_pred==0) { unsigned mag_arity = bound_args.size(); ptr_vector mag_domain; - for(unsigned i=0; iget_domain(i)); } } @@ -190,12 +178,12 @@ namespace datalog { e->get_data().m_value = mag_pred; } - app * res = m_manager.mk_app(mag_pred, bound_args.c_ptr()); + app * res = m.mk_app(mag_pred, bound_args.c_ptr()); m_pinned.push_back(res); return res; } - void mk_magic_sets::create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated) { + void mk_magic_sets::create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated, rule_set& result) { //TODO: maybe include relevant interpreted predicates from the original rule ptr_vector new_tail; svector negations; @@ -204,26 +192,26 @@ namespace datalog { negations.push_back(false); negations.append(tail_cnt, negated); - for(unsigned i=0; iget_decl())) { + for (unsigned i=0; iget_decl())) { continue; } app * mag_head = create_magic_literal(tail[i]); rule * r = m_context.get_rule_manager().mk(mag_head, i+1, new_tail.c_ptr(), negations.c_ptr()); TRACE("dl", r->display(m_context,tout); ); - m_rules.push_back(r); + result.add_rule(r); } } - void mk_magic_sets::transform_rule(const adornment & head_adornment, rule * r) { + void mk_magic_sets::transform_rule(const adornment & head_adornment, rule * r, rule_set& result) { app * head = r->get_head(); unsigned head_len = head->get_num_args(); SASSERT(head_len==head_adornment.size()); var_idx_set bound_vars; - for(unsigned i=0; iget_arg(i); - if(head_adornment[i]==AD_BOUND && is_var(arg)) { + if (head_adornment[i]==AD_BOUND && is_var(arg)) { bound_vars.insert(to_var(arg)->get_idx()); } } @@ -231,9 +219,9 @@ namespace datalog { unsigned processed_tail_len = r->get_uninterpreted_tail_size(); unsigned_vector exten_tails; unsigned_vector inten_tails; - for(unsigned i=0; iget_tail(i); - if(m_extentional.contains(t->get_decl())) { + if (m_extentional.contains(t->get_decl())) { exten_tails.push_back(i); } else { @@ -243,17 +231,17 @@ namespace datalog { ptr_vector new_tail; svector negations; - while(new_tail.size()!=processed_tail_len) { + while (new_tail.size()!=processed_tail_len) { bool intentional = false; int curr_index = pop_bound(exten_tails, r, bound_vars); - if(curr_index==-1) { + if (curr_index==-1) { curr_index = pop_bound(inten_tails, r,bound_vars); - if(curr_index!=-1) { + if (curr_index!=-1) { intentional = true; } } - if(curr_index==-1) { - if(!exten_tails.empty()) { + if (curr_index==-1) { + if (!exten_tails.empty()) { curr_index = exten_tails.back(); exten_tails.pop_back(); } @@ -266,24 +254,24 @@ namespace datalog { } SASSERT(curr_index!=-1); app * curr = r->get_tail(curr_index); - if(intentional) { + if (intentional) { curr = adorn_literal(curr, bound_vars); } new_tail.push_back(curr); negations.push_back(r->is_neg_tail(curr_index)); - collect_vars(m_manager, curr, bound_vars); + collect_vars(m, curr, bound_vars); } func_decl * new_head_pred; VERIFY( m_adorned_preds.find(adornment_desc(head->get_decl(), head_adornment), new_head_pred) ); - app * new_head = m_manager.mk_app(new_head_pred, head->get_args()); + app * new_head = m.mk_app(new_head_pred, head->get_args()); SASSERT(new_tail.size()==r->get_uninterpreted_tail_size()); - create_magic_rules(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr()); + create_magic_rules(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr(), result); unsigned tail_len = r->get_tail_size(); - for(unsigned i=processed_tail_len; iget_tail(i)); negations.push_back(r->is_neg_tail(i)); } @@ -292,43 +280,49 @@ namespace datalog { negations.push_back(false); rule * nr = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr()); - m_rules.push_back(nr); + result.add_rule(nr); nr->set_accounting_parent_object(m_context, r); } - void mk_magic_sets::create_transfer_rule(const adornment_desc & d) { - func_decl * adn_pred; - TRUSTME( m_adorned_preds.find(d, adn_pred) ); + void mk_magic_sets::create_transfer_rule(const adornment_desc & d, rule_set& result) { + func_decl * adn_pred = m_adorned_preds.find(d); unsigned arity = adn_pred->get_arity(); - SASSERT(arity==d.m_pred->get_arity()); + SASSERT(arity == d.m_pred->get_arity()); ptr_vector args; - for(unsigned i=0; iget_domain(i))); + for (unsigned i=0; iget_domain(i))); } - app * lit = m_manager.mk_app(d.m_pred, args.c_ptr()); - app * adn_lit = m_manager.mk_app(adn_pred, args.c_ptr()); + app * lit = m.mk_app(d.m_pred, args.c_ptr()); + app * adn_lit = m.mk_app(adn_pred, args.c_ptr()); app * mag_lit = create_magic_literal(adn_lit); app * tail[] = {lit, mag_lit}; rule * r = m_context.get_rule_manager().mk(adn_lit, 2, tail, 0); - m_rules.push_back(r); + result.add_rule(r); } rule_set * mk_magic_sets::operator()(rule_set const & source) { - SASSERT(!m_context.get_model_converter()); + + if (!m_context.magic_sets_for_queries()) { + return 0; + } + + app * goal_head = source.get_predicate_rules(m_goal)[0]->get_head(); + unsigned init_rule_cnt = source.get_num_rules(); { func_decl_set intentional; - for(unsigned i=0; iget_head()->get_decl()); + for (unsigned i=0; iget_decl(); + intentional.insert(pred); } //now we iterate through all predicates and collect the set of extentional ones const rule_dependencies * deps; rule_dependencies computed_deps(m_context); - if(source.is_closed()) { + if (source.is_closed()) { deps = &source.get_dependencies(); } else { @@ -337,9 +331,9 @@ namespace datalog { } rule_dependencies::iterator it = deps->begin(); rule_dependencies::iterator end = deps->end(); - for(; it!=end; ++it) { + for (; it!=end; ++it) { func_decl * pred = it->m_key; - if(intentional.contains(pred)) { + if (intentional.contains(pred)) { continue; } SASSERT(it->m_value->empty());//extentional predicates have no dependency @@ -347,48 +341,39 @@ namespace datalog { } } - SASSERT(m_rules.empty()); - - app * goal_head = m_goal_rule->get_head(); //adornment goal_adn; //goal_adn.populate(goal_head, ); var_idx_set empty_var_idx_set; adorn_literal(goal_head, empty_var_idx_set); - while(!m_todo.empty()) { + rule_set * result = alloc(rule_set, m_context); + result->inherit_predicates(source); + + while (!m_todo.empty()) { adornment_desc task = m_todo.back(); m_todo.pop_back(); const rule_vector & pred_rules = source.get_predicate_rules(task.m_pred); rule_vector::const_iterator it = pred_rules.begin(); rule_vector::const_iterator end = pred_rules.end(); - for(; it!=end; ++it) { + for (; it != end; ++it) { rule * r = *it; - transform_rule(task.m_adornment, r); + transform_rule(task.m_adornment, r, *result); } - if(!m_context.get_rel_context().get_relation(task.m_pred).empty()) { + if (!m_context.get_rel_context().get_relation(task.m_pred).empty()) { //we need a rule to copy facts that are already in a relation into the adorned //relation (since out intentional predicates can have facts, not only rules) - create_transfer_rule(task); + create_transfer_rule(task, *result); } } app * adn_goal_head = adorn_literal(goal_head, empty_var_idx_set); app * mag_goal_head = create_magic_literal(adn_goal_head); SASSERT(mag_goal_head->is_ground()); - //SASSERT(is_fact(m_manager, mag_goal_head)); - //m_context.add_fact(mag_goal_head); rule * mag_goal_rule = m_context.get_rule_manager().mk(mag_goal_head, 0, 0, 0); - m_rules.push_back(mag_goal_rule); + result->add_rule(mag_goal_rule); rule * back_to_goal_rule = m_context.get_rule_manager().mk(goal_head, 1, &adn_goal_head, 0); - m_rules.push_back(back_to_goal_rule); - - rule_set * result = static_cast(0); - result = alloc(rule_set, m_context); - unsigned fin_rule_cnt = m_rules.size(); - for(unsigned i=0; iadd_rule(m_rules.get(i)); - } + result->add_rule(back_to_goal_rule); return result; } }; diff --git a/src/muz_qe/dl_mk_magic_sets.h b/src/muz_qe/dl_mk_magic_sets.h index 2dc91c7e8..507f6c2bf 100644 --- a/src/muz_qe/dl_mk_magic_sets.h +++ b/src/muz_qe/dl_mk_magic_sets.h @@ -88,21 +88,20 @@ namespace datalog { typedef obj_map pred_adornment_map; typedef obj_map pred2pred; - context & m_context; - ast_manager & m_manager; - rule_ref_vector m_rules; - ast_ref_vector m_pinned; - rule_ref m_goal_rule; + context & m_context; + ast_manager & m; + ast_ref_vector m_pinned; /** \brief Predicates from the original set that appear in a head of a rule */ - func_decl_set m_extentional; + func_decl_set m_extentional; //adornment_set m_processed; vector m_todo; - adornment_map m_adorned_preds; - pred_adornment_map m_adornments; - pred2pred m_magic_preds; + adornment_map m_adorned_preds; + pred_adornment_map m_adornments; + pred2pred m_magic_preds; + func_decl_ref m_goal; void reset(); @@ -110,16 +109,16 @@ namespace datalog { int pop_bound(unsigned_vector & cont, rule * r, const var_idx_set & bound_vars); app * create_magic_literal(app * l); - void create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated); + void create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated, rule_set& result); app * adorn_literal(app * lit, const var_idx_set & bound_vars); - void transform_rule(const adornment & head_adornment, rule * r); - void create_transfer_rule(const adornment_desc & d); + void transform_rule(const adornment & head_adornment, rule * r, rule_set& result); + void create_transfer_rule(const adornment_desc & d, rule_set& result); public: /** \brief Create magic sets rule transformer for \c goal_rule. When applying the transformer, the \c goal_rule must be present in the \c rule_set that is being transformed. */ - mk_magic_sets(context & ctx, rule * goal_rule); + mk_magic_sets(context & ctx, func_decl* goal); rule_set * operator()(rule_set const & source); }; diff --git a/src/muz_qe/dl_mk_partial_equiv.cpp b/src/muz_qe/dl_mk_partial_equiv.cpp index 35f8ff8df..5ba1e71dd 100644 --- a/src/muz_qe/dl_mk_partial_equiv.cpp +++ b/src/muz_qe/dl_mk_partial_equiv.cpp @@ -23,7 +23,7 @@ Revision History: namespace datalog { bool mk_partial_equivalence_transformer::is_symmetry(rule const* r) { - func_decl* p = r->get_head()->get_decl(); + func_decl* p = r->get_decl(); return p->get_arity() == 2 && p->get_domain(0) == p->get_domain(1) && @@ -38,7 +38,7 @@ namespace datalog { bool mk_partial_equivalence_transformer::is_transitivity(rule const* r) { - func_decl* p = r->get_head()->get_decl(); + func_decl* p = r->get_decl(); if (p->get_arity() != 2 || p->get_domain(0) != p->get_domain(1) || r->get_tail_size() != 2 || @@ -144,6 +144,7 @@ namespace datalog { dealloc(res); return 0; } + res->inherit_predicates(source); return res; } diff --git a/src/muz_qe/dl_mk_quantifier_abstraction.cpp b/src/muz_qe/dl_mk_quantifier_abstraction.cpp index d4abb1ffc..68f5bc3a3 100644 --- a/src/muz_qe/dl_mk_quantifier_abstraction.cpp +++ b/src/muz_qe/dl_mk_quantifier_abstraction.cpp @@ -147,9 +147,10 @@ namespace datalog { mk_quantifier_abstraction::~mk_quantifier_abstraction() { } - func_decl* mk_quantifier_abstraction::declare_pred(func_decl* old_p) { + func_decl* mk_quantifier_abstraction::declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p) { - if (m_ctx.is_output_predicate(old_p)) { + if (rules.is_output_predicate(old_p)) { + dst.inherit_predicate(rules, old_p, old_p); return 0; } @@ -210,8 +211,8 @@ namespace datalog { return new_p; } - app_ref mk_quantifier_abstraction::mk_head(app* p, unsigned idx) { - func_decl* new_p = declare_pred(p->get_decl()); + app_ref mk_quantifier_abstraction::mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx) { + func_decl* new_p = declare_pred(rules, dst, p->get_decl()); if (!new_p) { return app_ref(p, m); } @@ -239,9 +240,9 @@ namespace datalog { return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m); } - app_ref mk_quantifier_abstraction::mk_tail(app* p) { + app_ref mk_quantifier_abstraction::mk_tail(rule_set const& rules, rule_set& dst, app* p) { func_decl* old_p = p->get_decl(); - func_decl* new_p = declare_pred(old_p); + func_decl* new_p = declare_pred(rules, dst, old_p); if (!new_p) { return app_ref(p, m); } @@ -332,18 +333,17 @@ namespace datalog { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j) { - tail.push_back(mk_tail(r.get_tail(j))); + tail.push_back(mk_tail(source, *result, r.get_tail(j))); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); } - head = mk_head(r.get_head(), cnt); + head = mk_head(source, *result, r.get_head(), cnt); fml = m.mk_implies(m.mk_and(tail.size(), tail.c_ptr()), head); rule_ref_vector added_rules(rm); proof_ref pr(m); - rm.mk_rule(fml, pr, added_rules); - result->add_rules(added_rules.size(), added_rules.c_ptr()); - TRACE("dl", added_rules.back()->display(m_ctx, tout);); + rm.mk_rule(fml, pr, *result); + TRACE("dl", result->last()->display(m_ctx, tout);); } // proof converter: proofs are not necessarily preserved using this transformation. diff --git a/src/muz_qe/dl_mk_quantifier_abstraction.h b/src/muz_qe/dl_mk_quantifier_abstraction.h index c7e6c6bb4..0e65c54f7 100644 --- a/src/muz_qe/dl_mk_quantifier_abstraction.h +++ b/src/muz_qe/dl_mk_quantifier_abstraction.h @@ -43,9 +43,9 @@ namespace datalog { obj_map m_old2new; qa_model_converter* m_mc; - func_decl* declare_pred(func_decl* old_p); - app_ref mk_head(app* p, unsigned idx); - app_ref mk_tail(app* p); + func_decl* declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p); + app_ref mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx); + app_ref mk_tail(rule_set const& rules, rule_set& dst, app* p); expr* mk_select(expr* a, unsigned num_args, expr* const* args); public: diff --git a/src/muz_qe/dl_mk_quantifier_instantiation.cpp b/src/muz_qe/dl_mk_quantifier_instantiation.cpp index 1d80d9b77..ddbddca01 100644 --- a/src/muz_qe/dl_mk_quantifier_instantiation.cpp +++ b/src/muz_qe/dl_mk_quantifier_instantiation.cpp @@ -232,20 +232,20 @@ namespace datalog { fml = m.mk_implies(fml, r.get_head()); TRACE("dl", r.display(m_ctx, tout); tout << mk_pp(fml, m) << "\n";); - rule_ref_vector added_rules(rm); + rule_set added_rules(m_ctx); proof_ref pr(m); rm.mk_rule(fml, pr, added_rules); if (r.get_proof()) { // use def-axiom to encode that new rule is a weakening of the original. proof* p1 = r.get_proof(); - for (unsigned i = 0; i < added_rules.size(); ++i) { - rule* r2 = added_rules[i].get(); + for (unsigned i = 0; i < added_rules.get_num_rules(); ++i) { + rule* r2 = added_rules.get_rule(i); r2->to_formula(fml); pr = m.mk_modus_ponens(m.mk_def_axiom(m.mk_implies(m.get_fact(p1), fml)), p1); r2->set_proof(m, pr); } } - rules.add_rules(added_rules.size(), added_rules.c_ptr()); + rules.add_rules(added_rules); } rule_set * mk_quantifier_instantiation::operator()(rule_set const & source) { @@ -286,7 +286,10 @@ namespace datalog { // model convertion: identity function. - if (!instantiated) { + if (instantiated) { + result->inherit_predicates(source); + } + else { dealloc(result); result = 0; } diff --git a/src/muz_qe/dl_mk_rule_inliner.cpp b/src/muz_qe/dl_mk_rule_inliner.cpp index 91cfbe3fc..c6ffa1378 100644 --- a/src/muz_qe/dl_mk_rule_inliner.cpp +++ b/src/muz_qe/dl_mk_rule_inliner.cpp @@ -230,10 +230,10 @@ namespace datalog { } } - bool mk_rule_inliner::inlining_allowed(func_decl * pred) + bool mk_rule_inliner::inlining_allowed(rule_set const& source, func_decl * pred) { if (//these three conditions are important for soundness - m_context.is_output_predicate(pred) || + source.is_output_predicate(pred) || m_preds_with_facts.contains(pred) || m_preds_with_neg_occurrence.contains(pred) || //this condition is used for breaking of cycles among inlined rules @@ -260,7 +260,7 @@ namespace datalog { unsigned rcnt = orig.get_num_rules(); for (unsigned i=0; iget_decl())) { + if (inlining_allowed(orig, r->get_decl())) { res->add_rule(r); } } @@ -324,7 +324,7 @@ namespace datalog { unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); - if (!inlining_allowed(tail_pred)) { + if (!inlining_allowed(orig, tail_pred)) { continue; } unsigned tail_pred_head_cnt = m_head_pred_ctr.get(tail_pred); @@ -359,7 +359,7 @@ namespace datalog { func_decl * head_pred = r->get_decl(); - if (inlining_allowed(head_pred)) { + if (inlining_allowed(orig, head_pred)) { //we have already processed inlined rules continue; } @@ -368,7 +368,7 @@ namespace datalog { unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); - if (!inlining_allowed(pred)) { + if (!inlining_allowed(orig, pred)) { continue; } if (m_head_pred_ctr.get(pred)<=1) { @@ -417,14 +417,14 @@ namespace datalog { const rule_vector& pred_rules = candidate_inlined_set->get_predicate_rules(pred); rule_vector::const_iterator iend = pred_rules.end(); for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) { - transform_rule(*iit, m_inlined_rules); + transform_rule(orig, *iit, m_inlined_rules); } } TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; ); } - bool mk_rule_inliner::transform_rule(rule * r0, rule_set& tgt) { + bool mk_rule_inliner::transform_rule(rule_set const& orig, rule * r0, rule_set& tgt) { bool modified = false; rule_ref_vector todo(m_rm); todo.push_back(r0); @@ -436,7 +436,7 @@ namespace datalog { unsigned i = 0; - for (; i < pt_len && !inlining_allowed(r->get_decl(i)); ++i) {}; + for (; i < pt_len && !inlining_allowed(orig, r->get_decl(i)); ++i) {}; SASSERT(!has_quantifier(*r.get())); @@ -478,12 +478,12 @@ namespace datalog { // this relation through inlining, // so we don't add its rules to the result - something_done |= !inlining_allowed(pred) && transform_rule(r, tgt); + something_done |= !inlining_allowed(orig, pred) && transform_rule(orig, r, tgt); } if (something_done && m_mc) { for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) { - if (inlining_allowed((*rit)->get_decl())) { + if (inlining_allowed(orig, (*rit)->get_decl())) { datalog::del_rule(m_mc, **rit); } } @@ -676,7 +676,7 @@ namespace datalog { m_head_index.insert(head); m_pinned.push_back(r); - if (m_context.is_output_predicate(headd) || + if (m_context.get_rules().is_output_predicate(headd) || m_preds_with_facts.contains(headd)) { can_remove.set(i, false); TRACE("dl", output_predicate(m_context, head, tout << "cannot remove: " << i << " "); tout << "\n";); @@ -692,7 +692,7 @@ namespace datalog { tl_sz == 1 && r->get_positive_tail_size() == 1 && !m_preds_with_facts.contains(r->get_decl(0)) - && !m_context.is_output_predicate(r->get_decl(0)); + && !m_context.get_rules().is_output_predicate(r->get_decl(0)); can_expand.set(i, can_exp); } @@ -883,6 +883,7 @@ namespace datalog { res = 0; } else { + res->inherit_predicates(source); m_context.add_model_converter(hsmc.get()); } diff --git a/src/muz_qe/dl_mk_rule_inliner.h b/src/muz_qe/dl_mk_rule_inliner.h index 5ef8db7eb..bec788ffe 100644 --- a/src/muz_qe/dl_mk_rule_inliner.h +++ b/src/muz_qe/dl_mk_rule_inliner.h @@ -129,7 +129,7 @@ namespace datalog { bool try_to_inline_rule(rule& tgt, rule& src, unsigned tail_index, rule_ref& res); - bool inlining_allowed(func_decl * pred); + bool inlining_allowed(rule_set const& orig, func_decl * pred); void count_pred_occurrences(rule_set const & orig); @@ -143,7 +143,7 @@ namespace datalog { bool forbid_multiple_multipliers(const rule_set & orig, rule_set const & proposed_inlined_rules); /** Return true if the rule was modified */ - bool transform_rule(rule * r, rule_set& tgt); + bool transform_rule(rule_set const& orig, rule * r, rule_set& tgt); /** Return true if some transformation was performed */ bool transform_rules(const rule_set & orig, rule_set & tgt); diff --git a/src/muz_qe/dl_mk_similarity_compressor.cpp b/src/muz_qe/dl_mk_similarity_compressor.cpp index 9868c82f6..0e101eb68 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.cpp +++ b/src/muz_qe/dl_mk_similarity_compressor.cpp @@ -24,15 +24,15 @@ Revision History: namespace datalog { mk_similarity_compressor::mk_similarity_compressor(context & ctx, unsigned threshold_count) : - plugin(5000), - m_context(ctx), - m_manager(ctx.get_manager()), - m_threshold_count(threshold_count), - m_result_rules(ctx.get_rule_manager()), - m_pinned(m_manager) { + plugin(5000), + m_context(ctx), + m_manager(ctx.get_manager()), + m_threshold_count(threshold_count), + m_result_rules(ctx.get_rule_manager()), + m_pinned(m_manager) { SASSERT(threshold_count>1); } - + void mk_similarity_compressor::reset() { m_rules.reset(); m_result_rules.reset(); @@ -43,7 +43,7 @@ namespace datalog { Allows to traverse head and positive tails in a single for loop starting from -1 */ app * get_by_tail_index(rule * r, int idx) { - if(idx==-1) { + if (idx == -1) { return r->get_head(); } SASSERT(idx(r->get_positive_tail_size())); @@ -59,15 +59,18 @@ namespace datalog { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; unsigned n = t1->get_num_args(); - for(unsigned i=0; iget_arg(i); expr * a2 = t2->get_arg(i); - res = aux_compare(is_var(a1), is_var(a2)); - if(res!=0) { return res; } - if(is_var(a1)) { + if (res != 0) { + return res; + } + if (is_var(a1)) { res = aux_compare(to_var(a1)->get_idx(), to_var(a2)->get_idx()); - if(res!=0) { return res; } + if (res != 0) { + return res; + } } } return 0; @@ -77,16 +80,16 @@ namespace datalog { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; unsigned n = t1->get_num_args(); - for(unsigned i=0; iget_arg(i))) { - SASSERT(t1->get_arg(i)==t2->get_arg(i)); + for (unsigned i=0; iget_arg(i))) { + SASSERT(t1->get_arg(i) == t2->get_arg(i)); continue; } - if((skip_countdown--)==0) { + if ((skip_countdown--) == 0) { continue; } res = aux_compare(t1->get_arg(i), t2->get_arg(i)); - if(res!=0) { return res; } + if (res!=0) { return res; } } return 0; } @@ -100,26 +103,26 @@ namespace datalog { */ int rough_compare(rule * r1, rule * r2) { int res = aux_compare(r1->get_tail_size(), r2->get_tail_size()); - if(res!=0) { return res; } + if (res!=0) { return res; } res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size()); - if(res!=0) { return res; } + if (res!=0) { return res; } res = aux_compare(r1->get_positive_tail_size(), r2->get_positive_tail_size()); - if(res!=0) { return res; } + if (res!=0) { return res; } int pos_tail_sz = r1->get_positive_tail_size(); - for(int i=-1; iget_decl(), t2->get_decl()); - if(res!=0) { return res; } + if (res!=0) { return res; } res = compare_var_args(t1, t2); - if(res!=0) { return res; } + if (res!=0) { return res; } } unsigned tail_sz = r1->get_tail_size(); - for(unsigned i=pos_tail_sz; iget_tail(i), r2->get_tail(i)); - if(res!=0) { return res; } + if (res!=0) { return res; } } return 0; @@ -132,9 +135,9 @@ namespace datalog { int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) { SASSERT(rough_compare(r1, r2)==0); int pos_tail_sz = r1->get_positive_tail_size(); - for(int i=-1; iget_num_args(); - for(unsigned i=0; iget_arg(i))) { + for (unsigned i=0; iget_arg(i))) { continue; } res.push_back(const_info(tail_index, i)); @@ -178,7 +181,7 @@ namespace datalog { void collect_const_indexes(rule * r, info_vector & res) { collect_const_indexes(r->get_head(), -1, res); unsigned pos_tail_sz = r->get_positive_tail_size(); - for(unsigned i=0; iget_tail(i), i, res); } } @@ -187,9 +190,9 @@ namespace datalog { void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) { unsigned const_cnt = const_infos.size(); tgt.reset(); - for(unsigned i=0; iget_arg(const_infos[i].arg_index())); - if(vals[i]!=val) { + if (vals[i]!=val) { vals[i] = 0; } } } unsigned removed_cnt = 0; - for(unsigned i=0; i possible_parents(const_cnt); - for(unsigned i=1; iget_head()->get_num_args() - count_variable_arguments(r->get_head()); unsigned pos_tail_sz = r->get_positive_tail_size(); - for(unsigned i=0; iget_tail(i)->get_num_args() - count_variable_arguments(r->get_tail(i)); } return res; @@ -313,7 +316,7 @@ namespace datalog { bool initial_comparator(rule * r1, rule * r2) { int res = rough_compare(r1, r2); - if(res!=0) { return res>0; } + if (res!=0) { return res>0; } return total_compare(r1, r2)>0; } @@ -348,7 +351,7 @@ namespace datalog { ptr_vector aux_domain; collect_orphan_sorts(r, const_infos, aux_domain); - func_decl* head_pred = r->get_head()->get_decl(); + func_decl* head_pred = r->get_decl(); symbol const& name_prefix = head_pred->get_name(); std::string name_suffix = "sc_" + to_string(const_cnt); func_decl * aux_pred = m_context.mk_fresh_head_predicate(name_prefix, symbol(name_suffix.c_str()), @@ -357,7 +360,7 @@ namespace datalog { relation_fact val_fact(m_manager, const_cnt); rule_vector::iterator it = first; - for(; it!=after_last; ++it) { + for (; it!=after_last; ++it) { collect_orphan_consts(*it, const_infos, val_fact); m_context.add_fact(aux_pred, val_fact); } @@ -367,7 +370,7 @@ namespace datalog { ptr_vector new_tail; svector new_negs; unsigned tail_sz = r->get_tail_size(); - for(unsigned i=0; iget_tail(i)); new_negs.push_back(r->is_neg_tail(i)); } @@ -375,7 +378,7 @@ namespace datalog { rule_counter ctr; ctr.count_rule_vars(m_manager, r); unsigned max_var_idx, new_var_idx_base; - if(ctr.get_max_positive(max_var_idx)) { + if (ctr.get_max_positive(max_var_idx)) { new_var_idx_base = max_var_idx+1; } else { @@ -387,15 +390,15 @@ namespace datalog { unsigned aux_column_index = 0; - for(unsigned i=0; i mod_args(mod_tail->get_num_args(), mod_tail->get_args()); - for(; im_threshold_count) { + if (it==after_last || !comparator.eq(*prev, *it)) { + if (grp_size>m_threshold_count) { merge_class(grp_begin, it); //group was processed, so we remove it from the class - if(it==after_last) { + if (it==after_last) { after_last=grp_begin; it=after_last; } @@ -484,9 +487,9 @@ namespace datalog { //TODO: compress also rules with pairs (or tuples) of equal constants #if 1 - if(const_cnt>0) { + if (const_cnt>0) { unsigned rule_cnt = static_cast(after_last-first); - if(rule_cnt>m_threshold_count) { + if (rule_cnt>m_threshold_count) { merge_class(first, after_last); return; } @@ -495,7 +498,7 @@ namespace datalog { //put rules which weren't merged into result rule_vector::iterator it = first; - for(; it!=after_last; ++it) { + for (; it!=after_last; ++it) { m_result_rules.push_back(*it); } } @@ -505,7 +508,7 @@ namespace datalog { m_modified = false; unsigned init_rule_cnt = source.get_num_rules(); SASSERT(m_rules.empty()); - for(unsigned i=0; i(0); - if(m_modified) { + if (m_modified) { result = alloc(rule_set, m_context); unsigned fin_rule_cnt = m_result_rules.size(); - for(unsigned i=0; iadd_rule(m_result_rules.get(i)); } + result->inherit_predicates(source); } reset(); return result; diff --git a/src/muz_qe/dl_mk_simple_joins.cpp b/src/muz_qe/dl_mk_simple_joins.cpp index 3a3f96acf..2fec78066 100644 --- a/src/muz_qe/dl_mk_simple_joins.cpp +++ b/src/muz_qe/dl_mk_simple_joins.cpp @@ -89,7 +89,7 @@ namespace datalog { m_consumers++; } if(m_stratified) { - unsigned head_stratum = pl.get_stratum(r->get_head()->get_decl()); + unsigned head_stratum = pl.get_stratum(r->get_decl()); SASSERT(head_stratum>=m_src_stratum); if(head_stratum==m_src_stratum) { m_stratified = false; @@ -383,7 +383,7 @@ namespace datalog { rule * one_parent = inf.m_rules.back(); - func_decl* parent_head = one_parent->get_head()->get_decl(); + func_decl* parent_head = one_parent->get_decl(); const char * one_parent_name = parent_head->get_name().bare_str(); std::string parent_name; if(inf.m_rules.size()>1) { @@ -714,13 +714,14 @@ namespace datalog { m_context.get_rule_manager().mk_rule_asserted_proof(*m_introduced_rules.back()); m_introduced_rules.pop_back(); } + result->inherit_predicates(source); return result; } }; rule_set * mk_simple_joins::operator()(rule_set const & source) { rule_set rs_aux_copy(m_context); - rs_aux_copy.add_rules(source); + rs_aux_copy.replace_rules(source); if(!rs_aux_copy.is_closed()) { rs_aux_copy.close(); } diff --git a/src/muz_qe/dl_mk_slice.cpp b/src/muz_qe/dl_mk_slice.cpp index c98d6503e..21c3486b3 100644 --- a/src/muz_qe/dl_mk_slice.cpp +++ b/src/muz_qe/dl_mk_slice.cpp @@ -703,7 +703,7 @@ namespace datalog { m_pinned.reset(); } - void mk_slice::declare_predicates() { + void mk_slice::declare_predicates(rule_set const& src, rule_set& dst) { obj_map::iterator it = m_sliceable.begin(), end = m_sliceable.end(); ptr_vector domain; func_decl* f; @@ -720,6 +720,7 @@ namespace datalog { f = m_ctx.mk_fresh_head_predicate(p->get_name(), symbol("slice"), domain.size(), domain.c_ptr(), p); m_pinned.push_back(f); m_predicates.insert(p, f); + dst.inherit_predicate(src, p, f); if (m_mc) { m_mc->add_predicate(p, f); } @@ -820,13 +821,14 @@ namespace datalog { m_mc = smc.get(); reset(); saturate(src); - declare_predicates(); + rule_set* result = alloc(rule_set, m_ctx); + declare_predicates(src, *result); if (m_predicates.empty()) { // nothing could be sliced. + dealloc(result); return 0; } TRACE("dl", display(tout);); - rule_set* result = alloc(rule_set, m_ctx); update_rules(src, *result); TRACE("dl", result->display(tout);); if (m_mc) { diff --git a/src/muz_qe/dl_mk_slice.h b/src/muz_qe/dl_mk_slice.h index 1b4312e77..aedc1feb0 100644 --- a/src/muz_qe/dl_mk_slice.h +++ b/src/muz_qe/dl_mk_slice.h @@ -83,7 +83,7 @@ namespace datalog { expr_ref_vector get_tail_conjs(rule const& r); - void declare_predicates(); + void declare_predicates(rule_set const& src, rule_set& dst); bool rule_updated(rule const& r); diff --git a/src/muz_qe/dl_mk_subsumption_checker.cpp b/src/muz_qe/dl_mk_subsumption_checker.cpp index fb55a377c..f50bd104c 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.cpp +++ b/src/muz_qe/dl_mk_subsumption_checker.cpp @@ -21,11 +21,8 @@ Revision History: #include #include"ast_pp.h" - #include "rewriter.h" #include "rewriter_def.h" - - #include"dl_mk_subsumption_checker.h" #include"dl_table_relation.h" @@ -82,7 +79,7 @@ namespace datalog { void mk_subsumption_checker::on_discovered_total_relation(func_decl * pred, rule * r) { //this should be rule marking a new relation as total SASSERT(!m_total_relations.contains(pred)); - SASSERT(!r || pred==r->get_head()->get_decl()); + SASSERT(!r || pred==r->get_decl()); SASSERT(!r || is_total_rule(r)); m_total_relations.insert(pred); @@ -102,7 +99,7 @@ namespace datalog { rule_set::iterator rend = rules.end(); for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) { rule * r = *rit; - func_decl * head_pred = r->get_head()->get_decl(); + func_decl * head_pred = r->get_decl(); if(is_total_rule(r) && !m_total_relations.contains(head_pred)) { on_discovered_total_relation(head_pred, r); new_discovered = true; @@ -196,10 +193,10 @@ namespace datalog { for(rule_set::iterator rit = rbegin; rit!=rend; ++rit) { rule * r = *rit; - func_decl * head_pred = r->get_head()->get_decl(); + func_decl * head_pred = r->get_decl(); if(m_total_relations.contains(head_pred)) { - if(!m_context.is_output_predicate(head_pred) || + if(!orig.is_output_predicate(head_pred) || total_relations_with_included_rules.contains(head_pred)) { //We just skip definitions of total non-output relations as //we'll eliminate them from the problem. @@ -217,7 +214,7 @@ namespace datalog { modified = true; } tgt.add_rule(totality_rule); - SASSERT(totality_rule->get_head()->get_decl()==head_pred); + SASSERT(totality_rule->get_decl()==head_pred); } else { modified = true; @@ -250,24 +247,23 @@ namespace datalog { return modified; } - void mk_subsumption_checker::scan_for_relations_total_due_to_facts() { + void mk_subsumption_checker::scan_for_relations_total_due_to_facts(rule_set const& source) { relation_manager& rm = m_context.get_rel_context().get_rmanager(); - decl_set candidate_preds; - m_context.collect_predicates(candidate_preds); + decl_set const& candidate_preds = m_context.get_predicates(); decl_set::iterator end = candidate_preds.end(); for(decl_set::iterator it = candidate_preds.begin(); it!=end; ++it) { func_decl * pred = *it; - if(m_total_relations.contains(pred)) { continue; } //already total + if (m_total_relations.contains(pred)) { continue; } //already total relation_base * rel = rm.try_get_relation(pred); - if(!rel || !rel->knows_exact_size()) { continue; } + if (!rel || !rel->knows_exact_size()) { continue; } unsigned arity = pred->get_arity(); - if(arity>30) { continue; } + if (arity > 30) { continue; } //for now we only check booleans domains for(unsigned i=0; iget_head()->get_decl(); + func_decl * pred = r->get_decl(); if(r->get_tail_size()!=0) { continue; } @@ -337,7 +333,7 @@ namespace datalog { m_have_new_total_rule = false; collect_ground_unconditional_rule_heads(source); - scan_for_relations_total_due_to_facts(); + scan_for_relations_total_due_to_facts(source); scan_for_total_rules(source); m_have_new_total_rule = false; @@ -361,6 +357,7 @@ namespace datalog { transform_rules(*old, *res); dealloc(old); } + res->inherit_predicates(source); return res; } diff --git a/src/muz_qe/dl_mk_subsumption_checker.h b/src/muz_qe/dl_mk_subsumption_checker.h index 59904b3ef..da42e4202 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.h +++ b/src/muz_qe/dl_mk_subsumption_checker.h @@ -64,8 +64,8 @@ namespace datalog { /** Function to be called when a new total relation is discovered */ void on_discovered_total_relation(func_decl * pred, rule * r); - void scan_for_total_rules(const rule_set & rules); - void scan_for_relations_total_due_to_facts(); + void scan_for_total_rules(rule_set const& rules); + void scan_for_relations_total_due_to_facts(rule_set const& rules); void collect_ground_unconditional_rule_heads(const rule_set & rules); diff --git a/src/muz_qe/dl_mk_unbound_compressor.cpp b/src/muz_qe/dl_mk_unbound_compressor.cpp index 40926c2a8..eec0b991d 100644 --- a/src/muz_qe/dl_mk_unbound_compressor.cpp +++ b/src/muz_qe/dl_mk_unbound_compressor.cpp @@ -26,9 +26,9 @@ namespace datalog { mk_unbound_compressor::mk_unbound_compressor(context & ctx) : plugin(500), m_context(ctx), - m_manager(ctx.get_manager()), + m(ctx.get_manager()), m_rules(ctx.get_rule_manager()), - m_pinned(m_manager) { + m_pinned(m) { } void mk_unbound_compressor::reset() { @@ -48,7 +48,7 @@ namespace datalog { unsigned var_idx = to_var(head_arg)->get_idx(); var_idx_set tail_vars; - collect_tail_vars(m_manager, r, tail_vars); + collect_tail_vars(m, r, tail_vars); return tail_vars.contains(var_idx); } @@ -81,14 +81,14 @@ namespace datalog { m_map.insert(ci, cpred); } - void mk_unbound_compressor::detect_tasks(unsigned rule_index) { + void mk_unbound_compressor::detect_tasks(rule_set const& source, unsigned rule_index) { rule * r = m_rules.get(rule_index); var_idx_set tail_vars; - collect_tail_vars(m_manager, r, tail_vars); + collect_tail_vars(m, r, tail_vars); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); - if (m_context.is_output_predicate(head_pred)) { + if (source.is_output_predicate(head_pred)) { //we don't compress output predicates return; } @@ -96,7 +96,7 @@ namespace datalog { unsigned n = head_pred->get_arity(); var_counter head_var_counter; - head_var_counter.count_vars(m_manager, head, 1); + head_var_counter.count_vars(m, head, 1); for (unsigned i=0; iget_arg(i); @@ -118,18 +118,18 @@ namespace datalog { } } - void mk_unbound_compressor::try_compress(unsigned rule_index) { + void mk_unbound_compressor::try_compress(rule_set const& source, unsigned rule_index) { start: rule * r = m_rules.get(rule_index); var_idx_set tail_vars; - collect_tail_vars(m_manager, r, tail_vars); + collect_tail_vars(m, r, tail_vars); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); unsigned head_arity = head_pred->get_arity(); var_counter head_var_counter; - head_var_counter.count_vars(m_manager, head); + head_var_counter.count_vars(m, head); unsigned arg_index; for (arg_index = 0; arg_index < head_arity; arg_index++) { @@ -163,13 +163,13 @@ namespace datalog { } } - app_ref chead(m_manager.mk_app(cpred, head_arity-1, cargs.c_ptr()), m_manager); + app_ref chead(m.mk_app(cpred, head_arity-1, cargs.c_ptr()), m); if (r->get_tail_size()==0 && m_context.get_rule_manager().is_fact(chead)) { m_non_empty_rels.insert(cpred); m_context.add_fact(chead); //remove the rule that became fact by placing the last rule on its place - m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_head()->get_decl()); + m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_decl()); m_rules.set(rule_index, m_rules.get(m_rules.size()-1)); m_rules.shrink(m_rules.size()-1); //since we moved the last rule to rule_index, we have to try to compress it as well @@ -181,10 +181,10 @@ namespace datalog { rule_ref new_rule(m_context.get_rule_manager().mk(r, chead), m_context.get_rule_manager()); new_rule->set_accounting_parent_object(m_context, r); - m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_head()->get_decl()); + m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_decl()); m_rules.set(rule_index, new_rule); - m_head_occurrence_ctr.inc(m_rules.get(rule_index)->get_head()->get_decl()); - detect_tasks(rule_index); + m_head_occurrence_ctr.inc(m_rules.get(rule_index)->get_decl()); + detect_tasks(source, rule_index); } m_modified = true; @@ -205,10 +205,10 @@ namespace datalog { } } SASSERT(dtail_args.size()==dtail_pred->get_arity()); - app_ref dtail(m_manager.mk_app(dtail_pred, dtail_args.size(), dtail_args.c_ptr()), m_manager); + app_ref dtail(m.mk_app(dtail_pred, dtail_args.size(), dtail_args.c_ptr()), m); svector tails_negated; - app_ref_vector tails(m_manager); + app_ref_vector tails(m); unsigned tail_len = r->get_tail_size(); for (unsigned i=0; iis_neg_tail(i)); @@ -232,17 +232,17 @@ namespace datalog { m_context.get_rule_manager().fix_unbound_vars(res, true); } - void mk_unbound_compressor::add_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index) { + void mk_unbound_compressor::add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index) { rule_ref new_rule(m_context.get_rule_manager()); mk_decompression_rule(r, tail_index, arg_index, new_rule); unsigned new_rule_index = m_rules.size(); m_rules.push_back(new_rule); m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule.get()); - m_head_occurrence_ctr.inc(new_rule->get_head()->get_decl()); + m_head_occurrence_ctr.inc(new_rule->get_decl()); - detect_tasks(new_rule_index); + detect_tasks(source, new_rule_index); m_modified = true; @@ -258,7 +258,7 @@ namespace datalog { //P:- R1(x), S1(x) } - void mk_unbound_compressor::replace_by_decompression_rule(unsigned rule_index, unsigned tail_index, unsigned arg_index) + void mk_unbound_compressor::replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index) { rule * r = m_rules.get(rule_index); @@ -269,12 +269,12 @@ namespace datalog { //we don't update the m_head_occurrence_ctr because the head predicate doesn't change - detect_tasks(rule_index); + detect_tasks(source, rule_index); m_modified = true; } - void mk_unbound_compressor::add_decompression_rules(unsigned rule_index) { + void mk_unbound_compressor::add_decompression_rules(rule_set const& source, unsigned rule_index) { unsigned_vector compressed_tail_pred_arg_indexes; @@ -306,11 +306,11 @@ namespace datalog { m_head_occurrence_ctr.get(t_pred)==0; if (can_remove_orig_rule || is_negated_predicate) { - replace_by_decompression_rule(rule_index, tail_index, arg_index); + replace_by_decompression_rule(source, rule_index, tail_index, arg_index); orig_rule_replaced = true; } else { - add_decompression_rule(r, tail_index, arg_index); + add_decompression_rule(source, r, tail_index, arg_index); } } if (orig_rule_replaced) { @@ -345,11 +345,11 @@ namespace datalog { for (unsigned i=0; iget_head()->get_decl()); + m_head_occurrence_ctr.inc(r->get_decl()); } for (unsigned i=0; iadd_rule(m_rules.get(i)); } + result->inherit_predicates(source); } reset(); return result; diff --git a/src/muz_qe/dl_mk_unbound_compressor.h b/src/muz_qe/dl_mk_unbound_compressor.h index cad953783..4e56a74fc 100644 --- a/src/muz_qe/dl_mk_unbound_compressor.h +++ b/src/muz_qe/dl_mk_unbound_compressor.h @@ -50,8 +50,8 @@ namespace datalog { typedef hashtable > in_progress_table; typedef svector todo_stack; - context & m_context; - ast_manager & m_manager; + context & m_context; + ast_manager & m; rule_ref_vector m_rules; bool m_modified; todo_stack m_todo; @@ -71,13 +71,13 @@ namespace datalog { bool is_unbound_argument(rule * r, unsigned head_index); bool has_unbound_head_var(rule * r); - void detect_tasks(unsigned rule_index); + void detect_tasks(rule_set const& source, unsigned rule_index); void add_task(func_decl * pred, unsigned arg_index); - void try_compress(unsigned rule_index); - void add_decompression_rules(unsigned rule_index); + void try_compress(rule_set const& source, unsigned rule_index); + void add_decompression_rules(rule_set const& source, unsigned rule_index); void mk_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index, rule_ref& res); - void add_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index); - void replace_by_decompression_rule(unsigned rule_index, unsigned tail_index, unsigned arg_index); + void add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index); + void replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index); void reset(); public: mk_unbound_compressor(context & ctx); diff --git a/src/muz_qe/dl_mk_unfold.cpp b/src/muz_qe/dl_mk_unfold.cpp index dfbd87122..a9357f88a 100644 --- a/src/muz_qe/dl_mk_unfold.cpp +++ b/src/muz_qe/dl_mk_unfold.cpp @@ -56,6 +56,7 @@ namespace datalog { for (; it != end; ++it) { expand_tail(**it, 0, source, *rules); } + rules->inherit_predicates(source); return rules; } diff --git a/src/muz_qe/dl_relation_manager.cpp b/src/muz_qe/dl_relation_manager.cpp index e001dd1a4..d92ae7dcc 100644 --- a/src/muz_qe/dl_relation_manager.cpp +++ b/src/muz_qe/dl_relation_manager.cpp @@ -124,14 +124,6 @@ namespace datalog { e->get_data().m_value = rel; } - void relation_manager::collect_predicates(decl_set & res) const { - relation_map::iterator it = m_relations.begin(); - relation_map::iterator end = m_relations.end(); - for(; it!=end; ++it) { - res.insert(it->m_key); - } - } - void relation_manager::collect_non_empty_predicates(decl_set & res) const { relation_map::iterator it = m_relations.begin(); relation_map::iterator end = m_relations.end(); @@ -539,8 +531,8 @@ namespace datalog { } } - void relation_manager::display_output_tables(std::ostream & out) const { - const decl_set & output_preds = get_context().get_output_predicates(); + void relation_manager::display_output_tables(rule_set const& rules, std::ostream & out) const { + const decl_set & output_preds = rules.get_output_predicates(); decl_set::iterator it=output_preds.begin(); decl_set::iterator end=output_preds.end(); for(; it!=end; ++it) { diff --git a/src/muz_qe/dl_relation_manager.h b/src/muz_qe/dl_relation_manager.h index 6b350642e..8715e5cff 100644 --- a/src/muz_qe/dl_relation_manager.h +++ b/src/muz_qe/dl_relation_manager.h @@ -22,7 +22,6 @@ Revision History: #include"map.h" #include"vector.h" - #include"dl_base.h" namespace datalog { @@ -35,8 +34,8 @@ namespace datalog { class finite_product_relation_plugin; class sieve_relation; class sieve_relation_plugin; + class rule_set; - typedef hashtable, ptr_eq > decl_set; class relation_manager { class empty_signature_relation_join_fn; @@ -153,7 +152,6 @@ namespace datalog { } } - void collect_predicates(decl_set & res) const; void collect_non_empty_predicates(decl_set & res) const; void restrict_predicates(const decl_set & preds); @@ -595,7 +593,7 @@ namespace datalog { void display(std::ostream & out) const; void display_relation_sizes(std::ostream & out) const; - void display_output_tables(std::ostream & out) const; + void display_output_tables(rule_set const& rules, std::ostream & out) const; private: relation_intersection_filter_fn * try_mk_default_filter_by_intersection_fn(const relation_base & t, diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index b7d6d9fae..92f504bcc 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -50,10 +50,6 @@ namespace datalog { : m(ctx.get_manager()), m_ctx(ctx) {} - bool rule_manager::is_predicate(func_decl * f) const { - return m_ctx.is_predicate(f); - } - void rule_manager::inc_ref(rule * r) { if (r) { SASSERT(r->m_ref_cnt != UINT_MAX); @@ -102,7 +98,7 @@ namespace datalog { } - void rule_manager::mk_rule(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name) { + void rule_manager::mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); proof_ref pr(p, m); expr_ref fml1(m); @@ -111,13 +107,13 @@ namespace datalog { pr = m.mk_asserted(fml1); } remove_labels(fml1, pr); - mk_rule_core_new(fml1, pr, rules, name); + mk_rule_core(fml1, pr, rules, name); } void rule_manager::mk_negations(app_ref_vector& body, svector& is_negated) { for (unsigned i = 0; i < body.size(); ++i) { expr* e = body[i].get(), *e1; - if (m.is_not(e, e1) && is_predicate(e1)) { + if (m.is_not(e, e1) && m_ctx.is_predicate(e1)) { check_app(e1); body[i] = to_app(e1); is_negated.push_back(true); @@ -128,7 +124,7 @@ namespace datalog { } } - void rule_manager::mk_rule_core_new(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name) { + void rule_manager::mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name) { hnf h(m); expr_ref_vector fmls(m); proof_ref_vector prs(m); @@ -138,11 +134,11 @@ namespace datalog { m_ctx.register_predicate(h.get_fresh_predicates()[i], false); } for (unsigned i = 0; i < fmls.size(); ++i) { - mk_rule_core2(fmls[i].get(), prs[i].get(), rules, name); + mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name); } } - void rule_manager::mk_rule_core2(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name) { + void rule_manager::mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { app_ref_vector body(m); app_ref head(m); @@ -189,7 +185,7 @@ namespace datalog { } r->set_proof(m, p); } - rules.push_back(r); + rules.add_rule(r); } unsigned rule_manager::extract_horn(expr* fml, app_ref_vector& body, app_ref& head) { @@ -224,7 +220,7 @@ namespace datalog { } - void rule_manager::mk_query(expr* query, func_decl_ref& qpred, rule_ref_vector& query_rules, rule_ref& query_rule) { + func_decl* rule_manager::mk_query(expr* query, rule_set& rules) { ptr_vector vars; svector names; app_ref_vector body(m); @@ -279,7 +275,9 @@ namespace datalog { } vars.reverse(); names.reverse(); - qpred = m_ctx.mk_fresh_head_predicate(symbol("query"), symbol(), vars.size(), vars.c_ptr(), body_pred); + func_decl* qpred = m_ctx.mk_fresh_head_predicate(symbol("query"), symbol(), vars.size(), vars.c_ptr(), body_pred); + m_ctx.register_predicate(qpred, false); + rules.set_output_predicate(qpred); expr_ref_vector qhead_args(m); for (unsigned i = 0; i < vars.size(); i++) { @@ -297,9 +295,8 @@ namespace datalog { if (m_ctx.generate_proof_trace()) { pr = m.mk_asserted(rule_expr); } - mk_rule(rule_expr, pr, query_rules); - SASSERT(query_rules.size() >= 1); - query_rule = query_rules.back(); + mk_rule(rule_expr, pr, rules); + return qpred; } void rule_manager::bind_variables(expr* fml, bool is_forall, expr_ref& result) { @@ -330,7 +327,7 @@ namespace datalog { return; } expr_ref_vector args(m); - if (!is_predicate(fml)) { + if (!m_ctx.is_predicate(fml)) { return; } for (unsigned i = 0; i < fml->get_num_args(); ++i) { @@ -355,19 +352,19 @@ namespace datalog { } class contains_predicate_proc { - rule_manager const& m; + context const& ctx; public: struct found {}; - contains_predicate_proc(rule_manager const& m): m(m) {} + contains_predicate_proc(context const& ctx): ctx(ctx) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app* n) { - if (m.is_predicate(n)) throw found(); + if (ctx.is_predicate(n)) throw found(); } }; bool rule_manager::contains_predicate(expr* fml) const { - contains_predicate_proc proc(*this); + contains_predicate_proc proc(m_ctx); try { quick_for_each_expr(proc, fml); } @@ -434,7 +431,7 @@ namespace datalog { bool is_neg = (is_negated != 0 && is_negated[i]); app * curr = tail[i]; - if (is_neg && !is_predicate(curr)) { + if (is_neg && !m_ctx.is_predicate(curr)) { curr = m.mk_not(curr); is_neg = false; } @@ -442,7 +439,7 @@ namespace datalog { has_neg = true; } app * tail_entry = TAG(app *, curr, is_neg); - if (is_predicate(curr)) { + if (m_ctx.is_predicate(curr)) { *uninterp_tail=tail_entry; uninterp_tail++; } @@ -755,7 +752,7 @@ namespace datalog { void rule_manager::check_valid_head(expr * head) const { SASSERT(head); - if (!is_predicate(head)) { + if (!m_ctx.is_predicate(head)) { std::ostringstream out; out << "Illegal head. The head predicate needs to be uninterpreted and registered (as recursive) " << mk_pp(head, m); throw default_exception(out.str()); @@ -966,7 +963,7 @@ namespace datalog { if (is_neg_tail(i)) out << "not "; app * t = get_tail(i); - if (ctx.get_rule_manager().is_predicate(t)) { + if (ctx.is_predicate(t)) { output_predicate(ctx, t, out); } else { diff --git a/src/muz_qe/dl_rule.h b/src/muz_qe/dl_rule.h index 3f535125f..666ddbd50 100644 --- a/src/muz_qe/dl_rule.h +++ b/src/muz_qe/dl_rule.h @@ -32,6 +32,7 @@ namespace datalog { class rule; class rule_manager; + class rule_set; class table; class context; @@ -74,13 +75,11 @@ namespace datalog { void bind_variables(expr* fml, bool is_forall, expr_ref& result); - void mk_rule_core(expr* fml, rule_ref_vector& rules, symbol const& name); - void mk_negations(app_ref_vector& body, svector& is_negated); - void mk_rule_core_new(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name); + void mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name); - void mk_rule_core2(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name); + void mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name); static expr_ref mk_implies(app_ref_vector const& body, expr* head); @@ -104,13 +103,13 @@ namespace datalog { The formula is of the form (forall (...) (forall (...) (=> (and ...) head))) */ - void mk_rule(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name = symbol::null); + void mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name = symbol::null); /** \brief Create a Datalog query from an expression. The formula is of the form (exists (...) (exists (...) (and ...)) */ - void mk_query(expr* query, func_decl_ref& query_pred, rule_ref_vector& query_rules, rule_ref& query_rule); + func_decl* mk_query(expr* query, rule_set& rules); /** \brief Create a Datalog rule head :- tail[0], ..., tail[n-1]. @@ -166,11 +165,6 @@ namespace datalog { bool is_fact(app * head) const; - bool is_predicate(func_decl * f) const; - bool is_predicate(expr * e) const { - return is_app(e) && is_predicate(to_app(e)->get_decl()); - } - static bool is_forall(ast_manager& m, expr* e, quantifier*& q); rule_counter& get_counter() { return m_counter; } diff --git a/src/muz_qe/dl_rule_set.cpp b/src/muz_qe/dl_rule_set.cpp index f9ae3620d..b0bcdec89 100644 --- a/src/muz_qe/dl_rule_set.cpp +++ b/src/muz_qe/dl_rule_set.cpp @@ -64,8 +64,7 @@ namespace datalog { reset_dealloc_values(m_data); } - void rule_dependencies::remove_m_data_entry(func_decl * key) - { + void rule_dependencies::remove_m_data_entry(func_decl * key) { item_set * itm_set = m_data.find(key); dealloc(itm_set); m_data.remove(key); @@ -109,7 +108,7 @@ namespace datalog { void rule_dependencies::populate(rule const* r) { TRACE("dl_verbose", tout << r->get_decl()->get_name() << "\n";); m_visited.reset(); - func_decl * d = r->get_head()->get_decl(); + func_decl * d = r->get_decl(); func_decl_set & s = ensure_key(d); for (unsigned i = 0; i < r->get_tail_size(); ++i) { @@ -164,7 +163,7 @@ namespace datalog { } ptr_vector::iterator rit = to_remove.begin(); ptr_vector::iterator rend = to_remove.end(); - for (; rit!=rend; ++rit) { + for (; rit != rend; ++rit) { remove_m_data_entry(*rit); } } @@ -173,7 +172,7 @@ namespace datalog { remove_m_data_entry(itm); iterator pit = begin(); iterator pend = end(); - for (; pit!=pend; ++pit) { + for (; pit != pend; ++pit) { item_set & itms = *pit->get_value(); itms.remove(itm); } @@ -244,7 +243,7 @@ namespace datalog { } curr_index++; } - if (res.size()::iterator it = other.m_orig2pred.begin(); + obj_map::iterator end = other.m_orig2pred.end(); + for (; it != end; ++it) { + m_orig2pred.insert(it->m_key, it->m_value); + m_refs.push_back(it->m_key); + m_refs.push_back(it->m_value); + } + } + { + obj_map::iterator it = other.m_pred2orig.begin(); + obj_map::iterator end = other.m_pred2orig.end(); + for (; it != end; ++it) { + m_pred2orig.insert(it->m_key, it->m_value); + m_refs.push_back(it->m_key); + m_refs.push_back(it->m_value); + } + } + } + + void rule_set::inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred) { + if (other.is_output_predicate(orig)) { + set_output_predicate(pred); + } + orig = other.get_orig(orig); + m_refs.push_back(pred); + m_refs.push_back(orig); + m_orig2pred.insert(orig, pred); + m_pred2orig.insert(pred, orig); + } + void rule_set::add_rule(rule * r) { TRACE("dl_verbose", r->display(m_context, tout << "add:");); SASSERT(!is_closed()); @@ -329,7 +379,7 @@ namespace datalog { void rule_set::del_rule(rule * r) { TRACE("dl", r->display(m_context, tout << "del:");); - func_decl* d = r->get_head()->get_decl(); + func_decl* d = r->get_decl(); rule_vector* rules = m_head2rules.find(d); #define DEL_VECTOR(_v) \ for (unsigned i = (_v).size(); i > 0; ) { \ @@ -345,32 +395,26 @@ namespace datalog { DEL_VECTOR(m_rules); } - void rule_set::ensure_closed() - { + void rule_set::ensure_closed() { if (!is_closed()) { VERIFY(close()); } } bool rule_set::close() { - SASSERT(!is_closed()); //the rule_set is not already closed - - + SASSERT(!is_closed()); //the rule_set is not already closed m_deps.populate(*this); m_stratifier = alloc(rule_stratifier, m_deps); - if (!stratified_negation()) { m_stratifier = 0; m_deps.reset(); return false; } - return true; } void rule_set::reopen() { SASSERT(is_closed()); - m_stratifier = 0; m_deps.reset(); } @@ -401,18 +445,20 @@ namespace datalog { return true; } - void rule_set::add_rules(const rule_set & src) { - SASSERT(!is_closed()); - unsigned n = src.get_num_rules(); - for (unsigned i=0; iget_predicate_strat(pred); } + void rule_set::split_founded_rules(func_decl_set& founded, func_decl_set& non_founded) { + founded.reset(); + non_founded.reset(); + { + decl2rules::iterator it = begin_grouped_rules(), end = end_grouped_rules(); + for (; it != end; ++it) { + non_founded.insert(it->m_key); + } + } + bool change = true; + while (change) { + change = false; + func_decl_set::iterator it = non_founded.begin(), end = non_founded.end(); + for (; it != end; ++it) { + rule_vector const& rv = get_predicate_rules(*it); + bool found = false; + for (unsigned i = 0; !found && i < rv.size(); ++i) { + rule const& r = *rv[i]; + bool is_founded = true; + for (unsigned j = 0; is_founded && j < r.get_uninterpreted_tail_size(); ++j) { + is_founded = founded.contains(r.get_decl(j)); + } + if (is_founded) { + founded.insert(*it); + non_founded.remove(*it); + change = true; + found = true; + } + } + } + } + } void rule_set::display(std::ostream & out) const { out << "; rule count: " << get_num_rules() << "\n"; @@ -451,17 +529,6 @@ namespace datalog { r->display(m_context, out); } } - -#if 0 //print dependencies - out<<"##\n"; - out< m_stratifier; //!< contains stratifier object iff the rule_set is closed + rule_ref_vector m_rules; //!< all rules + decl2rules m_head2rules; //!< mapping from head symbol to rules. + rule_dependencies m_deps; //!< dependencies + scoped_ptr m_stratifier; //!< contains stratifier object iff the rule_set is closed + func_decl_set m_output_preds; //!< output predicates + obj_map m_orig2pred; + obj_map m_pred2orig; + func_decl_ref_vector m_refs; //sometimes we need to return reference to an empty rule_vector, @@ -184,6 +188,12 @@ namespace datalog { rule_manager & get_rule_manager() const { return const_cast(m_rule_manager); } context& get_context() const { return m_context; } + + void inherit_predicates(rule_set const& other); + void inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred); + func_decl* get_orig(func_decl* pred) const; + func_decl* get_pred(func_decl* orig) const; + /** \brief Add rule \c r to the rule set. */ @@ -198,7 +208,7 @@ namespace datalog { \brief Add all rules from a different rule_set. */ void add_rules(const rule_set& src); - void add_rules(unsigned sz, rule * const * rules); + void replace_rules(const rule_set& other); /** \brief This method should be invoked after all rules are added to the rule set. @@ -216,11 +226,14 @@ namespace datalog { bool is_closed() const { return m_stratifier != 0; } unsigned get_num_rules() const { return m_rules.size(); } + bool empty() const { return m_rules.size() == 0; } rule * get_rule(unsigned i) const { return m_rules[i]; } + rule * last() const { return m_rules[m_rules.size()-1]; } rule_ref_vector const& get_rules() const { return m_rules; } const rule_vector & get_predicate_rules(func_decl * pred) const; + bool contains(func_decl* pred) const { return m_head2rules.contains(pred); } const rule_stratifier & get_stratifier() const { SASSERT(m_stratifier); @@ -230,9 +243,17 @@ namespace datalog { unsigned get_predicate_strat(func_decl * pred) const; const rule_dependencies & get_dependencies() const { SASSERT(is_closed()); return m_deps; } + // split predicats into founded and non-founded. + void split_founded_rules(func_decl_set& founded, func_decl_set& non_founded); void reset(); + void set_output_predicate(func_decl * pred) { m_refs.push_back(pred); m_output_preds.insert(pred); } + bool is_output_predicate(func_decl * pred) const { return m_output_preds.contains(pred); } + const func_decl_set & get_output_predicates() const { return m_output_preds; } + func_decl* get_output_predicate() const { SASSERT(m_output_preds.size() == 1); return *m_output_preds.begin(); } + + void display(std::ostream & out) const; /** diff --git a/src/muz_qe/dl_rule_transformer.cpp b/src/muz_qe/dl_rule_transformer.cpp index 5cc686052..0cad08cb4 100644 --- a/src/muz_qe/dl_rule_transformer.cpp +++ b/src/muz_qe/dl_rule_transformer.cpp @@ -80,36 +80,38 @@ namespace datalog { tout<<"init:\n"; rules.display(tout); ); + rule_set* new_rules = alloc(rule_set, rules); plugin_vector::iterator it = m_plugins.begin(); plugin_vector::iterator end = m_plugins.end(); for(; it!=end && !m_context.canceled(); ++it) { plugin & p = **it; - rule_set * new_rules = p(rules); - if (!new_rules) { + rule_set * new_rules1 = p(*new_rules); + if (!new_rules1) { continue; } - if (p.can_destratify_negation()) { - if (!new_rules->is_closed()) { - if (!new_rules->close()) { - warning_msg("a rule transformation skipped because it destratified negation"); - dealloc(new_rules); - continue; - } - } + if (p.can_destratify_negation() && + !new_rules1->is_closed() && + !new_rules1->close()) { + warning_msg("a rule transformation skipped " + "because it destratified negation"); + dealloc(new_rules1); + continue; } modified = true; - rules.reset(); - rules.add_rules(*new_rules); dealloc(new_rules); - rules.ensure_closed(); + new_rules = new_rules1; + new_rules->ensure_closed(); TRACE("dl_rule_transf", tout << typeid(p).name()<<":\n"; - rules.display(tout); + new_rules->display(tout); ); - } + if (modified) { + rules.replace_rules(*new_rules); + } + dealloc(new_rules); return modified; } diff --git a/src/muz_qe/dl_skip_table.cpp b/src/muz_qe/dl_skip_table.cpp deleted file mode 100644 index 28efaabee..000000000 --- a/src/muz_qe/dl_skip_table.cpp +++ /dev/null @@ -1,622 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_skip_table.h - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) - Leonardo de Moura (leonardo) 2010-10-14 - - -Revision History: - ---*/ - -#ifndef _EXTERNAL_RELEASE - -#include "dl_skip_table.h" -#include "dl_table.h" -#include "dl_context.h" - - -namespace datalog { - - skip_table & skip_table_plugin::get(table_base& r) { - return static_cast(r); - } - - skip_table const & skip_table_plugin::get(table_base const& r) { - return static_cast(r); - } - - table_base * skip_table_plugin::mk_empty(const table_signature & s) { - return alloc(skip_table, *this, s); - } - - skip_table* skip_table_plugin::mk_join( - table_base const& t1, table_base const& t2, table_signature const& result_sig, - unsigned_vector const& cols1, unsigned_vector const& cols2) { - skip_table const& s1 = get(t1); - skip_table const& s2 = get(t2); - imdd_manager& m = s1.get_imdd_manager(); - imdd_ref pr(m); - m.mk_join(s1.get_imdd(), s2.get_imdd(), pr, cols1, cols2); - return alloc(skip_table, s1.get_plugin(), result_sig, pr); - } - - skip_table* skip_table_plugin::mk_join_project( - table_base const& t1, table_base const& t2, table_signature const& result_sig, - unsigned_vector const& cols1, unsigned_vector const& cols2, - unsigned_vector const& proj_cols) { - - skip_table const& s1 = get(t1); - skip_table const& s2 = get(t2); - imdd_manager& m = s1.get_imdd_manager(); - imdd_ref pr(m); - m.mk_join_project(s1.get_imdd(), s2.get_imdd(), pr, cols1, cols2, proj_cols); - return alloc(skip_table, s1.get_plugin(), result_sig, pr); - } - - class skip_table_plugin::join_fn : public convenient_table_join_fn { - public: - join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2): - convenient_table_join_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2) { - } - - virtual table_base* operator()(const table_base & t1, const table_base & t2) { - return skip_table_plugin::mk_join(t1, t2, get_result_signature(), m_cols1, m_cols2); - } - }; - - table_join_fn * skip_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (check_kind(t1) && check_kind(t2)) { - return alloc(join_fn, t1, t2, col_cnt, cols1, cols2); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::join_project_fn : public convenient_table_join_project_fn { - public: - join_project_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols): - convenient_table_join_project_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, - removed_col_cnt, removed_cols) { - } - - virtual table_base* operator()(const table_base & t1, const table_base & t2) { - return skip_table_plugin::mk_join_project(t1, t2, get_result_signature(), m_cols1, m_cols2, m_removed_cols); - } - }; - - - - table_join_fn * skip_table_plugin::mk_join_project_fn( - const table_base & t1, const table_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols) { - if (check_kind(t1) && check_kind(t2)) { - return alloc(join_project_fn, t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::union_fn : public table_union_fn { - public: - virtual void operator()(table_base& tgt, const table_base& src, table_base* delta) { - skip_table& s1 = get(tgt); - skip_table const& s2 = get(src); - imdd_manager& m = s1.get_imdd_manager(); - imdd_ref r(m); - m.mk_union(s1.get_imdd(), s2.get_imdd(), r); - if (delta) { - skip_table& d = get(*delta); - if (m.is_subset(r, s1.get_imdd())) { - d.update(m.mk_empty(s1.get_signature().size())); - } - else { - d.update(r); - } - } - s1.update(r); - } - }; - - table_union_fn * skip_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { - if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { - return alloc(union_fn); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - skip_table* skip_table_plugin::mk_project(table_base const& src, table_signature const& result_sig, unsigned_vector const& cols) { - skip_table const& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - imdd_ref pr(m); - m.mk_project(s.get_imdd(), pr, cols.size(), cols.c_ptr()); - return alloc(skip_table, s.get_plugin(), result_sig, pr); - } - - - class skip_table_plugin::project_fn : public convenient_table_project_fn { - public: - project_fn(table_signature const& orig_sig, unsigned col_cnt, unsigned const* removed_cols): - convenient_table_project_fn(orig_sig, col_cnt, removed_cols) {} - - table_base* operator()(table_base const& src) { - return mk_project(src, get_result_signature(), m_removed_cols); - } - }; - - table_transformer_fn * skip_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { - if (check_kind(t)) { - return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::rename_fn : public convenient_table_rename_fn { - - void swap2(imdd_ref& n, unsigned col1, unsigned col2) { - imdd_manager& m = n.get_manager(); - imdd_ref tmp(m); - if (col1 == col2) { - return; - } - if (col1 > col2) { - std::swap(col1, col2); - } - for (unsigned i = col1; i < col2; ++i) { - m.mk_swap(n, tmp, i); - n = tmp; - } - for (unsigned i = col2 - 1; i > col1; ) { - --i; - m.mk_swap(n, tmp, i); - n = tmp; - } - } - public: - rename_fn(table_signature const& sig, unsigned cycle_len, unsigned const* cycle): - convenient_rename_fn(sig, cycle_len, cycle) {} - - table_base* operator()(table_base const& src) { - TRACE("skip", - for (unsigned i = 0; i < m_cycle.size(); ++i) { - tout << m_cycle[i] << " "; - } - tout << "\n"; - src.display(tout);); - skip_table const& s = get(src); - imdd_ref n(s.m_imdd, s.get_imdd_manager()); - unsigned cycle_len = m_cycle.size(); - unsigned col1, col2; - // TBD: review this for proper direction - for (unsigned i = 0; i + 1 < cycle_len; ++i) { - col1 = m_cycle[i]; - col2 = m_cycle[i+1]; - swap2(n, col1, col2); - } - if (cycle_len > 2) { - col1 = m_cycle[cycle_len-1]; - col2 = m_cycle[0]; - swap2(n, col1, col2); - } - skip_table* res = alloc(skip_table, s.get_plugin(), get_result_signature(), n); - TRACE("skip",res->display(tout);); - return res; - } - }; - - table_transformer_fn * skip_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) { - if (check_kind(t)) { - return alloc(rename_fn, t.get_signature(), len, cycle); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_identical_fn : public table_mutator_fn { - unsigned_vector m_cols; - - public: - filter_identical_fn(unsigned cnt, unsigned const* cols): - m_cols(cnt, cols) - {} - - void operator()(table_base & t) { - skip_table& s = get(t); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_identical(s.get_imdd(), s.m_imdd, m_cols.size(), m_cols.c_ptr(), true); - } - }; - - table_mutator_fn * skip_table_plugin::mk_filter_identical_fn(const table_base & t, unsigned col_cnt, - const unsigned * identical_cols) { - if (check_kind(t)) { - return alloc(filter_identical_fn, col_cnt, identical_cols); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_equal_fn : public table_mutator_fn { - unsigned m_col; - unsigned m_value; - public: - filter_equal_fn(const table_base & t, const table_element & v, unsigned col): - m_col(col), - m_value(static_cast(v)) - { - SASSERT(v <= UINT_MAX); - } - - virtual void operator()(table_base& src) { - skip_table& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_equal(s.get_imdd(), s.m_imdd, m_col, m_value); - } - }; - - table_mutator_fn * skip_table_plugin::mk_filter_equal_fn(const table_base & t, const table_element & value, - unsigned col) { - if (check_kind(t)) { - return alloc(filter_equal_fn, t, value, col); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_not_equal_fn : public table_mutator_fn { - unsigned m_col; - unsigned m_value; - public: - filter_not_equal_fn(const table_base & t, const table_element & v, unsigned col): - m_col(col), - m_value(static_cast(v)) - { - SASSERT(v <= UINT_MAX); - } - - virtual void operator()(table_base& src) { - skip_table& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_disequal(s.get_imdd(), s.m_imdd, m_col, m_value); - } - }; - - table_mutator_fn * skip_table_plugin::mk_filter_not_equal_fn(const table_base & t, const table_element & value, - unsigned col) { - if (check_kind(t)) { - return alloc(filter_not_equal_fn, t, value, col); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_distinct_fn : public table_mutator_fn { - unsigned m_col1; - unsigned m_col2; - public: - filter_distinct_fn(const table_base & t, unsigned col1, unsigned col2): - m_col1(col1), - m_col2(col2) { - } - - virtual void operator()(table_base& src) { - skip_table& s = get(src); - imdd_manager& m = s.get_imdd_manager(); - m.mk_filter_distinct(s.get_imdd(), s.m_imdd, m_col1, m_col2); - } - }; - - table_mutator_fn * skip_table_plugin::mk_filter_distinct_fn(const table_base & t, unsigned col1, unsigned col2) { - if (check_kind(t)) { - return alloc(filter_distinct_fn, t, col1, col2); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - // - // The default implementation uses an iterator - // if the condition is a comparison <, <=, then interval table native will be an advantage. - // - table_mutator_fn * skip_table_plugin::mk_filter_interpreted_fn(const table_base & t, app * condition) { - ast_manager& m = get_ast_manager(); - dl_decl_util& util = get_context().get_decl_util(); - uint64 value; - - if (m.is_eq(condition)) { - expr* x = condition->get_arg(0); - expr* y = condition->get_arg(1); - if (is_var(y)) { - std::swap(x,y); - } - if (is_var(x) && is_var(y)) { - unsigned cols[2] = { to_var(x)->get_idx(), to_var(y)->get_idx() }; - return mk_filter_identical_fn(t, 2, cols); - } - if (is_var(x) && util.is_numeral_ext(y, value)) { - return mk_filter_equal_fn(t, value, to_var(x)->get_idx()); - } - } - - if (m.is_not(condition) && is_app(condition->get_arg(0))) { - condition = to_app(condition->get_arg(0)); - if (m.is_eq(condition)) { - expr* x = condition->get_arg(0); - expr* y = condition->get_arg(1); - if (is_var(y)) { - std::swap(x,y); - } - if (is_var(x) && is_var(y)) { - return mk_filter_distinct_fn(t, to_var(x)->get_idx(), to_var(y)->get_idx()); - } - if (is_var(x) && util.is_numeral_ext(y, value)) { - return mk_filter_not_equal_fn(t, value, to_var(x)->get_idx()); - } - } - } - - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - class skip_table_plugin::filter_by_negation_fn : public convenient_table_negation_filter_fn { - public: - filter_by_negation_fn( - const table_base & tgt, const table_base & neg, - unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) - : convenient_table_negation_filter_fn(tgt, neg, joined_col_cnt, t_cols, negated_cols) { - - } - - // - // Compute - // { (x,y) | t(x,y) & ! exists z . negated_obj(x,z) } - // - // 1. Project z - // 2. Join with result. - // - - virtual void operator()(table_base & tgt0, const table_base & neg0) { - skip_table & tgt = get(tgt0); - const skip_table & neg = get(neg0); - unsigned_vector cols2(m_cols2); - unsigned_vector proj_cols; - table_base* t1 = 0; - if (!m_all_neg_bound) { - unsigned_vector proj_cols, remap; - table_signature sig2; - table_signature const& neg_sig = neg.get_signature(); - for (unsigned i = 0, j = 0; i < m_bound.size(); ++i) { - if (m_bound[i]) { - remap.push_back(j++); - sig2.push_back(neg_sig[i]); - } - else { - proj_cols.push_back(i); - remap.push_back(0); - } - } - for (unsigned i = 0; i < cols2.size(); ++i) { - cols2[i] = remap[cols2[i]]; - } - skip_table* t0 = skip_table_plugin::mk_project(neg, sig2, proj_cols); - t1 = t0->complement(); - t0->deallocate(); - proj_cols.reset(); - } - else { - t1 = neg.complement(); - } - for (unsigned i = 0; i < t1->get_signature().size(); ++i) { - proj_cols.push_back(tgt0.get_signature().size()+i); - } - skip_table* t2 = skip_table_plugin::mk_join_project(tgt0, *t1, tgt0.get_signature(), m_cols1, cols2, proj_cols); - t1->deallocate(); - tgt.update(*t2); - t2->deallocate(); - } - }; - - table_intersection_filter_fn * skip_table_plugin::mk_filter_by_negation_fn( - const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols) { - - if (check_kind(t) && check_kind(negated_obj)) { - return alloc(filter_by_negation_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); - } - TRACE("dl", tout << "could not handle operation\n";); - return 0; - } - - bool skip_table_plugin::can_handle_signature(table_signature const& sig) { - for (unsigned i = 0; i < sig.size(); ++i) { - if (sig[i] >= UINT_MAX) { - return false; - } - } - return true; - } - - // ------------------ - // skip_table - - - skip_table::skip_table(skip_table_plugin & p, const table_signature & sig): - table_base(p, sig), - m_imdd(p.get_imdd_manager().mk_empty(sig.size()), p.get_imdd_manager()) { - SASSERT(well_formed()); - } - - skip_table::skip_table(skip_table_plugin & p, const table_signature & sig, imdd* m): - table_base(p, sig), - m_imdd(m, p.get_imdd_manager()) { - SASSERT(well_formed()); - } - - skip_table::~skip_table() { - } - - - bool skip_table::well_formed() const { - table_signature const& sig = get_signature(); - return - get_plugin().can_handle_signature(sig) && - (get_imdd()->get_arity() == sig.size()); - } - - bool skip_table::empty() const { - return get_imdd()->empty(); - } - - void skip_table::update(imdd* n) { - m_imdd = n; - SASSERT(well_formed()); - } - - void skip_table::add_fact(const table_fact & f) { - imdd_manager& m = get_plugin().get_imdd_manager(); - unsigned const* fact = get_fact(f.c_ptr()); - m.add_fact(get_imdd(), m_imdd, f.size(), fact); - SASSERT(well_formed()); - } - - void skip_table::remove_fact(const table_element* f) { - imdd_manager& m = get_imdd_manager(); - unsigned const* fact = get_fact(f); - m.remove_facts(get_imdd(), m_imdd, get_signature().size(), fact, fact); - } - - bool skip_table::contains_fact(const table_fact & f) const { - imdd_manager& m = get_imdd_manager(); - unsigned const* fact = get_fact(f.c_ptr()); - return m.contains(get_imdd(), f.size(), fact); - } - - table_base * skip_table::clone() const { - return alloc(skip_table, get_plugin(), get_signature(), get_imdd()); - } - - table_base * skip_table::complement() const { - imdd_manager& m = get_plugin().get_imdd_manager(); - table_signature const& sig = get_signature(); - unsigned_vector mins, maxs; - for (unsigned i = 0; i < sig.size(); ++i) { - SASSERT(sig[i] < UINT_MAX); - mins.push_back(0); - maxs.push_back(static_cast(sig[i])); - } - imdd_ref cmpl(m); - m.mk_complement(get_imdd(), cmpl, sig.size(), mins.c_ptr(), maxs.c_ptr()); - return alloc(skip_table, get_plugin(), get_signature(), cmpl); - } - - unsigned const* skip_table::get_fact(table_element const* f) const { - table_signature const& sig = get_signature(); - const_cast(m_fact).reset(); - for (unsigned i = 0; i < sig.size(); ++i) { - const_cast(m_fact).push_back(static_cast(f[i])); - SASSERT(f[i] < UINT_MAX); - } - return m_fact.c_ptr(); - } - - - - class skip_table::our_iterator_core : public table_base::iterator_core { - skip_table const& m_table; - imdd_manager::iterator m_iterator; - - class our_row : public row_interface { - const our_iterator_core & m_parent; - public: - our_row(const our_iterator_core & parent) : row_interface(parent.m_table), m_parent(parent) {} - - virtual void get_fact(table_fact & result) const { - result.reset(); - unsigned arity = m_parent.m_iterator.get_arity(); - unsigned const* values = *(m_parent.m_iterator); - for (unsigned i = 0; i < arity; ++i) { - result.push_back(values[i]); - } - } - virtual table_element operator[](unsigned col) const { - SASSERT(col < m_parent.m_iterator.get_arity()); - unsigned const* values = *(m_parent.m_iterator); - return values[col]; - } - }; - - our_row m_row_obj; - - public: - struct b {}; - struct e {}; - - our_iterator_core(skip_table const& t, b): - m_table(t), - m_iterator(t.m_imdd.get_manager(), t.get_imdd()), - m_row_obj(*this) {} - - our_iterator_core(skip_table const& t, e): - m_table(t), - m_iterator(), - m_row_obj(*this) {} - - virtual bool is_finished() const { - return m_iterator == imdd_manager::iterator(); - } - - virtual row_interface & operator*() { - SASSERT(!is_finished()); - return m_row_obj; - } - - virtual void operator++() { - SASSERT(!is_finished()); - ++m_iterator; - } - }; - - - table_base::iterator skip_table::begin() const { - return mk_iterator(alloc(our_iterator_core, *this, our_iterator_core::b())); - } - - table_base::iterator skip_table::end() const { - return mk_iterator(alloc(our_iterator_core, *this, our_iterator_core::e())); - } - - unsigned skip_table::get_size_estimate_rows() const { - imdd_manager& m = get_plugin().get_imdd_manager(); - size_t sz = m.get_num_rows(get_imdd()); - unsigned sz0 = static_cast(sz); - SASSERT (sz == sz0 && "we need to use size_t or big-ints for row count"); - return sz0; - } - - unsigned skip_table::get_size_estimate_bytes() const { - imdd_manager& m = get_plugin().get_imdd_manager(); - return m.memory(get_imdd()); - } - -}; - -#endif diff --git a/src/muz_qe/dl_skip_table.h b/src/muz_qe/dl_skip_table.h deleted file mode 100644 index 6a9af9636..000000000 --- a/src/muz_qe/dl_skip_table.h +++ /dev/null @@ -1,161 +0,0 @@ -/*++ -Copyright (c) 2010 Microsoft Corporation - -Module Name: - - dl_skip_table.h - -Abstract: - - - -Author: - - Nikolaj Bjorner (nbjorner) - Leonardo de Moura (leonardo) 2010-10-14 - - -Revision History: - ---*/ - -#ifndef _DL_SKIP_TABLE_H_ -#define _DL_SKIP_TABLE_H_ - -#include "dl_base.h" -#include "imdd.h" - -namespace datalog { - class skip_table; - - class skip_table_plugin : public table_plugin { - friend class skip_table; - imdd_manager m_manager; - protected: - class join_fn; - class join_project_fn; - class union_fn; - class transformer_fn; - class rename_fn; - class project_fn; - class filter_equal_fn; - class filter_not_equal_fn; - class filter_identical_fn; - class filter_distinct_fn; - class filter_by_negation_fn; - - imdd_manager& get_imdd_manager() const { return const_cast(m_manager); } - - public: - typedef skip_table table; - - skip_table_plugin(relation_manager & manager) - : table_plugin(symbol("skip"), manager) {} - - virtual table_base * mk_empty(const table_signature & s); - - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual table_join_fn * mk_join_project_fn( - const table_base & t1, const table_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols); - virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta); - virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, - unsigned col); - virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); - virtual table_intersection_filter_fn * mk_filter_by_negation_fn( - const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - - virtual bool can_handle_signature(table_signature const& s); - - private: - static skip_table& get(table_base& r); - - static skip_table const & get(table_base const& r); - - static skip_table* mk_join(table_base const& t1, table_base const& t2, table_signature const& s, - unsigned_vector const& cols_t1, unsigned_vector const& cols_t2); - - static skip_table* mk_join_project(table_base const& t1, table_base const& t2, table_signature const& s, - unsigned_vector const& cols_t1, unsigned_vector const& cols_t2, unsigned_vector const& proj_cols); - - static skip_table* mk_project(table_base const& src, table_signature const& result_sig, - unsigned_vector const& cols); - - virtual table_mutator_fn * mk_filter_distinct_fn(const table_base & t, unsigned col1, unsigned col2); - - virtual table_mutator_fn * mk_filter_not_equal_fn(const table_base & t, const table_element & value, - unsigned col); - - }; - - class skip_table : public table_base { - friend class skip_table_plugin; - friend class skip_table_plugin::join_fn; - friend class skip_table_plugin::union_fn; - friend class skip_table_plugin::transformer_fn; - friend class skip_table_plugin::rename_fn; - friend class skip_table_plugin::project_fn; - friend class skip_table_plugin::filter_equal_fn; - friend class skip_table_plugin::filter_not_equal_fn; - friend class skip_table_plugin::filter_identical_fn; - friend class skip_table_plugin::filter_distinct_fn; - - class our_iterator_core; - - imdd_ref m_imdd; - unsigned_vector m_fact; - - imdd* get_imdd() const { return m_imdd.get(); } - - imdd_manager& get_imdd_manager() const { return get_plugin().get_imdd_manager(); } - - unsigned const* get_fact(table_element const* f) const; - - bool well_formed() const; - - void update(imdd* n); - - void update(skip_table& t) { update(t.m_imdd); } - - skip_table(skip_table_plugin & p, const table_signature & sig); - - skip_table(skip_table_plugin & p, const table_signature & sig, imdd*); - - skip_table(const skip_table & t); - - virtual ~skip_table(); - - public: - - skip_table_plugin & get_plugin() const { - return static_cast(table_base::get_plugin()); - } - - virtual bool empty() const; - virtual void add_fact(const table_fact & f); - virtual void remove_fact(const table_element * fact); - virtual bool contains_fact(const table_fact & f) const; - virtual table_base * complement() const; - virtual table_base * clone() const; - - virtual iterator begin() const; - virtual iterator end() const; - - virtual unsigned get_size_estimate_rows() const; - virtual unsigned get_size_estimate_bytes() const; - }; - - }; - - #endif /* _DL_SKIP_TABLE_H_ */ diff --git a/src/muz_qe/dl_sparse_table.cpp b/src/muz_qe/dl_sparse_table.cpp index 1449b7d3d..b912ef136 100644 --- a/src/muz_qe/dl_sparse_table.cpp +++ b/src/muz_qe/dl_sparse_table.cpp @@ -500,6 +500,9 @@ namespace datalog { char * reserve = m_data.get_reserve_ptr(); unsigned col_cnt = m_column_layout.size(); for(unsigned i=0; i= get_signature()[i]) { + std::cout << f[i] << " " << get_signature()[i] << "\n"; + } SASSERT(f[i]is_shared()) { - if (cache.find(d, r) && (r == 0 || !r->is_dead())) - return true; - } - return false; -} - -inline void cache_result(imdd2imdd_cache & cache, imdd * d, imdd * r) { - if (d->is_shared()) - cache.insert(d, r); -} - -inline bool is_cached(imdd_pair2imdd_cache & cache, imdd * d1, imdd * d2, imdd * & r) { - if (d1->is_shared() && d2->is_shared()) { - if (cache.find(d1, d2, r) && (r == 0 || !r->is_dead())) - return true; - } - return false; -} - -inline void cache_result(imdd_pair2imdd_cache & cache, imdd * d1, imdd * d2, imdd * r) { - if (d1->is_shared() && d2->is_shared()) - cache.insert(d1, d2, r); -} - -inline bool destructive_update_at(bool destructive, imdd * d) { - return destructive && !d->is_memoized() && !d->is_shared(); -} - -void sl_imdd_manager::inc_ref_eh(imdd * v) { - m_manager->inc_ref(v); -} - -void sl_imdd_manager::dec_ref_eh(imdd * v) { - m_manager->dec_ref(v); -} - -unsigned imdd::hc_hash() const { - unsigned r = 0; - r = get_arity(); - imdd_children::iterator it = begin_children(); - imdd_children::iterator end = end_children(); - for (; it != end; ++it) { - unsigned b = it->begin_key(); - unsigned e = it->end_key(); - imdd const * child = it->val(); - mix(b, e, r); - if (child) { - SASSERT(child->is_memoized()); - unsigned v = child->get_id(); - mix(v, v, r); - } - } - return r; -} - -bool imdd::hc_equal(imdd const * other) const { - if (m_arity != other->m_arity) - return false; - return m_children.is_equal(other->m_children); -} - -imdd_manager::delay_dealloc::~delay_dealloc() { - SASSERT(m_manager.m_to_delete.size() >= m_to_delete_size); - ptr_vector::iterator it = m_manager.m_to_delete.begin() + m_to_delete_size; - ptr_vector::iterator end = m_manager.m_to_delete.end(); - for (; it != end; ++it) { - SASSERT((*it)->is_dead()); - m_manager.deallocate_imdd(*it); - } - m_manager.m_to_delete.shrink(m_to_delete_size); - m_manager.m_delay_dealloc = m_delay_dealloc_value; -} - -imdd_manager::imdd_manager(): - m_sl_manager(m_alloc), - m_simple_max_entries(DEFAULT_SIMPLE_MAX_ENTRIES), - m_delay_dealloc(false) { - m_sl_manager.m_manager = this; - m_swap_new_child = 0; -} - -imdd * imdd_manager::_mk_empty(unsigned arity) { - SASSERT(arity > 0); - void * mem = m_alloc.allocate(sizeof(imdd)); - return new (mem) imdd(m_sl_manager, m_id_gen.mk(), arity); -} - -imdd * imdd_manager::defrag_core(imdd * d) { - if (d->is_memoized()) - return d; - unsigned h = d->get_arity(); - if (h == 1 && !is_simple_node(d)) - return d; - imdd * new_d = 0; - if (is_cached(m_defrag_cache, d, new_d)) - return new_d; - - if (h == 1) { - SASSERT(is_simple_node(d)); - imdd * new_can_d = memoize(d); - cache_result(m_defrag_cache, d, new_can_d); - return new_can_d; - } - - SASSERT(h > 1); - new_d = _mk_empty(h); - imdd_children::push_back_proc push_back(m_sl_manager, new_d->m_children); - bool has_new = false; - bool children_memoized = true; - - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * child = it->val(); - imdd * new_child = defrag_core(child); - if (child != new_child) - has_new = true; - if (!new_child->is_memoized()) - children_memoized = false; - push_back(it->begin_key(), it->end_key(), new_child); - } - - if (has_new) { - if (children_memoized && is_simple_node(new_d)) { - imdd * new_can_d = memoize(new_d); - if (new_can_d != new_d) { - SASSERT(new_d->get_ref_count() == 0); - delete_imdd(new_d); - } - new_d = new_can_d; - } - } - else { - SASSERT(!has_new); - delete_imdd(new_d); - new_d = d; - if (children_memoized && is_simple_node(new_d)) { - new_d = memoize(new_d); - } - } - - cache_result(m_defrag_cache, d, new_d); - return new_d; -} - -/** - \brief Compress the given IMDD by using hash-consing. -*/ -void imdd_manager::defrag(imdd_ref & d) { - delay_dealloc delay(*this); - m_defrag_cache.reset(); - d = defrag_core(d); -} - -/** - \brief Memoize the given IMDD. - Return an IMDD structurally equivalent to d. - This method assumes the children of d are memoized. - If that is not the case, the user should invoke defrag instead. -*/ -imdd * imdd_manager::memoize(imdd * d) { - if (d->is_memoized()) - return d; - unsigned h = d->get_arity(); - m_tables.reserve(h); - DEBUG_CODE({ - if (h > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - SASSERT(it->val()->is_memoized()); - } - } - }); - imdd * r = m_tables[h-1].insert_if_not_there(d); - if (r == d) { - r->mark_as_memoized(); - } - SASSERT(r->is_memoized()); - return r; -} - -/** - \brief Remove the given IMDD from the hash-consing table -*/ -void imdd_manager::unmemoize(imdd * d) { - SASSERT(d->is_memoized()); - m_tables[d->get_arity()-1].erase(d); - d->mark_as_memoized(false); -} - - -/** - \brief Remove the given IMDD (and its children) from the hash-consing tables. -*/ -void imdd_manager::unmemoize_rec(imdd * d) { - SASSERT(m_worklist.empty()); - m_worklist.push_back(d); - while (!m_worklist.empty()) { - d = m_worklist.back(); - if (d->is_memoized()) { - unmemoize(d); - SASSERT(!d->is_memoized()); - if (d->get_arity() > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) - m_worklist.push_back(it->val()); - } - } - } -} - -bool imdd_manager::is_simple_node(imdd * d) const { - return !d->m_children.has_more_than_k_entries(m_simple_max_entries); -} - -void imdd_manager::mark_as_dead(imdd * d) { - // The references to the children were decremented by delete_imdd. - SASSERT(!d->is_dead()); - d->m_children.deallocate_no_decref(m_sl_manager); - d->mark_as_dead(); - if (m_delay_dealloc) - m_to_delete.push_back(d); - else - deallocate_imdd(d); -} - -void imdd_manager::deallocate_imdd(imdd * d) { - SASSERT(d->is_dead()); - memset(d, 0, sizeof(*d)); - m_alloc.deallocate(sizeof(imdd), d); -} - -void imdd_manager::delete_imdd(imdd * d) { - SASSERT(m_worklist.empty()); - m_worklist.push_back(d); - - while (!m_worklist.empty()) { - d = m_worklist.back(); - m_worklist.pop_back(); - - m_id_gen.recycle(d->get_id()); - - SASSERT(d->get_ref_count() == 0); - if (d->is_memoized()) { - unsigned arity = d->get_arity(); - SASSERT(m_tables[arity-1].contains(d)); - m_tables[arity-1].erase(d); - } - - if (d->get_arity() > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * child = it->val(); - SASSERT(child); - child->dec_ref(); - if (child->get_ref_count() == 0) - m_worklist.push_back(child); - } - } - - mark_as_dead(d); - } -} - -/** - \brief Return a (non-memoized) shallow copy of d. -*/ -imdd * imdd_manager::copy_main(imdd * d) { - imdd * d_copy = _mk_empty(d->get_arity()); - d_copy->m_children.copy(m_sl_manager, d->m_children); - SASSERT(!d_copy->is_memoized()); - SASSERT(d_copy->get_ref_count() == 0); - return d_copy; -} - -/** - \brief Insert the values [b, e] into a IMDD of arity 1. - - If destructive == true, d is not memoized and is not shared, then a - destructive update is performed, and d is returned. - - Otherwise, a fresh IMDD is returned. -*/ -imdd * imdd_manager::insert_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == 1); - if (destructive_update_at(destructive, d)) { - add_child(d, b, e, 0); - return d; - } - else { - imdd * new_d = copy_main(d); - add_child(new_d, b, e, 0); - return memoize_new_imdd_if(memoize_res, new_d); - } -} - - -/** - \brief Remove the values [b, e] from an IMDD of arity 1. - - If destructive == true, d is not memoized and is not shared, then a - destructive update is performed, and d is returned. - - Otherwise, a fresh IMDD is returned. -*/ -imdd * imdd_manager::remove_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == 1); - if (destructive_update_at(destructive, d)) { - remove_child(d, b, e); - return d; - } - else { - imdd * new_d = copy_main(d); - remove_child(new_d, b, e); - return memoize_new_imdd_if(memoize_res, new_d); - } -} - -/** - \brief Auxiliary functor used to implement destructive version of mk_product. -*/ -struct imdd_manager::null2imdd_proc { - imdd * m_d2; - null2imdd_proc(imdd * d2):m_d2(d2) {} - imdd * operator()(imdd * d) { SASSERT(d == 0); return m_d2; } -}; - -/** - \brief Auxiliary functor used to implement destructive version of mk_product. -*/ -struct imdd_manager::mk_product_proc { - imdd_manager & m_manager; - imdd * m_d2; - bool m_memoize; - mk_product_proc(imdd_manager & m, imdd * d2, bool memoize): - m_manager(m), - m_d2(d2), - m_memoize(memoize) { - } - imdd * operator()(imdd * d1) { return m_manager.mk_product_core(d1, m_d2, true, m_memoize); } -}; - -imdd * imdd_manager::mk_product_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - if (destructive && !d1->is_shared()) { - if (d1->is_memoized()) - unmemoize(d1); - - if (d1->get_arity() == 1) { - null2imdd_proc f(d2); - d1->m_children.update_values(m_sl_manager, f); - d1->m_arity += d2->m_arity; - } - else { - mk_product_proc f(*this, d2, memoize_res); - d1->m_children.update_values(m_sl_manager, f); - d1->m_arity += d2->m_arity; - } - return d1; - } - else { - imdd * new_d1 = 0; - if (is_cached(m_mk_product_cache, d1, new_d1)) - return new_d1; - unsigned arity1 = d1->get_arity(); - unsigned arity2 = d2->get_arity(); - new_d1 = _mk_empty(arity1 + arity2); - imdd_children::push_back_proc push_back(m_sl_manager, new_d1->m_children); - imdd_children::iterator it = d1->begin_children(); - imdd_children::iterator end = d1->end_children(); - bool children_memoized = true; - for (; it != end; ++it) { - imdd * new_child; - if (arity1 == 1) - new_child = d2; - else - new_child = mk_product_core(it->val(), d2, false, memoize_res); - if (!new_child->is_memoized()) - children_memoized = false; - push_back(it->begin_key(), it->end_key(), new_child); - } - new_d1 = memoize_new_imdd_if(memoize_res && children_memoized, new_d1); - cache_result(m_mk_product_cache, d1, new_d1); - return new_d1; - } -} - -imdd * imdd_manager::mk_product_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - if (d1->empty() || d2->empty()) { - unsigned a1 = d1->get_arity(); - unsigned a2 = d2->get_arity(); - return _mk_empty(a1 + a2); - } - delay_dealloc delay(*this); - if (d1 == d2) - destructive = false; - m_mk_product_cache.reset(); - return mk_product_core(d1, d2, destructive, memoize_res); -} - -void imdd_manager::init_add_facts_new_children(unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res) { - if (m_add_facts_new_children[num] != 0) { - DEBUG_CODE({ - for (unsigned i = 1; i <= num; i++) { - SASSERT(m_add_facts_new_children[i] != 0); - }}); - return; - } - for (unsigned i = 1; i <= num; i++) { - if (m_add_facts_new_children[i] == 0) { - imdd * new_child = _mk_empty(i); - unsigned b = lowers[num - i]; - unsigned e = uppers[num - i]; - bool prev_memoized = true; - if (i == 1) { - add_child(new_child, b, e, 0); - } - else { - SASSERT(m_add_facts_new_children[i-1] != 0); - prev_memoized = m_add_facts_new_children[i-1]->is_memoized(); - add_child(new_child, b, e, m_add_facts_new_children[i-1]); - } - new_child = memoize_new_imdd_if(memoize_res && prev_memoized, new_child); - m_add_facts_new_children[i] = new_child; - } - } - DEBUG_CODE({ - for (unsigned i = 1; i <= num; i++) { - SASSERT(m_add_facts_new_children[i] != 0); - }}); -} - -imdd * imdd_manager::add_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - imdd_ref new_child(*this); - bool new_children_memoized = true; -#define INIT_NEW_CHILD() { \ - if (new_child == 0) { \ - init_add_facts_new_children(num - 1, lowers + 1, uppers + 1, memoize_res); \ - new_child = m_add_facts_new_children[num-1]; \ - if (!new_child->is_memoized()) new_children_memoized = false; \ - }} - - if (destructive_update_at(destructive, d)) { - if (num == 1) - return insert_main(d, *lowers, *uppers, destructive, memoize_res); - SASSERT(num > 1); - sbuffer to_insert; - new_child = m_add_facts_new_children[num-1]; - - unsigned b = *lowers; - unsigned e = *uppers; - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->end_children(); - for (; it != end && b <= e; ++it) { - imdd_children::entry const & curr_entry = *it; - if (e < curr_entry.begin_key()) - break; - if (b < curr_entry.begin_key()) { - INIT_NEW_CHILD(); - SASSERT(b <= curr_entry.begin_key() - 1); - to_insert.push_back(entry(b, curr_entry.begin_key() - 1, new_child)); - b = curr_entry.begin_key(); - } - imdd * curr_child = curr_entry.val(); - SASSERT(b >= curr_entry.begin_key()); - bool cover = b == curr_entry.begin_key() && e >= curr_entry.end_key(); - // If cover == true, then the curr_child is completely covered by the new facts, and it is not needed anymore. - // So, we can perform a destructive update. - imdd * new_curr_child = add_facts_core(curr_child, num - 1, lowers + 1, uppers + 1, cover, memoize_res); - - if (e >= curr_entry.end_key()) { - SASSERT(b <= curr_entry.end_key()); - to_insert.push_back(entry(b, curr_entry.end_key(), new_curr_child)); - } - else { - SASSERT(e < curr_entry.end_key()); - SASSERT(b <= e); - to_insert.push_back(entry(b, e, new_curr_child)); - } - b = curr_entry.end_key() + 1; - } - // Re-insert entries in m_add_facts_to_insert into d->m_children, - // and if b <= e also insert [b, e] -> new_child - if (b <= e) { - INIT_NEW_CHILD(); - add_child(d, b, e, new_child); - } - - svector::iterator it2 = to_insert.begin(); - svector::iterator end2 = to_insert.end(); - for (; it2 != end2; ++it2) { - imdd_children::entry const & curr_entry = *it2; - add_child(d, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val()); - } - - return d; - } - else { - imdd * new_d = 0; - if (is_cached(m_add_facts_cache, d, new_d)) - return new_d; - - if (num == 1) { - new_d = insert_main(d, *lowers, *uppers, destructive, memoize_res); - } - else { - new_d = copy_main(d); - new_child = m_add_facts_new_children[num-1]; - TRACE("add_facts_bug", tout << "after copying: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n";); - - unsigned b = *lowers; - unsigned e = *uppers; - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->end_children(); - for (; it != end && b <= e; ++it) { - imdd_children::entry const & curr_entry = *it; - if (e < curr_entry.begin_key()) - break; - if (b < curr_entry.begin_key()) { - INIT_NEW_CHILD(); - SASSERT(b <= curr_entry.begin_key() - 1); - add_child(new_d, b, curr_entry.begin_key() - 1, new_child); - TRACE("add_facts_bug", tout << "after inserting new child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n";); - b = curr_entry.begin_key(); - } - imdd * curr_child = curr_entry.val(); - imdd * new_curr_child = add_facts_core(curr_child, num - 1, lowers + 1, uppers + 1, false, memoize_res); - if (!new_curr_child->is_memoized()) - new_children_memoized = false; - if (e >= curr_entry.end_key()) { - SASSERT(b <= curr_entry.end_key()); - add_child(new_d, b, curr_entry.end_key(), new_curr_child); - TRACE("add_facts_bug", tout << "1) after inserting new curr child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n"; - tout << "new_curr_child: " << mk_ll_pp(new_curr_child, *this) << "\n";); - } - else { - SASSERT(e < curr_entry.end_key()); - SASSERT(b <= e); - add_child(new_d, b, e, new_curr_child); - TRACE("add_facts_bug", tout << "2) after inserting new curr child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n"; - tout << "new_curr_child: " << mk_ll_pp(new_curr_child, *this) << "\n";); - } - b = curr_entry.end_key() + 1; - } - if (b <= e) { - INIT_NEW_CHILD(); - add_child(new_d, b, e, new_child); - } - - new_d = memoize_new_imdd_if(memoize_res && d->is_memoized() && new_children_memoized, new_d); - } - - cache_result(m_add_facts_cache, d, new_d); - return new_d; - } -} - -imdd * imdd_manager::add_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - delay_dealloc delay(*this); - m_add_facts_cache.reset(); - m_add_facts_new_children.reset(); - m_add_facts_new_children.resize(num, 0); - return add_facts_core(d, num, lowers, uppers, destructive, memoize_res); -} - -inline void update_memoized_flag(imdd * d, bool & memoized_flag) { - if (d && !d->is_memoized()) - memoized_flag = false; -} - -void imdd_manager::push_back_entries(unsigned head, imdd_children::iterator & it, imdd_children::iterator & end, - imdd_children::push_back_proc & push_back, bool & children_memoized) { - if (it != end) { - push_back(head, it->end_key(), it->val()); - update_memoized_flag(it->val(), children_memoized); - ++it; - for (; it != end; ++it) { - update_memoized_flag(it->val(), children_memoized); - push_back(it->begin_key(), it->end_key(), it->val()); - } - } -} - -/** - \brief Push the entries starting at head upto the given limit (no included). - That is, we are copying the entries in the interval [head, limit). -*/ -void imdd_manager::push_back_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, - unsigned limit, imdd_children::push_back_proc & push_back, bool & children_memoized) { - SASSERT(it != end); - SASSERT(head <= it->end_key()); - SASSERT(head >= it->begin_key()); - SASSERT(head < limit); - while (head < limit && it != end) { - if (it->end_key() < limit) { - update_memoized_flag(it->val(), children_memoized); - push_back(head, it->end_key(), it->val()); - ++it; - if (it != end) - head = it->begin_key(); - } - else { - SASSERT(it->end_key() >= limit); - update_memoized_flag(it->val(), children_memoized); - push_back(head, limit-1, it->val()); - head = limit; - } - } - SASSERT(head == limit || it == end || head == it->begin_key()); -} - -void imdd_manager::move_head(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned new_head) { - SASSERT(new_head >= head); - SASSERT(head >= it->begin_key()); - SASSERT(head <= it->end_key()); - SASSERT(new_head <= it->end_key()); - if (new_head < it->end_key()) { - head = new_head+1; - SASSERT(head <= it->end_key()); - } - else { - SASSERT(new_head == it->end_key()); - ++it; - if (it != end) - head = it->begin_key(); - } -} - -/** - \brief Copy the entries starting at head upto the given limit (no included). - That is, we are copying the entries in the interval [head, limit). -*/ -void imdd_manager::copy_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, - unsigned limit, sbuffer & result) { - SASSERT(it != end); - SASSERT(head <= it->end_key()); - SASSERT(head >= it->begin_key()); - SASSERT(head < limit); - while (head < limit && it != end) { - if (it->end_key() < limit) { - result.push_back(entry(head, it->end_key(), it->val())); - ++it; - if (it != end) - head = it->begin_key(); - } - else { - SASSERT(it->end_key() >= limit); - result.push_back(entry(head, limit-1, it->val())); - head = limit; - } - } - SASSERT(head == limit || it == end || head == it->begin_key()); -} - -imdd * imdd_manager::mk_union_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - if (d1 == d2) - return d1; - if (destructive_update_at(destructive, d1)) { - if (d1->get_arity() == 1) { - imdd_children::iterator it = d2->begin_children(); - imdd_children::iterator end = d2->end_children(); - for (; it != end; ++it) - add_child(d1, it->begin_key(), it->end_key(), 0); - return d1; - } - else { - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - imdd_children::iterator it1 = d1->m_children.find_geq(it2->begin_key()); - imdd_children::iterator end1 = d1->end_children(); - sbuffer to_insert; - unsigned head1 = it1 != end1 ? it1->begin_key() : UINT_MAX; - SASSERT(it2 != end2); - unsigned head2 = it2->begin_key(); - while (true) { - if (it1 == end1) { - // copy it2 to d1 - // Remark: we don't need to copy to to_insert, since we will not be using it1 anymore. - // That is, we can directly insert into d1->m_children. - if (it2 != end2) { - add_child(d1, head2, it2->end_key(), it2->val()); - ++it2; - for (; it2 != end2; ++it2) - add_child(d1, it2->begin_key(), it2->end_key(), it2->val()); - } - break; - } - - if (it2 == end2) { - break; - } - - if (head1 < head2) { - it1.move_to(head2); - head1 = it1 != end1 ? (it1->begin_key() < head2?head2:it1->begin_key()): UINT_MAX; - } - else if (head1 > head2) { - copy_upto(head2, it2, end2, head1, to_insert); - } - else { - SASSERT(head1 == head2); - unsigned tail = std::min(it1->end_key(), it2->end_key()); - imdd * new_child = 0; - SASSERT(d1->get_arity() > 1); - bool cover = head1 == it1->begin_key() && tail == it1->end_key(); - // If cover == true, then the it1->val() (curr_child) is completely covered by - // the new_child, and it is not needed anymore. - // So, we can perform a destructive update. - new_child = mk_union_core(it1->val(), it2->val(), cover, memoize_res); - if (new_child != it1->val()) - to_insert.push_back(entry(head1, tail, new_child)); - move_head(head1, it1, end1, tail); - move_head(head2, it2, end2, tail); - } - } - sbuffer::const_iterator it3 = to_insert.begin(); - sbuffer::const_iterator end3 = to_insert.end(); - for (; it3 != end3; ++it3) - add_child(d1, it3->begin_key(), it3->end_key(), it3->val()); - return d1; - } - } - else { - imdd * r = 0; - if (is_cached(m_union_cache, d1, d2, r)) - return r; - - unsigned arity = d1->get_arity(); - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it1 = d1->begin_children(); - imdd_children::iterator end1 = d1->end_children(); - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - SASSERT(it1 != end1); - SASSERT(it2 != end2); - unsigned head1 = it1->begin_key(); - unsigned head2 = it2->begin_key(); - bool children_memoized = true; - while (true) { - if (it1 == end1) { - // copy it2 to result - push_back_entries(head2, it2, end2, push_back, children_memoized); - break; - } - if (it2 == end2) { - // copy it1 to result - push_back_entries(head1, it1, end1, push_back, children_memoized); - break; - } - - if (head1 < head2) { - push_back_upto(head1, it1, end1, head2, push_back, children_memoized); - } - else if (head1 > head2) { - push_back_upto(head2, it2, end2, head1, push_back, children_memoized); - } - else { - SASSERT(head1 == head2); - unsigned tail = std::min(it1->end_key(), it2->end_key()); - imdd * new_child = 0; - if (arity > 1) { - new_child = mk_union_core(it1->val(), it2->val(), false, memoize_res); - update_memoized_flag(new_child, children_memoized); - } - push_back(head1, tail, new_child); - move_head(head1, it1, end1, tail); - move_head(head2, it2, end2, tail); - } - } - r = memoize_new_imdd_if(memoize_res && children_memoized, r); - cache_result(m_union_cache, d1, d2, r); - return r; - } -} - -void imdd_manager::reset_union_cache() { - m_union_cache.reset(); -} - -imdd * imdd_manager::mk_union_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2) - return d1; - if (d1->empty()) - return d2; - if (d2->empty()) - return d1; - delay_dealloc delay(*this); - reset_union_cache(); - return mk_union_core(d1, d2, destructive, memoize_res); -} - -void imdd_manager::mk_union_core_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2 || d2->empty()) - return; - if (d1->empty()) { - d1 = d2; - return; - } - d1 = mk_union_core(d1, d2, true, memoize_res); -} - -void imdd_manager::mk_union_core(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2 || d2->empty()) { - r = d1; - return; - } - if (d1->empty()) { - r = d2; - return; - } - TRACE("mk_union_core", - tout << "d1:\n"; - display_ll(tout, d1); - tout << "d2:\n"; - display_ll(tout, d2);); - r = mk_union_core(d1, d2, false, memoize_res); -} - -imdd * imdd_manager::mk_complement_core(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - SASSERT(!d->empty()); - SASSERT(*mins <= *maxs); - unsigned arity = d->get_arity(); - imdd_ref new_child(*this); - bool new_children_memoized = true; -#undef INIT_NEW_CHILD -#define INIT_NEW_CHILD() { \ - if (arity > 1 && new_child == 0) { \ - init_add_facts_new_children(num - 1, mins + 1, maxs + 1, memoize_res); \ - new_child = m_add_facts_new_children[num-1]; \ - SASSERT(new_child != 0); \ - if (!new_child->is_memoized()) new_children_memoized = false; \ - }} - - if (false && destructive_update_at(destructive, d)) { - // TODO - NOT_IMPLEMENTED_YET(); - return 0; - } - else { - destructive = false; - imdd * r = 0; - if (is_cached(m_complement_cache, d, r)) - return r; - - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - unsigned prev_key = *mins; - for (; it != end; ++it) { - SASSERT(it->begin_key() >= *mins); - SASSERT(it->end_key() <= *maxs); - if (prev_key < it->begin_key()) { - INIT_NEW_CHILD(); - push_back(prev_key, it->begin_key() - 1, new_child); - } - if (arity > 1) { - imdd * new_curr = mk_complement_core(it->val(), num - 1, mins + 1, maxs + 1, false, memoize_res); - if (new_curr != 0) { - push_back(it->begin_key(), it->end_key(), new_curr); - if (!new_curr->is_memoized()) - new_children_memoized = false; - } - } - prev_key = it->end_key() + 1; - } - - if (prev_key <= *maxs) { - INIT_NEW_CHILD(); - push_back(prev_key, *maxs, new_child); - } - - if (r->empty()) { - delete_imdd(r); - r = 0; - } - - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_complement_cache, d, r); - return r; - } -} - -imdd * imdd_manager::mk_complement_main(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res) { - unsigned arity = d->get_arity(); - SASSERT(arity == num); - // reuse m_add_facts_new_children for creating the universe-set IMDDs - m_add_facts_new_children.reset(); - m_add_facts_new_children.resize(num+1, 0); - - if (d->empty()) { - // return the universe-set - init_add_facts_new_children(num, mins, maxs, memoize_res); - return m_add_facts_new_children[num]; - } - - delay_dealloc delay(*this); - m_complement_cache.reset(); - imdd * r = mk_complement_core(d, num, mins, maxs, destructive, memoize_res); - if (r == 0) - return _mk_empty(arity); - else - return r; -} - -/** - \brief Replace the IMDD children with new_children. - The function will also decrement the ref-counter of every IMDD in new_children, since - it assumes the counter was incremented when the IMDD was inserted into the buffer. -*/ -void imdd::replace_children(sl_imdd_manager & m, sbuffer & new_children) { - m_children.reset(m); - imdd_children::push_back_proc push_back(m, m_children); - svector::iterator it = new_children.begin(); - svector::iterator end = new_children.end(); - for (; it != end; ++it) { - SASSERT(it->val() == 0 || it->val()->get_ref_count() > 0); - push_back(it->begin_key(), it->end_key(), it->val()); - SASSERT(it->val() == 0 || it->val()->get_ref_count() > 1); - if (it->val() != 0) - it->val()->dec_ref(); - } -} - -imdd * imdd_manager::mk_filter_equal_core(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res) { - SASSERT(!d->empty()); - SASSERT(vidx >= 0 && vidx < d->get_arity()); - unsigned arity = d->get_arity(); - if (destructive_update_at(destructive, d)) { - if (vidx == 0) { - imdd * child = 0; - if (d->m_children.find(value, child)) { - imdd_ref ref(*this); - ref = child; // protect child, we don't want the following reset to delete it. - d->m_children.reset(m_sl_manager); - add_child(d, value, child); - return d; - } - else { - return 0; - } - } - SASSERT(arity > 1); - sbuffer to_insert; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = mk_filter_equal_core(curr_child, vidx-1, value, true, memoize_res); - if (new_child != 0) { - new_child->inc_ref(); // protect new child, we will be resetting d->m_children later. - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - } - if (to_insert.empty()) { - return 0; - } - d->replace_children(m_sl_manager, to_insert); - return d; - } - - imdd * r = 0; - if (is_cached(m_filter_equal_cache, d, r)) - return r; - bool new_children_memoized = true; - - if (vidx == 0) { - // found filter variable - imdd * child = 0; - if (d->m_children.find(value, child)) { - r = _mk_empty(arity); - add_child(r, value, child); - if (child && !child->is_memoized()) - new_children_memoized = false; - } - else { - r = 0; - } - } - else { - SASSERT(arity > 1); - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = mk_filter_equal_core(curr_child, vidx-1, value, false, memoize_res); - if (new_child != 0) { - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - } - - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_filter_equal_cache, d, r); - return r; -} - -imdd * imdd_manager::mk_filter_equal_main(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res) { - unsigned arity = d->get_arity(); - SASSERT(vidx >= 0 && vidx < arity); - if (d->empty()) - return d; - delay_dealloc delay(*this); - m_filter_equal_cache.reset(); - imdd * r = mk_filter_equal_core(d, vidx, value, destructive, memoize_res); - if (r == 0) - return _mk_empty(arity); - else - return r; -} - - -/** - \brief create map from imdd nodes to the interval of variables that are covered by variable. - -*/ - -static void mk_interval_set_intersect(sl_manager_base &m, sl_interval_set const& src, unsigned b, unsigned e, sl_interval_set& dst) { - sl_interval_set::iterator it = src.find_geq(b); - sl_interval_set::iterator end = src.end(); - for (; it != end; ++it) { - unsigned b1 = it->begin_key(); - unsigned e1 = it->end_key(); - if (e < b1) { - break; - } - if (b1 < b) { - b1 = b; - } - if (e < e1) { - e1 = e; - } - SASSERT(b <= b1 && b1 <= e1 && e1 <= e); - dst.insert(m, b1, e1); - } -} - -static void mk_interval_set_union(sl_manager_base &m, sl_interval_set& dst, sl_interval_set const& src) { - sl_interval_set::iterator it = src.begin(), end = src.end(); - for (; it != end; ++it) { - dst.insert(m, it->begin_key(), it->end_key()); - } -} - -void imdd_manager::reset_fi_intervals(sl_imanager& m) { - for (unsigned i = 0; i < m_alloc_is.size(); ++i) { - m_alloc_is[i]->deallocate(m); - dealloc(m_alloc_is[i]); - } - m_alloc_is.reset(); - m_imdd2interval_set.reset(); -} - -sl_interval_set const* imdd_manager::init_fi_intervals(sl_imanager& m, imdd* d, unsigned var, unsigned num_found) { - sl_interval_set* result = 0; -#define ALLOC_RESULT() if (!result) { result = alloc(sl_interval_set, m); m_alloc_is.push_back(result); } - if (m_imdd2interval_set.find(d, result)) { - return result; - } - bool _is_fi_var = is_fi_var(var); - unsigned new_num_found = _is_fi_var?num_found+1:num_found; - bool last_fi_var = (new_num_found == m_fi_num_vars); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd_children::entry const & curr_entry = *it; - unsigned b = curr_entry.begin_key(); - unsigned e = curr_entry.end_key(); - - if (last_fi_var) { - ALLOC_RESULT(); - result->insert(m, b, e, 1); - } - else { - SASSERT(d->get_arity() > 1); - imdd* curr_child = curr_entry.val(); - sl_interval_set const* is2 = init_fi_intervals(m, curr_child, var+1, new_num_found); - if (!is2) { - continue; - } - if (_is_fi_var) { - sl_interval_set is3(m); - mk_interval_set_intersect(m, *is2, b, e, is3); - if (!is3.empty()) { - ALLOC_RESULT(); - mk_interval_set_union(m, *result, is3); - } - is3.deallocate(m); - } - else { - if (is2 && result != is2) { - ALLOC_RESULT(); - mk_interval_set_union(m, *result, *is2); - } - } - } - } - m_imdd2interval_set.insert(d, result); - return result; -} - -inline mk_fi_result mk(imdd * d) { - mk_fi_result r; - r.m_d = d; - return r; -} - -inline mk_fi_result mk(fi_cache_entry * e) { - mk_fi_result r; - r.m_entry = e; - return r; -} - -fi_cache_entry * imdd_manager::mk_fi_cache_entry(imdd * d, unsigned lower, unsigned upper, unsigned num_pairs, imdd_value_pair pairs[]) { - void * mem = m_fi_entries.allocate(sizeof(fi_cache_entry) + sizeof(imdd_value_pair) * num_pairs); - DEBUG_CODE({ - for (unsigned i = 0; i < num_pairs; i++) { - SASSERT(pairs[i].first != 0); - } - }); - return new (mem) fi_cache_entry(d, lower, upper, num_pairs, pairs); -} - -struct imdd_value_pair_lt_value { - bool operator()(imdd_value_pair const & p1, imdd_value_pair const & p2) const { - return p1.second < p2.second; - } -}; - -// Review note: identical nodes should be unit intervals? -// 1 -> 1 -> x - -mk_fi_result imdd_manager::mk_filter_identical_core(imdd * d, unsigned var, unsigned num_found, unsigned lower, unsigned upper, - bool destructive, bool memoize_res) { - TRACE("imdd", tout << "#" << d->get_id() << " var: " << var << " num_found: " << num_found << - " lower: " << lower << " upper: " << upper << "\n";); - - unsigned arity = d->get_arity(); -#define MK_EMPTY_ENTRY (num_found == 0 && destructive_update_at(destructive, d))?mk(static_cast(0)):mk(static_cast(0)) - sl_interval_set* node_ranges = m_imdd2interval_set.find(d); - if (!node_ranges) { - return MK_EMPTY_ENTRY; - } - sl_interval_set::iterator sl_it = node_ranges->find_geq(lower); - if (sl_it == node_ranges->end() || upper < sl_it->begin_key()) { - return MK_EMPTY_ENTRY; - } - - bool new_children_memoized = true; - bool _is_fi_var = is_fi_var(var); - if (num_found == 0) { - SASSERT(arity > 1); - if (destructive_update_at(destructive, d)) { - sbuffer to_insert; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - if (_is_fi_var) { - fi_cache_entry * child_entry = (mk_filter_identical_core(curr_child, - var+1, - 1, - it->begin_key(), - it->end_key(), - false, - memoize_res)).m_entry; - for (unsigned i = 0; child_entry && i < child_entry->m_num_result; i++) { - imdd * new_child = child_entry->m_result[i].first; - unsigned value = child_entry->m_result[i].second; - to_insert.push_back(entry(value, value, new_child)); - } - } - else { - imdd * new_child = (mk_filter_identical_core(curr_child, var+1, 0, 0, UINT_MAX, false, memoize_res)).m_d; - if (new_child != 0) { - new_child->inc_ref(); - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - } - } - if (to_insert.empty()) { - return mk(static_cast(0)); - } - d->replace_children(m_sl_manager, to_insert); - return mk(d); - } - else { - // num_found == 0 && variable is not part of the filter && no destructive update. - imdd * r = 0; - if (is_cached(m_fi_top_cache, d, r)) - return mk(r); - r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - if (_is_fi_var) { - fi_cache_entry * entry = (mk_filter_identical_core(curr_child, - var+1, - 1, - it->begin_key(), - it->end_key(), - false, - memoize_res)).m_entry; - for (unsigned i = 0; entry && i < entry->m_num_result; i++) { - imdd * new_child = entry->m_result[i].first; - unsigned value = entry->m_result[i].second; - push_back(value, value, new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - } - else { - imdd * new_child = (mk_filter_identical_core(curr_child, var+1, 0, 0, UINT_MAX, false, memoize_res)).m_d; - if (new_child != 0) { - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - } - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_fi_top_cache, d, r); - return mk(r); - } - } - else { - SASSERT(num_found > 0); - fi_cache_entry d_entry(d, lower, upper); - fi_cache_entry * r_entry; - if (d->is_shared() && m_fi_bottom_cache.find(&d_entry, r_entry)) - return mk(r_entry); - sbuffer result; - if (_is_fi_var) { - unsigned new_num_found = num_found + 1; - bool last_fi_var = (new_num_found == m_fi_num_vars); - imdd_children::iterator it = d->m_children.find_geq(lower); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - unsigned curr_lower = it->begin_key(); - unsigned curr_upper = it->end_key(); - if (curr_lower < lower) - curr_lower = lower; - if (curr_upper > upper) - curr_upper = upper; - if (curr_lower <= curr_upper) { - if (last_fi_var) { - for (unsigned i = curr_lower; i <= curr_upper; i++) { - imdd * new_d = _mk_empty(arity); - add_child(new_d, i, curr_child); - new_d = memoize_new_imdd_if(memoize_res && (curr_child == 0 || curr_child->is_memoized()), new_d); - result.push_back(imdd_value_pair(new_d, i)); - } - } - else { - fi_cache_entry * new_entry = (mk_filter_identical_core(curr_child, - var+1, - new_num_found, - curr_lower, - curr_upper, - false, - memoize_res)).m_entry; - for (unsigned i = 0; new_entry && i < new_entry->m_num_result; i++) { - SASSERT(new_entry->m_result[i].first != 0); - imdd * curr_child = new_entry->m_result[i].first; - unsigned value = new_entry->m_result[i].second; - imdd * new_d = _mk_empty(arity); - add_child(new_d, value, curr_child); - SASSERT(curr_child != 0); - new_d = memoize_new_imdd_if(memoize_res && curr_child->is_memoized(), new_d); - result.push_back(imdd_value_pair(new_d, value)); - } - } - } - if (curr_upper == upper) - break; - } - } - else { - SASSERT(arity > 1); - u_map value2imdd; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - SASSERT(curr_child != 0); - fi_cache_entry * new_entry = (mk_filter_identical_core(curr_child, - var+1, - num_found, - lower, - upper, - false, - memoize_res)).m_entry; - for (unsigned i = 0; new_entry && i < new_entry->m_num_result; i++) { - unsigned value = new_entry->m_result[i].second; - imdd * new_child = new_entry->m_result[i].first; - imdd * new_d = 0; - if (!value2imdd.find(value, new_d)) { - new_d = _mk_empty(arity); - value2imdd.insert(value, new_d); - result.push_back(imdd_value_pair(new_d, value)); - } - SASSERT(new_d != 0); - add_child(new_d, it->begin_key(), it->end_key(), new_child); - } - } - std::sort(result.begin(), result.end(), imdd_value_pair_lt_value()); - } - r_entry = mk_fi_cache_entry(d, lower, upper, result.size(), result.c_ptr()); - if (d->is_shared()) - m_fi_bottom_cache.insert(r_entry); - return mk(r_entry); - } -} - -imdd * imdd_manager::mk_filter_identical_main(imdd * d, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) { - if (d->empty() || num_vars < 2) - return d; - TRACE("imdd", - tout << "vars: "; - for (unsigned i = 0; i < num_vars; ++i) { - tout << vars[i] << " "; - } - tout << "\n"; - tout << "rows: " << get_num_rows(d) << "\n"; - display_ll(tout, d); ); - unsigned arity = d->get_arity(); - DEBUG_CODE(for (unsigned i = 0; i < num_vars; ++i) SASSERT(vars[i] < arity);); - m_fi_num_vars = num_vars; - m_fi_begin_vars = vars; - m_fi_end_vars = vars + num_vars; - delay_dealloc delay(*this); - m_fi_bottom_cache.reset(); - m_fi_top_cache.reset(); - m_fi_entries.reset(); - - sl_imanager imgr; - init_fi_intervals(imgr, d, 0, 0); - - TRACE("imdd_verbose", - ptr_addr_hashtable ht; - imdd2intervals::iterator it = m_imdd2interval_set.begin(); - imdd2intervals::iterator end = m_imdd2interval_set.end(); - for (; it != end; ++it) { - tout << it->m_key->get_id() << " "; - if (it->m_value) { - if (ht.contains(it->m_value)) { - tout << "dup\n"; - continue; - } - ht.insert(it->m_value); - sl_interval_set::iterator sit = it->m_value->begin(); - sl_interval_set::iterator send = it->m_value->end(); - for (; sit != send; ++sit) { - tout << "[" << sit->begin_key() << ":" << sit->end_key() << "] "; - } - } - else { - tout << "{}"; - } - tout << "\n"; - }); - mk_fi_result r = mk_filter_identical_core(d, 0, 0, 0, UINT_MAX, destructive, memoize_res); - reset_fi_intervals(imgr); - - TRACE("imdd", if (r.m_d) display_ll(tout << "result\n", r.m_d);); - if (r.m_d == 0) - return _mk_empty(arity); - else - return r.m_d; -} - -void imdd_manager::swap_in(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars) { - // variables are sorted. - DEBUG_CODE(for (unsigned i = 0; i + 1 < num_vars; ++i) SASSERT(vars[i] < vars[i+1]);); - imdd_ref tmp1(*this), tmp2(*this); - tmp1 = d; - SASSERT(num_vars > 1); - unsigned v1 = vars[0]+1; // next position to swap to. - for (unsigned i = 1; i < num_vars; ++i) { - unsigned v2 = vars[i]; - SASSERT(v1 <= v2); - for (unsigned j = v2-1; j >= v1; --j) { - mk_swap(tmp1, tmp2, j); - tmp1 = tmp2; - } - ++v1; - } - TRACE("imdd", - for (unsigned i = 0; i < num_vars; ++i) tout << vars[i] << " "; - tout << "\n"; - display_ll(tout << "in\n", d); - display_ll(tout << "out\n", tmp1); - tout << "\n";); - r = tmp1; -} - -void imdd_manager::swap_out(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars) { - // variables are sorted. - DEBUG_CODE(for (unsigned i = 0; i + 1 < num_vars; ++i) SASSERT(vars[i] < vars[i+1]);); - imdd_ref tmp1(*this), tmp2(*this); - tmp1 = d; - SASSERT(num_vars > 1); - unsigned v1 = vars[0]+num_vars-1; // position of next variable to be swapped. - for (unsigned i = num_vars; i > 1; ) { - --i; - unsigned v2 = vars[i]; - for (unsigned j = v1; j < v2; ++j) { - mk_swap(tmp1, tmp2, j); - tmp1 = tmp2; - } - --v1; - } - TRACE("imdd", - for (unsigned i = 0; i < num_vars; ++i) tout << vars[i] << " "; - tout << "\n"; - display_ll(tout << "out\n", d); - display_ll(tout << "in\n", tmp1); - tout << "\n";); - r = tmp1; -} - -void imdd_manager::filter_identical_core2(imdd* d, unsigned num_vars, unsigned b, unsigned e, ptr_vector& ch) { - SASSERT(num_vars > 0); - imdd* r = 0; - - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->m_children.end(); - - for (; it != end; ++it) { - unsigned b1 = it->begin_key(); - unsigned e1 = it->end_key(); - if (e < b1) { - break; - } - if (b1 < b) { - b1 = b; - } - if (e <= e1) { - e1 = e; - } - SASSERT(b <= b1 && b1 <= e1 && e1 <= e); - imdd* curr_child = it->val(); - if (num_vars == 1) { - for (unsigned i = b1; i <= e1; ++i) { - r = _mk_empty(d->get_arity()); - add_child(r, i, i, it->val()); - r = memoize_new_imdd_if(!it->val() || it->val()->is_memoized(), r); - ch.push_back(r); - } - continue; - } - ptr_vector ch2; - filter_identical_core2(curr_child, num_vars-1, b1, e1, ch2); - for (unsigned i = 0; i < ch2.size(); ++i) { - r = _mk_empty(d->get_arity()); - unsigned key = ch2[i]->begin_children()->begin_key(); - SASSERT(ch2[i]->begin_children()->end_key() == key); - add_child(r, key, key, ch2[i]); - r = memoize_new_imdd_if(ch2[i]->is_memoized(), r); - ch.push_back(r); - } - } -} - -imdd* imdd_manager::filter_identical_core2(imdd* d, unsigned var, unsigned num_vars, bool memoize_res) { - imdd* r = 0; - if (m_filter_identical_cache.find(d, r)) { - return r; - } - - bool children_memoized = true; - - r = _mk_empty(d->get_arity()); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - - if (var > 0) { - for (; it != end; ++it) { - imdd* curr_child = it->val(); - imdd* new_child = filter_identical_core2(curr_child, var-1, num_vars, memoize_res); - if (new_child) { - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) { - children_memoized = false; - } - } - } - } - else { - ptr_vector ch; - - for (; it != end; ++it) { - imdd* curr_child = it->val(); - filter_identical_core2(curr_child, num_vars-1, it->begin_key(), it->end_key(), ch); - } - for (unsigned i = 0; i < ch.size(); ++i) { - unsigned key = ch[i]->begin_children()->begin_key(); - SASSERT(ch[i]->begin_children()->end_key() == key); - push_back(key, key, ch[i]); - if (!ch[i]->is_memoized()) { - children_memoized = false; - } - } - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - m_filter_identical_cache.insert(d, r); - r = memoize_new_imdd_if(r && memoize_res && children_memoized, r); - return r; -} - -void imdd_manager::filter_identical_main2(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) { - m_filter_identical_cache.reset(); - imdd_ref tmp1(*this), tmp2(*this); - swap_in(d, tmp1, num_vars, vars); - tmp2 = filter_identical_core2(tmp1, vars[0], num_vars, memoize_res); - if (!tmp2) { - r = _mk_empty(d->get_arity()); - } - else { - swap_out(tmp2, r, num_vars, vars); - } - m_filter_identical_cache.reset(); -} - -void imdd_manager::filter_identical_main3(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) { - for (unsigned i = 0; i+1 < num_vars; ++i) { - imdd_ref tmp(*this); - tmp = r; - filter_identical_main3(tmp, r, vars[i], false, vars[i+1], false, memoize_res); - } -} - -void imdd_manager::filter_identical_main3(imdd * d, imdd_ref& r, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res) { - r = filter_identical_loop3(d, v1, del1, v2, del2, memoize_res); - if (r == 0) { - r = _mk_empty(d->get_arity()-del1-del2); - } - m_filter_identical_cache.reset(); -} - -imdd* imdd_manager::filter_identical_loop3(imdd * d, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res) { - imdd* r; - if (m_filter_identical_cache.find(d, r)) { - return r; - } - if (v1 == 0) { - return filter_identical_mk_nodes(d, v2, del1, del2, memoize_res); - } - - r = _mk_empty(d->get_arity()-del1-del2); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - bool children_memoized = true; - - for (; it != end; ++it) { - imdd* curr_child = it->val(); - imdd* new_child = filter_identical_loop3(curr_child, v1-1, del1, v2-1, del2, memoize_res); - if (!new_child) { - continue; - } - if (new_child->empty()) { - delete_imdd(new_child); - continue; - } - if (!new_child->is_memoized()) { - children_memoized = false; - } - push_back(it->begin_key(), it->end_key(), new_child); - } - if (r->empty()) { - delete_imdd(r); - r = 0; - } - m_filter_identical_cache.insert(d, r); - r = memoize_new_imdd_if(r && memoize_res && children_memoized, r); - return r; -} - -void imdd_manager::merge_intervals(svector& dst, svector const& src) { - svector& tmp = m_i_nodes_tmp; - tmp.reset(); - // invariant: intervals are sorted. - for (unsigned i = 0, j = 0; i < src.size() || j < dst.size();) { - SASSERT(!(i + 1 < src.size()) || src[i].m_hi < src[i+1].m_lo); - SASSERT(!(i + 1 < dst.size()) || dst[i].m_hi < dst[i+1].m_lo); - SASSERT(!(i < src.size()) || src[i].m_lo <= src[i].m_hi); - SASSERT(!(i < dst.size()) || dst[i].m_lo <= dst[i].m_hi); - if (i < src.size() && j < dst.size()) { - if (src[i].m_lo == dst[j].m_lo) { - tmp.push_back(src[i]); - ++i; - ++j; - } - else if (src[i].m_lo < dst[j].m_lo) { - tmp.push_back(src[i]); - ++i; - } - else { - tmp.push_back(dst[j]); - ++j; - } - } - else if (i < src.size()) { - tmp.push_back(src[i]); - ++i; - } - else { - tmp.push_back(dst[j]); - ++j; - } - } - dst.reset(); - dst.append(tmp); -} - -/** - * Propagate intervals down: what intervals can reach which nodes. - */ -imdd* imdd_manager::filter_identical_mk_nodes(imdd* d, unsigned v, bool del1, bool del2, bool memoize_res) { - SASSERT(v > 0); - - TRACE("imdd", display_ll(tout << "v: " << v << "\n", d);); - - // - // (0) - // Create map d |-> [I] from nodes to ordered set of disjoint intervals that visit the node. - // - // For each level up to 'v' create a list of nodes visited - // insert to a map the set of intervals that visit the node. - // - m_nodes.reset(); - filter_id_map& nodes = m_nodes; - imdd* d1, *d2, *d3; - vector > levels; - levels.push_back(ptr_vector()); - - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd* curr_child; - for (; it != end; ++it) { - curr_child = it->val(); - svector& iv = nodes.init(curr_child); - if (iv.empty()) { - levels.back().push_back(curr_child); - } - iv.push_back(interval(it->begin_key(), it->end_key())); - } - - for (unsigned j = 0; j+1 < v; ++j) { - levels.push_back(ptr_vector()); - for (unsigned i = 0; i < levels[j].size(); ++i) { - d1 = levels[j][i]; - svector& i_nodes = nodes.init(d1); - it = d1->begin_children(); - end = d1->end_children(); - for(; it != end; ++it) { - imdd* curr_child = it->val(); - svector& i_nodes2 = nodes.init(curr_child); - if (i_nodes2.empty()) { - levels[j+1].push_back(curr_child); - i_nodes2.append(i_nodes); - } - else { - merge_intervals(i_nodes2, i_nodes); - } - } - } - } - - TRACE("imdd", - for (unsigned i = 0; i < levels.size(); ++i) { - tout << "Level: " << i << "\n"; - for (unsigned j = 0; j < levels[i].size(); ++j) { - tout << levels[i][j]->get_id() << " "; - svector const& i_nodes = nodes.init(levels[i][j]); - for (unsigned k = 0; k < i_nodes.size(); ++k) { - tout << i_nodes[k].m_lo << ":" << i_nodes[k].m_hi << " "; - } - tout << "\n"; - } - } - ); - - - // - // (1) - // Intersect with children: - // - d [l1:h1:ch1][l2:h2:ch2][...] - // => produce_units: d |-> [uI1 |-> d'[uI1:ch1], uI2 |-> d''[uI2:ch1], ...] // unit intervals - // => del2: d |-> [I |-> union of ch1, ch2, .. under intersection] - // => del1 & !del2: d |-> [I |-> d'[I:ch]] // intersections of intervals. - // - - m_nodes_dd.reset(); - filter_idd_map& nodes_dd = m_nodes_dd; - SASSERT(levels.size() == v); - for (unsigned i = 0; i < levels[v-1].size(); ++i) { - d1 = levels[v-1][i]; - svector const & i_nodes = nodes.init(d1); - it = d1->begin_children(); - end = d1->end_children(); - unsigned j = 0; - svector& i_nodes_dd = nodes_dd.init(d1); - while (it != end && j < i_nodes.size()) { - unsigned lo1 = it->begin_key(); - unsigned hi1 = it->end_key(); - unsigned lo2 = i_nodes[j].m_lo; - unsigned hi2 = i_nodes[j].m_hi; - if (hi2 < lo1) { - ++j; - } - else if (hi1 < lo2) { - ++it; - } - // lo1 <= hi2 && lo2 <= hi1 - else { - curr_child = it->val(); - unsigned lo = std::max(lo1, lo2); - unsigned hi = std::min(hi1, hi2); - SASSERT(lo <= hi); - - if (!del1 && !del2) { - for (unsigned k = lo; k <= hi; ++k) { - imdd* d2 = _mk_empty(d1->get_arity()); - add_child(d2, k, k, curr_child); - i_nodes_dd.push_back(interval_dd(k, k, d2)); - } - } - else if (del2) { - i_nodes_dd.push_back(interval_dd(lo, hi, curr_child)); // retrofill after loop. - } - else { - imdd* d2 = _mk_empty(d1->get_arity()); - add_child(d2, lo, hi, curr_child); - i_nodes_dd.push_back(interval_dd(lo, hi, d2)); - } - if (hi2 <= hi) { - ++j; - } - if (hi1 <= hi) { - ++it; - } - } - } - // take union of accumulated children. - // retrofill union inside list. - if (del2) { - d2 = 0; - for (unsigned k = 0; k < i_nodes_dd.size(); ++k) { - d3 = i_nodes_dd[k].m_dd; - if (!d2) { - d2 = d3; - } - else { - d2 = mk_union_core(d2, d3, true, memoize_res); - } - } - for (unsigned k = 0; k < i_nodes_dd.size(); ++k) { - i_nodes_dd[k].m_dd = d2; - } - } - } - - TRACE("imdd", print_filter_idd(tout, nodes_dd);); - - // - // (2) - // Move up: - // d1 |-> [I1] // intervals visiting d1 - // d1 |-> [lo:hi:child] // children of d1 - // child |-> [I2 |-> child'] // current decomposition - // result: - // d3 = d1' |-> [lo:hi:child'] - // d1 |-> [I3 |-> d3] for I3 in merge of [I1] and [I2] - // - // The merge is defined as the intersection of intervals that reside in I1 and - // the fractions in I2. They are decomposed so that all intervals are covered. - // By construction I2 are contained in I1, - // but they may be overlapping among different I2. - // - - for (unsigned i = v-1; i > 0; ) { - --i; - for (unsigned j = 0; j < levels[i].size(); ++j) { - d1 = levels[i][j]; - m_i_nodes_dd.reset(); - svector i_nodes = nodes.init(d1); - svector& i_nodes_dd = nodes_dd.init(d1); - it = d1->begin_children(); - end = d1->end_children(); - unsigned num_children = 0; - for( ; it != end; ++it, ++num_children); - - m_offsets.reset(); - unsigned_vector& offsets = m_offsets; - offsets.resize(num_children); - it = d1->begin_children(); - for( ; it != end; ++it) { - curr_child = it->val(); - refine_intervals(i_nodes, nodes_dd.init(curr_child)); - } - - for (unsigned k = 0; k < i_nodes.size(); ++k) { - interval const& intv = i_nodes[k]; - d3 = _mk_empty(d1->get_arity()-del2); - it = d1->begin_children(); - for(unsigned child_id = 0; it != end; ++it, ++child_id) { - curr_child = it->val(); - svector const& ch_nodes_dd = nodes_dd.init(curr_child); - unsigned offset = offsets[child_id]; - TRACE("imdd_verbose", tout << intv.m_lo << ":" << intv.m_hi << "\n"; - for (unsigned l = offset; l < ch_nodes_dd.size(); ++l) { - tout << ch_nodes_dd[l].m_lo << ":" << ch_nodes_dd[l].m_hi << " " << ch_nodes_dd[l].m_dd->get_id() << " "; - } - tout << "\n"; - ); - - unsigned hi, lo; - d2 = 0; - while (offset < ch_nodes_dd.size() && !d2) { - lo = ch_nodes_dd[offset].m_lo; - hi = ch_nodes_dd[offset].m_hi; - if (intv.m_hi < lo) { - break; - } - if (hi < intv.m_lo) { - ++offset; - continue; - } - SASSERT(lo <= intv.m_lo); - SASSERT(intv.m_hi <= hi); - d2 = ch_nodes_dd[offset].m_dd; - if (intv.m_hi == hi) { - ++offset; - } - } - offsets[child_id] = offset; - if (d2) { - add_child(d3, it->begin_key(), it->end_key(), d2); - } - } - if (d3->empty()) { - delete_imdd(d3); - } - else { - i_nodes_dd.push_back(interval_dd(intv.m_lo, intv.m_hi, d3)); - } - } - TRACE("imdd", tout << d1->get_id() << ": "; print_interval_dd(tout, i_nodes_dd);); - - } - } - - TRACE("imdd", print_filter_idd(tout, nodes_dd);); - - // - // (3) - // Finalize: - // d |-> [I1:child] // children of d - // child |-> [I2 |-> child'] // current decomposition - // result: - // d' = union of child' // if del1 - // d' |-> [I2:child'] // if !del1 - // - - - it = d->begin_children(); - end = d->end_children(); - d1 = _mk_empty(d->get_arity()-del1-del2); - m_i_nodes_dd.reset(); - m_i_nodes_tmp.reset(); - svector& i_nodes_dd = m_i_nodes_dd; - svector& i_nodes_tmp = m_i_nodes_dd_tmp; - for (; it != end; ++it) { - curr_child = it->val(); - i_nodes_tmp.reset(); - svector const& i_nodes_dd1 = nodes_dd.init(curr_child); - for (unsigned i = 0, j = 0; i < i_nodes_dd.size() || j < i_nodes_dd1.size(); ) { - if (i < i_nodes_dd.size() && j < i_nodes_dd1.size()) { - interval_dd const& iv1 = i_nodes_dd[i]; - interval_dd const& iv2 = i_nodes_dd1[j]; - if (iv1.m_lo == iv2.m_lo) { - SASSERT(iv1.m_hi == iv2.m_hi); - SASSERT(iv1.m_dd == iv2.m_dd); - i_nodes_tmp.push_back(iv1); - ++i; - ++j; - } - else if (iv1.m_lo < iv2.m_lo) { - SASSERT(iv1.m_hi < iv2.m_lo); - i_nodes_tmp.push_back(iv1); - ++i; - } - else { - SASSERT(iv2.m_hi < iv1.m_lo); - i_nodes_tmp.push_back(iv2); - ++j; - } - } - else if (i < i_nodes_dd.size()) { - i_nodes_tmp.push_back(i_nodes_dd[i]); - ++i; - } - else if (j < i_nodes_dd1.size()) { - i_nodes_tmp.push_back(i_nodes_dd1[j]); - ++j; - } - } - i_nodes_dd.reset(); - i_nodes_dd.append(i_nodes_tmp); - } - - for (unsigned i = 0; i < i_nodes_dd.size(); ++i) { - imdd* ch = i_nodes_dd[i].m_dd; - unsigned lo = i_nodes_dd[i].m_lo; - unsigned hi = i_nodes_dd[i].m_hi; - if (del1) { - d1 = mk_union_core(d1, ch, true, memoize_res); - } - else { - add_child(d1, lo, hi, ch); - } - } - - TRACE("imdd", display_ll(tout, d1);); - - return d1; -} - - -void imdd_manager::print_interval_dd(std::ostream& out, svector const& m) { - for (unsigned i = 0; i < m.size(); ++i) { - out << m[i].m_lo << ":" << m[i].m_hi << " " << m[i].m_dd->get_id() << " "; - } - out << "\n"; -} - -void imdd_manager::print_filter_idd(std::ostream& out, filter_idd_map const& m) { - filter_idd_map::iterator it = m.begin(), end = m.end(); - for (unsigned i = 0; it != end; ++it, ++i) { - out << i << ": "; - print_interval_dd(out, *it); - } -} - -/** - \brief partition intervals of i_nodes by sub-intervals that are used in i_nodes_dd. - Assumption: - - All values covered in i_nodes_dd are present in i_nodes. -*/ - -void imdd_manager::refine_intervals(svector& i_nodes, svector const& i_nodes_dd) { - m_i_nodes.reset(); - svector& result = m_i_nodes; - unsigned sz1 = i_nodes.size(); - unsigned sz2 = i_nodes_dd.size(); - for (unsigned i = 0, j = 0; i < sz1; ++i) { - interval& iv = i_nodes[i]; - result.push_back(iv); - unsigned lo = iv.m_lo; - unsigned hi = iv.m_hi; - for (; j < sz2; ++j) { - unsigned lo1 = i_nodes_dd[j].m_lo; - unsigned hi1 = i_nodes_dd[j].m_hi; - SASSERT(lo <= hi); - SASSERT(lo1 <= hi1); - if (hi < lo1) { - break; - } - if (lo < lo1) { - result.back().m_hi = lo1-1; - result.push_back(interval(lo1, hi)); - lo = lo1; - } - SASSERT(lo1 <= lo); - if (lo > hi1) { - continue; - } - if (hi1 < hi) { - result.back().m_hi = hi1; - result.push_back(interval(hi1+1, hi)); - lo = hi1+1; - } - } - } - i_nodes.reset(); - i_nodes.append(result); -} - - - -void imdd_manager::mk_project_core(imdd * d, imdd_ref & r, unsigned var, unsigned num_found, bool memoize_res) { - unsigned arity = d->get_arity(); - bool _is_proj_var = is_proj_var(var); - - if (_is_proj_var && arity == (m_proj_num_vars - num_found)) { - r = 0; // 0 here means the unit element (aka the 0-tuple). - return; - } - - imdd * _r = 0; - if (is_cached(m_proj_cache, d, _r)) { - r = _r; - return; - } - - bool new_children_memoized = true; - if (_is_proj_var) { - SASSERT(d != 0); - SASSERT(d->get_arity() > 1); - unsigned new_var = var + 1; - unsigned new_num_found = num_found + 1; - bool found_all = new_num_found == m_proj_num_vars; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_ref tmp_r(*this); - CTRACE("imdd", it == end, display_ll(tout, d);); - SASSERT(it != end); - for (; it != end; ++it) { - imdd_ref new_child(*this); - if (found_all) - new_child = it->val(); - else - mk_project_core(it->val(), new_child, new_var, new_num_found, memoize_res); - - if (tmp_r == 0) - tmp_r = new_child; - else - mk_union_core(tmp_r, new_child, tmp_r, memoize_res); - SASSERT(tmp_r != 0); - } - SASSERT(tmp_r != 0); - SASSERT(tmp_r->get_arity() == d->get_arity() - (m_proj_num_vars - num_found)); - r = tmp_r; - } - else { - SASSERT(num_found < m_proj_num_vars); - unsigned new_var = var+1; - _r = _mk_empty(arity - (m_proj_num_vars - num_found)); - imdd_children::push_back_proc push_back(m_sl_manager, _r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd_ref new_child(*this); - mk_project_core(curr_child, new_child, new_var, num_found, memoize_res); - push_back(it->begin_key(), it->end_key(), new_child); - if (new_child != 0 && !new_child->is_memoized()) - new_children_memoized = false; - } - SASSERT(!_r->empty()); - _r = memoize_new_imdd_if(memoize_res && new_children_memoized, _r); - r = _r; - } - cache_result(m_proj_cache, d, r); -} - -void imdd_manager::mk_project_dupdt_core(imdd_ref & d, unsigned var, unsigned num_found, bool memoize_res) { - unsigned arity = d->get_arity(); - bool _is_proj_var = is_proj_var(var); - - if (_is_proj_var && arity == (m_proj_num_vars - num_found)) { - d = 0; // 0 here means the unit element (aka the 0-tuple). - return; - } - - if (!destructive_update_at(true, d)) { - mk_project_core(d, d, var, num_found, memoize_res); - return; - } - - if (_is_proj_var) { - SASSERT(d != 0); - SASSERT(d->get_arity() > 1); - unsigned new_var = var + 1; - unsigned new_num_found = num_found + 1; - bool found_all = new_num_found == m_proj_num_vars; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - imdd_ref r(*this); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); - if (!found_all) { - mk_project_dupdt_core(new_child, new_var, new_num_found, memoize_res); - } - if (r == 0) - r = new_child; - else - mk_union_core_dupdt(r, new_child, memoize_res); - } - // we traverse the children of d, and decrement the reference counter of each one of them. - d->m_children.reset_no_decref(m_sl_manager); - d = r; - SASSERT(r != 0); - SASSERT(r->get_arity() == d->get_arity() - (m_proj_num_vars - num_found)); - } - else { - SASSERT(!_is_proj_var); - sbuffer to_insert; - unsigned new_var = var+1; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); - mk_project_dupdt_core(new_child, new_var, num_found, memoize_res); - if (new_child) - new_child->inc_ref(); // protect new child, since we will insert it into to_insert - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - SASSERT(!to_insert.empty()); - d->replace_children(m_sl_manager, to_insert); - } -} - -void imdd_manager::mk_project_init(unsigned num_vars, unsigned * vars) { - m_proj_num_vars = num_vars; - m_proj_begin_vars = vars; - m_proj_end_vars = vars + num_vars; - m_proj_cache.reset(); - reset_union_cache(); -} - -void imdd_manager::mk_project_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res) { - if (num_vars == 0) { - return; - } - unsigned arity = d->get_arity(); - SASSERT(num_vars < arity); - if (d->empty()) { - d = _mk_empty(arity - num_vars); - return; - } - delay_dealloc delay(*this); - mk_project_init(num_vars, vars); - mk_project_dupdt_core(d, 0, 0, memoize_res); -} - -void imdd_manager::mk_project(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res) { - if (num_vars == 0) { - r = d; - return; - } - unsigned arity = d->get_arity(); - SASSERT(num_vars < arity); - if (d->empty()) { - r = _mk_empty(arity - num_vars); - return; - } - delay_dealloc delay(*this); - mk_project_init(num_vars, vars); - mk_project_core(d, r, 0, 0, memoize_res); - - STRACE("imdd_trace", tout << "mk_project(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num_vars; i++) tout << vars[i] << ", "; - tout << memoize_res << ");\n";); -} - -void imdd_manager::mk_join( - imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - bool memoize_res) { - SASSERT(vars1.size() == vars2.size()); - imdd_ref tmp(*this); - unsigned d1_arity = d1->get_arity(); - mk_product(d1, d2, tmp); - for (unsigned i = 0; i < vars1.size(); ++i) { - unsigned vars[2] = { vars1[i], vars2[i] + d1_arity }; - mk_filter_identical_dupdt(tmp, 2, vars); - } - r = tmp; // memoize_new_imdd_if(memoize_res, tmp); -} - -#if 0 -void imdd_manager::mk_join_project( - imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - unsigned_vector const& proj_vars, bool memoize_res) { - SASSERT(vars1.size() == vars2.size()); - imdd_ref tmp(*this); - unsigned d1_arity = d1->get_arity(); - mk_product(d1, d2, tmp); - for (unsigned i = 0; i < vars1.size(); ++i) { - unsigned vars[2] = { vars1[i], vars2[i] + d1_arity }; - mk_filter_identical(tmp, tmp, 2, vars); - } - mk_project(tmp, tmp, proj_vars.size(), proj_vars.c_ptr()); - TRACE("dl", - tout << "vars: "; - for (unsigned i = 0; i < vars1.size(); ++i) tout << vars1[i] << ":" << vars2[i] << " "; - tout << "\n"; - tout << "proj: "; - for (unsigned i = 0; i < proj_vars.size(); ++i) tout << proj_vars[i] << " "; - tout << "\n"; - tout << "arity: " << d1->get_arity() << " + " << d2->get_arity() << "\n"; - display_ll(tout << "d1\n", d1); - display_ll(tout << "d2\n", d2); - display_ll(tout << "result\n", tmp); - tout << "\n"; - ); - - r = tmp; // memoize_new_imdd_if(memoize_res, tmp); -} -#endif - - -void imdd_manager::mk_join_project( - imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - unsigned_vector const& proj_vars, bool memoize_res) { - SASSERT(vars1.size() == vars2.size()); - imdd_ref tmp(*this); - mk_product(d1, d2, tmp); - unsigned d1_arity = d1->get_arity(); - unsigned sz = tmp->get_arity(); - - // set up schedule for join-project. - unsigned_vector remap; - svector projected; - unsigned_vector ref_count; - for (unsigned i = 0; i < sz; ++i) { - remap.push_back(i); - } - projected.resize(sz, false); - ref_count.resize(sz, 0); - for (unsigned i = 0; i < proj_vars.size(); ++i) { - projected[proj_vars[i]] = true; - } - for (unsigned i = 0; i < vars1.size(); ++i) { - ref_count[vars1[i]]++; - ref_count[vars2[i]+d1_arity]++; - } - -#define UPDATE_REMAP() \ - for (unsigned i = 0, j = 0; i < sz; ++i) { \ - remap[i] = j; \ - if (!projected[i] || ref_count[i] > 0) { \ - ++j; \ - } \ - } \ - - - UPDATE_REMAP(); - // project unused variables: - unsigned_vector proj2; - for (unsigned i = 0; i < sz; ++i) { - if (projected[i] && ref_count[i] == 0) { - proj2.push_back(i); - } - } - mk_project(tmp, tmp, proj2.size(), proj2.c_ptr()); - - for (unsigned i = 0; i < vars1.size(); ++i) { - unsigned idx1 = vars1[i]; - unsigned idx2 = vars2[i]+d1_arity; - ref_count[idx1]--; - ref_count[idx2]--; - bool del1 = ref_count[idx1] == 0 && projected[idx1]; - bool del2 = ref_count[idx2] == 0 && projected[idx2]; - - filter_identical_main3(tmp, tmp, remap[idx1], del1, remap[idx2], del2, memoize_res); - if (del1 || del2) { - UPDATE_REMAP(); - } - } - TRACE("imdd", - tout << "vars: "; - for (unsigned i = 0; i < vars1.size(); ++i) tout << vars1[i] << ":" << vars2[i] << " "; - tout << "\n"; - tout << "proj: "; - for (unsigned i = 0; i < proj_vars.size(); ++i) tout << proj_vars[i] << " "; - tout << "\n"; - tout << "arity: " << d1->get_arity() << " + " << d2->get_arity() << "\n"; - display_ll(tout << "d1\n", d1); - display_ll(tout << "d2\n", d2); - display_ll(tout << "result\n", tmp); - tout << "\n"; - ); - - r = tmp; // memoize_new_imdd_if(memoize_res, tmp); -} - - - -imdd * imdd_manager::mk_swap_new_child(unsigned lower, unsigned upper, imdd * grandchild) { - if (m_swap_new_child == 0) { - m_swap_new_child = _mk_empty(grandchild == 0 ? 1 : grandchild->get_arity() + 1); - add_child(m_swap_new_child, lower, upper, grandchild); - if (grandchild && !grandchild->is_memoized()) - m_swap_granchildren_memoized = false; - inc_ref(m_swap_new_child); - } - SASSERT(m_swap_new_child != 0); - return m_swap_new_child; -} - -void imdd_manager::mk_swap_acc1_dupdt(imdd_ref & d, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res) { - SASSERT(d->get_ref_count() == 1); - sbuffer to_insert; - imdd_children::ext_iterator it; - imdd_children::ext_iterator end; - d->m_children.move_geq(it, lower); - while (it != end && lower <= upper) { - imdd_children::entry const & curr_entry = *it; - unsigned curr_entry_begin_key = curr_entry.begin_key(); - unsigned curr_entry_end_key = curr_entry.end_key(); - imdd * curr_entry_val = curr_entry.val(); - bool move_head = true; - if (upper < curr_entry_begin_key) - break; - if (lower < curr_entry_begin_key) { - to_insert.push_back(entry(lower, curr_entry_begin_key - 1, grandchild)); - lower = curr_entry_begin_key; - } - SASSERT(lower >= curr_entry_begin_key); - imdd * curr_grandchild = curr_entry_val; - imdd_ref new_grandchild(*this); - SASSERT((curr_grandchild == 0) == (grandchild == 0)); - if (curr_grandchild != 0) { - bool cover = lower == curr_entry_begin_key && upper >= curr_entry_end_key; - // If cover == true, then the curr_child is completely covered, and it is not needed anymore. - // So, we can perform a destructive update. - if (cover) { - new_grandchild = curr_grandchild; // take over the ownership of curr_grandchild - it.erase(m_sl_manager); - move_head = false; // it.erase is effectively moving the head. - mk_union_core_dupdt(new_grandchild, grandchild, memoize_res); - } - else { - mk_union_core(curr_grandchild, grandchild, new_grandchild, memoize_res); - } - if (!new_grandchild->is_memoized()) - m_swap_granchildren_memoized = false; - // protect new_grandchild, since it will be stored in to_insert. - new_grandchild->inc_ref(); - } - if (upper >= curr_entry_end_key) { - to_insert.push_back(entry(lower, curr_entry_end_key, new_grandchild)); - } - else { - to_insert.push_back(entry(lower, upper, new_grandchild)); - } - lower = curr_entry_end_key + 1; - if (move_head) - ++it; - } - svector::iterator it2 = to_insert.begin(); - svector::iterator end2 = to_insert.end(); - for (; it2 != end2; ++it2) { - imdd_children::entry const & curr_entry = *it2; - add_child(d, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val()); - if (curr_entry.val()) - curr_entry.val()->dec_ref(); // to_insert will be destroyed, so decrement ref. - } - if (lower <= upper) { - add_child(d, lower, upper, grandchild); - } - TRACE("mk_swap_bug", tout << "after mk_swap_acc1_dupt\n" << mk_ll_pp(d, *this) << "\n";); -} - -void imdd_manager::mk_swap_acc1(imdd * d, imdd_ref & r, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res) { - copy(d, r); - imdd_children::iterator it = d->m_children.find_geq(lower); - imdd_children::iterator end = d->end_children(); - for (; it != end && lower <= upper; ++it) { - imdd_children::entry const & curr_entry = *it; - if (upper < curr_entry.begin_key()) - break; - if (lower < curr_entry.begin_key()) { - SASSERT(lower <= curr_entry.begin_key() - 1); - add_child(r, lower, curr_entry.begin_key() - 1, grandchild); - lower = curr_entry.begin_key(); - } - SASSERT(lower >= curr_entry.begin_key()); - imdd * curr_grandchild = curr_entry.val(); - SASSERT((curr_grandchild == 0) == (grandchild == 0)); - imdd_ref new_curr_grandchild(*this); - mk_union_core(curr_grandchild, grandchild, new_curr_grandchild, memoize_res); - if (new_curr_grandchild && !new_curr_grandchild->is_memoized()) - m_swap_granchildren_memoized = false; - if (upper >= curr_entry.end_key()) { - add_child(r, lower, curr_entry.end_key(), new_curr_grandchild); - } - else { - SASSERT(upper < curr_entry.end_key()); - SASSERT(lower <= upper); - add_child(r, lower, upper, new_curr_grandchild); - } - lower = curr_entry.end_key() + 1; - } - if (lower <= upper) { - add_child(r, lower, upper, grandchild); - } - TRACE("mk_swap_bug", tout << "after mk_swap_acc1\n" << mk_ll_pp(r, *this) << "\n";); -} - -/** - \brief Auxiliary function for mk_swap_core -*/ -void imdd_manager::mk_swap_acc2(imdd_ref & r, unsigned lower1, unsigned upper1, unsigned lower2, unsigned upper2, imdd * grandchild, bool memoize_res) { - SASSERT((r->get_arity() == 2 && grandchild == 0) || - (r->get_arity() == grandchild->get_arity() + 2)); - SASSERT(r->get_ref_count() <= 1); // we perform destructive updates on r. - SASSERT(!r->is_memoized()); - TRACE("mk_swap_bug", - tout << mk_ll_pp(r, *this) << "\n"; - tout << "adding\n[" << lower1 << ", " << upper1 << "] -> [" << lower2 << ", " << upper2 << "] ->\n"; - tout << mk_ll_pp(grandchild, *this) << "\n";); - - sbuffer to_insert; - imdd_children::ext_iterator it; - imdd_children::ext_iterator end; - r->m_children.move_geq(it, lower1); - SASSERT(m_swap_new_child == 0); - - while(it != end && lower1 <= upper1) { - imdd_children::entry const & curr_entry = *it; - unsigned curr_entry_begin_key = curr_entry.begin_key(); - unsigned curr_entry_end_key = curr_entry.end_key(); - imdd * curr_entry_val = curr_entry.val(); - bool move_head = true; - TRACE("mk_swap_bug", tout << lower1 << " " << upper1 << " " << curr_entry_begin_key << "\n";); - if (upper1 < curr_entry_begin_key) - break; - if (lower1 < curr_entry_begin_key) { - imdd * new_child = mk_swap_new_child(lower2, upper2, grandchild); - SASSERT(lower1 <= curr_entry_begin_key - 1); - add_child(r, lower1, curr_entry_begin_key - 1, new_child); - lower1 = curr_entry_begin_key; - } - SASSERT(lower1 >= curr_entry_begin_key); - imdd * curr_child = curr_entry_val; - SASSERT(curr_child != 0); - SASSERT(!curr_child->is_memoized()); - imdd_ref new_curr_child(*this); - bool destructive = curr_child->get_ref_count() == 1 && lower1 == curr_entry_begin_key && upper1 >= curr_entry_end_key; - if (destructive) { - new_curr_child = curr_child; // take over curr_child. - it.erase(m_sl_manager); - move_head = false; // it.erase is effectively moving the head. - mk_swap_acc1_dupdt(new_curr_child, lower2, upper2, grandchild, memoize_res); - } - else { - mk_swap_acc1(curr_child, new_curr_child, lower2, upper2, grandchild, memoize_res); - } - new_curr_child->inc_ref(); // it will be stored in to_insert - if (upper1 >= curr_entry_end_key) { - to_insert.push_back(entry(lower1, curr_entry_end_key, new_curr_child)); - } - else { - to_insert.push_back(entry(lower1, upper1, new_curr_child)); - } - lower1 = curr_entry_end_key + 1; - if (move_head) - ++it; - } - svector::iterator it2 = to_insert.begin(); - svector::iterator end2 = to_insert.end(); - for (; it2 != end2; ++it2) { - imdd_children::entry const & curr_entry = *it2; - add_child(r, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val()); - SASSERT(curr_entry.val()); - curr_entry.val()->dec_ref(); // to_insert will be destroyed, so decrement ref. - } - if (lower1 <= upper1) { - imdd * new_child = mk_swap_new_child(lower2, upper2, grandchild); - add_child(r, lower1, upper1, new_child); - } - if (m_swap_new_child != 0) { - dec_ref(m_swap_new_child); - m_swap_new_child = 0; - } - TRACE("mk_swap_bug", tout << "after mk_swap_acc2\n" << mk_ll_pp(r, *this) << "\n";); -} - -/** - \brief Memoize the given IMDD assuming all grandchildren of d are memoized. -*/ -imdd * imdd_manager::mk_swap_memoize(imdd * d) { - if (d->is_memoized()) - return d; - bool children_memoized = true; - imdd * new_d = _mk_empty(d->get_arity()); - imdd_children::push_back_proc push_back(m_sl_manager, new_d->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * child = it->val(); - imdd * new_child = memoize(child); - if (!new_child->is_memoized()) - children_memoized = false; - push_back(it->begin_key(), it->end_key(), new_child); - } - - if (children_memoized && is_simple_node(new_d)) { - imdd * new_can_d = memoize(new_d); - if (new_can_d != new_d) { - SASSERT(new_d->get_ref_count() == 0); - delete_imdd(new_d); - } - new_d = new_can_d; - } - return new_d; -} - -/** - \brief Swap the two top vars. -*/ -void imdd_manager::mk_swap_top_vars(imdd * d, imdd_ref & r, bool memoize_res) { - SASSERT(d->get_arity() >= 2); - r = _mk_empty(d->get_arity()); - m_swap_granchildren_memoized = true; - m_swap_new_child = 0; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - SASSERT(curr_child != 0); - SASSERT(m_swap_new_child == 0); - imdd_children::iterator it2 = curr_child->begin_children(); - imdd_children::iterator end2 = curr_child->end_children(); - for (; it2 != end2; ++it2) { - imdd * grandchild = it2->val(); - mk_swap_acc2(r, it2->begin_key(), it2->end_key(), it->begin_key(), it->end_key(), grandchild, memoize_res); - } - SASSERT(m_swap_new_child == 0); - } - - if (memoize_res && m_swap_granchildren_memoized) { - r = mk_swap_memoize(r); - } - TRACE("mk_swap_bug", tout << "result:\n" << mk_ll_pp(r, *this) << "\n";); -} - -void imdd_manager::mk_swap_core(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - - imdd * _r = 0; - if (is_cached(m_swap_cache, d, _r)) { - r = _r; - return; - } - - if (vidx == 0) { - mk_swap_top_vars(d, r, memoize_res); - } - else { - SASSERT(vidx > 0); - bool new_children_memoized = true; - unsigned new_vidx = vidx - 1; - _r = _mk_empty(arity); - imdd_children::push_back_proc push_back(m_sl_manager, _r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd_ref new_child(*this); - mk_swap_core(curr_child, new_child, new_vidx, memoize_res); - push_back(it->begin_key(), it->end_key(), new_child); - if (new_child != 0 && !new_child->is_memoized()) - new_children_memoized = false; - } - SASSERT(!_r->empty()); - _r = memoize_new_imdd_if(memoize_res && new_children_memoized, _r); - r = _r; - } - cache_result(m_swap_cache, d, r); -} - -void imdd_manager::mk_swap_dupdt_core(imdd_ref & d, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - - if (!destructive_update_at(true, d)) { - mk_swap_core(d, d, vidx, memoize_res); - return; - } - - if (vidx == 0) { - mk_swap_top_vars(d, d, memoize_res); - return; - } - - SASSERT(vidx > 0); - sbuffer to_insert; - unsigned new_vidx = vidx-1; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); - mk_swap_dupdt_core(new_child, new_vidx, memoize_res); - new_child->inc_ref(); // protect new child, since we insert into to_insert - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - SASSERT(!to_insert.empty()); - d->replace_children(m_sl_manager, to_insert); -} - -void imdd_manager::mk_swap_dupdt(imdd_ref & d, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - SASSERT(vidx < arity - 1); - if (d->empty()) { - return; - } - m_swap_cache.reset(); - reset_union_cache(); - delay_dealloc delay(*this); - mk_swap_dupdt_core(d, vidx, memoize_res); -} - -void imdd_manager::mk_swap(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res) { - SASSERT(d); - unsigned arity = d->get_arity(); - SASSERT(arity > 1); - SASSERT(vidx < arity - 1); - if (d->empty()) { - r = d; - return; - } - TRACE("mk_swap_bug", tout << "mk_swap: " << vidx << "\n"; display_ll(tout, d); ); - TRACE("mk_swap_bug2", tout << "mk_swap: " << vidx << "\n"; display_ll(tout, d); ); - m_swap_cache.reset(); - reset_union_cache(); - delay_dealloc delay(*this); - mk_swap_core(d, r, vidx, memoize_res); - TRACE("mk_swap_bug2", tout << "mk_swap result\n"; display_ll(tout, r); ); - STRACE("imdd_trace", tout << "mk_swap(0x" << d << ", 0x" << r.get() << ", " << vidx << ", " << memoize_res << ");\n";); -} - -imdd_manager::iterator::iterator(imdd_manager const & m, imdd const * d) { - if (d->empty()) { - m_done = true; - return; - } - m_done = false; - unsigned arity = d->get_arity(); - m_iterators.resize(arity); - m_element.resize(arity); - begin_iterators(d, 0); -} - -void imdd_manager::iterator::begin_iterators(imdd const * curr, unsigned start_idx) { - for (unsigned i = start_idx; i < get_arity(); i++) { - m_iterators[i] = curr->begin_children(); - imdd_children::iterator & it = m_iterators[i]; - SASSERT(!it.at_end()); - m_element[i] = it->begin_key(); - curr = it->val(); - } -} - -imdd_manager::iterator & imdd_manager::iterator::operator++() { - unsigned i = get_arity(); - while (i > 0) { - --i; - imdd_children::iterator & it = m_iterators[i]; - if (m_element[i] < it->end_key()) { - m_element[i]++; - begin_iterators(it->val(), i+1); - return *this; - } - else { - ++it; - if (!it.at_end()) { - m_element[i] = it->begin_key(); - begin_iterators(it->val(), i+1); - return *this; - } - } - } - m_done = true; // at end... - return *this; -} - -bool imdd_manager::iterator::operator==(iterator const & it) const { - if (m_done && it.m_done) - return true; - if (m_done || it.m_done) - return false; - if (m_element.size() != it.m_element.size()) - return false; - unsigned sz = m_element.size(); - for (unsigned i = 0; i < sz; i++) - if (m_element[i] != it.m_element[i]) - return false; - return true; -} - -bool imdd_manager::is_equal_core(imdd * d1, imdd * d2) { - if (d1 == d2) - return true; - if (d1->is_memoized() && d2->is_memoized()) - return false; - SASSERT(d1->get_arity() == d2->get_arity()); - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - bool shared = d1->is_shared() && d2->is_shared(); - bool r; - if (shared && m_is_equal_cache.find(d1, d2, r)) - return r; - - if (d1->get_arity() == 1) { - r = d1->m_children.is_equal(d2->m_children); - } - else { - imdd_children::iterator it1 = d1->begin_children(); - imdd_children::iterator end1 = d1->end_children(); - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - SASSERT(it1 != end1); - SASSERT(it2 != end2); - if (it1->begin_key() != it2->begin_key()) { - r = false; - } - else { - while (true) { - if (it1 == end1) { - r = (it2 == end2); - break; - } - if (it2 == end2) { - r = (it1 == end1); - break; - } - SASSERT(it1->val() != 0 && it2->val() != 0); - if (!is_equal_core(it1->val(), it2->val())) { - r = false; - break; - } - if (it1->end_key() < it2->end_key()) { - unsigned prev_end_key = it1->end_key(); - ++it1; - if (it1 == end1 || it1->begin_key() != prev_end_key + 1) { - r = false; - break; - } - } - else if (it1->end_key() > it2->end_key()) { - unsigned prev_end_key = it2->end_key(); - ++it2; - if (it2 == end2 || it2->begin_key() != prev_end_key + 1) { - r = false; - break; - } - } - else { - SASSERT(it1->end_key() == it2->end_key()); - ++it1; - ++it2; - } - } - } - } - if (shared) - m_is_equal_cache.insert(d1, d2, r); - return r; -} - -bool imdd_manager::is_equal(imdd * d1, imdd * d2) { - if (d1 == d2) - return true; - if (d1->is_memoized() && d2->is_memoized()) - return false; - if (d1->get_arity() != d2->get_arity()) - return false; - if (d1->empty() && d2->empty()) - return true; - if (d1->empty() || d2->empty()) - return false; - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - m_is_equal_cache.reset(); - return is_equal_core(d1, d2); -} - -/** - \brief Return true if the given imdd contains the tuple (values[0], ..., values[num-1]) -*/ -bool imdd_manager::contains(imdd * d, unsigned num, unsigned const * values) const { - SASSERT(d->get_arity() == num); - imdd * curr = d; - for (unsigned i = 0; i < num; i++) { - imdd * child; - if (!curr->m_children.find(values[i], child)) - return false; - curr = child; - } - return true; -} - -inline bool overlap(unsigned b1, unsigned e1, unsigned b2, unsigned e2) { - // [b1, e1] [b2, e2] - if (e1 < b2) - return false; - // [b2, e2] [b1, e1] - if (e2 < b1) - return false; - return true; -} - -bool imdd_manager::subsumes_core(imdd * d1, imdd * d2) { - if (d1 == d2) - return true; - SASSERT(d1->get_arity() == d2->get_arity()); - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - bool shared = d1->is_shared() && d2->is_shared(); - bool r; - if (shared && m_subsumes_cache.find(d1, d2, r)) - return r; - - imdd_children::iterator it1 = d1->begin_children(); - imdd_children::iterator end1 = d1->end_children(); - imdd_children::iterator it2 = d2->begin_children(); - imdd_children::iterator end2 = d2->end_children(); - SASSERT(it1 != end1); - SASSERT(it2 != end2); - if (it1->begin_key() > it2->begin_key()) { - r = false; - goto subsumes_end; - } - - if (d1->get_arity() == 1) { - // leaf case - while(true) { - SASSERT(it1 != end1); - SASSERT(it2 != end2); - it1.move_to(it2->begin_key()); - if (it1 == end1 || - it1->begin_key() > it2->begin_key() || // missed beginning of it2 curr entry - it1->end_key() < it2->end_key() // missed end of it2 curr entry - ) { - r = false; - goto subsumes_end; - } - SASSERT(it1->end_key() >= it2->end_key()); - ++it2; - if (it2 == end2) { - r = true; - goto subsumes_end; - } - } - } - else { - // non-leaf case - while (true) { - SASSERT(it1 != end1); - SASSERT(it2 != end2); - SASSERT(it1->val() != 0); - SASSERT(it2->val() != 0); - it1.move_to(it2->begin_key()); - if (it1 == end1 || - it1->begin_key() > it2->begin_key() // missed beginning of it2 curr entry - ) { - r = false; - goto subsumes_end; - } - // the beginning of it2 is inside it1 - SASSERT(it2->begin_key() >= it1->begin_key() && it2->begin_key() <= it1->end_key()); - // it1: [ ][ ][ ] - // it2 [ ] - while (true) { - SASSERT(it1 != end1); - SASSERT(it2 != end2); - // there is a overlap between the current entry in it1 and the current entry in it2 - SASSERT(overlap(it1->begin_key(), it1->end_key(), - it2->begin_key(), it2->end_key())); - if (!subsumes_core(it1->val(), it2->val())) { - r = false; - goto subsumes_end; - } - if (it1->end_key() >= it2->end_key()) { - ++it2; // processed the whole entry in it2 - break; - } - SASSERT(it1->end_key() < it2->end_key()); - unsigned prev_end_key = it1->end_key(); - ++it1; - if (it1 == end1 || it1->begin_key() != prev_end_key + 1) { - r = false; - goto subsumes_end; - } - } - if (it2 == end2) { - r = true; - goto subsumes_end; - } - } - } - subsumes_end: - if (shared) - m_subsumes_cache.insert(d1, d2, r); - return r; -} - -/** - \brief Return true is d1 is a superset of d2, or equal to d2. -*/ -bool imdd_manager::subsumes(imdd * d1, imdd * d2) { - SASSERT(d1->get_arity() == d2->get_arity()); - if (d1 == d2) - return true; - if (d2->empty()) - return true; - if (d1->empty()) { - SASSERT(!d2->empty()); - return false; - } - SASSERT(!d1->empty()); - SASSERT(!d2->empty()); - m_subsumes_cache.reset(); - return subsumes_core(d1, d2); -} - -void imdd_manager::remove_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers) { - SASSERT(d->get_arity() == num); - d = remove_facts_main(d, num, lowers, uppers, true, true); -} - -void imdd_manager::remove_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers) { - SASSERT(d->get_arity() == num); - r = remove_facts_main(d, num, lowers, uppers, false, true); -} - -imdd * imdd_manager::remove_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num); - delay_dealloc delay(*this); - m_remove_facts_cache.reset(); - return remove_facts_core(d, num, lowers, uppers, destructive, memoize_res); -} - -imdd * imdd_manager::remove_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) { - SASSERT(d->get_arity() == num && num > 0); - - imdd * new_d = 0; - unsigned b = *lowers; - unsigned e = *uppers; - sbuffer to_insert, to_remove; - imdd_children::iterator it = d->m_children.find_geq(b); - imdd_children::iterator end = d->end_children(); - bool is_destructive = destructive_update_at(destructive, d); - - if (!is_destructive && is_cached(m_remove_facts_cache, d, new_d)) { - return new_d; - } - - if (num == 1) { - new_d = remove_main(d, *lowers, *uppers, destructive, memoize_res); - if (!is_destructive) { - cache_result(m_remove_facts_cache, d, new_d); - } - return new_d; - } - - if (it == end || e < it->begin_key()) { - if (!is_destructive) { - cache_result(m_remove_facts_cache, d, d); - } - return d; - } - - if (!is_destructive) { - new_d = copy_main(d); - } - else { - new_d = d; - } - for (; it != end && b <= e; ++it) { - // - // remove ([b:e]::rest), [b_key:e_key] * ch) = - // { [b_key:e_key] * ch } if e < b_key - // { [b_key:e_key] * ch' } if b <= b_key <= e_key <= e - // { [b_key:e]*ch' [e+1:e_key]*ch } if b <= b_key <= e < e_key - // { [b_key:b-1]*ch [b:e]*ch', [e+1:e_key]*ch } if b_key < b <= e < e_key - // { [b_key:b-1]*ch [b:e_key]*ch' } if b_key < b <= e_key <= e - // where - // ch' = remove(rest, ch) - // assumption: remove_child retains the cases where ch is in the tree. - // - - imdd_children::entry const & curr_entry = *it; - unsigned b_key = curr_entry.begin_key(); - unsigned e_key = curr_entry.end_key(); - imdd* curr_child = curr_entry.val(); - imdd* new_curr_child = 0; - - if (e < b_key) { - break; - } - - new_curr_child = remove_facts_core(curr_child, num-1, lowers+1, uppers+1, destructive, memoize_res); - - if (new_curr_child == curr_child) { - continue; - } - - if (new_curr_child != 0) { - if (b <= b_key && e_key <= e) { - to_insert.push_back(entry(b_key, e_key, new_curr_child)); - } - if (b <= b_key && e < e_key) { - to_insert.push_back(entry(b_key, e, new_curr_child)); - } - if (b_key < b && e < e_key) { - to_insert.push_back(entry(b, e, new_curr_child)); - } - if (b_key < b && e_key <= e) { - to_insert.push_back(entry(b, e_key, new_curr_child)); - } - } - if (is_destructive) { - to_remove.push_back(entry(b, e, 0)); - } - else { - remove_child(new_d, b, e); - } - b = e_key + 1; - } - for (unsigned i = 0; i < to_remove.size(); ++i) { - remove_child(new_d, to_remove[i].begin_key(), to_remove[i].end_key()); - } - for (unsigned i = 0; i < to_insert.size(); ++i) { - add_child(new_d, to_insert[i].begin_key(), to_insert[i].end_key(), to_insert[i].val()); - } - if (!is_destructive) { - cache_result(m_remove_facts_cache, d, new_d); - } - return new_d; -} - -void imdd_manager::display(std::ostream & out, imdd const * d, unsigned_vector & intervals, bool & first) const { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - if (d->get_arity() == 1) { - for (; it != end; ++it) { - SASSERT(it->val() == 0); - if (first) { - first = false; - } - else { - out << ",\n "; - } - out << "("; - unsigned sz = intervals.size(); - for (unsigned i = 0; i < sz; i+=2) { - out << "[" << intervals[i] << ", " << intervals[i+1] << "]"; - out << ", "; - } - out << "[" << it->begin_key() << ", " << it->end_key() << "])"; - } - } - else { - for (; it != end; ++it) { - intervals.push_back(it->begin_key()); - intervals.push_back(it->end_key()); - display(out, it->val(), intervals, first); - intervals.pop_back(); - intervals.pop_back(); - } - } -} - -void imdd_manager::display(std::ostream & out, imdd const * d) const { - unsigned_vector intervals; - bool first = true; - out << "{"; - display(out, d, intervals, first); - out << "}"; -} - -struct display_ll_context { - typedef map, default_eq > imdd2uint; - std::ostream & m_out; - unsigned m_next_id; - imdd2uint m_map; - display_ll_context(std::ostream & out):m_out(out), m_next_id(1) {} - - void display_tabs(unsigned num_tabs) { - for (unsigned i = 0; i < num_tabs; i++) - m_out << " "; - } - - void display_node(unsigned num_tabs, imdd const * d) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - if (it == end) { - m_out << "{}"; - return; - } - if (d->get_arity() == 1) { - // leaf - m_out << "{"; - for (bool first = true; it != end; ++it) { - if (first) - first = false; - else - m_out << ", "; - m_out << "[" << it->begin_key() << ", " << it->end_key() << "]"; - } - m_out << "}"; - } - else { - m_out << "{\n"; - display_tabs(num_tabs); - while (it != end) { - m_out << " [" << it->begin_key() << ", " << it->end_key() << "] -> "; - display(num_tabs+1, it->val()); - ++it; - if (it != end) { - m_out << "\n"; - display_tabs(num_tabs); - } - else { - m_out << "}"; - } - } - } - m_out << (d->is_memoized() ? "*" : "") << "$" << d->memory(); - } - - void display(unsigned num_tabs, imdd const * d) { - if (d == 0) { - m_out << ""; - return; - } - unsigned id; - if (m_map.find(const_cast(d), id)) { - m_out << "#" << id; - } - else if (d->is_shared()) { - id = m_next_id; - m_next_id++; - m_map.insert(const_cast(d), id); - m_out << "#" << id << ":"; - display_node(num_tabs, d); - } - else { - display_node(num_tabs, d); - } - } -}; - -void imdd_manager::display_ll(std::ostream & out, imdd const * d) const { - display_ll_context ctx(out); - ctx.display(0, d); - out << "\n"; -} - -struct addone_proc { - unsigned m_r; - addone_proc():m_r(0) {} - void operator()(imdd * d) { m_r++; } -}; - -/** - \brief Return the number of nodes in an IMDD. - This is *not* a constant time operation. - It is linear on the size of the IMDD -*/ -unsigned imdd_manager::get_num_nodes(imdd const * d) const { - addone_proc p; - for_each(const_cast(d), p); - return p.m_r; -} - -struct addmem_proc { - unsigned m_r; - addmem_proc():m_r(0) {} - void operator()(imdd * d) { m_r += d->memory(); } -}; - -/** - \brief Return the amount of memory consumed by the given IMDD. -*/ -unsigned imdd_manager::memory(imdd const * d) const { - addmem_proc p; - for_each(const_cast(d), p); - return p.m_r; -} - -/** - \brief Return number of rows the given IMDD represents. -*/ -size_t imdd_manager::get_num_rows(imdd const* root) const { - obj_map counts; - ptr_vector todo; - todo.push_back(root); - while (!todo.empty()) { - imdd const* d = todo.back(); - if (counts.contains(d)) { - todo.pop_back(); - continue; - } - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - bool all_valid = true; - size_t count = 0, c; - for (; it != end; ++it) { - imdd const* ch = it->val(); - if (!ch) { - count += (it->end_key()-it->begin_key()+1); - } - else if (counts.find(ch, c)) { - count += c*(it->end_key()-it->begin_key()+1); - } - else { - all_valid = false; - todo.push_back(ch); - } - } - if (all_valid) { - todo.pop_back(); - counts.insert(d, count); - } - } - return counts.find(root); -} - -imdd * imdd_manager::add_bounded_var_core(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res) { - if (d == 0) { - SASSERT(before_vidx == 0); - imdd * r = _mk_empty(1); - add_child(r, lower, upper, 0); - r = memoize_new_imdd_if(memoize_res, r); - return r; - } - imdd * r = 0; - if (is_cached(m_add_bounded_var_cache, d, r)) - return r; - if (before_vidx == 0) { - imdd * r = _mk_empty(d->get_arity() + 1); - add_child(r, lower, upper, d); - r = memoize_new_imdd_if(d->is_memoized() && memoize_res, r); - return r; - } - - if (destructive_update_at(destructive, d)) { - sbuffer to_insert; - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = add_bounded_var_core(curr_child, before_vidx-1, lower, upper, true, memoize_res); - new_child->inc_ref(); // we will be resetting d->m_children later. - to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child)); - } - SASSERT(!to_insert.empty()); - d->replace_children(m_sl_manager, to_insert); - return d; - } - - bool new_children_memoized = true; - r = _mk_empty(d->get_arity() + 1); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd * new_child = add_bounded_var_core(curr_child, before_vidx-1, lower, upper, false, memoize_res); - push_back(it->begin_key(), it->end_key(), new_child); - if (!new_child->is_memoized()) - new_children_memoized = false; - } - r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r); - cache_result(m_add_bounded_var_cache, d, r); - return r; -} - -/** - \brief Add a variable (bounded by lower and upper) before the variable before_var. - - That is, for each tuple (v_1, ..., v_n) in the IMDD \c d, the resultant IMDD contains the - tuples - - (v_1, ..., v_{after_vidx}, lower, ..., v_n) - (v_1, ..., v_{after_vidx}, lower+1, ..., v_n) - ... - (v_1, ..., v_{after_vidx}, upper, ..., v_n) - - This function is mainly used to implement mk_filter. - - \pre after_vidx < d->get_arity() -*/ -imdd * imdd_manager::add_bounded_var_main(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res) { - SASSERT(before_vidx <= d->get_arity()); - if (d->empty()) - return d; - m_add_bounded_var_cache.reset(); - delay_dealloc delay(*this); - imdd * r = add_bounded_var_core(d, before_vidx, lower, upper, destructive, memoize_res); - return r; -} - -filter_cache_entry * imdd_manager::mk_filter_cache_entry(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r) { - void * mem = m_filter_entries.allocate(filter_cache_entry::get_obj_size(ctx_sz)); - return new (mem) filter_cache_entry(d, r, ctx_sz, ctx); -} - -imdd * imdd_manager::is_mk_filter_cached(imdd * d, unsigned ctx_sz, unsigned * ctx) { - if (!d->is_shared()) - return 0; - m_filter_entries.push_scope(); - filter_cache_entry * tmp_entry = mk_filter_cache_entry(d, ctx_sz, ctx, 0); - filter_cache_entry * e = 0; - bool r = m_filter_cache.find(tmp_entry, e); - m_filter_entries.pop_scope(); - if (!r || e->m_r->is_dead()) - return 0; - return e->m_r; -} - -void imdd_manager::cache_mk_filter(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r) { - if (d->is_shared()) { - filter_cache_entry * new_entry = mk_filter_cache_entry(d, ctx_sz, ctx, r); - m_filter_cache.insert(new_entry); - } -} - -void imdd_manager::init_mk_filter(unsigned arity, unsigned num_vars, unsigned * vars) { - m_filter_cache.reset(); - m_filter_entries.reset(); - m_filter_context.reset(); - m_filter_num_vars = num_vars; - m_filter_begin_vars = vars; - m_filter_end_vars = vars + num_vars; - DEBUG_CODE({ - for (unsigned i = 0; i < num_vars; i++) { - SASSERT(vars[i] < arity); - } - }); -} - -template -void imdd_manager::mk_filter_dupdt_core(imdd_ref & d, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res) { - SASSERT(!d->empty()); - - if (!destructive_update_at(true, d)) { - mk_filter_core(d, d, vidx, num_found, proc, memoize_res); - return; - } - - bool _is_filter_var = is_filter_var(vidx); - if (_is_filter_var) - num_found++; - unsigned new_vidx = vidx+1; - imdd_ref new_r(*this); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd_ref new_child(*this); - it.take_curr_ownership(m_sl_manager, new_child); // take ownership of the current child. - if (!_is_filter_var) { - mk_filter_dupdt_core(new_child, new_vidx, num_found, proc, memoize_res); - add_bounded_var_dupdt(new_child, num_found, it->begin_key(), it->end_key(), memoize_res); - if (new_r == 0) - new_r = new_child; - else - mk_union_core_dupdt(new_r, new_child, memoize_res); - } - else { - m_filter_context.push_back(it->begin_key()); - m_filter_context.push_back(it->end_key()); - if (num_found < m_filter_num_vars) { - mk_filter_dupdt_core(new_child, new_vidx, num_found, proc, memoize_res); - } - else { - proc(m_filter_context.c_ptr(), new_child, new_child, memoize_res); - } - m_filter_context.pop_back(); - m_filter_context.pop_back(); - if (new_r == 0) - new_r = new_child; - else - mk_union_core_dupdt(new_r, new_child, memoize_res); - } - } - d->m_children.reset(m_sl_manager); - d = new_r; -} - -template -void imdd_manager::mk_filter_core(imdd * d, imdd_ref & r, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res) { - SASSERT(!d->empty()); - - imdd * _r = is_mk_filter_cached(d, m_filter_context.size(), m_filter_context.c_ptr()); - if (_r) { - r = _r; - return; - } - - bool _is_filter_var = is_filter_var(vidx); - if (_is_filter_var) - num_found++; - unsigned new_vidx = vidx+1; - imdd_ref new_r(*this); - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - SASSERT(it != end); - for (; it != end; ++it) { - imdd * curr_child = it->val(); - imdd_ref new_child(*this); - if (!_is_filter_var) { - mk_filter_core(curr_child, new_child, new_vidx, num_found, proc, memoize_res); - imdd_ref tmp(*this); - add_bounded_var(new_child, tmp, num_found, it->begin_key(), it->end_key(), memoize_res); - if (new_r == 0) - new_r = tmp; - else - mk_union_core(new_r, tmp, new_r, memoize_res); - } - else { - m_filter_context.push_back(it->begin_key()); - m_filter_context.push_back(it->end_key()); - if (num_found < m_filter_num_vars) { - mk_filter_core(curr_child, new_child, new_vidx, num_found, proc, memoize_res); - } - else { - proc(m_filter_context.c_ptr(), curr_child, new_child, memoize_res); - } - m_filter_context.pop_back(); - m_filter_context.pop_back(); - if (new_r == 0) - new_r = new_child; - else - mk_union_core(new_r, new_child, new_r, memoize_res); - } - } - r = new_r; - cache_mk_filter(d, m_filter_context.size(), m_filter_context.c_ptr(), r); -} - -template -void imdd_manager::mk_filter_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res) { - if (d->empty()) - return; - unsigned arity = d->get_arity(); - init_mk_filter(arity, num_vars, vars); - mk_filter_dupdt_core(d, 0, 0, proc, memoize_res); - if (d == 0) - d = _mk_empty(arity); -} - -template -void imdd_manager::mk_filter(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res) { - if (d->empty()) { - r = d; - return; - } - unsigned arity = d->get_arity(); - init_mk_filter(arity, num_vars, vars); - mk_filter_core(d, r, 0, 0, proc, memoize_res); - if (r == 0) - r = _mk_empty(arity); -} - -imdd * imdd_manager::mk_distinct_imdd(unsigned l1, unsigned u1, unsigned l2, unsigned u2, imdd * d, bool memoize_res) { - unsigned d_arity; - if (d == 0) { - d_arity = 0; - } - else { - d_arity = d->get_arity(); - memoize_res = memoize_res && d->is_memoized(); - } - imdd * r = _mk_empty(d_arity + 2); - imdd_children::push_back_proc push_back(m_sl_manager, r->m_children); - - TRACE("mk_distinct_imdd", tout << "STARTING: " << l1 << " " << u1 << " " << l2 << " " << u2 << "\n";); - -#define ADD_ENTRY(L1, U1, L2, U2) { \ - TRACE("mk_distinct_imdd", tout << "[" << L1 << " " << U1 << " " << L2 << " " << U2 << "]\n";); \ - imdd * new_child = _mk_empty(d_arity + 1); \ - add_child(new_child, L2, U2, d); \ - new_child = memoize_new_imdd_if(memoize_res, new_child); \ - push_back(L1, U1, new_child);} - - if (u1 < l2 || u2 < l1) { - ADD_ENTRY(l1, u1, l2, u2); // the intervals are disjoint - } - else { - if (l1 < l2) { - SASSERT(u1 >= l2); - // [l1 ... - // [l2 ... - // --> - // [l1, l2-1] X [l2, u2] - ADD_ENTRY(l1, l2-1, l2, u2); - } - - unsigned l = std::max(l1, l2); - unsigned u = std::min(u1, u2); - // [l, l] X [l2, l-1] // if l-1 >= l2 (i.e., l > l2) - // [l, l] X [l+1, u2] - // [l+1, l+1] X [l2, l] - // [l+1, l+1] X [l+2, u2] - // [l+2, l+2] X [l2, l+1] - // [l+2, l+2] X [l+3, u2] - // ... - // [u, u] X [l2, u-1] - // [u, u] X [u+1, u2] // if u+1 <= u2 (i.e., u < u2) - for (unsigned i = l; i <= u; i++) { - if (i > l2 && i < u2) { - // ADD_ENTRY(i, i, l2, i-1); - // ADD_ENTRY(i, i, i+1, u2); - imdd * new_child = _mk_empty(d_arity + 1); - add_child(new_child, l2, i-1, d); - add_child(new_child, i+1, u2, d); - new_child = memoize_new_imdd_if(memoize_res, new_child); - push_back(i, i, new_child); - } - else if (i > l2) { - SASSERT(!(i < u2)); - ADD_ENTRY(i, i, l2, i-1); - } - else if (i < u2) { - SASSERT(!(i > l2)); - ADD_ENTRY(i, i, i+1, u2); - } - } - - if (u1 > u2) { - // ... u1] - // ... u2] - // --> - // [u2+1, u1] X [l2, u2] - SASSERT(u2 >= l1); - ADD_ENTRY(u2+1, u1, l2, u2); - } - } -#undef ADD_ENTRY - - r = memoize_new_imdd_if(memoize_res, r); - return r; -} - -/** - \brief Auxiliary functor used to implement mk_filter_distinct -*/ -struct distinct_proc { - imdd_manager & m_manager; - - distinct_proc(imdd_manager & m): - m_manager(m) { - } - - void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res) { - r = m_manager.mk_distinct_imdd(lowers_uppers[0], lowers_uppers[1], lowers_uppers[2], lowers_uppers[3], d, memoize_res); - } -}; - -void imdd_manager::mk_filter_distinct_dupdt(imdd_ref & d, unsigned v1, unsigned v2, bool memoize_res) { - unsigned vars[2] = { v1, v2 }; - distinct_proc proc(*this); - mk_filter_dupdt(d, 2, vars, proc, memoize_res); -} - -void imdd_manager::mk_filter_distinct(imdd * d, imdd_ref & r, unsigned v1, unsigned v2, bool memoize_res) { - unsigned vars[2] = { v1, v2 }; - distinct_proc proc(*this); - mk_filter(d, r, 2, vars, proc, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_distinct(0x" << d << ", 0x" << r.get() << ", " << v1 << ", " << v2 << ", " << memoize_res << ");\n";); -} - -imdd * imdd_manager::mk_disequal_imdd(unsigned l1, unsigned u1, unsigned value, imdd * d, bool memoize_res) { - unsigned d_arity; - if (d == 0) { - d_arity = 0; - } - else { - d_arity = d->get_arity(); - memoize_res = memoize_res && d->is_memoized(); - } - imdd * r = _mk_empty(d_arity + 1); - if (value < l1 || value > u1) { - add_child(r, l1, u1, d); - } - else { - SASSERT(l1 <= value && value <= u1); - if (l1 < value) { - add_child(r, l1, value-1, d); - } - if (value < u1) { - add_child(r, value+1, u1, d); - } - } - r = memoize_new_imdd_if(memoize_res, r); - return r; -} - -struct disequal_proc { - imdd_manager & m_manager; - unsigned m_value; - - disequal_proc(imdd_manager & m, unsigned v): - m_manager(m), - m_value(v) { - } - - void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res) { - r = m_manager.mk_disequal_imdd(lowers_uppers[0], lowers_uppers[1], m_value, d, memoize_res); - } -}; - -void imdd_manager::mk_filter_disequal_dupdt(imdd_ref & d, unsigned var, unsigned value, bool memoize_res) { - unsigned vars[1] = { var }; - disequal_proc proc(*this, value); - mk_filter_dupdt(d, 1, vars, proc, memoize_res); -} - -void imdd_manager::mk_filter_disequal(imdd * d, imdd_ref & r, unsigned var, unsigned value, bool memoize_res) { - unsigned vars[1] = { var }; - disequal_proc proc(*this, value); - mk_filter(d, r, 1, vars, proc, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_disequal(0x" << d << ", 0x" << r.get() << ", " << var << ", " << value << ", " << memoize_res << ");\n";); -} - - diff --git a/src/muz_qe/imdd.h b/src/muz_qe/imdd.h deleted file mode 100644 index b2da275bf..000000000 --- a/src/muz_qe/imdd.h +++ /dev/null @@ -1,849 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - imdd.h - -Abstract: - - Interval based Multiple-valued Decision Diagrams. - -Author: - - Leonardo de Moura (leonardo) 2010-10-13. - -Revision History: - ---*/ -#ifndef _IMDD_H_ -#define _IMDD_H_ - -#include"id_gen.h" -#include"hashtable.h" -#include"map.h" -#include"obj_hashtable.h" -#include"obj_pair_hashtable.h" -#include"buffer.h" -#include"interval_skip_list.h" -#include"region.h" -#include"obj_ref.h" - -class imdd; -class imdd_manager; - -/** - \brief Manager for skip-lists used to implement IMDD nodes. -*/ -class sl_imdd_manager : public random_level_manager { - imdd_manager * m_manager; // real manager - small_object_allocator & m_alloc; - friend class imdd_manager; -public: - sl_imdd_manager(small_object_allocator & alloc):m_alloc(alloc) {} - void * allocate(size_t size) { return m_alloc.allocate(size); } - void deallocate(size_t size, void* p) { m_alloc.deallocate(size, p); } - void inc_ref_eh(imdd * v); - void dec_ref_eh(imdd * v); -}; - -#define IMDD_BUCKET_CAPACITY 128 -#define IMDD_MAX_LEVEL 32 - -typedef interval_skip_list, - IMDD_BUCKET_CAPACITY, - IMDD_MAX_LEVEL, - true, /* support ref-counting */ - sl_imdd_manager> > imdd_children; - -typedef interval_skip_list, - IMDD_BUCKET_CAPACITY, - IMDD_MAX_LEVEL, - false, - sl_manager_base > > sl_interval_set; - -/* - Notes: - - - We use reference counting for garbage collecting IMDDs nodes. - - - Each IMDD node has a "memoized" flag. If the flag is true, the we use hash-consing for this node. - - - The children of a memoized node must be memoized. - - - The children of a non-memoized node may be memoized. - - - The "memoized" flag cannot be reset after it was set. - - - The result of some operations may be cached. We only use caching for - operations processing memoized nodes. - - - For non-memoized nodes, if m_ref_count <= 1, destructive updates may be performed by some operations. - - - IMPORTANT: "memoized" flag == false doesn't imply m_ref_count <= 1. -*/ - - -/** - \brief IMDDs -*/ -class imdd { - -protected: - friend class imdd_manager; - - unsigned m_id; //!< Unique ID - unsigned m_ref_count; - unsigned m_arity:30; - unsigned m_memoized:1; - unsigned m_dead:1; - imdd_children m_children; - - void inc_ref() { - m_ref_count ++; - } - - void dec_ref() { - SASSERT(m_ref_count > 0); - m_ref_count --; - } - - void mark_as_memoized(bool flag = true) { - SASSERT(is_memoized() != flag); - m_memoized = flag; - } - - void mark_as_dead() { SASSERT(!m_dead); m_dead = true; } - - void replace_children(sl_imdd_manager & m, sbuffer & new_children); - -public: - imdd(sl_imdd_manager & m, unsigned id, unsigned arity):m_id(id), m_ref_count(0), m_arity(arity), m_memoized(false), m_dead(false), m_children(m) {} - unsigned get_id() const { return m_id; } - unsigned get_ref_count() const { return m_ref_count; } - bool is_memoized() const { return m_memoized; } - bool is_shared() const { return m_ref_count > 1; } - bool is_dead() const { return m_dead; } - unsigned get_arity() const { return m_arity; } - imdd_children::iterator begin_children() const { return m_children.begin(); } - imdd_children::iterator end_children() const { return m_children.end(); } - unsigned hc_hash() const; // hash code for hash-consing. - bool hc_equal(imdd const * other) const; // eq function for hash-consing - bool empty() const { return m_children.empty(); } - unsigned hash() const { return m_id; } - unsigned memory() const { return sizeof(imdd) + m_children.memory() - sizeof(imdd_children); } -}; - -// ----------------------------------- -// -// IMDD hash-consing -// -// ----------------------------------- - -// this is the internal hashing functor for hash-consing IMDDs. -struct imdd_hash_proc { - unsigned operator()(imdd const * d) const { return d->hc_hash(); } -}; - -// This is the internal comparison functor for hash-consing IMDDs. -struct imdd_eq_proc { - bool operator()(imdd const * d1, imdd const * d2) const { return d1->hc_equal(d2); } -}; - -typedef ptr_hashtable imdd_table; -typedef obj_hashtable imdd_cache; -typedef obj_map imdd2imdd_cache; -typedef obj_pair_map imdd_pair2imdd_cache; -typedef obj_pair_map imdd_pair2bool_cache; -typedef obj_map imdd2intervals; - -typedef std::pair imdd_value_pair; - -struct fi_cache_entry { - imdd * m_d; - unsigned m_lower; - unsigned m_upper; - unsigned m_hash; - unsigned m_num_result; - imdd_value_pair m_result[0]; - - void mk_hash() { - m_hash = hash_u_u(m_d->get_id(), hash_u_u(m_lower, m_upper)); - } - - fi_cache_entry(imdd * d, unsigned l, unsigned u): - m_d(d), - m_lower(l), - m_upper(u) { - mk_hash(); - } - - fi_cache_entry(imdd * d, unsigned l, unsigned u, unsigned num, imdd_value_pair result[]): - m_d(d), - m_lower(l), - m_upper(u), - m_num_result(num) { - mk_hash(); - memcpy(m_result, result, sizeof(imdd_value_pair)*num); - } - - unsigned hash() const { - return m_hash; - } - - bool operator==(fi_cache_entry const & other) const { - return - m_d == other.m_d && - m_lower == other.m_lower && - m_upper == other.m_upper; - } -}; - -typedef obj_hashtable imdd_fi_cache; -typedef union { - imdd * m_d; - fi_cache_entry * m_entry; -} mk_fi_result; - -struct filter_cache_entry { - imdd * m_d; - imdd * m_r; - unsigned m_hash; - unsigned m_ctx_size; - unsigned m_ctx[0]; // lower and upper bounds that are part of the context. - - static unsigned get_obj_size(unsigned ctx_size) { - return sizeof(filter_cache_entry) + ctx_size * sizeof(unsigned); - } - - void mk_hash() { - if (m_ctx_size > 0) - m_hash = string_hash(reinterpret_cast(m_ctx), m_ctx_size * sizeof(unsigned), m_d->get_id()); - else - m_hash = m_d->get_id(); - } - - filter_cache_entry(imdd * d, imdd * r, unsigned ctx_size, unsigned * ctx): - m_d(d), - m_r(r), - m_ctx_size(ctx_size) { - memcpy(m_ctx, ctx, sizeof(unsigned)*m_ctx_size); - mk_hash(); - } - - unsigned hash() const { - return m_hash; - } - - bool operator==(filter_cache_entry const & other) const { - if (m_d != other.m_d) - return false; - if (m_ctx_size != other.m_ctx_size) - return false; - for (unsigned i = 0; i < m_ctx_size; i++) - if (m_ctx[i] != other.m_ctx[i]) - return false; - return true; - } -}; - -typedef obj_hashtable imdd_mk_filter_cache; - -typedef obj_ref imdd_ref; - -class imdd_manager { - typedef imdd_children::entry entry; - small_object_allocator m_alloc; - id_gen m_id_gen; - vector m_tables; // we keep a table for each height. - sl_imdd_manager m_sl_manager; - unsigned m_simple_max_entries; //!< maximum number of entries in a "simple" node. - bool m_delay_dealloc; - ptr_vector m_to_delete; //!< list of IMDDs marked as dead. These IMDDs may still be in cache tables. - - // generic cache and todo-lists - ptr_vector m_worklist; - imdd_cache m_visited; - - void mark_as_dead(imdd * d); - void deallocate_imdd(imdd * d); - void delete_imdd(imdd * d); - - class delay_dealloc; - friend class delay_dealloc; - - class delay_dealloc { - imdd_manager & m_manager; - bool m_delay_dealloc_value; - unsigned m_to_delete_size; - public: - delay_dealloc(imdd_manager & m): - m_manager(m), - m_delay_dealloc_value(m_manager.m_delay_dealloc), - m_to_delete_size(m_manager.m_to_delete.size()) { - m_manager.m_delay_dealloc = true; - } - ~delay_dealloc(); - }; - - bool is_simple_node(imdd * d) const; - - void add_child(imdd * d, unsigned lower, unsigned upper, imdd * child) { - d->m_children.insert(m_sl_manager, lower, upper, child); - } - - void add_child(imdd * d, unsigned value, imdd * child) { - add_child(d, value, value, child); - } - - void remove_child(imdd * d, unsigned lower, unsigned upper) { - d->m_children.remove(m_sl_manager, lower, upper); - } - - imdd * copy_main(imdd * d); - - imdd * insert_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res); - - imdd * remove_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res); - - imdd2imdd_cache m_mk_product_cache; - struct null2imdd_proc; - struct mk_product_proc; - friend struct mk_product_proc; - imdd * mk_product_core(imdd * d1, imdd * d2, bool destructive, bool memoize); - imdd * mk_product_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res); - - imdd2imdd_cache m_add_facts_cache; - ptr_vector m_add_facts_new_children; - void init_add_facts_new_children(unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res); - imdd * add_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - imdd * add_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - - imdd2imdd_cache m_remove_facts_cache; - imdd * remove_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - imdd * remove_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res); - - - imdd2imdd_cache m_defrag_cache; - imdd * defrag_core(imdd * d); - - imdd_pair2imdd_cache m_union_cache; - void push_back_entries(unsigned head, imdd_children::iterator & it, imdd_children::iterator & end, - imdd_children::push_back_proc & push_back, bool & children_memoized); - void push_back_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned limit, - imdd_children::push_back_proc & push_back, bool & children_memoized); - void move_head(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned new_head); - void copy_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned limit, sbuffer & result); - void reset_union_cache(); - imdd * mk_union_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res); - imdd * mk_union_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res); - void mk_union_core_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res); - void mk_union_core(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res); - - imdd_pair2bool_cache m_is_equal_cache; - bool is_equal_core(imdd * d1, imdd * d2); - - imdd_pair2bool_cache m_subsumes_cache; - bool subsumes_core(imdd * d1, imdd * d2); - - imdd2imdd_cache m_complement_cache; - imdd * mk_complement_core(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res); - imdd * mk_complement_main(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res); - - imdd2imdd_cache m_filter_equal_cache; - imdd * mk_filter_equal_core(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res); - imdd * mk_filter_equal_main(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res); - - - // original - imdd2intervals m_imdd2interval_set; - ptr_vector m_alloc_is; - typedef sl_manager_base sl_imanager; - void reset_fi_intervals(sl_imanager& m); - sl_interval_set const* init_fi_intervals(sl_imanager& m, imdd* d, unsigned var, unsigned num_found); - - imdd2imdd_cache m_fi_top_cache; - imdd_fi_cache m_fi_bottom_cache; - unsigned m_fi_num_vars; - unsigned * m_fi_begin_vars; - unsigned * m_fi_end_vars; - region m_fi_entries; - bool is_fi_var(unsigned v) const { return std::find(m_fi_begin_vars, m_fi_end_vars, v) != m_fi_end_vars; } - fi_cache_entry * mk_fi_cache_entry(imdd * d, unsigned lower, unsigned upper, unsigned num_pairs, imdd_value_pair pairs[]); - mk_fi_result mk_filter_identical_core(imdd * d, unsigned offset, unsigned num_found, unsigned lower, unsigned upper, - bool destructive, bool memoize_res); - imdd * mk_filter_identical_main(imdd * d, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res); - - // v2 - obj_map m_filter_identical_cache; - void filter_identical_core2(imdd* d, unsigned num_vars, unsigned b, unsigned e, ptr_vector& ch); - imdd* filter_identical_core2(imdd* d, unsigned var, unsigned num_vars, bool memoize_res); - void filter_identical_main2(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res); - void swap_in(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars); - void swap_out(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars); - - // v3 - struct interval { - unsigned m_lo; - unsigned m_hi; - interval(unsigned lo, unsigned hi): m_lo(lo), m_hi(hi) {} - }; - struct interval_dd : public interval { - imdd* m_dd; - interval_dd(unsigned lo, unsigned hi, imdd* d): interval(lo, hi), m_dd(d) {} - }; - template - class id_map { - unsigned m_T; - unsigned_vector m_Ts; - svector*> m_vecs; - unsigned_vector m_alloc; - unsigned m_first_free; - void hard_reset() { - std::for_each(m_vecs.begin(), m_vecs.end(), delete_proc >()); - m_alloc.reset(); - m_first_free = 0; - m_vecs.reset(); - m_Ts.reset(); - m_T = 0; - } - - void allocate_entry(unsigned id) { - if (m_vecs[id]) { - return; - } - while (m_first_free < m_alloc.size()) { - if (m_vecs[m_first_free] && m_Ts[m_first_free] < m_T) { - svector* v = m_vecs[m_first_free]; - m_vecs[m_first_free] = 0; - m_vecs[id] = v; - ++m_first_free; - return; - } - ++m_first_free; - } - m_vecs[id] = alloc(svector); - m_alloc.push_back(id); - } - public: - id_map():m_T(0) {} - ~id_map() { hard_reset(); } - void reset() { ++m_T; m_first_free = 0; if (m_T == UINT_MAX) hard_reset(); } - svector& init(imdd* d) { - unsigned id = d->get_id(); - if (id >= m_vecs.size()) { - m_vecs.resize(id+1); - m_Ts.resize(id+1); - } - if (m_Ts[id] < m_T) { - allocate_entry(id); - m_vecs[id]->reset(); - m_Ts[id] = m_T; - } - return *m_vecs[id]; - } - - typedef svector data; - struct iterator { - unsigned m_offset; - id_map const& m; - iterator(unsigned o, id_map const& m):m_offset(o),m(m) {} - data const & operator*() const { return *m.m_vecs[m_offset]; } - data const * operator->() const { return &(operator*()); } - data * operator->() { return &(operator*()); } - iterator & operator++() { ++m_offset; return move_to_used(); } - iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - bool operator==(iterator const & it) const { return m_offset == it.m_offset; } - bool operator!=(iterator const & it) const { return m_offset != it.m_offset; } - iterator & move_to_used() { - while (m_offset < m.m_vecs.size() && - m.m_Ts[m_offset] < m.m_T) { - ++m_offset; - } - return *this; - } - }; - iterator begin() const { return iterator(0, *this).move_to_used(); } - iterator end() const { return iterator(m_vecs.size(), *this); } - }; - typedef id_map filter_id_map; - typedef id_map filter_idd_map; - filter_id_map m_nodes; - filter_idd_map m_nodes_dd; - svector m_i_nodes_dd, m_i_nodes_dd_tmp; - svector m_i_nodes, m_i_nodes_tmp; - unsigned_vector m_offsets; - void filter_identical_main3(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res); - void filter_identical_main3(imdd * d, imdd_ref& r, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res); - imdd* filter_identical_loop3(imdd * d, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res); - void refine_intervals(svector& i_nodes, svector const& i_nodes_dd); - void merge_intervals(svector& dst, svector const& src); - imdd* filter_identical_mk_nodes(imdd* d, unsigned v, bool del1, bool del2, bool memoize_res); - void print_filter_idd(std::ostream& out, filter_idd_map const& m); - void print_interval_dd(std::ostream& out, svector const& m); - - - - unsigned m_proj_num_vars; - unsigned * m_proj_begin_vars; - unsigned * m_proj_end_vars; - imdd2imdd_cache m_proj_cache; - bool is_proj_var(unsigned v) const { return std::find(m_proj_begin_vars, m_proj_end_vars, v) != m_proj_end_vars; } - void mk_project_init(unsigned num_vars, unsigned * vars); - void mk_project_core(imdd * d, imdd_ref & r, unsigned var, unsigned num_found, bool memoize_res); - void mk_project_dupdt_core(imdd_ref & d, unsigned var, unsigned num_found, bool memoize_res); - - imdd2imdd_cache m_swap_cache; - imdd * m_swap_new_child; - bool m_swap_granchildren_memoized; - imdd * mk_swap_new_child(unsigned lower, unsigned upper, imdd * child); - void mk_swap_acc1_dupdt(imdd_ref & d, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res); - void mk_swap_acc1(imdd * d, imdd_ref & r, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res); - void mk_swap_acc2(imdd_ref & r, unsigned lower1, unsigned upper1, unsigned lower2, unsigned upper2, imdd * grandchild, bool memoize_res); - void mk_swap_top_vars(imdd * d, imdd_ref & r, bool memoize_res); - imdd * mk_swap_memoize(imdd * d); - void mk_swap_core(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res); - void mk_swap_dupdt_core(imdd_ref & d, unsigned vidx, bool memoize_res); - - imdd2imdd_cache m_add_bounded_var_cache; - imdd * add_bounded_var_core(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res); - imdd * add_bounded_var_main(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res); - - friend struct distinct_proc; - imdd * mk_distinct_imdd(unsigned l1, unsigned u1, unsigned l2, unsigned u2, imdd * d, bool memoize_res = true); - - imdd_mk_filter_cache m_filter_cache; - region m_filter_entries; - unsigned m_filter_num_vars; - unsigned * m_filter_begin_vars; - unsigned * m_filter_end_vars; - unsigned_vector m_filter_context; - bool is_filter_var(unsigned v) const { return std::find(m_filter_begin_vars, m_filter_end_vars, v) != m_filter_end_vars; } - filter_cache_entry * mk_filter_cache_entry(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r); - imdd * is_mk_filter_cached(imdd * d, unsigned ctx_sz, unsigned * ctx); - void cache_mk_filter(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r); - void init_mk_filter(unsigned arity, unsigned num_vars, unsigned * vars); - template - void mk_filter_dupdt_core(imdd_ref & d, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res); - template - void mk_filter_core(imdd * d, imdd_ref & r, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res); - /** - \brief Filter the elements of the given IMDD using the given filter. - - The FilterProc template parameter is a filter for computing subsets of sets of the form: - - [L_1, U_1] X [L_2, U_2] X ... X [L_n, U_n] X d (where d is an IMDD) - - where n == num_vars - - The subset of elements is returned as an IMDD. - - FilterProc must have a method of the form: - - void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res); - - The size of the array lowers_uppers is 2*num_vars - - The arity of the resultant IMDD must be num_vars + d->get_arity(). - */ - template - void mk_filter_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res = true); - template - void mk_filter(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res = true); - - imdd * mk_disequal_imdd(unsigned l1, unsigned u1, unsigned value, imdd * d, bool memoize_res); - friend struct disequal_proc; - -public: - imdd_manager(); - - void inc_ref(imdd * d) { - if (d) - d->inc_ref(); - } - - void dec_ref(imdd * d) { - if (d) { - d->dec_ref(); - if (d->get_ref_count() == 0) - delete_imdd(d); - } - } - - unsigned get_num_nodes(imdd const * d) const; - - // count number of keys (rows) in table as if table is uncompressed. - size_t get_num_rows(imdd const* d) const; - - unsigned memory(imdd const * d) const; - -private: - imdd * _mk_empty(unsigned arity); - -public: - imdd * mk_empty(unsigned arity) { - imdd * r = _mk_empty(arity); - STRACE("imdd_trace", tout << "mk_empty(" << arity << ", 0x" << r << ");\n";); - return r; - } - -private: - imdd * memoize(imdd * d); - -public: - void memoize(imdd_ref const & d, imdd_ref & r) { r = memoize(d.get()); } - - void memoize(imdd_ref & d) { d = memoize(d.get()); } - - imdd * memoize_new_imdd_if(bool cond, imdd * r) { - if (cond && is_simple_node(r)) { - SASSERT(!r->is_shared()); - imdd * can_r = memoize(r); - if (can_r != r) { - SASSERT(r->get_ref_count() == 0); - delete_imdd(r); - } - return can_r; - } - return r; - } - -public: - void defrag(imdd_ref & d); - - void unmemoize(imdd * d); - - void unmemoize_rec(imdd * d); - - void copy(imdd * d, imdd_ref & r) { r = copy_main(d); } - - void insert_dupdt(imdd_ref & d, unsigned b, unsigned e, bool memoize_res = true) { - d = insert_main(d, b, e, true, memoize_res); - } - - void insert(imdd * d, imdd_ref & r, unsigned b, unsigned e, bool memoize_res = true) { - r = insert_main(d, b, e, false, memoize_res); - } - - void mk_product_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res = true) { - d1 = mk_product_main(d1.get(), d2, true, memoize_res); - } - - void mk_product(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res = true) { - r = mk_product_main(d1, d2, false, memoize_res); - STRACE("imdd_trace", tout << "mk_product(0x" << d1 << ", 0x" << d2 << ", 0x" << r.get() << ", " << memoize_res << ");\n";); - } - - void mk_union_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res = true) { - d1 = mk_union_main(d1.get(), d2, true, memoize_res); - } - - void mk_union(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res = true) { - r = mk_union_main(d1, d2, false, memoize_res); - STRACE("imdd_trace", tout << "mk_union(0x" << d1 << ", 0x" << d2 << ", 0x" << r.get() << ", " << memoize_res << ");\n";); - } - - void mk_complement_dupdt(imdd_ref & d, unsigned num, unsigned const * mins, unsigned const * maxs, bool memoize_res = true) { - d = mk_complement_main(d, num, mins, maxs, true, memoize_res); - } - - void mk_complement(imdd * d, imdd_ref & r, unsigned num, unsigned const * mins, unsigned const * maxs, bool memoize_res = true) { - r = mk_complement_main(d, num, mins, maxs, false, memoize_res); - - STRACE("imdd_trace", tout << "mk_complement(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num; i++) tout << mins[i] << ", " << maxs[i] << ", "; - tout << memoize_res << ");\n";); - } - - void mk_filter_equal_dupdt(imdd_ref & d, unsigned vidx, unsigned value, bool memoize_res = true) { - d = mk_filter_equal_main(d, vidx, value, true, memoize_res); - } - - void mk_filter_equal(imdd * d, imdd_ref & r, unsigned vidx, unsigned value, bool memoize_res = true) { - r = mk_filter_equal_main(d, vidx, value, false, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_equal(0x" << d << ", 0x" << r.get() << ", " << vidx << ", " << value << ", " << memoize_res << ");\n";); - } - - void mk_filter_identical_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res = true) { - // d = mk_filter_identical_main(d, num_vars, vars, true, memoize_res); - filter_identical_main3(d, d, num_vars, vars, true, memoize_res); - } - - void mk_filter_identical(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res = true) { - filter_identical_main3(d, r, num_vars, vars, false, memoize_res); - - STRACE("imdd_trace", tout << "mk_filter_identical(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num_vars; i++) tout << vars[i] << ", "; - tout << memoize_res << ");\n";); - } - - void mk_project_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res = true); - - void mk_project(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res = true); - - // swap vidx and vidx+1 - void mk_swap_dupdt(imdd_ref & d, unsigned vidx, bool memoize_res = true); - - // swap vidx and vidx+1 - void mk_swap(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res = true); - - void add_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res = true) { - d = add_facts_main(d, num, lowers, uppers, true, memoize_res); - } - - void add_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res = true) { - r = add_facts_main(d, num, lowers, uppers, false, memoize_res); - - STRACE("imdd_trace", tout << "add_facts(0x" << d << ", 0x" << r.get() << ", "; - for (unsigned i = 0; i < num; i++) tout << lowers[i] << ", " << uppers[i] << ", "; - tout << memoize_res << ");\n";); - } - - void add_fact_dupdt(imdd_ref & d, unsigned num, unsigned const * values, bool memoize_res = true) { - add_facts_dupdt(d, num, values, values, memoize_res); - } - - void add_fact(imdd * d, imdd_ref & r, unsigned num, unsigned const * values, bool memoize_res = true) { - add_facts(d, r, num, values, values, memoize_res); - } - - void add_bounded_var_dupdt(imdd_ref & d, unsigned before_vidx, unsigned lower, unsigned upper, bool memoize_res = true) { - d = add_bounded_var_main(d, before_vidx, lower, upper, true, memoize_res); - } - - void add_bounded_var(imdd * d, imdd_ref & r, unsigned before_vidx, unsigned lower, unsigned upper, bool memoize_res = true) { - r = add_bounded_var_main(d, before_vidx, lower, upper, false, memoize_res); - } - - void mk_filter_distinct_dupdt(imdd_ref & d, unsigned v1, unsigned v2, bool memoize_res = true); - - void mk_filter_distinct(imdd * d, imdd_ref & r, unsigned v1, unsigned v2, bool memoize_res = true); - - void mk_filter_disequal_dupdt(imdd_ref & d, unsigned var, unsigned value, bool memoize_res = true); - - void mk_filter_disequal(imdd * d, imdd_ref & r, unsigned var, unsigned value, bool memoize_res = true); - - void mk_join(imdd * d1, imdd * d2, imdd_ref & r, unsigned_vector const& vars1, unsigned_vector const& vars2, bool memoize_res = true); - - void mk_join_project(imdd * d1, imdd * d2, imdd_ref & r, - unsigned_vector const& vars1, unsigned_vector const& vars2, - unsigned_vector const& proj_vars, bool memoize_res = true); - - void mk_join_dupdt(imdd_ref & d1, imdd * d2, unsigned num_vars, unsigned const * vars1, unsigned const * vars2, bool memoize_res = true); - - void remove_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers); - - void remove_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers); - - bool is_equal(imdd * d1, imdd * d2); - - bool contains(imdd * d, unsigned num, unsigned const * values) const; - - bool contains(imdd * d, unsigned v) const { return contains(d, 1, &v); } - - bool contains(imdd * d, unsigned v1, unsigned v2) const { unsigned vs[2] = {v1, v2}; return contains(d, 2, vs); } - - bool contains(imdd * d, unsigned v1, unsigned v2, unsigned v3) const { unsigned vs[3] = {v1,v2,v3}; return contains(d, 3, vs); } - - bool subsumes(imdd * d1, imdd * d2); - - bool is_subset(imdd * d1, imdd * d2) { return subsumes(d2, d1); } - -private: - void display(std::ostream & out, imdd const * d, unsigned_vector & intervals, bool & first) const; - -public: - void display(std::ostream & out, imdd const * d) const; - - void display_ll(std::ostream & out, imdd const * d) const; - - /** - \brief Execute proc (once) in each node in the IMDD rooted by d. - */ - template - void for_each(imdd * d, Proc & proc) const { - // for_each is a generic procedure, we don't know what proc will actually do. - // So, it is not safe to reuse m_worklist and m_visited. - ptr_buffer worklist; - imdd_cache visited; - worklist.push_back(d); - while (!worklist.empty()) { - d = worklist.back(); - worklist.pop_back(); - if (d->is_shared() && visited.contains(d)) - continue; - if (d->is_shared()) - visited.insert(d); - proc(d); - if (d->get_arity() > 1) { - imdd_children::iterator it = d->begin_children(); - imdd_children::iterator end = d->end_children(); - for (; it != end; ++it) - worklist.push_back(it->val()); - } - } - } - - class iterator { - bool m_done; - svector m_iterators; - svector m_element; - - void begin_iterators(imdd const * curr, unsigned start_idx); - - public: - iterator():m_done(true) {} - iterator(imdd_manager const & m, imdd const * d); - - unsigned get_arity() const { return m_element.size(); } - unsigned * operator*() const { return m_element.c_ptr(); } - iterator & operator++(); - - bool operator==(iterator const & it) const; - bool operator!=(iterator const & it) const { return !operator==(it); } - }; - - friend class iterator; - - iterator begin(imdd const * d) const { return iterator(*this, d); } - iterator end(imdd const * d) const { return iterator(); } -}; - -inline std::ostream & operator<<(std::ostream & out, imdd_ref const & r) { - r.get_manager().display(out, r.get()); - return out; -} - -struct mk_imdd_pp { - imdd * m_d; - imdd_manager & m_manager; - mk_imdd_pp(imdd * d, imdd_manager & m):m_d(d), m_manager(m) {} -}; - -inline mk_imdd_pp mk_pp(imdd * d, imdd_manager & m) { - return mk_imdd_pp(d, m); -} - -inline std::ostream & operator<<(std::ostream & out, mk_imdd_pp const & pp) { - pp.m_manager.display(out, pp.m_d); - return out; -} - -struct mk_imdd_ll_pp : public mk_imdd_pp { - mk_imdd_ll_pp(imdd * d, imdd_manager & m):mk_imdd_pp(d, m) {} -}; - -inline mk_imdd_ll_pp mk_ll_pp(imdd * d, imdd_manager & m) { - return mk_imdd_ll_pp(d, m); -} - -inline std::ostream & operator<<(std::ostream & out, mk_imdd_ll_pp const & pp) { - pp.m_manager.display_ll(out, pp.m_d); - return out; -} - -#endif /* _IMDD_H_ */ - diff --git a/src/muz_qe/interval_skip_list.h b/src/muz_qe/interval_skip_list.h deleted file mode 100644 index 7865e6ca5..000000000 --- a/src/muz_qe/interval_skip_list.h +++ /dev/null @@ -1,1876 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - interval_skip_list.h - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-10-01. - -Revision History: - ---*/ -#ifndef _INTERVAL_SKIP_LIST_H_ -#define _INTERVAL_SKIP_LIST_H_ - -#include"skip_list_base.h" - -/* - Interval skip lists implement a mapping from keys to values. - The key must be a total order. - - It compress consecutive entries k->v and (k+1)->v by - using intervals. Internally, we have [k1, k2]->v to represent - the set of entries k1->v, (k1+1)->v, ..., k2->v. - - For improving cache behavior, the entries are packed in - buckets. As regular skip-lists, a node/bucket may have - several next/forward pointers. - - Interval skip lists can also encode sets. In this case, - there is no value type. We achieve that by allowing the template - to be instantiated with an arbitrary "entry" class for encoding - [k1, k2]->v. This class must provide the methods: - - key const & begin_key() const - key const & end_key() const - value const & val() const - void set_begin_key(key const & k) - void set_end_key(key const & k) - void set_val(value const & v) - void display(ostream & out) const - bool operator==(entry const & other) const; - - And a definition for the key and value types. -*/ - -/** - \brief Default interval_skip_list entry. - It is useful for implementing mappings. -*/ -template -class default_islist_entry { -public: - typedef Key key; - typedef Value value; -private: - key m_begin_key; - key m_end_key; - value m_value; -public: - default_islist_entry() {} - default_islist_entry(key const & b, key const & e, value const & v): - m_begin_key(b), - m_end_key(e), - m_value(v) { - } - key const & begin_key() const { return m_begin_key; } - key const & end_key() const { return m_end_key; } - value const & val() const { return m_value; } - void set_begin_key(key const & k) { m_begin_key = k; } - void set_end_key(key const & k) { m_end_key = k; } - void set_val(value const & v) { m_value = v; } - void display(std::ostream & out) const { - out << "[" << begin_key() << ", " << end_key() << "] -> " << val(); - } -}; - -/** - \brief Default interval_skip_list entry for encoding sets. -*/ -template -struct default_set_islist_entry { -public: - typedef Key key; - typedef unsigned value; // don't care type -private: - key m_begin_key; - key m_end_key; -public: - default_set_islist_entry() {} - default_set_islist_entry(key const & b, key const & e): - m_begin_key(b), - m_end_key(e) { - } - key const & begin_key() const { return m_begin_key; } - key const & end_key() const { return m_end_key; } - unsigned const & val() const { static unsigned v = 0; return v; } - void set_begin_key(key const & k) { m_begin_key = k; } - void set_end_key(key const & k) { m_end_key = k; } - void set_val(value const & v) { /* do nothing */ } - void display(std::ostream & out) const { - out << "[" << begin_key() << ", " << end_key() << "]"; - } -}; - - -/** - \brief An interval skip list. - - See comments at skip_list_base.h -*/ -template -class interval_skip_list : public skip_list_base { -protected: - typedef typename skip_list_base::bucket bucket; -public: - typedef typename Traits::manager manager; - typedef typename Traits::entry entry; - typedef typename entry::key key; - typedef typename entry::value value; - -protected: - bool lt(key const & k1, key const & k2) const { return skip_list_base::lt(k1, k2); } - - static key const & first_key(bucket const * bt) { return bt->first_entry().begin_key(); } - - static key const & last_key(bucket const * bt) { return bt->last_entry().end_key(); } - - static void set_entry(bucket * bt, unsigned idx, key const & b, key const & e, value const & v) { - entry & en = bt->get(idx); - en.set_begin_key(b); - en.set_end_key(e); - en.set_val(v); - } - - /** - \brief Add first entry to the list. - - \remark This method will invoke inc_ref_eh for v. - */ - void insert_first_entry(manager & m, key const & b, key const & e, value const & v) { - entry en; - en.set_begin_key(b); - en.set_end_key(e); - en.set_val(v); - skip_list_base::insert_first_entry(m, en); - } - - /** - \brief Return true if the given key \c k is in the entry \c e. That is, - k \in [e.begin_key(), e.end_key()]. - */ - bool contains(entry const & e, key const & k) const { return this->leq(e.begin_key(), k) && this->leq(k, e.end_key()); } - - /** - \brief Return true if the given key \c k is in the entry \c e. That is, - k \in [e.begin_key(), e.end_key()]. If that is the case, then store e.value() in v. - Otherwise, return false. - */ - bool contains(entry const & e, key const & k, value & v) const { - if (contains(e, k)) { - v = e.val(); - return true; - } - else { - return false; - } - } - - /** - \brief Search for a key in a bucket starting at position s_idx using binary search. - - Return true if k was found in the bucket and store the index of - the entry containing \c k in \c idx. - - Otherwise, return false and \c idx will contain the index - s.t. - (idx == s_idx || entry[idx-1].end_key() < k) && (idx == bt->size() || k < entry[idx].begin_key()) - */ - bool find_core(bucket const * bt, unsigned s_idx, key const & k, unsigned & idx) const { - if (s_idx >= bt->size()) { - idx = s_idx; - return false; - } - int low = s_idx; - int high = bt->size() - 1; - for (;;) { - int mid = low + ((high - low) / 2); - entry const & mid_entry = bt->get(mid); - if (this->gt(k, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - idx = static_cast(mid) + 1; - SASSERT(idx >= s_idx); - SASSERT(idx == s_idx || lt(bt->get(idx - 1).end_key(), k)); - SASSERT(idx == bt->size() || lt(k, bt->get(idx).begin_key())); - return false; - } - - } - else if (lt(k, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - idx = static_cast(mid); - SASSERT(idx >= s_idx); - SASSERT(idx == s_idx || lt(bt->get(idx - 1).end_key(), k)); - SASSERT(idx == bt->size() || lt(k, bt->get(idx).begin_key())); - return false; - } - } - else { - SASSERT(contains(mid_entry, k)); - SASSERT(mid >= 0); - idx = static_cast(mid); - SASSERT(idx >= s_idx); - return true; - } - } - } - - /** - \brief Search for a key in a bucket using binary search. - - Return true if k was found in the bucket and store the index of - the entry containing \c k in \c idx. - - Otherwise, return false and \c idx will contain the index - s.t. - (idx == 0 || entry[idx-1].end_key() < k) && (idx == bt->size() || k < entry[idx].begin_key() - */ - bool find_core(bucket const * bt, key const & k, unsigned & idx) const { - bool r = find_core(bt, 0, k, idx); - SASSERT(!r || contains(bt->get(idx), k)); - SASSERT( r || idx == 0 || lt(bt->get(idx - 1).end_key(), k)); - SASSERT( r || idx == bt->size() || lt(k, bt->get(idx).begin_key())); - return r; - } - - /** - \brief Search for the given key in the interval skip list. - Return true if the key is stored in the list, and store the location of the entry that contains the k in the - pair (bt, idx). The location should be read as the key is in the entry idx of the bucket bt. - Otherwise, returns false and (bt, idx) contains: - Case 1: bt != 0 && 0 < idx < bt->size() && bt->get(idx-1).end_key() < k && k < bt->get(idx).begin_key() - Case 2: bt != 0 && idx == 0 && (pred_bucket(bt) == m_header || last_key(pred_bucket(bt)) < k) && k < first_key(bt) - Case 3: bt == 0 && idx == 0 && k is greater than all keys in the list. - bt != m_header - - Even when find_core returns false, the pair (bt, idx) can be used to create an iterator object - to traverse keys >= k. - */ - template - bool find_core(key const & k, bucket * & bt, unsigned & idx, bucket * pred_vect[]) const { - bucket * curr = this->m_header; - unsigned i = this->m_header->level(); - bucket * next; - while (i > 0) { - i--; - for (;;) { - next = curr->get_next(i); - if (next != 0 && lt(first_key(next), k)) - curr = next; - else - break; - } - if (UpdatePredVect) - pred_vect[i] = curr; - } - - SASSERT(next == curr->get_next(0)); - - // the search_key must be in the current bucket, or in the first entry of the next bucket (if the next bucket is not 0). - SASSERT(curr->empty() || lt(first_key(curr), k)); - SASSERT(next == 0 || this->geq(first_key(next), k)); - DEBUG_CODE({ - if (UpdatePredVect && next != 0) - for (unsigned i = 0; i < next->level(); i++) - SASSERT(pred_vect[i]->get_next(i) == next); - }); - - if (next != 0 && contains(next->first_entry(), k)) { - bt = next; - idx = 0; - return true; - } - - bool r = find_core(curr, k, idx); - if (idx == curr->size()) { - SASSERT(!r); - bt = next; - idx = 0; - } - else { - SASSERT(idx < curr->size()); - bt = curr; - } - SASSERT(bt != this->m_header); - SASSERT((bt == 0 && idx == 0) || (bt != 0 && idx < bt->size())); - SASSERT(!r || contains(bt->get(idx), k)); - SASSERT(r || - // Case 1 - (bt != 0 && 0 < idx && idx < bt->size() && lt(bt->get(idx-1).end_key(), k) && lt(k, bt->get(idx).begin_key())) || - // Case 2 - (bt != 0 && idx == 0 && (this->pred_bucket(bt) == this->m_header || lt(last_key(this->pred_bucket(bt)), k)) && lt(k, first_key(bt))) || - // Case 3 - (bt == 0 && idx == 0) // k is greater than all keys in the list. - ); - return r; - } - - /** - \brief Return true if the two entries (that satisfy lt(e1, e2)) can be merged. - */ - bool can_be_merged(entry const & e1, entry const & e2) const { - return this->val_eq(e1.val(), e2.val()) && this->eq(this->succ(e1.end_key()), e2.begin_key()); - } - - /** - \brief Try to merge the last entry with bt with the first entry of its successor. - - \remark pred_vect contains the predecessors of the successor of bt. - */ - void merge_first_of_succ_if_possible(manager & m, bucket * bt, bucket * pred_vect[]) { - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); - bucket * next_bucket = bt->get_next(0); - if (next_bucket != 0) { - entry & curr_entry = bt->last_entry(); - entry & next_entry = next_bucket->get(0); - if (can_be_merged(curr_entry, next_entry)) { - curr_entry.set_end_key(next_entry.end_key()); - this->del_entry(m, next_bucket, 0); // del_entry invokes dec_ref_eh - if (next_bucket->empty()) - this->del_bucket(m, next_bucket, pred_vect); - } - } - } - - /** - \brief Try to merge the entry at position idx with the next entry if possible. - */ - void merge_next_if_possible(manager & m, bucket * bt, unsigned idx, bucket * pred_vect[]) { - SASSERT(!bt->empty()); - if (idx + 1 == bt->size()) { - // it is the last element - merge_first_of_succ_if_possible(m, bt, pred_vect); - } - else { - entry & curr_entry = bt->get(idx); - entry & next_entry = bt->get(idx+1); - if (can_be_merged(curr_entry, next_entry)) { - curr_entry.set_end_key(next_entry.end_key()); - this->del_entry(m, bt, idx+1); // del_entry invokes dec_ref_eh - } - } - } - - /** - \brief Try to merge the entry at position idx with the previous entry if possible. - - \remark This method assumes that idx > 0. - */ - void merge_prev_if_possible(manager & m, bucket * bt, unsigned idx) { - SASSERT(idx > 0); - entry & curr_entry = bt->get(idx); - entry & prev_entry = bt->get(idx-1); - if (can_be_merged(prev_entry, curr_entry)) { - prev_entry.set_end_key(curr_entry.end_key()); - this->del_entry(m, bt, idx); // del_entry invokes dec_ref_eh - } - } - - /** - \brief Delete entries starting at indices [s_idx, e_idx), assuming e_idx < bt->size() - - \remark The pre-condition guarantees that the bucket will not be empty after the entries - are deleted. - - \remark If RECYCLE_ENTRY is true, then method will try to recycle position s_idx if it is deleted, and will return true. - Position s_idx will be an empty slot in this case. - */ - template - bool del_entries(manager & m, bucket * bt, unsigned s_idx, unsigned e_idx) { - bool result = false; - if (RECYCLE_ENTRY && s_idx + 1 < e_idx) { - // The position s_idx will be recycled, but the reference to the value stored there - // will be lost. - this->dec_ref(m, bt->get(s_idx).val()); - s_idx++; - result = true; - } - TRACE("del_entries_upto_bug", this->display(tout, bt); tout << "[" << s_idx << ", " << e_idx << ")\n";); - SASSERT(e_idx >= s_idx); - SASSERT(e_idx < bt->size()); - // move entries - unsigned num_removed = e_idx - s_idx; - entry * dest_it = bt->get_entries() + s_idx; - entry * source_it = bt->get_entries() + e_idx; - entry * source_end = bt->get_entries() + bt->size(); - if (Traits::ref_count) { - // invoke dec_ref_eh for entries between dest_it and source_it, since they are being removed - entry * it = dest_it; - for (; it < source_it; ++it) { - this->dec_ref(m, it->val()); - } - } - for (; source_it < source_end; ++dest_it, ++source_it) { - *dest_it = *source_it; - } - // update size - bt->shrink(num_removed); - SASSERT(!bt->empty()); - TRACE("del_entries_upto_bug", this->display(tout, bt);); - return result; - } - - /** - \brief Delete all keys (starting at position s_idx) in the given bucket that are <= k. - The method assumes that k < bt->last_key(). - This condition guarantees that the bucket will not be empty after removing the keys. - - - \remark If RECYCLE_ENTRY is true, then method will try to recycle position s_idx if it is deleted, and will return true. - Position s_idx will be an empty slot in this case. - */ - template - bool del_last_entries_upto(manager & m, bucket * bt, unsigned s_idx, key const & k) { - SASSERT(s_idx < bt->size()); - SASSERT(lt(k, last_key(bt))); - int low = s_idx; - int high = bt->size() - 1; - SASSERT(low <= high); - for (;;) { - int mid = low + ((high - low) / 2); - SASSERT(mid < static_cast(bt->size())); - entry & mid_entry = bt->get(mid); - if (this->gt(k, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - // mid entry must be deleted since k > mid_entry.end_key(). - TRACE("del_entries_upto_bug", tout << "exit 1) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - SASSERT(mid + 1 < static_cast(bt->size())); // Reason: method pre-condition: lt(k, last_key(bt)) - return del_entries(m, bt, s_idx, mid + 1); - } - } - else if (lt(k, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - // mid entry must not be deleted since k < mid_entry.begin_key(). - TRACE("del_entries_upto_bug", tout << "exit 2) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - SASSERT(mid < static_cast(bt->size())); // Reason: loop invariant - return del_entries(m, bt, s_idx, mid); - } - } - else { - SASSERT(contains(mid_entry, k)); - if (lt(k, mid_entry.end_key())) { - TRACE("del_entries_upto_bug", tout << "exit 3) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - mid_entry.set_begin_key(this->succ(k)); - SASSERT(mid < static_cast(bt->size())); // Reason: loop invariant - return del_entries(m, bt, s_idx, mid); - } - else { - // mid_entry should also be removed - TRACE("del_entries_upto_bug", tout << "exit 4) mid: " << mid << "\n"; this->display(tout, mid_entry); tout << "\n";); - SASSERT(mid + 1 < static_cast(bt->size())); // Reason: method pre-condition: lt(k, last_key(bt)) - return del_entries(m, bt, s_idx, mid + 1); - } - } - } - } - - /** - \brief Keep deleting keys <= k in bt and its successors. - */ - void del_entries_upto_loop(manager & m, bucket * bt, key const & k, bucket * pred_vect []) { - SASSERT(this->check_pred_vect(bt, pred_vect)); - while (bt != 0) { - key const & bt_last_key = last_key(bt); - if (lt(k, bt_last_key)) { - del_last_entries_upto(m, bt, 0, k); - return; - } - else if (this->eq(k, bt_last_key)) { - this->del_bucket(m, bt, pred_vect); - return; - } - else { - SASSERT(this->gt(k, bt_last_key)); - bucket * next = bt->get_next(0); - this->del_bucket(m, bt, pred_vect); - bt = next; - // continue deleting... - } - } - } - - /** - \brief Delete entries starting at position 0 such that keys are <= k. - - If INSERT == true, then try to save/recycle an entry. Return true, if - managed to recycle the entry. - - The bucket bt may be deleted when INSERT==false and k >= last_key(bt). - - - pred_vect must contain the predecessors of bt. - - - next_pred_vect is an uninitialized predecessor vector. It may be initialized - when INSERT == true. If needed it is initialized using - update_predecessor_vector(pred_vect, bt, next_pred_vect); - */ - template - bool del_entries_upto(manager & m, bucket * bt, key const & k, bucket * pred_vect[], bucket * next_pred_vect[]) { - SASSERT(this->check_pred_vect(bt, pred_vect)); // pred_vect contains the predecessors of bt. - if (lt(k, first_key(bt))) { - // nothing to be done... - return false; // didn't manage to recycle entry. - } - - key const & bt_last_key = last_key(bt); - TRACE("del_entries_upto_bug", tout << "bt_last_key: " << bt_last_key << "\n";); - if (this->lt(k, bt_last_key)) { - return del_last_entries_upto(m, bt, 0, k); - } - else { - if (INSERT) { - // Invoke DEC-REF for all entries in bt - this->dec_ref(m, bt); - // REMARK: the slot 0 will be reused, but the element there is gone. - bt->set_size(1); - if (this->gt(k, bt_last_key)) { - bucket * next = bt->get_next(0); - if (next != 0) { - this->update_predecessor_vector(pred_vect, bt, next_pred_vect); - del_entries_upto_loop(m, next, k, next_pred_vect); - } - } - return true; // recycled entry. - } - else { - bucket * next = bt->get_next(0); - this->del_bucket(m, bt, pred_vect); // it will invoke dec_ref_eh for all values in bt. - // pred_vect does not need to be updated since it contains the predecessors of - // bt, since bt was deleted they are now the predecessors of its successor. - if (next != 0) { - del_entries_upto_loop(m, next, k, pred_vect); - } - return false; // don't care in this case, since it is not an insertion. - } - } - } - - /** - \brief Delete entries starting at position s_idx (> 0) such that keys are <= k. - The bucket bt cannot be deleted since s_idx > 0. - - If INSERT == true, then try to save/recycle an entry. Return true, if - managed to recycle the entry. - - - pred_vect must contain the predecessors of bt->get_next(0). - */ - template - bool del_entries_upto(manager & m, bucket * bt, unsigned s_idx, key const & k, bucket * pred_vect[]) { - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); // pred_vect contains the predecessors of the successor of bt. - SASSERT(s_idx > 0); - TRACE("del_entries_upto_bug", - tout << "INSERT: " << INSERT << "\n"; - tout << "del_entries_upto, s_idx: " << s_idx << ", k: " << k << "\n"; - this->display(tout, bt); - tout << "\n"; - this->display_predecessor_vector(tout, pred_vect);); - - if (s_idx >= bt->size()) { - // nothing to do in bt, moving to successors... - del_entries_upto_loop(m, bt->get_next(0), k, pred_vect); - return false; // didn't manage to recycle an entry - } - - if (lt(k, bt->get(s_idx).begin_key())) { - // nothing to be done... - return false; // didn't manage to recycle an entry - } - - key const & bt_last_key = last_key(bt); - TRACE("del_entries_upto_bug", tout << "bt_last_key: " << bt_last_key << "\n";); - if (lt(k, bt_last_key)) { - return del_last_entries_upto(m, bt, s_idx, k); - } - else { - if (this->gt(k, bt_last_key)) { - del_entries_upto_loop(m, bt->get_next(0), k, pred_vect); - } - if (Traits::ref_count) { - // Invoke dec_ref_eh for all values in [s_idx, bt->size()) - unsigned sz = bt->size(); - for (unsigned i = s_idx; i < sz; i++) - this->dec_ref(m, bt->get(i).val()); - } - if (INSERT) { - SASSERT(s_idx < bt->size()); - bt->set_size(s_idx + 1); - return true; // recycled an entry - - } - else { - bt->set_size(s_idx); - return false; // don't care. it is not an insertion. - } - } - } - - /** - \brief Insert entry [b,e]->v in the beginning of the bucket bt. - */ - void insert_begin(manager & m, bucket * bt, key const & b, key const & e, value const & v, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "insert_begin: [" << b << ", " << e << "] -> " << v << "\n"; this->display(tout, bt);); - SASSERT(this->check_pred_vect(bt, pred_vect)); - SASSERT(!bt->empty()); - SASSERT(bt->size() <= bt->capacity()); - SASSERT(this->leq(b, first_key(bt))); - bucket * next_pred_vect[Traits::max_level]; - next_pred_vect[0] = 0; - - this->inc_ref(m, v); - - // Delete entries that will be overlapped by new entry. - // Try to reuse a slot that was deleted... - bool recycled = del_entries_upto(m, bt, e, pred_vect, next_pred_vect); - if (recycled) { - set_entry(bt, 0, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - TRACE("interval_skip_list_bug", this->display_physical(tout);); - if (next_pred_vect[0] != 0) { - // the vector next_pred_vect was initialized by del_entries_upto. - merge_next_if_possible(m, bt, 0, next_pred_vect); - } - else { - this->update_predecessor_vector(pred_vect, bt); - merge_next_if_possible(m, bt, 0, pred_vect); - } - return; - } - // check if can merge with first entry in the bucket. - entry & fe = bt->first_entry(); - if (this->val_eq(fe.val(), v) && this->eq(fe.begin_key(), this->succ(e))) { - // can merge - fe.set_begin_key(b); - // A new reference to v was not created. So, we must invoke dec_ref_eh since we increased the counter above. - this->dec_ref(m, v); - return; - } - // Is there space for the new entry? - if (bt->size() == bt->capacity()) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - } - else { - // there is no space - this->splice(m, bt, pred_vect); - } - } - this->open_space(bt, 0); - set_entry(bt, 0, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - SASSERT(!can_be_merged(bt->get(0), bt->get(1))); - } - - /** - \brief Insert the entry [b, e]->v at position idx. - */ - void insert_at(manager & m, bucket * bt, unsigned idx, key const & b, key const & e, value const & v, bucket * pred_vect[]) { - SASSERT(idx > 0); - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); - - this->inc_ref(m, v); - TRACE("insert_at_bug", tout << "before del_entries_upto:\n"; this->display_physical(tout);); - - bool recycled = del_entries_upto(m, bt, idx, e, pred_vect); - - TRACE("insert_at_bug", tout << "after del_entries_upto:\n"; this->display_physical(tout);); - - if (recycled) { - set_entry(bt, idx, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - merge_next_if_possible(m, bt, idx, pred_vect); - merge_prev_if_possible(m, bt, idx); - TRACE("insert_at_bug", tout << "using recycled:\n"; this->display_physical(tout);); - return; - } - - // Is there space for the new entry? - if (bt->size() == bt->capacity()) { - // there is no space - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - // there is no need to update pred_vect, since the list contains only one bucket. - } - else { - this->splice(m, bt, pred_vect); - bucket * new_next = bt->get_next(0); - SASSERT(bt->size() == bt->capacity()/2); - if (idx == bt->capacity()/2) { - entry & bt_last_entry = bt->last_entry(); - if (this->val_eq(bt_last_entry.val(), v) && this->eq(bt_last_entry.end_key(), this->pred(b))) { - // merged with the last key of bt - bt_last_entry.set_end_key(e); - // A new reference to v was not created. So, we must invoke dec_ref_eh since we increased the counter above. - this->dec_ref(m, v); - return; - } - entry & new_next_first_entry = new_next->first_entry(); - if (this->val_eq(new_next_first_entry.val(), v) && this->eq(new_next_first_entry.begin_key(), this->succ(e))) { - // merged with the first key of new_next - new_next_first_entry.set_begin_key(b); - // A new reference to v was not created. So, we must invoke dec_ref_eh since we increased the counter above. - this->dec_ref(m, v); - return; - } - // insert in the end of bt. - bt->set_size(bt->capacity()/2 + 1); - set_entry(bt, bt->capacity()/2, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - return; - } - else if (idx > bt->capacity()/2) { - idx -= bt->capacity()/2; - SASSERT(idx > 0); - bt = new_next; - this->update_predecessor_vector(pred_vect, bt); - } - } - } - SASSERT(idx > 0); - this->open_space(bt, idx); - set_entry(bt, idx, b, e, v); // Reference to v was stored, and inc_ref_eh was invoked above. - merge_next_if_possible(m, bt, idx, pred_vect); - merge_prev_if_possible(m, bt, idx); - TRACE("insert_at_bug", tout << "using open-space:\n"; this->display_physical(tout);); - } - - /** - \brief Insert the entry [b,e]->v into the bucket bt. - - pred_vect contains the predecessors of the successor of bt (i.e., bt->get_next(0)) - */ - void insert_inside(manager & m, bucket * bt, key const & b, key const & e, value const & v, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "insert_inside: [" << b << ", " << e << "] -> " << v << "\n";); - SASSERT(this->check_pred_vect(bt->get_next(0), pred_vect)); - SASSERT(!bt->empty()); - SASSERT(bt->size() <= bt->capacity()); - // perform binary search to find position to insert [b, e]->v - int low = 0; - int high = bt->size() - 1; - for (;;) { - int mid = low + ((high - low) / 2); - entry & mid_entry = bt->get(mid); - if (this->gt(b, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - // insert after mid_entry since b > mid_entry.end_key(). - insert_at(m, bt, mid+1, b, e, v, pred_vect); - return; - } - } - else if (lt(b, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - // insert before mid_entry since b < mid_entry.begin_key(). - SASSERT(mid > 0); // Reason: insert_begin would have been called instead. - insert_at(m, bt, mid, b, e, v, pred_vect); - return; - } - } - else { - SASSERT(contains(mid_entry, b)); - TRACE("insert_inside_bug", tout << "insert_inside:\n"; this->display(tout, bt);); - if (this->val_eq(mid_entry.val(), v)) { - if (this->gt(e, mid_entry.end_key())) { - // No need to create space. - // We did not create a new reference to v. - mid_entry.set_end_key(e); - del_entries_upto(m, bt, mid+1, e, pred_vect); - merge_next_if_possible(m, bt, mid, pred_vect); - return; - } - } - else { - if (this->gt(b, mid_entry.begin_key())) { - if (this->lt(e, mid_entry.end_key())) { - // New interval is the middle of existing interval - - // We must INVOKE add_ref_eh for mid_entry.val() and v. - this->inc_ref(m, v); - this->inc_ref(m, mid_entry.val()); // mid_entry was split in two. - - // we need two new entries. - if (bt->size() >= bt->capacity() - 1) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - } - else { - this->splice(m, bt, pred_vect); - int new_sz = bt->size(); - bucket * new_next = bt->get_next(0); - if (mid >= new_sz) { - mid -= new_sz; - SASSERT(mid >= 0); - bt = new_next; - } - } - } - this->open_2spaces(bt, mid); - entry & mid1_entry = bt->get(mid); - entry & new_entry = bt->get(mid+1); - entry & mid2_entry = bt->get(mid+2); - mid2_entry = mid1_entry; - mid1_entry.set_end_key(this->pred(b)); - new_entry.set_begin_key(b); - new_entry.set_end_key(e); - new_entry.set_val(v); - mid2_entry.set_begin_key(this->succ(e)); - } - else { - mid_entry.set_end_key(this->pred(b)); - insert_at(m, bt, mid+1, b, e, v, pred_vect); - } - } - else { - SASSERT(this->eq(b, mid_entry.begin_key())); - SASSERT(mid > 0); // Reason: insert_begin would have been called instead. - insert_at(m, bt, mid, b, e, v, pred_vect); - } - } - return; - } - } - } - - /** - \brief Remove [b,e]->v from the beginning of the bucket bt. - */ - void remove_begin(manager & m, bucket * bt, key const & b, key const & e, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "remove_begin: [" << b << ", " << e << "]\n";); - SASSERT(!bt->empty()); - SASSERT(pred_vect[0]->get_next(0) == bt); - del_entries_upto(m, bt, e, pred_vect, 0); - } - - /** - \brief Remove [b,e]->v from the bucket bt. - */ - void remove_inside(manager & m, bucket * bt, key const & b, key const & e, bucket * pred_vect[]) { - TRACE("interval_skip_list_bug", tout << "remove_inside: [" << b << ", " << e << "]\n";); - // perform binary search to find position to insert [b, e]->v - int low = 0; - int high = bt->size() - 1; - for (;;) { - int mid = low + ((high - low) / 2); - entry & mid_entry = bt->get(mid); - if (this->gt(b, mid_entry.end_key())) { - low = mid + 1; - if (low > high) { - // insert after mid_entry since b > mid_entry.end_key(). - del_entries_upto(m, bt, mid+1, e, pred_vect); - return; - } - } - else if (this->lt(b, mid_entry.begin_key())) { - high = mid - 1; - if (low > high) { - // insert before mid_entry since b < mid_entry.begin_key(). - SASSERT(mid > 0); // Reason: remove_begin would have been called instead. - del_entries_upto(m, bt, mid, e, pred_vect); - return; - } - } - else { - SASSERT(contains(mid_entry, b)); - if (this->gt(b, mid_entry.begin_key())) { - if (this->lt(e, mid_entry.end_key())) { - // The removed interval is inside of an existing interval. - - // mid_entry will be split in two. So, we must invoke add_ref_eh for mid_entry.val() - this->inc_ref(m, mid_entry.val()); - - // We need to break mid_entry in two parts. - if (bt->size() == bt->capacity()) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(this->first_bucket() == bt && this->first_bucket()->get_next(0) == 0); - this->expand_first_bucket(m); - bt = this->first_bucket(); - SASSERT(bt->size() < bt->capacity()); - } - else { - this->splice(m, bt, pred_vect); - if (mid >= static_cast(bt->size())) { - // mid_entry moved to new (successor) bucket - mid -= bt->size(); - bt = bt->get_next(0); - } - } - } - this->open_space(bt, mid); - entry & mid1_entry = bt->get(mid); - entry & mid2_entry = bt->get(mid+1); - mid1_entry.set_end_key(this->pred(b)); - mid2_entry.set_begin_key(this->succ(e)); - } - else { - mid_entry.set_end_key(this->pred(b)); - del_entries_upto(m, bt, mid+1, e, pred_vect); - } - } - else { - SASSERT(this->eq(b, mid_entry.begin_key())); - SASSERT(mid > 0); // Reason: remove_begin would have been called instead. - del_entries_upto(m, bt, mid, e, pred_vect); - } - return; - } - } - } - -public: - interval_skip_list() { - } - - interval_skip_list(manager & m):skip_list_base(m) { - } - - ~interval_skip_list() { - } - - /** - \brief Copy the elements of other. - This method assumes that the *this* skip-list is empty. - */ - void copy(manager & m, interval_skip_list const & other) { - SASSERT(this->empty()); - other.clone_core(m, this); - } - - /** - \brief Return the smallest key stored in the interval skip list. - */ - key const & smallest() const { - SASSERT(!this->empty()); - return this->first_bucket()->get(0).begin_key(); - } - - /** - \brief Search for the given key in the interval skip list. - Return true if the key is stored in the list, and store the associated value in \c v. - */ - bool contains(key const & k, value & v) const { - bucket * bt; - unsigned idx; - if (find_core(k, bt, idx, 0)) { - v = bt->get(idx).val(); - return true; - } - return false; - } - - /** - \brief Alias for #contains. - */ - bool find(key const & k, value & v) const { - return contains(k, v); - } - -private: - /** - \brief Search for a bucket based on the key \c k. - - curr, next and pred_vect are output arguments. - - pred_vect must be an array of size level(). - - Post-conditions: - - pred_vect contains the predecessors of next. - That is, pred_vect[i] is the predecessor of level i. - - next is the successor of curr. - - pred_vect[0] == curr. - - curr == m_header || first_key(curr) < k - - next == 0 || k <= first_key(next) - */ - void find_bucket(key const & k, bucket * & curr, bucket * & next, bucket * pred_vect[]) { - SASSERT(this->level() > 0); - curr = this->m_header; - unsigned i = curr->level(); - SASSERT(i > 0); - while (i > 0) { - i--; - for (;;) { - next = curr->get_next(i); - if (next != 0 && lt(first_key(next), k)) - curr = next; - else - break; - } - pred_vect[i] = curr; - } - - SASSERT(next == curr->get_next(0)); - SASSERT(pred_vect[0] == curr); - DEBUG_CODE({ - if (next != 0) - for (unsigned i = 0; i < next->level(); i++) - SASSERT(pred_vect[i]->get_next(i) == next); - }); - SASSERT(curr == this->m_header || lt(first_key(curr), k)); - SASSERT(next == 0 || this->leq(k, first_key(next))); - } - -public: - - /** - \brief Insert the entries [i -> v] for every i \in [b, e]. - */ - void insert(manager & m, key const & b, key const & e, value const & v) { - SASSERT(this->leq(b, e)); - if (this->empty()) { - insert_first_entry(m, b, e, v); - return; - } - - // find the bucket where the new entries should be stored. - - // pred_vect[i] contains a pointer to the rightmost bucket of - // level i or higher that is to the left of the location of - // the insertion. - bucket * pred_vect[Traits::max_level]; - bucket * curr, * next; - find_bucket(b, curr, next, pred_vect); - - if (curr == this->m_header) { - SASSERT(next != 0); - // entry must be inserted in the first bucket. - SASSERT(this->first_bucket() == next); - insert_begin(m, next, b, e, v, pred_vect); - } - else if (next == 0 || this->gt(first_key(next), b)) { - insert_inside(m, curr, b, e, v, pred_vect); - } - else { - SASSERT(!curr->empty()); - SASSERT(!next->empty()); - SASSERT(next != 0); - SASSERT(this->eq(first_key(next), b)); - // Bucket curr is the predecessor of next. - SASSERT(curr->get_next(0) == next); - - // check if we can merge with last entry of curr - entry & curr_last_entry = curr->last_entry(); - if (this->val_eq(curr_last_entry.val(), v) && this->eq(curr_last_entry.end_key(), this->pred(b))) { - // No new reference to v was create, we don't need to invok inc_ref_eh - curr_last_entry.set_end_key(e); - del_entries_upto(m, next, e, pred_vect, 0); - merge_first_of_succ_if_possible(m, curr, pred_vect); - return; - } - insert_begin(m, next, b, e, v, pred_vect); - } - } - - /** - \brief Insert key [k->v]. - */ - void insert(manager & m, key const & k, value const & v) { - insert(m, k, k, v); - } - - class push_back_proc; - friend class push_back_proc; - - /** - \brief Functor for efficiently inserting elements in the end of the skip list. - - \remark The context becomes invalid if the skip-list is updated by other methods. - */ - class push_back_proc { - friend class interval_skip_list; - manager & m_manager; - interval_skip_list & m_list; - bucket * m_pred_vect[Traits::max_level]; - - bucket * last_bucket() const { return m_pred_vect[0]; } - - public: - push_back_proc(manager & m, interval_skip_list & l): - m_manager(m), - m_list(l) { - // initialize m_pred_vect - unsigned lvl = m_list.level(); - bucket * curr = m_list.m_header; - bucket * next; - unsigned i = lvl; - while (i > 0) { - i--; - for (;;) { - next = curr->get_next(i); - if (next != 0) - curr = next; - else - break; - } - m_pred_vect[i] = curr; - } - SASSERT(next == 0); - } - - interval_skip_list & list() { - return m_list; - } - - bool empty() const { - return m_list.empty(); - } - - key const & last_key() const { - return last_bucket()->last_entry().end_key(); - } - - void operator()(key const & b, key const & e, value const & v) { - SASSERT(m_list.leq(b, e)); - if (m_list.empty()) { - m_list.insert_first_entry(m_manager, b, e, v); - bucket * new_bucket = m_list.first_bucket(); - skip_list_base::update_predecessor_vector(m_pred_vect, new_bucket); - } - else { - bucket * bt = last_bucket(); - entry & et = bt->last_entry(); - SASSERT(m_list.lt(et.end_key(), b)); - // first check if new entry can be merged with the last entry in the list - if (m_list.val_eq(et.val(), v) && m_list.eq(et.end_key(), m_list.pred(b))) { - // can merge - et.set_end_key(e); - return; - } - // insert in the last bucket - unsigned sz = bt->size(); - if (sz >= bt->capacity()) { - if (bt->capacity() < Traits::max_capacity) { - SASSERT(m_list.first_bucket() == bt && m_list.first_bucket()->get_next(0) == 0); - m_list.expand_first_bucket(m_manager); - bt = m_list.first_bucket(); - SASSERT(bt->size() < bt->capacity()); - skip_list_base::update_predecessor_vector(m_pred_vect, bt); - sz = bt->size(); - } - else { - // last bucket is full... creating new bucket... - unsigned new_bucket_lvl = m_manager.random_level(Traits::max_level); - bucket * new_bucket = interval_skip_list::mk_bucket(m_manager, new_bucket_lvl); - m_list.update_list_level(m_manager, new_bucket_lvl, m_pred_vect); - for (unsigned i = 0; i < new_bucket_lvl; i++) { - SASSERT(m_pred_vect[i]->get_next(i) == 0); - m_pred_vect[i]->set_next(i, new_bucket); - m_pred_vect[i] = new_bucket; - SASSERT(m_pred_vect[i]->get_next(i) == 0); - } - SASSERT(last_bucket() == new_bucket); - bt = new_bucket; - sz = 0; - } - } - SASSERT(sz < bt->capacity()); - m_list.inc_ref(m_manager, v); - bt->expand(1); - interval_skip_list::set_entry(bt, sz, b, e, v); - } - } - }; - - /** - \brief For each i \in [b, e] remove any entry [i->v] if it is in the list. - */ - void remove(manager & m, key const & b, key const & e) { - SASSERT(this->leq(b, e)); - if (this->empty()) - return; - bucket * pred_vect[Traits::max_level]; - bucket * curr, * next; - - find_bucket(b, curr, next, pred_vect); - - if (curr == this->m_header) { - SASSERT(next != 0); - remove_begin(m, next, b, e, pred_vect); - } - else if (next == 0 || this->gt(first_key(next), b)) { - remove_inside(m, curr, b, e, pred_vect); - } - else { - SASSERT(next != 0); - SASSERT(this->eq(first_key(next), b)); - remove_begin(m, next, b, e, pred_vect); - } - } - - /** - \brief Remove entry [k->v] for some v, if it is in the list. - */ - void remove(manager & m, key const & k) { - remove(m, k, k); - } - - /** - \brief Alias for #remove. - */ - void erase(manager & m, key const & b, key const & e) { - remove(m, b, e); - } - - /** - \brief Alias for #remove. - */ - void erase(manager & m, key const & k) { - remove(m, k, k); - } - - /** - \begin Traverse the list applying the functor f. - The functor must have a method - - bool operator()(key const & b, key const & e, value const & v) - - The method will invoke f(b, e, v) whenever the entries [i -> v] for i \in [b, e] are - in the list. - - If the functor returns false, then the traversal is interrupted. - */ - template - void for_each(Functor & f) const { - SASSERT(this->m_header->empty()); - bucket * curr = this->first_bucket(); - while (curr != 0) { - unsigned sz = curr->size(); - for (unsigned i = 0; i < sz; i++) { - entry const & e = curr->get(i); - if (!f(e.begin_key(), e.end_key(), e.val())) - return; - } - curr = curr->get_next(0); - } - } - - /** - \brief Return the next/successor buffer, but skipping buffers that do not contains keys greater than or equal to k. - */ - bucket * next_bucket(bucket const * bt, key const & k) const { - bucket * curr = bt->get_next(0); // move to successor - if (curr == 0) - return 0; - unsigned i = curr->level(); - unsigned max = i; - bucket * next = 0; - while (i > 0) { - --i; - for (;;) { - next = curr->get_next(i); - if (next != 0 && this->leq(first_key(next), k)) { - TRACE("interval_skip_list", tout << "next_bucket(" << k << "), i: " << i << " skipping #" << this->get_bucket_idx(curr); - tout << ", moving to: #" << this->get_bucket_idx(next) << "\n"; this->display(tout, next);); - curr = next; - if (curr->level() > max) { - max = curr->level(); - i = curr->level(); - TRACE("interval_skip_list", tout << "max: " << max << ", curr->level(): " << curr->level() << ", i: " << i << "\n";); - break; - } - } - else { - break; - } - } - } - SASSERT(i == 0); - SASSERT(curr->get_next(0) == next); - SASSERT(next == 0 || lt(k, first_key(next))); - return curr; - } - - class iterator; - friend class iterator; - - class iterator { - interval_skip_list const * m_list; - bucket const * m_curr; - unsigned m_idx; - public: - iterator():m_list(0), m_curr(0), m_idx(0) {} - iterator(interval_skip_list const * l, bucket const * b = 0, unsigned idx = 0):m_list(l), m_curr(b), m_idx(idx) {} - entry const & operator*() const { return m_curr->get(m_idx); } - entry const * operator->() const { return &(operator*()); } - iterator & operator++() { - SASSERT(m_curr); - m_idx++; - if (m_idx >= m_curr->size()) { - m_idx = 0; - m_curr = m_curr->get_next(0); - } - return *this; - } - iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } - - bool at_end() const { - return m_curr == 0; - } - - /** - \brief Move the iterator to the next entry of the form ([b, e] -> v) s.t. - - 1) k in [b, e], or - 2) b > k and for every entry ([b',e']->v') between the old current entry and ([b,e]->v), we - have k > e' - - If such entry does not exist, then the iterator is moved to the end. - That is, at_end() returns true. - */ - void move_to(key const & k) { - SASSERT(m_curr); - SASSERT(m_idx < m_curr->size()); - entry const & curr_entry = m_curr->get(m_idx); - if (m_list->gt(k, curr_entry.end_key())) { - m_list->find_core(m_curr, m_idx+1, k, m_idx); - if (m_idx < m_curr->size()) - return; // found new position - SASSERT(m_idx == m_curr->size()); - m_curr = m_list->next_bucket(m_curr, k); - if (m_curr != 0) { - // search for k in the current buffer. - m_list->find_core(m_curr, k, m_idx); - if (m_idx == m_curr->size()) { - // k is greater than all keys in the list. - m_curr = 0; - m_idx = 0; - } - } - else { - SASSERT(m_curr == 0); - m_idx = 0; - } - } - } - - bool operator==(iterator const & it) const { - SASSERT(m_list == it.m_list); - return m_curr == it.m_curr && m_idx == it.m_idx; - } - - bool operator!=(iterator const & it) const { - SASSERT(m_list == it.m_list); - return m_curr != it.m_curr; - } - - /** - \brief Take the ownership of the current value and reset it to 0. - - \warning This method should be used with extreme care, since it puts the interval skip list - in an inconsistent state that must be restored. This method should be only used by developers - familiar with the interval skip-lists internal invariants. - For users not familiar with internal invariants, they should only use the reset method in the skip list class - after this method is invoked. - - Store in r the current value. - */ - template - void take_curr_ownership(manager & m, ObjRef & r) { - SASSERT(!at_end()); - entry & curr = const_cast(operator*()); // <<< HACK - r = curr.val(); - if (Traits::ref_count) - m.dec_ref_eh(curr.val()); - curr.set_val(0); - } - }; - - iterator begin() const { return iterator(this, this->first_bucket()); } - - iterator end() const { return iterator(this); } - - /** - \brief Return an iterator starting at the first entry that contains k. - If the skip-list does not contain k, then return the "end()" iterator. - */ - iterator find(key const & k) const { - bucket * bt; - unsigned idx; - if (find_core(k, bt, idx, 0)) { - return iterator(this, bt, idx); - } - else { - return end(); - } - } - - iterator find_geq(key const & k) const { - bucket * bt; - unsigned idx; - find_core(k, bt, idx, 0); - return iterator(this, bt, idx); - } - - /** - \brief Return true if the skip lists are equal. - */ - bool is_equal(interval_skip_list const & other) const { - iterator it1 = begin(); - iterator end1 = end(); - iterator it2 = other.begin(); - iterator end2 = other.end(); - for (; it1 != end1 && it2 != end2; it1++, it2++) { - entry const & e1 = *it1; - entry const & e2 = *it2; - if (!this->eq(e1.begin_key(), e2.begin_key())) - return false; - if (!this->eq(e1.end_key(), e2.end_key())) - return false; - if (!this->val_eq(e1.val(), e2.val())) - return false; - } - return true; - } - - /** - \brief Update the values stored in the skip-list by appling the given - functor to them. The functor must provide the operation: - - value operator()(value const & v); - - The functor must be injective. That is - - x != y implies f(x) != f(y) - - If a non-injective functor is used, then the resultant skip-list may - not be in a consistent state. - */ - template - void update_values(manager & m, InjectiveFunction & f) { - if (!this->empty()) { - iterator it = begin(); - iterator it_end = end(); - for (; it != it_end; ++it) { - entry & curr = const_cast(*it); - value const & old_val = curr.val(); - value new_val = f(old_val); - this->inc_ref(m, new_val); - this->dec_ref(m, old_val); - curr.set_val(new_val); - } - SASSERT(check_invariant()); - } - } - - class ext_iterator; - friend class ext_iterator; - - class ext_iterator { - friend class interval_skip_list; - - interval_skip_list * m_list; - bucket * m_curr; - unsigned m_idx; - bucket * m_pred_vect[Traits::max_level]; - - void move_next_bucket() { - m_list->update_predecessor_vector(m_pred_vect, m_curr); - m_idx = 0; - m_curr = m_curr->get_next(0); - } - - public: - ext_iterator():m_list(0), m_curr(0), m_idx(0) {} - - entry const & operator*() const { return m_curr->get(m_idx); } - - entry const * operator->() const { return &(operator*()); } - - ext_iterator & operator++() { - SASSERT(m_curr); - m_idx++; - if (m_idx >= m_curr->size()) - move_next_bucket(); - return *this; - } - - ext_iterator operator++(int) { ext_iterator tmp = *this; ++*this; return tmp; } - - bool at_end() const { - return m_curr == 0; - } - - bool operator==(ext_iterator const & it) const { - return m_curr == it.m_curr && m_idx == it.m_idx; - } - - bool operator!=(ext_iterator const & it) const { - return m_curr != it.m_curr; - } - - void erase(manager & m) { - SASSERT(!at_end()); - SASSERT(m_curr->size() > 0); - if (m_curr->size() > 1) { - m_list->del_entry(m, m_curr, m_idx); - if (m_idx >= m_curr->size()) - move_next_bucket(); - } - else { - SASSERT(m_curr->size() == 1); - bucket * old_curr = m_curr; - m_curr = m_curr->get_next(0); - m_list->del_bucket(m, old_curr, m_pred_vect); - } - } - }; - - void move_begin(ext_iterator & it) { - if (!this->empty()) { - it.m_list = this; - it.m_curr = this->first_bucket(); - it.m_idx = 0; - unsigned lvl = this->level(); - for (unsigned i = 0; i < lvl; i++) - it.m_pred_vect[i] = this->m_header; - } - else { - it.m_curr = 0; - it.m_idx = 0; - } - } - - void move_geq(ext_iterator & it, key const & k) { - it.m_list = this; - find_core(k, it.m_curr, it.m_idx, it.m_pred_vect); - } - -private: - /** - \brief Auxiliary data-structure used to implement the join of two interval_skip_lists. - To implement an efficient join, we want to be able to skip as many entries as possible. - */ - struct join_state { - bucket * m_bucket; - unsigned m_entry_idx; - key m_head; // it it a key in [m_bucket->m_entries[m_entry_idx].begin_key(), m_bucket->m_entries[m_entry_idx].end_key()] - public: - join_state(bucket * bt): - m_bucket(bt), - m_entry_idx(0), - m_head(bt->first_entry().begin_key()) { - } - - bool done() const { - return m_bucket == 0; - } - - key const & head() const { - SASSERT(!done()); - return m_head; - } - - key const & tail() const { - SASSERT(!done()); - SASSERT(m_entry_idx < m_bucket->size()); - return m_bucket->get(m_entry_idx).end_key(); - } - - value const & val() const { - SASSERT(!done()); - return m_bucket->get(m_entry_idx).val(); - } - }; - - /** - \brief Create a join_state auxiliary data-structure for performing a join starting at key k. - */ - join_state mk_join_state(key const & k) const { - return join_state(next_bucket(this->m_header, k)); - } - - /** - \brief Move the join_state towards k. - */ - void move_js(join_state & js, key const & k) const { - SASSERT(!js.done()); - if (this->leq(k, js.tail())) { - // We can't skip the current entry, because k in inside it. - // So, we just update the head. - js.m_head = k; - } - else { - // Moving to the next entry. - js.m_entry_idx++; - if (js.m_entry_idx < js.m_bucket->size()) { - // Update js.m_head with the beginning of the next entry. - js.m_head = js.m_bucket->get(js.m_entry_idx).begin_key(); - } - else { - // We processed all entries in the current bucket. So, set state to js.m_move_next. - js.m_bucket = next_bucket(js.m_bucket, k); - js.m_entry_idx = 0; - if (js.m_bucket != 0) - js.m_head = first_key(js.m_bucket); - } - } - } - -public: - - /** - \brief Join two interval_skip_lists and apply the given functor in the process. - - The functor must have a method - - bool operator()(key const & b, key const & e, value const & v1, value const & v2) - - The method will invoke f(b, e, v1, v2) whenever forall i \in [b, e] entries [i -> v1] are in the *this* list, and [i->v2] in the *other* list. - */ - template - void join(interval_skip_list const & other, Functor & f) { - if (this->empty() || other.empty()) - return; - key const & f1 = smallest(); - key const & f2 = other.smallest(); - key const & smallest_key = leq(f1, f2) ? f1 : f2; - join_state s1 = mk_join_state(smallest_key); - join_state s2 = other.mk_join_state(smallest_key); - while (!s1.done() && !s2.done()) { - key const & h1 = s1.head(); - key const & h2 = s2.head(); - if (eq(h1, h2)) { - key const & t1 = s1.tail(); - key const & t2 = s2.tail(); - key const & t = leq(t1, t2) ? t1 : t2; - f(h1, t, s1.val(), s2.val()); - key next_key = succ(t); - move_js(s1, next_key); - move_js(s2, next_key); - } - else if (lt(h1, h2)) { - move_js(s1, h2); - } - else { - SASSERT(lt(h2, h1)); - move_js(s2, h1); - } - } - } - -#ifdef Z3DEBUG -private: - bool check_invariant(entry const & e) const { - SASSERT(this->leq(e.begin_key(), e.end_key())); - return true; - } - - /** - \brief Return true if the last key of \c e1 is less than the first key of \c e2. - */ - bool lt(entry const & e1, entry const & e2) const { - return lt(e1.end_key(), e2.begin_key()); - } - - bool check_invariant(bucket const * bt) const { - SASSERT(bt->size() <= bt->capacity()); - SASSERT(bt == this->m_header || !bt->empty()); - for (unsigned i = 0; i < bt->size(); i++) { - entry const & curr = bt->get(i); - check_invariant(curr); - if (i > 0) { - entry const & prev = bt->get(i-1); - CTRACE("interval_skip_list", !lt(prev, curr), this->display_physical(tout);); - SASSERT(lt(prev, curr)); - CTRACE("interval_skip_list", can_be_merged(prev, curr), this->display_physical(tout);); - SASSERT(!can_be_merged(prev, curr)); - } - } - return true; - } - -public: - bool check_invariant() const { - SASSERT(this->m_header->empty()); - for (unsigned i = 0; i < this->m_header->level(); i++) { - // traverse buckets using get_next(i) pointers - bucket const * curr = this->m_header->get_next(i); - while (curr != 0) { - SASSERT(!curr->empty()); // only the header is empty. - bucket const * next = curr->get_next(i); - if (next != 0) { - SASSERT(next->level() >= i); - SASSERT(i == 0 || this->is_reachable_at_i(curr, next, i-1)); - SASSERT(!next->empty()); - entry const & last_of_curr = curr->last_entry(); - entry const & first_of_next = next->first_entry(); - CTRACE("interval_skip_list", !lt(last_of_curr, first_of_next), - this->display_physical(tout); - tout << "\ncurr:\n"; - this->display(tout, curr); - tout << "\nnext:\n"; - this->display(tout, next);); - SASSERT(lt(last_of_curr, first_of_next)); - CTRACE("interval_skip_list", can_be_merged(last_of_curr, first_of_next), - this->display_physical(tout); - tout << "\ncurr:\n"; - this->display(tout, curr); - tout << "\nnext:\n"; - this->display(tout, next);); - SASSERT(!can_be_merged(last_of_curr, first_of_next)); - } - curr = next; - } - } - bucket const * curr = this->m_header; - while (curr != 0) { - check_invariant(curr); - curr = curr->get_next(0); - } - return true; - } -#endif - - static void display_size_info(std::ostream & out) { - skip_list_base::display_size_info_core(out, sizeof(interval_skip_list)); - } - - /** - \brief Return the amount of memory consumed by the list. - */ - unsigned memory() const { - return this->memory_core(sizeof(interval_skip_list)); - } - -}; - -/** - \brief Traits for instantiating a mapping from unsigned to Value using the interval_skip_list template. -*/ -template, - unsigned MaxCapacity=32, - unsigned MaxLevel=32, - bool RefCount=false, - typename Manager=sl_manager_base > -struct unsigned_interval_skip_list_traits : private EqProc { - typedef default_islist_entry entry; - typedef Manager manager; - typedef typename entry::key key; - typedef typename entry::value value; - static const unsigned max_capacity = MaxCapacity; - static const unsigned initial_capacity = 2; - static const unsigned max_level = MaxLevel; - static const bool ref_count = RefCount; - - bool lt(key const & k1, key const & k2) const { return k1 < k2; } - bool eq(key const & k1, key const & k2) const { return k1 == k2; } - key succ(key const & k) const { return k + 1; } - key pred(key const & k) const { SASSERT(k > 0); return k - 1; } - bool val_eq(value const & v1, value const & v2) const { return EqProc::operator()(v1, v2); } -}; - -/** - \brief Traits for instantiating a set of unsigned values using the interval_skip_list template. -*/ -template > -struct unsigned_set_interval_skip_list_traits { - typedef default_set_islist_entry entry; - typedef Manager manager; - typedef typename entry::key key; - typedef typename entry::value value; - static const unsigned max_capacity = MaxCapacity; - static const unsigned initial_capacity = 2; - static const unsigned max_level = MaxLevel; - static const bool ref_count = false; - - bool lt(key const & k1, key const & k2) const { return k1 < k2; } - bool eq(key const & k1, key const & k2) const { return k1 == k2; } - key succ(key const & k) const { return k + 1; } - key pred(key const & k) const { SASSERT(k > 0); return k - 1; } - bool val_eq(value const & v1, value const & v2) const { return true; } -}; - -/** - \brief Generic adapater for generating a set-like API on top of the map API -*/ -template -class map2set_adapter : public Map { - typedef typename Map::manager manager; - typedef typename Map::key key; - typedef typename Map::value value; - - template - struct for_each_functor_adapter : public Functor { - for_each_functor_adapter(Functor const & f):Functor(f) { - } - bool operator()(key const & b, key const & e, value const & v) { - return Functor::operator()(b, e); - } - }; - -public: - map2set_adapter(manager & m): - Map(m) { - SASSERT(this->m_header != 0); - } - - void insert(manager & m, key const & b, key const & e) { - value _dummy; - Map::insert(m, b, e, _dummy); - } - - void insert(manager & m, key const & k) { - value _dummy; - Map::insert(m, k, k, _dummy); - } - - bool contains(key const & k) const { - value _dummy; - return Map::contains(k); - } - - bool find(key const & k) const { - return contains(k); - } - - /** - \begin Traverse the set applying the functor f. - The functor must have a method - - bool operator()(key const & b, key const & e) - - The method will invoke f(b, e) whenever the interval [b, e] are - in the set. - - If the functor returns false, then the traversal is interrupted. - */ - template - void for_each(Functor & f) const { - for_each_functor_adapter F(f); - Map::for_each(F); - } -}; - - -/** - \brief A set of unsigned values using interval_skip_list. -*/ -template > -class unsigned_isp_set : public map2set_adapter > > { -public: - unsigned_isp_set(Manager & m): - map2set_adapter > >(m) { - } -}; - -#endif /* _INTERVAL_SKIP_LIST_H_ */ - - diff --git a/src/muz_qe/pdr_dl_interface.cpp b/src/muz_qe/pdr_dl_interface.cpp index e2232dafe..63ac6cf4b 100644 --- a/src/muz_qe/pdr_dl_interface.cpp +++ b/src/muz_qe/pdr_dl_interface.cpp @@ -25,7 +25,6 @@ Revision History: #include "dl_mk_rule_inliner.h" #include "dl_rule.h" #include "dl_rule_transformer.h" -#include "dl_mk_extract_quantifiers.h" #include "smt2parser.h" #include "pdr_context.h" #include "pdr_dl_interface.h" @@ -33,7 +32,6 @@ Revision History: #include "dl_mk_slice.h" #include "dl_mk_unfold.h" #include "dl_mk_coalesce.h" -#include "pdr_quantifiers.h" using namespace pdr; @@ -57,41 +55,36 @@ dl_interface::~dl_interface() { // re-use existing context. // void dl_interface::check_reset() { - datalog::rule_ref_vector const& new_rules = m_ctx.get_rules().get_rules(); + datalog::rule_set const& new_rules = m_ctx.get_rules(); datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules(); bool is_subsumed = !old_rules.empty(); - for (unsigned i = 0; is_subsumed && i < new_rules.size(); ++i) { + for (unsigned i = 0; is_subsumed && i < new_rules.get_num_rules(); ++i) { is_subsumed = false; for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) { - if (m_ctx.check_subsumes(*old_rules[j], *new_rules[i])) { + if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) { is_subsumed = true; } } if (!is_subsumed) { - TRACE("pdr", new_rules[i]->display(m_ctx, tout << "Fresh rule ");); + TRACE("pdr", new_rules.get_rule(i)->display(m_ctx, tout << "Fresh rule ");); m_context->reset(); } } - m_old_rules.reset(); - m_old_rules.add_rules(new_rules.size(), new_rules.c_ptr()); + m_old_rules.replace_rules(new_rules); } lbool dl_interface::query(expr * query) { //we restore the initial state in the datalog context m_ctx.ensure_opened(); - m_pdr_rules.reset(); m_refs.reset(); m_pred2slice.reset(); ast_manager& m = m_ctx.get_manager(); - datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); + datalog::rule_manager& rm = m_ctx.get_rule_manager(); datalog::rule_set old_rules(m_ctx.get_rules()); func_decl_ref query_pred(m); - datalog::rule_ref_vector query_rules(rule_manager); - datalog::rule_ref query_rule(rule_manager); - rule_manager.mk_query(query, query_pred, query_rules, query_rule); - m_ctx.add_rules(query_rules); + rm.mk_query(query, m_ctx.get_rules()); expr_ref bg_assertion = m_ctx.get_background_assertion(); check_reset(); @@ -107,7 +100,6 @@ lbool dl_interface::query(expr * query) { ); - m_ctx.set_output_predicate(query_pred); m_ctx.apply_default_transformation(); if (m_ctx.get_params().slice()) { @@ -115,8 +107,6 @@ lbool dl_interface::query(expr * query) { datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); - query_pred = slice->get_predicate(query_pred.get()); - m_ctx.set_output_predicate(query_pred); // track sliced predicates. obj_map const& preds = slice->get_predicates(); @@ -142,23 +132,14 @@ lbool dl_interface::query(expr * query) { --num_unfolds; } } - // remove universal quantifiers from body. + query_pred = m_ctx.get_rules().get_output_predicate(); - - datalog::mk_extract_quantifiers* extract_quantifiers = alloc(datalog::mk_extract_quantifiers, m_ctx); - datalog::rule_transformer extract_q_tr(m_ctx); - extract_q_tr.register_plugin(extract_quantifiers); - m_ctx.transform_rules(extract_q_tr); - - IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); - m_pdr_rules.add_rules(m_ctx.get_rules()); + m_pdr_rules.replace_rules(m_ctx.get_rules()); m_pdr_rules.close(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); - - quantifier_model_checker quantifier_mc(*m_context, m, extract_quantifiers->quantifiers(), m_pdr_rules); datalog::scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode. @@ -173,20 +154,7 @@ lbool dl_interface::query(expr * query) { return l_false; } - lbool result; - while (true) { - result = m_context->solve(); - if (result == l_true && extract_quantifiers->has_quantifiers()) { - result = quantifier_mc.check(); - if (result != l_false) { - return result; - } - // else continue - } - else { - return result; - } - } + return m_context->solve(); } diff --git a/src/muz_qe/pdr_manager.cpp b/src/muz_qe/pdr_manager.cpp index 04facc776..3e79e4f00 100644 --- a/src/muz_qe/pdr_manager.cpp +++ b/src/muz_qe/pdr_manager.cpp @@ -132,7 +132,7 @@ namespace pdr { for_each_expr(collect_decls, m_relation_info[i].m_body); } for (unsigned i = 0; i < rules.size(); ++i) { - bound_decls.insert(rules[i]->get_head()->get_decl()); + bound_decls.insert(rules[i]->get_decl()); } for (unsigned i = 0; i < rules.size(); ++i) { unsigned u_sz = rules[i]->get_uninterpreted_tail_size(); diff --git a/src/muz_qe/pdr_quantifiers.cpp b/src/muz_qe/pdr_quantifiers.cpp deleted file mode 100644 index 4a7b4b995..000000000 --- a/src/muz_qe/pdr_quantifiers.cpp +++ /dev/null @@ -1,660 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - pdr_quantifiers.cpp - -Abstract: - - Module for handling quantifiers in rule bodies. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-05-19. - -Revision History: - ---*/ - -#include "pdr_quantifiers.h" -#include "pdr_context.h" -#include "qe.h" -#include "var_subst.h" -#include "dl_rule_set.h" -#include "ast_smt2_pp.h" -#include "model_smt2_pp.h" -#include "ast_smt_pp.h" -#include "expr_abstract.h" -#include "dl_mk_extract_quantifiers.h" -#include "qe_lite.h" -#include "well_sorted.h" -#include "expr_safe_replace.h" - - -namespace pdr { - - /** - \brief model check a potential model against quantifiers in bodies of rules. - - \return true if the model rooted in 'root' is checks with the quantifiers, otherwise - 'false' and a set of instantiations that contradict the current model. - */ - - static void get_nodes(model_node& root, ptr_vector& nodes) { - ptr_vector todo; - todo.push_back(&root); - while (!todo.empty()) { - model_node* n = todo.back(); - todo.pop_back(); - nodes.push_back(n); - todo.append(n->children().size(), n->children().c_ptr()); - } - } - - quantifier_model_checker::~quantifier_model_checker() { - obj_map::iterator it = m_reachable.begin(), end = m_reachable.end(); - for (; it != end; ++it) { - m.dec_ref(it->m_value); - } - } - - void quantifier_model_checker::generalize_binding(expr_ref_vector const& binding, vector& bindings) { - expr_ref_vector new_binding(m); - generalize_binding(binding, 0, new_binding, bindings); - } - - void quantifier_model_checker::generalize_binding( - expr_ref_vector const& binding, unsigned idx, - expr_ref_vector& new_binding, vector& bindings) { - if (idx == binding.size()) { - bool non_zero = false; - for (unsigned i = 0; i < binding.size(); ++i) { - if (new_binding[i].get()) { - non_zero = true; - } - else { - new_binding[i] = binding[i]; - } - } - if (non_zero) { - TRACE("pdr", - for (unsigned i = 0; i < new_binding.size(); ++i) { - tout << mk_pp(new_binding[i].get(), m) << " "; - } - tout << "\n";); - bindings.push_back(new_binding); - } - return; - } - model_node& node = *m_current_node; - expr_ref_vector ands(m); - expr* e1, *e2; - datalog::flatten_and(node.state(), ands); - new_binding.push_back(0); - generalize_binding(binding, idx + 1, new_binding, bindings); - for (unsigned i = 0; i < ands.size(); ++i) { - if (m.is_eq(ands[i].get(), e1, e2)) { - if (e2 == binding[idx]) { - new_binding[new_binding.size()-1] = e1; - generalize_binding(binding, idx + 1, new_binding, bindings); - } - } - } - new_binding.pop_back(); - } - - - void quantifier_model_checker::add_binding(quantifier* q, expr_ref_vector& binding) { - if (binding.size() != q->get_num_decls()) { - // not a full binding. It may happen that the quantifier got simplified. - return; - } - apply_binding(q, binding); - vector bindings; - generalize_binding(binding, bindings); - for (unsigned i = 0; i < bindings.size(); ++i) { - apply_binding(q, bindings[i]); - } - } - - void quantifier_model_checker::apply_binding(quantifier* q, expr_ref_vector& binding) { - datalog::scoped_no_proof _scp(m); - app_ref_vector& var_inst = m_current_pt->get_inst(m_current_rule); - expr_ref e(m); - var_subst vs(m, false); - inv_var_shifter invsh(m); - vs(q->get_expr(), binding.size(), binding.c_ptr(), e); - invsh(e, q->get_num_decls(), e); - expr_ref_vector inst(m); - inst.append(var_inst.size(), (expr*const*)var_inst.c_ptr()); - inst.reverse(); - expr_abstract(m, 0, inst.size(), inst.c_ptr(), e, e); - if (m_instantiations.contains(to_app(e))) { - return; - } - m_instantiated_rules.push_back(m_current_rule); - m_instantiations.push_back(to_app(e)); - TRACE("pdr", tout << mk_pp(q, m) << "\n"; - tout << "binding: "; - for (unsigned i = 0; i < binding.size(); ++i) { - tout << mk_pp(binding[i].get(), m) << " "; - } - tout << "\n"; - tout << "inst: "; - for (unsigned i = 0; i < var_inst.size(); ++i) { - tout << mk_pp(var_inst[i].get(), m) << " "; - } - tout << "\n"; - tout << mk_pp(e, m) << "\n"; - ); - } - - - // As & not Body_i is satisfiable - // then instantiate with model for parameters to Body_i - - void quantifier_model_checker::find_instantiations(quantifier_ref_vector const& qs, unsigned level) { - find_instantiations_proof_based(qs, level); - } - - class collect_insts { - ast_manager& m; - ptr_vector m_binding; - vector m_bindings; - ptr_vector m_quantifiers; - public: - collect_insts(ast_manager& m): m(m) { } - - void operator()(expr* n) { - expr* not_q_or_i, *e1, *e2, *e3; - if (m.is_quant_inst(n, not_q_or_i, m_binding)) { - VERIFY(m.is_or(not_q_or_i, e1, e2)); - VERIFY(m.is_not(e1, e3)); - SASSERT(is_quantifier(e3)); - m_quantifiers.push_back(to_quantifier(e3)); - m_bindings.push_back(expr_ref_vector(m,m_binding.size(), m_binding.c_ptr())); - m_binding.reset(); - } - else if ((m.is_rewrite(n, e1, e2) || - (m.is_rewrite_star(n) && - (e3 = to_app(n)->get_arg(to_app(n)->get_num_args()-1), - e1 = to_app(e3)->get_arg(0), - e2 = to_app(e3)->get_arg(1), - true))) && - is_quantifier(e1) && m.is_false(e2)) { - quantifier* q = to_quantifier(e1); - m_quantifiers.push_back(q); - m_bindings.push_back(expr_ref_vector(m)); - expr_ref_vector& b = m_bindings.back(); - for (unsigned i = 0; i < q->get_num_decls(); ++i) { - b.push_back(m.mk_fresh_const("V", q->get_decl_sort(i))); - } - } - } - - void reset() { - m_quantifiers.reset(); - m_bindings.reset(); - } - - unsigned size() const { return m_quantifiers.size(); } - ptr_vector const& quantifiers() const { return m_quantifiers; } - vector const& bindings() const { return m_bindings; } - - }; - - void quantifier_model_checker::find_instantiations_proof_based(quantifier_ref_vector const& qs, unsigned level) { - bool found_instance = false; - - datalog::scoped_proof _scp(m); - - expr_ref_vector fmls(m); - smt_params fparams; - SASSERT(m.proofs_enabled()); - fparams.m_mbqi = true; - - fmls.push_back(m_A.get()); - fmls.append(m_Bs); - TRACE("pdr", - tout << "assert\n"; - for (unsigned i = 0; i < fmls.size(); ++i) { - tout << mk_pp(fmls[i].get(), m) << "\n"; - }); - - smt::kernel solver(m, fparams); - for (unsigned i = 0; i < fmls.size(); ++i) { - solver.assert_expr(fmls[i].get()); - } - lbool result = solver.check(); - - TRACE("pdr", tout << result << "\n";); - - if (m_rules_model_check != l_false) { - m_rules_model_check = result; - } - - if (result != l_false) { - return; - } - - map qid_map; - quantifier* q; - for (unsigned i = 0; i < qs.size(); ++i) { - q = qs[i]; - qid_map.insert(q->get_qid(), q); - } - - proof* p = solver.get_proof(); - TRACE("pdr", tout << mk_ismt2_pp(p, m) << "\n";); - collect_insts collector(m); - for_each_expr(collector, p); - ptr_vector const& quants = collector.quantifiers(); - - for (unsigned i = 0; i < collector.size(); ++i) { - symbol qid = quants[i]->get_qid(); - if (!qid_map.find(qid, q)) { - TRACE("pdr", tout << "Could not find quantifier " << mk_pp(quants[i], m) << "\n";); - continue; - } - expr_ref_vector const& binding = collector.bindings()[i]; - - TRACE("pdr", tout << "Instantiating:\n" << mk_pp(quants[i], m) << "\n"; - for (unsigned j = 0; j < binding.size(); ++j) { - tout << mk_pp(binding[j], m) << " "; - } - tout << "\n";); - - expr_ref_vector new_binding(m); - for (unsigned j = 0; j < binding.size(); ++j) { - new_binding.push_back(binding[j]); - } - add_binding(q, new_binding); - found_instance = true; - } - if (found_instance) { - m_rules_model_check = l_false; - } - else if (m_rules_model_check != l_false) { - m_rules_model_check = l_undef; - } - } - - - /** - For under-approximations: - - m_reachable: set of reachable states, per predicate - - rules: P(x) :- B[x,y] & Fa z . Q(y,z) - Q(y,z) :- C[y,z,u] & Fa w . R(u,w) - - qis: Fa z . Q(y,z) - - M: model satisfying P(x) & B[x,y] - - B'[x,y]: body with reachable states substituted for predicates. - - Q'[y,z]: reachable states substituted for Q. - - S'[x]: Ex y . B'[x,y] & Fa z . Q'[y, z] - - Method: - - 1. M |= Fa z . Q'[y, z] => done - - Weaker variant: - Check B[x,y] & Fa z . Q'[y, z] for consistency. - - 2. Otherwise, extract instantiations. - - 3. Update reachable (for next round): - - Q'[y,z] := Q'[y,z] \/ C'[y,z,u] & Fa w . R'(u,w) - - */ - - - /** - For over-approximations: - - - pt - predicate transformer for rule: - P(x) :- Body1(x,y) || Body2(x,z) & (Fa u . Q(u,x,z)). - - rule - P(x) :- Body2(x,z) - - - qis - Fa u . Q(u,x,z) - - - A := node.state(x) && Body2(x,y) - - - - Bs := array of Bs of the form: - . Fa u . Q(u, P_x, P_y) - instantiate quantifier to P variables. - . B := inv(Q_0,Q_1,Q_2) - . B := inv(u, P_x, P_y) := B[u/Q_0, P_x/Q_1, P_y/Q_2] - . B := Fa u . inv(u, P_x, P_y) - - */ - - void quantifier_model_checker::update_reachable(func_decl* f, expr* e) { - expr* e_old; - m.inc_ref(e); - if (m_reachable.find(f, e_old)) { - m.dec_ref(e_old); - } - m_reachable.insert(f, e); - } - - - expr_ref quantifier_model_checker::get_reachable(func_decl* p) { - expr* e = 0; - if (!m_reachable.find(p, e)) { - e = m_ctx.get_pred_transformer(p).initial_state(); - update_reachable(p, e); - } - return expr_ref(e, m); - } - - void quantifier_model_checker::add_over_approximations(quantifier_ref_vector& qis, model_node& n) { - add_approximations(qis, n, true); - } - - void quantifier_model_checker::add_under_approximations(quantifier_ref_vector& qis, model_node& n) { - add_approximations(qis, n, false); - } - - void quantifier_model_checker::add_approximations(quantifier_ref_vector& qis, model_node& n, bool is_over) { - pred_transformer& pt = n.pt(); - manager& pm = pt.get_pdr_manager(); - unsigned level = n.level(); - expr_ref_vector Bs(m); - expr_ref B(m), v(m); - quantifier_ref q(m); - datalog::scoped_no_proof _no_proof(m); - scoped_ptr rep = mk_default_expr_replacer(m); - for (unsigned j = 0; j < qis.size(); ++j) { - q = qis[j].get(); - SASSERT(is_forall(q)); - app_ref_vector& inst = pt.get_inst(m_current_rule); - TRACE("pdr", - tout << "q:\n" << mk_pp(q, m) << "\n"; - tout << "level: " << level << "\n"; - model_smt2_pp(tout, m, n.get_model(), 0); - m_current_rule->display(m_ctx.get_context(), tout << "rule:\n"); - ); - - var_subst vs(m, false); - vs(q, inst.size(), (expr*const*)inst.c_ptr(), B); - q = to_quantifier(B); - TRACE("pdr", tout << "q instantiated:\n" << mk_pp(q, m) << "\n";); - - app* a = to_app(q->get_expr()); - func_decl* f = a->get_decl(); - pred_transformer& pt2 = m_ctx.get_pred_transformer(f); - if (is_over) { - B = pt2.get_formulas(level - 1, false); - } - else { - B = get_reachable(f); - SASSERT(is_well_sorted(m, B)); - } - TRACE("pdr", tout << "B:\n" << mk_pp(B, m) << "\n";); - - expr_safe_replace sub(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - v = m.mk_const(pm.o2n(pt2.sig(i),0)); - sub.insert(v, a->get_arg(i)); - } - sub(B); - TRACE("pdr", tout << "B substituted:\n" << mk_pp(B, m) << "\n";); - datalog::flatten_and(B, Bs); - for (unsigned i = 0; i < Bs.size(); ++i) { - m_Bs.push_back(m.update_quantifier(q, Bs[i].get())); - } - } - } - - /** - \brief compute strongest post-conditions for each predicate transformer. - (or at least something sufficient to change the set of current counter-examples) - */ - void quantifier_model_checker::weaken_under_approximation() { - - datalog::rule_set::decl2rules::iterator it = m_rules.begin_grouped_rules(), end = m_rules.end_grouped_rules(); - - for (; it != end; ++it) { - func_decl* p = it->m_key; - datalog::rule_vector& rules = *it->m_value; - expr_ref_vector bodies(m); - for (unsigned i = 0; i < rules.size(); ++i) { - bodies.push_back(strongest_post_condition(*rules[i])); - } - update_reachable(p, m.mk_or(bodies.size(), bodies.c_ptr())); - } - } - - expr_ref quantifier_model_checker::strongest_post_condition(datalog::rule& r) { - pred_transformer& pt = m_ctx.get_pred_transformer(r.get_decl()); - manager& pm = pt.get_pdr_manager(); - quantifier_ref_vector* qis = 0; - m_quantifiers.find(&r, qis); - expr_ref_vector body(m), inst(m); - expr_ref fml(m), v(m); - app* a; - func_decl* p; - svector names; - unsigned ut_size = r.get_uninterpreted_tail_size(); - unsigned t_size = r.get_tail_size(); - var_subst vs(m, false); - ptr_vector vars; - uint_set empty_index_set; - qe_lite qe(m); - - r.get_vars(vars); - - if (qis) { - quantifier_ref_vector const& qi = *qis; - for (unsigned i = 0; i < qi.size(); ++i) { - quantifier* q = qi[i]; - fml = q->get_expr(); - a = to_app(fml); - p = a->get_decl(); - expr* p_reach = get_reachable(p); - pred_transformer& pt2 = m_ctx.get_pred_transformer(p); - expr_safe_replace sub(m); - for (unsigned j = 0; j < a->get_num_args(); ++j) { - v = m.mk_const(pm.o2n(pt2.sig(j),0)); - sub.insert(v, a->get_arg(j)); - } - sub(p_reach, fml); - uint_set is; - for (unsigned j = 0; j < q->get_num_decls(); ++j) { - is.insert(j); - } - fml = m.mk_not(fml); - qe(is, true, fml); - fml = m.mk_not(fml); - body.push_back(m.update_quantifier(q, fml)); - } - } - - a = r.get_head(); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - v = m.mk_var(vars.size()+i, m.get_sort(a->get_arg(i))); - body.push_back(m.mk_eq(v, a->get_arg(i))); - } - for (unsigned i = 0; i < ut_size; ++i) { - a = r.get_tail(i); - p = a->get_decl(); - pred_transformer& pt2 = m_ctx.get_pred_transformer(p); - expr* p_reach = get_reachable(p); - expr_safe_replace sub(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - v = m.mk_const(pm.o2n(pt2.sig(i),0)); - sub.insert(v, a->get_arg(i)); - } - sub(p_reach, fml); - body.push_back(fml); - } - for (unsigned i = ut_size; i < t_size; ++i) { - body.push_back(r.get_tail(i)); - } - fml = m.mk_and(body.size(), body.c_ptr()); - vars.reverse(); - for (unsigned i = 0; i < vars.size(); ++i) { - names.push_back(symbol(i)); - } - if (!vars.empty()) { - fml = m.mk_exists(vars.size(), vars.c_ptr(), names.c_ptr(), fml); - SASSERT(is_well_sorted(m, fml)); - } - - for (unsigned i = 0; i < r.get_head()->get_num_args(); ++i) { - inst.push_back(m.mk_const(pm.o2n(pt.sig(i),0))); - } - vs(fml, inst.size(), inst.c_ptr(), fml); - SASSERT(is_well_sorted(m, fml)); - if (!vars.empty()) { - fml = to_quantifier(fml)->get_expr(); - qe(empty_index_set, false, fml); - fml = m.mk_exists(vars.size(), vars.c_ptr(), names.c_ptr(), fml); - SASSERT(is_well_sorted(m, fml)); - m_ctx.get_context().get_rewriter()(fml); - } - SASSERT(is_well_sorted(m, fml)); - - IF_VERBOSE(0, verbose_stream() << "instantiate to:\n" << mk_pp(fml, m) << "\n";); - return fml; - } - - - void quantifier_model_checker::model_check_node(model_node& node) { - TRACE("pdr", node.display(tout, 0);); - pred_transformer& pt = node.pt(); - manager& pm = pt.get_pdr_manager(); - expr_ref A(m), C(m); - expr_ref_vector As(m); - m_Bs.reset(); - // - // nodes from leaves that are repeated - // inside the search tree don't have models. - // - if (!node.get_model_ptr()) { - return; - } - m_current_rule = node.get_rule(); - m_current_pt = &pt; - m_current_node = &node; - if (!m_current_rule) { - return; - } - - quantifier_ref_vector* qis = 0; - m_quantifiers.find(m_current_rule, qis); - if (!qis) { - return; - } - unsigned level = node.level(); - if (level == 0) { - return; - } - - As.push_back(pt.get_propagation_formula(m_ctx.get_pred_transformers(), level)); - As.push_back(node.state()); - As.push_back(pt.rule2tag(m_current_rule)); - m_A = pm.mk_and(As); - - // Add quantifiers: - // add_over_approximations(*qis, node); - add_under_approximations(*qis, node); - - TRACE("pdr", - tout << "A:\n" << mk_pp(m_A, m) << "\n"; - tout << "quantifier:\n"; - for (unsigned i = 0; i < qis->size(); ++i) { - tout << mk_pp((*qis)[i].get(), m) << " "; - } - tout << "\n"; - tout << "B:\n"; - for (unsigned i = 0; i < m_Bs.size(); ++i) { - tout << mk_pp(m_Bs[i].get(), m) << "\n"; - } - ast_smt_pp pp(m); - pp.add_assumption(m_A); - for (unsigned i = 0; i < m_Bs.size(); ++i) { - pp.add_assumption(m_Bs[i].get()); - } - pp.display_smt2(tout, m.mk_true()); - ); - - find_instantiations(*qis, level); - } - - lbool quantifier_model_checker::model_check(model_node& root) { - m_instantiations.reset(); - m_instantiated_rules.reset(); - m_rules_model_check = l_true; - ptr_vector nodes; - get_nodes(root, nodes); - for (unsigned i = nodes.size(); i > 0; ) { - --i; - model_check_node(*nodes[i]); - } - if (m_rules_model_check == l_false) { - weaken_under_approximation(); - } - return m_rules_model_check; - } - - void quantifier_model_checker::refine() { - datalog::mk_extract_quantifiers eq(m_ctx.get_context()); - datalog::rule_manager& rm = m_rules.get_rule_manager(); - datalog::rule_set new_rules(m_rules.get_context()); - datalog::rule_set::iterator it = m_rules.begin(), end = m_rules.end(); - for (; it != end; ++it) { - datalog::rule* r = *it; - datalog::rule_counter vc(true); - unsigned max_var = vc.get_max_rule_var(*r); - app_ref_vector body(m); - for (unsigned i = 0; i < m_instantiations.size(); ++i) { - if (r == m_instantiated_rules[i]) { - eq.ensure_predicate(m_instantiations[i].get(), max_var, body); - } - } - if (body.empty()) { - new_rules.add_rule(r); - } - else { - for (unsigned i = 0; i < r->get_tail_size(); ++i) { - body.push_back(r->get_tail(i)); - } - quantifier_ref_vector* qs = 0; - m_quantifiers.find(r, qs); - m_quantifiers.remove(r); - datalog::rule_ref new_rule(rm); - new_rule = rm.mk(r->get_head(), body.size(), body.c_ptr(), 0, r->name(), false); - new_rules.add_rule(new_rule); - m_quantifiers.insert(new_rule, qs); - IF_VERBOSE(1, - verbose_stream() << "instantiating quantifiers\n"; - r->display(m_ctx.get_context(), verbose_stream()); - verbose_stream() << "replaced by\n"; - new_rule->display(m_ctx.get_context(), verbose_stream());); - } - } - new_rules.close(); - m_rules.reset(); - m_rules.add_rules(new_rules); - m_rules.close(); - m_ctx.update_rules(m_rules); - TRACE("pdr", m_rules.display(tout);); - } - - lbool quantifier_model_checker::check() { - lbool result = model_check(m_ctx.get_root()); - if (result == l_false) { - refine(); - } - return result; - } -}; - diff --git a/src/muz_qe/pdr_quantifiers.h b/src/muz_qe/pdr_quantifiers.h deleted file mode 100644 index 941fab3d9..000000000 --- a/src/muz_qe/pdr_quantifiers.h +++ /dev/null @@ -1,117 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - pdr_quantifiers.h - -Abstract: - - Module for handling quantifiers in rule bodies. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-05-19. - -Revision History: - ---*/ - -#ifndef _PDR_QUANTIFIERS_H_ -#define _PDR_QUANTIFIERS_H_ - -#include "ast.h" -#include "lbool.h" -#include "dl_rule.h" -#include "obj_pair_hashtable.h" - -namespace datalog { - class rule_set; -}; - -namespace pdr { - - class model_node; - class pred_transformer; - class context; - - class quantifier_model_checker { - context& m_ctx; - ast_manager& m; - obj_map& m_quantifiers; - datalog::rule_set& m_rules; - - obj_map m_reachable; // set of reachable states - expr_ref m_A; - expr_ref_vector m_Bs; - pred_transformer* m_current_pt; - datalog::rule const* m_current_rule; - model_node* m_current_node; - lbool m_rules_model_check; - app_ref_vector m_instantiations; - ptr_vector m_instantiated_rules; - - void model_check_node(model_node& node); - - void weaken_under_approximation(); - - void find_instantiations(quantifier_ref_vector const& qs, unsigned level); - - void find_instantiations_proof_based(quantifier_ref_vector const& qs, unsigned level); - - void add_binding(quantifier* q, expr_ref_vector& binding); - - void apply_binding(quantifier* q, expr_ref_vector& binding); - - void generalize_binding(expr_ref_vector const& binding, vector& bindings); - - void generalize_binding(expr_ref_vector const& binding, unsigned idx, expr_ref_vector& new_binding, vector& bindings); - - void refine(); - - - /** - \brief model check a potential model against quantifiers in bodies of rules. - - \return true if the model rooted in 'root' is checks with the quantifiers, otherwise - 'false' and a set of instantiations that contradict the current model. - */ - - lbool model_check(model_node& root); - - void add_over_approximations(quantifier_ref_vector& qis, model_node& n); - - void add_under_approximations(quantifier_ref_vector& qis, model_node& n); - - void add_approximations(quantifier_ref_vector& qis, model_node& n, bool is_over); - - expr_ref get_reachable(func_decl* f); - - void update_reachable(func_decl* f, expr* e); - - expr_ref strongest_post_condition(datalog::rule& r); - - public: - quantifier_model_checker( - context& ctx, - ast_manager& m, - obj_map& quantifiers, - datalog::rule_set& rules) : - m_ctx(ctx), - m(m), - m_quantifiers(quantifiers), - m_rules(rules), - m_A(m), - m_Bs(m), - m_current_pt(0), - m_current_rule(0), - m_current_node(0), - m_instantiations(m) {} - - ~quantifier_model_checker(); - - lbool check(); - }; - -}; -#endif diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index 12045047b..08ac1fa14 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -34,7 +34,40 @@ Revision History: #include"dl_table_relation.h" namespace datalog { - + + class rel_context::scoped_query { + context& m_ctx; + rule_set m_rules; + decl_set m_preds; + bool m_was_closed; + public: + scoped_query(context& ctx): + m_ctx(ctx), + m_rules(ctx.get_rules()), + m_preds(ctx.get_predicates()), + m_was_closed(ctx.is_closed()) + { + if (m_was_closed) { + ctx.reopen(); + } + } + ~scoped_query() { + m_ctx.reopen(); + m_ctx.restrict_predicates(m_preds); + m_ctx.replace_rules(m_rules); + if (m_was_closed) { + m_ctx.close(); + } + } + + void reset() { + m_ctx.reopen(); + m_ctx.restrict_predicates(m_preds); + m_ctx.replace_rules(m_rules); + m_ctx.close(); + } + }; + rel_context::rel_context(context& ctx) : m_context(ctx), m(ctx.get_manager()), @@ -56,9 +89,7 @@ namespace datalog { get_rmanager().register_plugin(alloc(bound_relation_plugin, get_rmanager())); get_rmanager().register_plugin(alloc(interval_relation_plugin, get_rmanager())); get_rmanager().register_plugin(alloc(karr_relation_plugin, get_rmanager())); - - -} + } rel_context::~rel_context() { if (m_last_result_relation) { @@ -67,25 +98,6 @@ namespace datalog { } } - void rel_context::collect_predicates(decl_set & res) { - unsigned rule_cnt = m_context.get_rules().get_num_rules(); - for (unsigned rindex=0; rindexget_head()->get_decl()); - unsigned tail_len = r->get_uninterpreted_tail_size(); - for (unsigned tindex=0; tindexget_tail(tindex)->get_decl()); - } - } - decl_set::iterator oit = m_output_preds.begin(); - decl_set::iterator oend = m_output_preds.end(); - for (; oit!=oend; ++oit) { - res.insert(*oit); - } - get_rmanager().collect_predicates(res); - } - - lbool rel_context::saturate() { m_context.ensure_closed(); @@ -93,9 +105,7 @@ namespace datalog { unsigned remaining_time_limit = m_context.soft_timeout(); unsigned restart_time = m_context.initial_restart_timeout(); - rule_set original_rules(m_context.get_rules()); - decl_set original_predicates; - m_context.collect_predicates(original_predicates); + scoped_query scoped_query(m_context); m_code.reset(); instruction_block termination_code; @@ -106,6 +116,7 @@ namespace datalog { TRACE("dl", m_context.display(tout);); while (true) { + m_context.ensure_closed(); m_context.transform_rules(); if (m_context.canceled()) { result = l_undef; @@ -165,45 +176,20 @@ namespace datalog { } termination_code.reset(); - m_context.reopen(); - restrict_predicates(original_predicates); - m_context.replace_rules(original_rules); - m_context.close(); + scoped_query.reset(); } - m_context.reopen(); - restrict_predicates(original_predicates); m_context.record_transformed_rules(); - m_context.replace_rules(original_rules); - m_context.close(); TRACE("dl", m_ectx.report_big_relations(100, tout);); m_code.process_all_costs(); m_code.make_annotations(m_ectx); return result; } - -#define BEGIN_QUERY() \ - rule_set original_rules(m_context.get_rules()); \ - decl_set original_preds; \ - m_context.collect_predicates(original_preds); \ - bool was_closed = m_context.is_closed(); \ - if (was_closed) { \ - m_context.reopen(); \ - } \ - -#define END_QUERY() \ - m_context.reopen(); \ - m_context.replace_rules(original_rules); \ - restrict_predicates(original_preds); \ - \ - if (was_closed) { \ - m_context.close(); \ - } \ lbool rel_context::query(unsigned num_rels, func_decl * const* rels) { get_rmanager().reset_saturated_marks(); - BEGIN_QUERY(); + scoped_query _scoped_query(m_context); for (unsigned i = 0; i < num_rels; ++i) { - set_output_predicate(rels[i]); + m_context.set_output_predicate(rels[i]); } m_context.close(); reset_negated_tables(); @@ -238,27 +224,16 @@ namespace datalog { case l_undef: break; } - END_QUERY(); return res; } lbool rel_context::query(expr* query) { get_rmanager().reset_saturated_marks(); - BEGIN_QUERY(); + scoped_query _scoped_query(m_context); rule_manager& rm = m_context.get_rule_manager(); - rule_ref qrule(rm); - rule_ref_vector qrules(rm); func_decl_ref query_pred(m); try { - rm.mk_query(query, query_pred, qrules, qrule); - } - catch(default_exception& exn) { - m_context.close(); - m_context.set_status(INPUT_ERROR); - throw exn; - } - try { - m_context.add_rules(qrules); + query_pred = rm.mk_query(query, m_context.get_rules()); } catch (default_exception& exn) { m_context.close(); @@ -266,29 +241,17 @@ namespace datalog { throw exn; } - set_output_predicate(qrule->get_head()->get_decl()); m_context.close(); reset_negated_tables(); if (m_context.generate_explanations()) { - rule_transformer transformer(m_context); - //expl_plugin is deallocated when transformer goes out of scope - mk_explanations * expl_plugin = - alloc(mk_explanations, m_context, m_context.explanations_on_relation_level()); - transformer.register_plugin(expl_plugin); - m_context.transform_rules(transformer); - - //we will retrieve the predicate with explanations instead of the original query predicate - query_pred = expl_plugin->get_e_decl(query_pred); - const rule_vector & query_rules = m_context.get_rules().get_predicate_rules(query_pred); - SASSERT(query_rules.size()==1); - qrule = query_rules.back(); + m_context.transform_rules(alloc(mk_explanations, m_context)); } + query_pred = m_context.get_rules().get_pred(query_pred); + if (m_context.magic_sets_for_queries()) { - rule_transformer transformer(m_context); - transformer.register_plugin(alloc(mk_magic_sets, m_context, qrule.get())); - m_context.transform_rules(transformer); + m_context.transform_rules(alloc(mk_magic_sets, m_context, query_pred)); } lbool res = saturate(); @@ -304,7 +267,6 @@ namespace datalog { } } - END_QUERY(); return res; } @@ -372,15 +334,8 @@ namespace datalog { } } - void rel_context::set_output_predicate(func_decl * pred) { - if (!m_output_preds.contains(pred)) { - m_output_preds.insert(pred); - } - } - - void rel_context::restrict_predicates( const decl_set & res ) { - set_intersection(m_output_preds, res); - get_rmanager().restrict_predicates(res); + void rel_context::restrict_predicates(func_decl_set const& predicates) { + get_rmanager().restrict_predicates(predicates); } relation_base & rel_context::get_relation(func_decl * pred) { return get_rmanager().get_relation(pred); } @@ -510,8 +465,8 @@ namespace datalog { } } - void rel_context::display_output_facts(std::ostream & out) const { - get_rmanager().display_output_tables(out); + void rel_context::display_output_facts(rule_set const& rules, std::ostream & out) const { + get_rmanager().display_output_tables(rules, out); } void rel_context::display_facts(std::ostream& out) const { diff --git a/src/muz_qe/rel_context.h b/src/muz_qe/rel_context.h index 08ee868c0..3ee9f76da 100644 --- a/src/muz_qe/rel_context.h +++ b/src/muz_qe/rel_context.h @@ -37,11 +37,12 @@ namespace datalog { relation_manager m_rmanager; expr_ref m_answer; relation_base * m_last_result_relation; - decl_set m_output_preds; fact_vector m_table_facts; execution_context m_ectx; instruction_block m_code; + class scoped_query; + void reset_negated_tables(); relation_plugin & get_ordinary_relation_plugin(symbol relation_name); @@ -78,13 +79,7 @@ namespace datalog { The function deallocates unsused relations, it does not deal with rules. */ - void restrict_predicates(const decl_set & res); - - void collect_predicates(decl_set & res); - - void set_output_predicate(func_decl * pred); - bool is_output_predicate(func_decl * pred) { return m_output_preds.contains(pred); } - const decl_set & get_output_predicates() const { return m_output_preds; } + void restrict_predicates(func_decl_set const& predicates); /** @@ -102,7 +97,7 @@ namespace datalog { */ void store_relation(func_decl * pred, relation_base * rel); - void display_output_facts(std::ostream & out) const; + void display_output_facts(rule_set const& rules, std::ostream & out) const; void display_facts(std::ostream & out) const; void display_profile(std::ostream& out) const; diff --git a/src/muz_qe/tab_context.cpp b/src/muz_qe/tab_context.cpp index 9ee059f44..44d9d94e2 100644 --- a/src/muz_qe/tab_context.cpp +++ b/src/muz_qe/tab_context.cpp @@ -191,6 +191,7 @@ namespace tb { unsigned get_index() const { return m_index; } void set_index(unsigned index) { m_index = index; } app* get_head() const { return m_head; } + func_decl* get_decl() const { return m_head->get_decl(); } void set_head(app* h) { m_head = h; } unsigned get_parent_index() const { return m_parent_index; } unsigned get_parent_rule() const { return m_parent_rule; } @@ -447,7 +448,7 @@ namespace tb { void insert(ref& g) { unsigned idx = m_rules.size(); m_rules.push_back(g); - func_decl* f = g->get_head()->get_decl(); + func_decl* f = g->get_decl(); map::obj_map_entry* e = m_index.insert_if_not_there2(f, unsigned_vector()); SASSERT(e); e->get_data().m_value.push_back(idx); @@ -613,7 +614,7 @@ namespace tb { bool match_head(clause const& g) { return - m_head->get_decl() == g.get_head()->get_decl() && + m_head->get_decl() == g.get_decl() && m_matcher(m_head, g.get_head(), m_subst, m_sideconds) && match_predicates(0, g); } @@ -1080,7 +1081,7 @@ namespace tb { bool unify(clause const& tgt, unsigned idx, clause const& src, bool compute_subst, ref& result) { qe_lite qe(m); reset(); - SASSERT(tgt.get_predicate(idx)->get_decl() == src.get_head()->get_decl()); + SASSERT(tgt.get_predicate(idx)->get_decl() == src.get_decl()); unsigned var_cnt = std::max(tgt.get_num_vars(), src.get_num_vars()); m_S1.reserve(2, var_cnt); if (!m_unifier(tgt.get_predicate(idx), src.get_head(), m_S1)) { @@ -1380,11 +1381,10 @@ namespace datalog { m_displayed_rules.reset(); m_rules.init(m_ctx.get_rules()); m_selection.init(m_rules); - rule_ref_vector query_rules(rm); + rule_set query_rules(m_ctx); rule_ref clause(rm); - func_decl_ref query_pred(m); - rm.mk_query(query, query_pred, query_rules, clause); - + rm.mk_query(query, query_rules); + clause = query_rules.last(); ref g = alloc(tb::clause, m); g->init(clause); g->set_head(m.mk_false()); diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index fe2699504..6cdfe1623 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -175,8 +175,6 @@ unsigned read_datalog(char const * file) { TRACE("dl_compiler", ctx.display(tout);); datalog::rule_set original_rules(ctx.get_rules()); - datalog::decl_set original_predicates; - ctx.collect_predicates(original_predicates); datalog::instruction_block rules_code; datalog::instruction_block termination_code; @@ -237,7 +235,6 @@ unsigned read_datalog(char const * file) { termination_code.reset(); ex_ctx.reset(); ctx.reopen(); - ctx.restrict_predicates(original_predicates); ctx.replace_rules(original_rules); ctx.close(); } @@ -248,7 +245,7 @@ unsigned read_datalog(char const * file) { rules_code.display(ctx.get_rel_context(), tout);); if (ctx.get_params().output_tuples()) { - ctx.get_rel_context().display_output_facts(std::cout); + ctx.get_rel_context().display_output_facts(ctx.get_rules(), std::cout); } display_statistics( diff --git a/src/test/dl_query.cpp b/src/test/dl_query.cpp index 4dc770056..3fab95583 100644 --- a/src/test/dl_query.cpp +++ b/src/test/dl_query.cpp @@ -59,7 +59,7 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, } relation_manager & rel_mgr_q = ctx_b.get_rel_context().get_rmanager(); - decl_set out_preds = ctx_b.get_output_predicates(); + decl_set out_preds = ctx_b.get_rules().get_output_predicates(); decl_set::iterator it = out_preds.begin(); decl_set::iterator end = out_preds.end(); for(; it!=end; ++it) { diff --git a/src/test/dl_table.cpp b/src/test/dl_table.cpp index 0c8afcdc2..32a6f65e3 100644 --- a/src/test/dl_table.cpp +++ b/src/test/dl_table.cpp @@ -1,7 +1,6 @@ #ifdef _WINDOWS #include "dl_context.h" #include "dl_table.h" -#include "dl_skip_table.h" typedef datalog::table_base* (*mk_table_fn)(datalog::relation_manager& m, datalog::table_signature& sig); @@ -11,13 +10,6 @@ static datalog::table_base* mk_bv_table(datalog::relation_manager& m, datalog::t return p->mk_empty(sig); } -static datalog::table_base* mk_skip_table(datalog::relation_manager& m, datalog::table_signature& sig) { - datalog::table_plugin * p = m.get_table_plugin(symbol("skip")); - SASSERT(p); - return p->mk_empty(sig); -} - - static void test_table(mk_table_fn mk_table) { datalog::table_signature sig; sig.push_back(2); @@ -96,13 +88,9 @@ void test_dl_bitvector_table() { test_table(mk_bv_table); } -void test_dl_skip_table() { - test_table(mk_skip_table); -} void tst_dl_table() { test_dl_bitvector_table(); - test_dl_skip_table(); } #else void tst_dl_table() { diff --git a/src/test/imdd.cpp b/src/test/imdd.cpp deleted file mode 100644 index bfaca274c..000000000 --- a/src/test/imdd.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - imdd.cpp - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-10-14. - -Revision History: - ---*/ -#include"imdd.h" - -#if !defined(_AMD64_) && defined(Z3DEBUG) - -static void tst0() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m), d4(m); - d1 = m.mk_empty(1); - d2 = m.mk_empty(1); - m.insert_dupdt(d1, 10, 20); - m.insert_dupdt(d1, 31, 50); - m.insert_dupdt(d2, 1, 5); - m.insert_dupdt(d2, 11, 13); - m.mk_product(d1, d2, d4); - m.mk_product(d4, d2, d4); - m.mk_product_dupdt(d1, d2); - std::cout << "d1:\n" << mk_ll_pp(d1, m) << "\n-------\n"; - m.mk_product_dupdt(d1, d2); - std::cout << "d4:\n" << mk_ll_pp(d4, m) << "\nd1:\n" << mk_ll_pp(d1, m) << "\nd2:\n" << mk_ll_pp(d2, m) << "\n"; - std::cout << d1 << "\n" << d2 << "\n"; - m.mk_product_dupdt(d1, d1); - std::cout << "d1 X d1:\n" << mk_ll_pp(d1, m) << "\n"; -} - -static void add_triple(imdd_manager & m, imdd_ref & d, unsigned l1, unsigned u1, unsigned l2, unsigned u2, unsigned l3, unsigned u3, - bool destructive = false, bool memoize = true) { - unsigned lowers[3] = {l1, l2, l3}; - unsigned uppers[3] = {u1, u2, u3}; - if (destructive) - m.add_facts_dupdt(d, 3, lowers, uppers, memoize); - else - m.add_facts(d, d, 3, lowers, uppers, memoize); - // std::cout << mk_ll_pp(d, m) << "\n"; -} - -static void add_pair(imdd_manager & m, imdd_ref & d, unsigned l1, unsigned u1, unsigned l2, unsigned u2, bool destructive = false, bool memoize = true) { - unsigned lowers[2] = {l1, l2}; - unsigned uppers[2] = {u1, u2}; - if (destructive) - m.add_facts_dupdt(d, 2, lowers, uppers, memoize); - else - m.add_facts(d, d, 2, lowers, uppers, memoize); - // std::cout << mk_ll_pp(d, m) << "\n"; -} - -static void add_some_facts(imdd_manager & m, imdd_ref & d, bool destructive = false, bool memoize = true) { - std::cout << "destructive: " << destructive << ", memoize: " << memoize << std::endl; - add_triple(m, d, 1, 10, 3, 3, 0, 100, destructive, memoize); - std::cout << mk_ll_pp(d, m) << std::endl; - SASSERT(m.contains(d, 2, 3, 20)); - SASSERT(!m.contains(d, 2, 4, 20)); - SASSERT(!m.contains(d, 2, 3, 200)); - SASSERT(!m.contains(d, 0, 3, 200)); - SASSERT(m.contains(d,1,3,0)); - add_triple(m, d, 3, 6, 3, 4, 7, 101, destructive, memoize); - std::cout << mk_ll_pp(d, m) << std::endl; - add_triple(m, d, 3, 6, 2, 2, 7, 101, destructive, memoize); - std::cout << mk_ll_pp(d, m) << std::endl; - add_triple(m, d, 3, 6, 5, 6, 7, 101, destructive, memoize); - SASSERT(m.contains(d, 2, 3, 20)); - std::cout << mk_ll_pp(d, m) << std::endl; - SASSERT(!m.contains(d, 2, 4, 20)); - SASSERT(m.contains(d, 3, 4, 20)); - SASSERT(!m.contains(d, 2, 3, 200)); - SASSERT(!m.contains(d, 0, 3, 200)); - SASSERT(m.contains(d,1,3,0)); -} - -static void tst1() { - std::cout << "--------------------------------\n"; - imdd_manager m; - { - imdd_ref d(m); - d = m.mk_empty(3); - add_some_facts(m, d); - } - { - imdd_ref d(m); - d = m.mk_empty(3); - add_some_facts(m, d, true, false); - m.defrag(d); - std::cout << mk_ll_pp(d, m) << "\n"; - } -} - -static void tst2() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 10, 20, 11, 21, 12, 22); - add_triple(m, d1, 30, 40, 31, 41, 32, 42); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - m.mk_union(d1, d2, d3); - SASSERT(m.subsumes(d3, d1)); - SASSERT(m.subsumes(d3, d2)); - SASSERT(!m.subsumes(d1, d3)); - SASSERT(!m.subsumes(d2, d3)); - std::cout << "d3: " << d3.get() << "\n" << mk_ll_pp(d3, m) << "\n"; - m.mk_union_dupdt(d1, d2, false); - std::cout << "d1: " << d1.get() << "\n" << mk_ll_pp(d1, m) << "\n"; - SASSERT(m.is_equal(d1, d3)); - SASSERT(!m.is_equal(d2, d3)); - SASSERT(!m.is_equal(d2, d1)); - std::cout << "memory(d1): " << m.memory(d1) << "\n"; -} - -static void tst3() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m); - d1 = m.mk_empty(3); - unsigned mins[3] = {0,0,0}; - unsigned maxs[3] = {127, 511, 255}; - - m.mk_complement(d1, d1, 3, mins, maxs); - std::cout << d1 << "\n"; - m.mk_complement(d1, d1, 3, mins, maxs); - std::cout << d1 << "\n"; - SASSERT(d1->empty()); - - d1 = m.mk_empty(3); - add_triple(m, d1, 10, 20, 11, 21, 12, 22); - add_triple(m, d1, 30, 40, 31, 41, 32, 42); - std::cout << d1 << "\n"; - m.mk_complement(d1, d1, 3, mins, maxs); - std::cout << mk_ll_pp(d1,m) << "\n"; - m.mk_filter_equal(d1, d1, 1, 15); - std::cout << "after selecting second column = 15\n" << mk_ll_pp(d1,m) << "\n"; -} - -static void tst4() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 1, 2, 2, 5, 4, 4); - add_triple(m, d1, 3, 4, 3, 4, 2, 5); - std::cout << "testing iterator:\n"; - imdd_manager::iterator it = m.begin(d1); - imdd_manager::iterator end = m.end(d1); - for (; it != end; ++it) { - unsigned * tuple = *it; - std::cout << "["; - for (unsigned i = 0; i < d1->get_arity(); i++) { - if (i > 0) - std::cout << ", "; - std::cout << tuple[i]; - } - std::cout << "]\n"; - } -} - -static void tst5() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - std::cout.flush(); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 100, 10, 20, 3, 10); - std::cout << mk_ll_pp(d1,m) << std::endl; - add_triple(m, d1, 5, 100, 34, 50, 98, 110); - std::cout << mk_ll_pp(d1,m) << std::endl; - unsigned vals[3] = {6, 8, 3}; - SASSERT(!m.contains(d1, 3, vals)); - add_triple(m, d1, 6, 25, 8, 30, 14, 50); - std::cout << mk_ll_pp(d1,m) << std::endl; - SASSERT(!m.contains(d1, 3, vals)); - unsigned vars[2] = {0, 2}; - d2 = d1; - d3 = d1; - m.mk_filter_identical(d1, d1, 2, vars); - vars[1] = 1; - std::cout << "d1:\n" << mk_ll_pp(d1,m) << "\n"; - m.mk_filter_identical(d2, d2, 2, vars); - std::cout << "d2:\n" << mk_ll_pp(d2,m) << "\n"; - vars[0] = 1; - vars[1] = 2; - m.mk_filter_identical(d3, d3, 2, vars); - std::cout << "d3:\n" << mk_ll_pp(d3,m) << "\n"; -} - -static void add_5tuple(imdd_manager & m, imdd_ref & d, unsigned l1, unsigned u1, unsigned l2, unsigned u2, unsigned l3, unsigned u3, - unsigned l4, unsigned u4, unsigned l5, unsigned u5, bool destructive = false, bool memoize = true) { - unsigned lowers[5] = {l1, l2, l3, l4, l5}; - unsigned uppers[5] = {u1, u2, u3, u4, u5}; - if (destructive) - m.add_facts_dupdt(d, 5, lowers, uppers, memoize); - else - m.add_facts(d, d, 5, lowers, uppers, memoize); - // std::cout << mk_ll_pp(d, m) << "\n"; -} - -static void tst6() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m); - std::cout.flush(); - d1 = m.mk_empty(5); - // TODO: make a more complicated mk_filter_identical example -} - -static void tst7() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << "mk_project\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - unsigned vars[1] = {1}; - m.mk_project(d2, d3, 1, vars); - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst8() { - std::cout << "--------------------------------\n"; - // enable_trace("mk_swap_bug"); - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - m.mk_swap(d2, d3, 0); - std::cout << "after swap 0<->1\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - m.mk_swap(d2, d3, 1); - std::cout << "after swap 1<->2\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst9() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(5); - add_5tuple(m, d2, 2,2, 3,3, 1, 1, 5, 10, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - add_5tuple(m, d2, 2,2, 3,3, 1, 1, 15, 20, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - add_5tuple(m, d2, 4,4, 5,5, 1, 1, 5, 10, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - add_5tuple(m, d2, 4,4, 5,5, 1, 1, 15, 20, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - m.mk_swap(d2, d3, 2); - std::cout << "after swap 2<->3\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst10() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 100, 10, 20, 3, 10); - add_triple(m, d1, 5, 100, 34, 50, 98, 110); - m.add_bounded_var(d1, d2, 0, 66, 72); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - m.add_bounded_var(d1, d3, 1, 64, 73); - std::cout << mk_ll_pp(d3, m) << "\n"; -} - -static void tst11() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 100, 10, 20, 3, 10); - add_triple(m, d1, 5, 100, 34, 50, 98, 110); - add_triple(m, d1, 20, 30, 5, 25, 11, 13); - m.mk_filter_distinct(d1, d2, 1, 2); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << "filter_distinct(1,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst12() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 1, 10, 5, 25, 10, 13); - m.mk_filter_distinct(d1, d2, 1, 2); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << "filter_distinct(1,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst13() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 25, 1, 10, 10, 13); - add_triple(m, d1, 5, 25, 20, 30, 10, 13); - m.mk_filter_distinct(d1, d2, 0, 2); - std::cout << mk_ll_pp(d1, m) << "\n"; - std::cout << "filter_distinct(0,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst14() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 25, 1, 10, 10, 13); - add_triple(m, d1, 5, 25, 20, 30, 15, 18); - std::cout << "destructive version\n"; - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_distinct_dupdt(d1, 0, 2); - std::cout << "filter_distinct(0,2):\n"; - std::cout << mk_ll_pp(d1, m) << "\n"; -} - -static void tst15() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 5, 1, 10, 5, 5); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_distinct(d1, d2, 0, 2); - std::cout << "filter_distinct(0,2):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst16() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 15, 1, 10, 50, 500); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_disequal(d1, d2, 1, 4); - std::cout << "filter_disequal(var1,4):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst17() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(3); - add_triple(m, d1, 5, 15, 10, 10, 50, 500); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_filter_disequal(d1, d2, 1, 10); - std::cout << "filter_disequal(var1,10):\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst18() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d1(m), d2(m), d3(m); - d1 = m.mk_empty(2); - add_pair(m, d1, 1112, 1290, 1302, 1302); - std::cout << mk_ll_pp(d1, m) << "\n"; - m.mk_swap(d1, d2, 0); - std::cout << "mk_swap 0:\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst19() { - std::cout << "--------------------------------\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << "mk_project_dupdt\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - unsigned vars[1] = {1}; - m.mk_project_dupdt(d2, 1, vars); - std::cout << "new table\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void init(unsigned* v, unsigned a, unsigned b, unsigned c) { - v[0] = a; - v[1] = b; - v[2] = c; -} - -static void tst20() { - std::cout << "--------------------------------\n"; - std::cout << "remove_facts\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - // - // [15, 22] -> #1:{ - // [15, 23] -> {[7, 18]}*$80}*$80 - // [28, 42] -> #2:{ - // [29, 39] -> {[34, 46], [100, 200]}*$160 - // [50, 60] -> {[100, 200]}*$80}*$80}$80 - // - unsigned lowers[3] = {23,1,1}; - unsigned uppers[3] = {24,1,1}; - - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - lowers[0] = 22; - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 0); - init(uppers, 24, 23, 0); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 7); - init(uppers, 24, 23, 18); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (narrow first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 18); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 17); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 19); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; - - init(lowers, 30, 20, 120); - init(uppers, 40, 60, 140); - m.remove_facts(d2, d3, 3, lowers, uppers); - std::cout << "new table (split second interval)\n"; - std::cout << mk_ll_pp(d3, m) << "\n"; -} - - -static void tst21() { - std::cout << "--------------------------------\n"; - std::cout << "remove_facts\n"; - imdd_manager m; - imdd_ref d2(m), d3(m); - d2 = m.mk_empty(3); - add_triple(m, d2, 15, 22, 15, 23, 7, 18); - add_triple(m, d2, 28, 42, 29, 39, 34, 46); - add_triple(m, d2, 28, 42, 29, 39, 100, 200); - add_triple(m, d2, 28, 42, 50, 60, 100, 200); - std::cout << mk_ll_pp(d2, m) << "\n"; - // - // [15, 22] -> #1:{ - // [15, 23] -> {[7, 18]}*$80}*$80 - // [28, 42] -> #2:{ - // [29, 39] -> {[34, 46], [100, 200]}*$160 - // [50, 60] -> {[100, 200]}*$80}*$80}$80 - // - unsigned lowers[3] = {23,1,1}; - unsigned uppers[3] = {24,1,1}; - - d3 = d2; - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - d2 = d3; - - lowers[0] = 22; - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 0); - init(uppers, 24, 23, 0); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (no change)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 7); - init(uppers, 24, 23, 18); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (narrow first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 18); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 17); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 22, 15, 8); - init(uppers, 24, 23, 19); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split first interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; - - init(lowers, 30, 20, 120); - init(uppers, 40, 60, 140); - m.remove_facts_dupdt(d2, 3, lowers, uppers); - std::cout << "new table (split second interval)\n"; - std::cout << mk_ll_pp(d2, m) << "\n"; -} - -static void tst22() { - std::cout << "--------------------------------\n"; - std::cout << "swap\n"; - imdd_manager m; - imdd_ref d2(m), d3(m), d4(m); - - d2 = m.mk_empty(3); - random_gen rand; - for (unsigned i = 0; i < 130; ++i) { - unsigned a = rand(20); - unsigned b = rand(20); - unsigned c = rand(20); - add_triple(m, d2, a, a, b, b, c, c); - } - std::cout << mk_ll_pp(d2, m) << "\n"; - - m.mk_swap(d2, d3, 0, true); - std::cout << mk_ll_pp(d3, m) << "\n"; - - m.mk_swap(d3, d4, 0, true); - std::cout << mk_ll_pp(d4, m) << "\n"; - SASSERT(m.is_subset(d2, d4)); - SASSERT(m.is_subset(d4, d2)); -} - -void tst_imdd() { - // enable_trace("imdd_add_bug"); - // enable_trace("add_facts_bug"); - enable_trace("mk_distinct_imdd"); - enable_trace("mk_union_core"); - tst22(); - tst0(); - tst1(); - tst2(); - tst3(); - tst4(); - tst5(); - tst6(); - tst7(); - tst19(); - tst8(); - tst9(); - tst10(); - tst11(); - tst12(); - tst13(); - tst14(); - tst15(); - tst16(); - tst17(); - tst18(); - tst20(); - tst21(); - -} - -#else - -void tst_imdd() { -} - -#endif diff --git a/src/test/interval_skip_list.cpp b/src/test/interval_skip_list.cpp deleted file mode 100644 index bda85c86e..000000000 --- a/src/test/interval_skip_list.cpp +++ /dev/null @@ -1,716 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - interval_skip_list.cpp - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2010-10-05. - -Revision History: - ---*/ - -#include"interval_skip_list.h" -#include"map.h" -#include"vector.h" - -typedef sl_manager_base slist_manager; -template class interval_skip_list, 4, 4, false, slist_manager> >; -typedef interval_skip_list, 4, 4, false, slist_manager> > slist; -typedef u_map u2u_map; -typedef unsigned_isp_set<4, 4, slist_manager> uset; - -static void tst1() { - slist_manager m; - slist l(m); - SASSERT(l.check_invariant()); - SASSERT(l.empty()); - // l.display_physical(std::cout); - l.insert(m, 20, 30, 5); - l.insert(m, 31, 32, 5); - l.insert(m, 18, 19, 5); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.insert(m, 10, 15, 8); - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - l.insert(m, 5, 25, 7); - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - l.insert(m, 23, 27, 5); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst2() { - slist_manager(m); - slist l(m); - for(unsigned i = 0; i < 50; i++) { - l.insert(m,i,i,i*10); - } - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - for (unsigned i = 0; i < 25; i++) { - l.insert(m,i*2,i*2+1,i*20+1); - } - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - for (unsigned i = 0; i < 15; i++) { - l.insert(m,i*3,i*3+2,i*30+1); - } - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - // l.compress(4); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static bool check_interval(slist & l, unsigned k1, unsigned k2, unsigned val) { - for (unsigned i = k1; i <= k2; i++) { - DEBUG_CODE({ - unsigned _val; - SASSERT(l.contains(i, _val) && _val == val); - }); - } - return true; -} - -static bool check_no_interval(slist & l, unsigned k1, unsigned k2) { - for (unsigned i = k1; i <= k2; i++) { - DEBUG_CODE({ - unsigned _val; - SASSERT(!l.contains(i, _val)); - }); - } - return true; -} - -static void tst4() { - slist_manager m; - slist l(m); - l.insert(m, 1, 10, 0); - l.insert(m, 2, 5, 1); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,1,0)); - SASSERT(check_interval(l,2,5,1)); - SASSERT(check_interval(l,6,10,0)); - SASSERT(check_no_interval(l,11,20)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst5() { - slist_manager m; - slist l(m); - l.insert(m, 1, 5, 0); - l.insert(m, 8, 100, 1); - l.insert(m, 8, 20, 1); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,5,0)); - SASSERT(check_no_interval(l,6,7)); - SASSERT(check_interval(l,8,100,1)); - SASSERT(check_no_interval(l,101,200)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst6() { - slist_manager m; - slist l(m); - l.insert(m, 1, 5, 0); - l.insert(m, 8, 100, 1); - l.insert(m, 3, 20, 1); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,2,0)); - SASSERT(check_interval(l,3,100,1)); - SASSERT(check_no_interval(l,101,200)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst7() { - slist_manager m; - slist l(m); - l.insert(m, 1, 5, 0); - l.insert(m, 8, 100, 1); - l.insert(m, 2, 12, 0); - SASSERT(check_no_interval(l,0,0)); - SASSERT(check_interval(l,1,12,0)); - SASSERT(check_interval(l,13,100,1)); - SASSERT(check_no_interval(l,101,200)); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst8() { - slist_manager m; - slist l(m); - for (unsigned i = 0; i < 100; i++) { - l.insert(m, 10*i, 10*i+5, i); - } - SASSERT(!l.empty()); - l.insert(m, 0, 10000, 0); - SASSERT(!l.has_more_than_k_entries(1)); - // l.display_physical(std::cout); - l.deallocate(m); -} - -struct for_each_contains { - slist const & m_other; - - for_each_contains(slist const & other):m_other(other) {} - - bool operator()(unsigned b, unsigned e, unsigned v) { - for (unsigned i = b; i <= e; i++) { - DEBUG_CODE({ - unsigned _v; - SASSERT(m_other.contains(i, _v)); - SASSERT(v == _v); - }); - } - return true; - } -}; - -static void random_tsts(unsigned num_ops, unsigned max_key, unsigned max_val, unsigned max_interval_size) { - slist_manager m; - slist m1(m); - u2u_map m2; - for (unsigned i = 0; i < num_ops; i++) { - SASSERT(m1.check_invariant()); - TRACE("interval_skip_list", tout << "i: " << i << "\n"; m1.display_physical(tout);); - // std::cout << i << std::endl; - int op = rand()%8; - if (op < 3) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - unsigned val = rand() % max_val; - m1.insert(m, bg, bg+sz, val); - for (unsigned j = bg; j <= bg+sz; j++) { - DEBUG_CODE({ - unsigned _val; - SASSERT(m1.contains(j, _val)); - CTRACE("interval_skip_list", val != _val, tout << "i: " << i << ", j: " << j << ", val: " << val << ", _val: " << _val << "\n"; m1.display_physical(tout);); - SASSERT(val == _val); - TRACE("interval_skip_list", tout << "[insert]: " << j << " -> " << val << "\n";); - }); - m2.insert(j, val); - } - } - else if (op < 4) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - m1.erase(m, bg, bg+sz); - for (unsigned i = bg; i <= bg+sz; i++) { - m2.erase(i); - } - } - else if (op < 5) { - slist m1_copy(m); - m1_copy.copy(m, m1); - for_each_contains proc1(m1); - for_each_contains proc2(m1_copy); - m1.for_each(proc2); - m1_copy.for_each(proc1); - // m1.display_physical(std::cout); - // std::cout << "COPY===>\n"; - // m1_copy->display_physical(std::cout); - m1_copy.deallocate(m); - } - else if (op < 6) { - m1.compress(m, 3); - } - else { - SASSERT(m1.check_invariant()); - u2u_map::iterator it = m2.begin(); - u2u_map::iterator end = m2.end(); - for (; it != end; ++it) { - DEBUG_CODE({ - unsigned _val; - CTRACE("interval_skip_list", !m1.contains(it->m_key, _val), - tout << it->m_key << " -> " << it->m_value << "\n"; - m1.display_physical(tout);); - SASSERT(m1.contains(it->m_key, _val)); - SASSERT(it->m_value == _val); - }); - } - } - } - // m1.display_physical(std::cout); - // m1.compress(4); - // m1.display_physical(std::cout); - m1.deallocate(m); -} - -static void tst9() { - slist_manager m; - slist l(m); - l.insert(m,10,10,1); - l.insert(m,9,9,0); - l.insert(m,8,8,2); - l.insert(m,7,7,3); - l.insert(m,6,8,3); - SASSERT(!l.has_more_than_k_buckets(1)); - SASSERT(check_no_interval(l,0,5)); - SASSERT(check_interval(l,6,8,3)); - SASSERT(check_interval(l,9,9,0)); - SASSERT(check_interval(l,10,10,1)); - SASSERT(check_no_interval(l,11,20)); - l.deallocate(m); -} - -static void tst10() { - slist_manager m; - slist l(m); - l.insert(m,10,10,1); - l.insert(m,13,16,2); - l.insert(m,17,28,3); - l.remove(m,12,19); - SASSERT(l.check_invariant()); - SASSERT(check_no_interval(l,0,9)); - SASSERT(check_interval(l,10,10,1)); - SASSERT(check_no_interval(l,12,19)); - SASSERT(check_interval(l,20,28,3)); - SASSERT(check_no_interval(l,29,100)); - l.remove(m,10,11); - SASSERT(l.check_invariant()); - SASSERT(check_no_interval(l,0,19)); - SASSERT(check_interval(l,20,28,3)); - SASSERT(check_no_interval(l,29,100)); - l.remove(m,0,1000); - SASSERT(l.empty()); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -static void tst11() { - slist_manager m; - slist l(m); - l.insert(m,11,20,1); - l.insert(m,21,30,2); - l.insert(m,31,40,3); - l.insert(m,41,50,4); - l.insert(m,51,60,5); - l.compress(m,4); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 25, 26); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,24,2)); - SASSERT(check_no_interval(l,25,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 44,48); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,24,2)); - SASSERT(check_no_interval(l,25,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,43,4)); - SASSERT(check_no_interval(l,44,48)); - SASSERT(check_interval(l,49,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 22,24); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,21,2)); - SASSERT(check_no_interval(l,22,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,43,4)); - SASSERT(check_no_interval(l,44,48)); - SASSERT(check_interval(l,49,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - l.remove(m, 42,49); - SASSERT(check_no_interval(l,0,10)); - SASSERT(check_interval(l,11,20,1)); - SASSERT(check_interval(l,21,21,2)); - SASSERT(check_no_interval(l,22,26)); - SASSERT(check_interval(l,27,30,2)); - SASSERT(check_interval(l,31,40,3)); - SASSERT(check_interval(l,41,41,4)); - SASSERT(check_no_interval(l,42,49)); - SASSERT(check_interval(l,50,50,4)); - SASSERT(check_interval(l,51,60,5)); - SASSERT(check_no_interval(l,61,100)); - SASSERT(l.check_invariant()); - // l.display_physical(std::cout); - l.deallocate(m); -} - -static void tst12() { - slist_manager m; - slist l(m); - l.insert(m,10,10,0); - l.insert(m,9,9,0); - SASSERT(l.check_invariant()); - l.insert(m,8,9,1); - SASSERT(l.check_invariant()); - l.insert(m,7,7,2); - SASSERT(l.check_invariant()); - l.insert(m,6,6,3); - SASSERT(l.check_invariant()); - l.insert(m,4,5,2); - SASSERT(l.check_invariant()); - l.insert(m,3,9,0); - // l.display_physical(std::cout); - l.deallocate(m); -} - -static void tst13() { - slist_manager m; - uset s(m); - s.insert(m, 10, 30); - s.insert(m, 32, 40); - s.display(std::cout); - std::cout << ", mem: " << s.memory() << "\n"; - s.deallocate(m); -} - -struct obj { - unsigned m_val; - unsigned m_ref_count; - void inc_ref() { - m_ref_count++; - } - void dec_ref() { - SASSERT(m_ref_count > 0); - m_ref_count--; - if (m_ref_count == 0) - dealloc(this); - } - obj(unsigned v):m_val(v), m_ref_count(0) { - } -}; - -std::ostream & operator<<(std::ostream & out, obj * o) { - out << o->m_val << "{" << o->m_ref_count << "}"; - return out; -} - -struct obj_slist_manager : public sl_manager_base { - void inc_ref_eh(obj * v) { - v->inc_ref(); - } - - void dec_ref_eh(obj * v) { - v->dec_ref(); - } -}; - -struct inc_ref_functor { - unsigned_vector & refs; - inc_ref_functor(unsigned_vector & r):refs(r) {} - bool operator()(unsigned b, unsigned e, obj * val) { - refs[val->m_val]++; - return true; - } -}; - -template class interval_skip_list, 16, 16, true, obj_slist_manager> >; -typedef interval_skip_list, 16, 16, true, obj_slist_manager> > obj_slist; - -void random_tsts_ref(unsigned num_ops, unsigned num_objs, unsigned max_key, unsigned max_interval_size) { - obj_slist_manager m; - obj_slist l(m); - ptr_vector objs; - unsigned_vector refs; - for (unsigned i = 0; i < num_objs; i++) { - objs.push_back(alloc(obj, i)); - objs.back()->inc_ref(); - refs.push_back(1); - } - - for (unsigned i = 0; i < num_ops; i++) { - SASSERT(l.check_invariant()); - TRACE("interval_skip_list", tout << "i: " << i << "\n"; l.display_physical(tout); tout << "\n";); - int op = rand()%5; - if (op < 3) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - unsigned val = rand() % num_objs; - TRACE("interval_skip_list", tout << "[inserting]: [" << bg << ", " << (bg+sz) << "] -> " << objs[val] << "\n";); - l.insert(m, bg, bg+sz, objs[val]); - SASSERT(objs[val]->m_ref_count > 1); - } - else if (op < 4) { - unsigned bg = rand() % max_key; - unsigned sz = rand() % max_interval_size; - if (sz == 0) sz = 1; - TRACE("interval_skip_list", tout << "[erasing]: [" << bg << ", " << (bg+sz) << "]\n";); - l.erase(m, bg, bg+sz); - } - else if (op < 5) { - obj_slist l_copy(m); - l_copy.copy(m, l); - TRACE("interval_skip_list", tout << "[copying]\n";); - l_copy.deallocate(m); - TRACE("interval_skip_list", tout << "[deleting copy]\n";); - } - else { - TRACE("interval_skip_list", tout << "[compressing]\n";); - l.compress(m, 3); - } - // check ref-counts - inc_ref_functor proc(refs); - l.for_each(proc); - for (unsigned i = 0; i < num_objs; i++) { - CTRACE("interval_skip_list", refs[i] != objs[i]->m_ref_count, - tout << "i: " << i << ", objs[i]: " << objs[i] << ", refs[i]: " << refs[i] << "\n\n"; - l.display_physical(tout);); - SASSERT(refs[i] == objs[i]->m_ref_count); - refs[i] = 1; - } - } - l.deallocate(m); - for (unsigned i = 0; i < num_objs; i++) { - SASSERT(objs[i]->m_ref_count == 1); - objs[i]->dec_ref(); - } -} - -void tst_ref() { - obj_slist_manager m; - obj_slist l(m); - for (unsigned i = 0; i < 30; i++) { - obj * n = alloc(obj, i); - l.insert(m, i*10, i*10+3, n); - // l.display_physical(std::cout); - // std::cout << "memory: " << l.memory() << "\n"; - } - l.deallocate(m); - -} - -void tst_push_back_aux(slist::push_back_proc & push_back, unsigned num_ops, unsigned max_int, unsigned max_sep, unsigned max_val) { - unsigned prev_key; - - if (push_back.empty()) - prev_key = 0; - else - prev_key = push_back.last_key(); - - for (unsigned i = 0; i < num_ops; i++) { - unsigned next_key = prev_key + 1; - next_key += (rand() % max_sep); - unsigned sz = rand() % max_int; - if (sz == 0) sz = 1; - unsigned val = rand() % max_val; - push_back(next_key, next_key+sz, val); - SASSERT(!push_back.empty()); - prev_key = push_back.last_key(); - } -} - -void tst_push_back1(unsigned num_ops, unsigned max_int, unsigned max_sep, unsigned max_val) { - slist_manager m; - slist l(m); - slist::push_back_proc push_back(m, l); - - tst_push_back_aux(push_back, num_ops, max_int, max_sep, max_val); - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -void tst_push_back2(unsigned num_ops, unsigned max_int, unsigned max_sep, unsigned max_val) { - slist_manager m; - slist l(m); - - // insert some random values before creating push_back functor - for (unsigned i = 0; i < num_ops; i++) { - unsigned next_key = rand() % (num_ops * max_int/2); - unsigned sz = rand() % max_int; - if (sz == 0) sz = 1; - unsigned val = rand() % max_val; - l.insert(m, next_key, next_key+sz, val); - } - - slist::push_back_proc push_back(m, l); - - tst_push_back_aux(push_back, num_ops, max_int, max_sep, max_val); - - // l.display_physical(std::cout); - SASSERT(l.check_invariant()); - l.deallocate(m); -} - -void tst_find_geq1() { - slist_manager m; - slist l(m); - l.insert(m, 10, 20, 4); - l.insert(m, 23, 30, 3); - l.insert(m, 40, 45, 10); - l.insert(m, 50, 66, 1); - l.insert(m, 100, 120, 21); - l.insert(m, 140, 200, 2); - slist::iterator it = l.find_geq(22); - SASSERT(it->begin_key() == 23); - it = l.find_geq(42); - SASSERT(it->begin_key() == 40); - it.move_to(130); - SASSERT(it->begin_key() == 140); - it.move_to(400); - SASSERT(it == l.end()); - it = l.find_geq(300); - SASSERT(it == l.end()); - it = l.find_geq(9); - SASSERT(it->begin_key() == 10); - it.move_to(105); - SASSERT(it->begin_key() == 100); - it = l.find_geq(15); - SASSERT(it->begin_key() == 10); - it.move_to(31); - SASSERT(it->begin_key() == 40); - it = l.find_geq(22); - SASSERT(it->begin_key() == 23); - it = l.find_geq(124); - SASSERT(it->begin_key() == 140); - it = l.find_geq(102); - SASSERT(it->begin_key() == 100); - // l.display_physical(std::cout); - l.deallocate(m); -} - -struct add42 { - unsigned operator()(unsigned v) { return v + 42; } -}; - -void tst_move_to() { - slist_manager m; - slist l(m); - for (unsigned i = 0; i < 500; i++) - l.insert(m, i*10, i*10 + 5, i); - l.compress(m, 4); - slist::iterator it = l.find_geq(137); - SASSERT(it->begin_key() == 140); - it.move_to(947); - SASSERT(it->begin_key() == 950); - it.move_to(4955); - SASSERT(it->begin_key() == 4950); - it.move_to(4955); - SASSERT(it->begin_key() == 4950); - it.move_to(4956); - SASSERT(it->begin_key() == 4960); - it.move_to(4982); - SASSERT(it->begin_key() == 4980); - it.move_to(4987); - SASSERT(it->begin_key() == 4990); - it.move_to(4990); - SASSERT(it->begin_key() == 4990); - it.move_to(4995); - SASSERT(it->begin_key() == 4990); - it.move_to(4996); - SASSERT(it.at_end()); - // l.display_physical(std::cout); - add42 f; - // l.display(std::cout); std::cout << "\n"; - l.update_values(m, f); - // l.display(std::cout); std::cout << "\n"; - l.deallocate(m); -} - -static void tst_ext_iterator() { - slist_manager m; - slist l(m); - for (unsigned i = 0; i < 20; i++) - l.insert(m, i*10, i*10 + 5, i); - l.compress(m, 4); - l.display_physical(std::cout); std::cout << "\n"; - slist::ext_iterator it; - slist::ext_iterator end; - SASSERT(end.at_end()); - l.move_geq(it, 92); - SASSERT(!it.at_end()); - SASSERT(it->begin_key() == 90); - it++; - SASSERT(it->begin_key() == 100); - it.erase(m); - SASSERT(it->begin_key() == 110); - it.erase(m); - SASSERT(it->begin_key() == 120); - it.erase(m); - it.erase(m); - it.erase(m); - it.erase(m); - SASSERT(it->begin_key() == 160); - SASSERT(l.check_invariant()); - l.display_physical(std::cout); std::cout << "\n"; - l.move_geq(it, 0); - SASSERT(it->begin_key() == 0); - it.erase(m); - SASSERT(it->begin_key() == 10); - it.erase(m); - SASSERT(it->begin_key() == 20); - it.erase(m); - SASSERT(it->begin_key() == 30); - it.erase(m); - SASSERT(it->begin_key() == 40); - it.erase(m); - SASSERT(it->begin_key() == 50); - l.display_physical(std::cout); std::cout << "\n"; - l.deallocate(m); -} - -void tst_interval_skip_list() { - std::cout << "unsigned map stats:\n"; - slist::display_size_info(std::cout); - std::cout << "\nunsigned set stats:\n"; - uset::display_size_info(std::cout); - std::cout << "\n"; - tst1(); -// enable_trace("interval_skip_list_insert_bug"); -// enable_trace("interval_skip_list_bug"); -// enable_trace("del_entries_upto_bug"); -// enable_trace("insert_inside_bug"); -// enable_trace("insert_at_bug"); - tst2(); - tst4(); - tst5(); - tst6(); - tst7(); - tst8(); - tst9(); - tst10(); - tst11(); - tst12(); - tst13(); - tst_find_geq1(); - tst_move_to(); - tst_push_back1(300, 4, 2, 10); - tst_push_back2(300, 4, 2, 10); - random_tsts(1000, 20, 20, 5); - random_tsts_ref(1000, 20, 20, 5); - tst_ref(); - tst_ext_iterator(); -} - - diff --git a/src/test/main.cpp b/src/test/main.cpp index 6ecfd6d4f..6f802cec1 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -22,7 +22,7 @@ if (do_display_usage) \ std::cout << #MODULE << "\n"; \ for (int i = 0; i < argc; i++) \ - if (strcmp(argv[i], #MODULE) == 0) { \ + if (test_all || strcmp(argv[i], #MODULE) == 0) { \ enable_trace(#MODULE); \ enable_debug(#MODULE); \ timeit timeit(true, s.c_str()); \ @@ -60,6 +60,7 @@ void display_usage() { std::cout << " /h prints this message.\n"; std::cout << " /v:level be verbose, where is the verbosity level.\n"; std::cout << " /w enable warning messages.\n"; + std::cout << " /a run all unit tests that don't require arguments.\n"; #if defined(Z3DEBUG) || defined(_TRACE) std::cout << "\nDebugging support:\n"; #endif @@ -71,7 +72,7 @@ void display_usage() { #endif } -void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage) { +void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage, bool& test_all) { int i = 1; while (i < argc) { char * arg = argv[i]; @@ -99,6 +100,9 @@ void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage) { else if (strcmp(opt_name, "w") == 0) { enable_warning_messages(true); } + else if (strcmp(opt_name, "a") == 0) { + test_all = true; + } #ifdef _TRACE else if (strcmp(opt_name, "tr") == 0) { if (!opt_arg) @@ -122,7 +126,8 @@ void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage) { int main(int argc, char ** argv) { memory::initialize(0); bool do_display_usage = false; - parse_cmd_line_args(argc, argv, do_display_usage); + bool test_all = false; + parse_cmd_line_args(argc, argv, do_display_usage, test_all); TST(random); TST(vector); TST(symbol_table); @@ -151,7 +156,6 @@ int main(int argc, char ** argv) { TST(simple_parser); TST(api); TST(old_interval); - TST(interval_skip_list); TST(no_overflow); TST(memory); TST(get_implied_equalities); @@ -170,7 +174,6 @@ int main(int argc, char ** argv) { TST(dl_util); TST(dl_product_relation); TST(dl_relation); - TST(imdd); TST(parray); TST(stack); TST(escaped); diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index c77978647..df0124aea 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -1299,9 +1299,10 @@ bool mpz_manager::is_int64(mpz const & a) const { if (is_small(a)) return true; #ifndef _MP_GMP - if (!is_uint64(a)) + if (!is_uint64(a)) { return false; - uint64 num = get_uint64(a); + } + uint64 num get_uint64(a); uint64 msb = static_cast(1) << 63; uint64 msb_val = msb & num; if (a.m_val >= 0) { From 6a36116b5cd537e159a0fcc8f8e12d78d6f3cf0c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 9 Apr 2013 10:16:37 -0700 Subject: [PATCH 02/91] stash --- src/muz_qe/dl_compiler.cpp | 7 ++++--- src/muz_qe/dl_instruction.cpp | 3 ++- src/muz_qe/dl_mk_magic_sets.cpp | 1 - src/muz_qe/dl_sparse_table.cpp | 5 +++++ src/muz_qe/dl_util.h | 6 ++++++ src/muz_qe/rel_context.cpp | 17 ++++++++--------- src/test/dl_query.cpp | 13 +++++++++---- 7 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index c898f7964..1bc39d213 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -61,13 +61,14 @@ namespace datalog { void compiler::make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc) { relation_signature aux_sig; - relation_signature::from_join(m_reg_signatures[t1], m_reg_signatures[t2], vars.size(), - vars.get_cols1(), vars.get_cols2(), aux_sig); + relation_signature sig1 = m_reg_signatures[t1]; + relation_signature sig2 = m_reg_signatures[t2]; + relation_signature::from_join(sig1, sig2, vars.size(), vars.get_cols1(), vars.get_cols2(), aux_sig); relation_signature res_sig; relation_signature::from_project(aux_sig, removed_cols.size(), removed_cols.c_ptr(), res_sig); - result = get_fresh_register(res_sig); + acc.push_back(instruction::mk_join_project(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), removed_cols.size(), removed_cols.c_ptr(), result)); } diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz_qe/dl_instruction.cpp index 401a42e98..547752563 100644 --- a/src/muz_qe/dl_instruction.cpp +++ b/src/muz_qe/dl_instruction.cpp @@ -723,7 +723,8 @@ namespace datalog { instr_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, reg_idx result) : m_rel1(rel1), m_rel2(rel2), m_cols1(joined_col_cnt, cols1), - m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols), m_res(result) {} + m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols), m_res(result) { + } virtual bool perform(execution_context & ctx) { ctx.make_empty(m_res); if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { diff --git a/src/muz_qe/dl_mk_magic_sets.cpp b/src/muz_qe/dl_mk_magic_sets.cpp index 6885edc4e..e69be05da 100644 --- a/src/muz_qe/dl_mk_magic_sets.cpp +++ b/src/muz_qe/dl_mk_magic_sets.cpp @@ -318,7 +318,6 @@ namespace datalog { } rule_set * mk_magic_sets::operator()(rule_set const & source) { - SASSERT(!m_context.get_model_converter()); unsigned init_rule_cnt = source.get_num_rules(); { func_decl_set intentional; diff --git a/src/muz_qe/dl_sparse_table.cpp b/src/muz_qe/dl_sparse_table.cpp index 1449b7d3d..dde09428f 100644 --- a/src/muz_qe/dl_sparse_table.cpp +++ b/src/muz_qe/dl_sparse_table.cpp @@ -500,6 +500,11 @@ namespace datalog { char * reserve = m_data.get_reserve_ptr(); unsigned col_cnt = m_column_layout.size(); for(unsigned i=0; i= get_signature()[i]) { + std::cout << "***************************\n"; + std::cout << f[i] << " " << get_signature()[i] << "\n"; + } //the value fits into the table signature + SASSERT(f[i]get_fact(tf); @@ -127,6 +128,7 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, void dl_query_test_wpa(smt_params & fparams, params_ref& params) { params.set_bool("magic_sets_for_queries", true); ast_manager m; + random_gen ran(0); reg_decl_plugins(m); arith_util arith(m); const char * problem_dir = "C:\\tvm\\src\\z3_2\\debug\\test\\w0.datalog"; @@ -151,8 +153,8 @@ void dl_query_test_wpa(smt_params & fparams, params_ref& params) { TRUSTME( ctx.try_get_sort_constant_count(var_sort, var_sz) ); for(unsigned attempt=0; attempt Date: Wed, 10 Apr 2013 11:54:35 -0700 Subject: [PATCH 03/91] add .gitattributes Signed-off-by: Nuno Lopes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..b86ed5df7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +src/api/dotnet/Properties/AssemblyInfo.cs text eol=crlf From 14172d3fae2960398b7ffc3563bb09baf12e0368 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 10 Apr 2013 14:49:07 -0700 Subject: [PATCH 04/91] fix crash in dl_interp_tail_simplifier when no transformation is performed Signed-off-by: Nuno Lopes --- src/muz_qe/dl_mk_interp_tail_simplifier.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp index 51bbd52df..d0b94f582 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp @@ -583,11 +583,12 @@ namespace datalog { } rule_set * res = alloc(rule_set, m_context); - if (!transform_rules(source, *res)) { + if (transform_rules(source, *res)) { + res->inherit_predicates(source); + } else { dealloc(res); res = 0; } - res->inherit_predicates(source); return res; } From 2685c605e54fe5461c3f6d1b593b16c20354eb13 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 10 Apr 2013 16:37:47 -0700 Subject: [PATCH 05/91] [datalog] fix a few bugs related with output predicates (by me & Nikolaj) Signed-off-by: Nuno Lopes --- src/muz_qe/dl_mk_rule_inliner.cpp | 14 +++++++------- src/muz_qe/dl_mk_rule_inliner.h | 4 ++-- src/muz_qe/dl_mk_similarity_compressor.cpp | 6 +++--- src/muz_qe/dl_mk_similarity_compressor.h | 2 +- src/muz_qe/dl_rule_set.cpp | 10 +++++----- src/muz_qe/rel_context.cpp | 2 ++ 6 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/muz_qe/dl_mk_rule_inliner.cpp b/src/muz_qe/dl_mk_rule_inliner.cpp index c6ffa1378..8428d9049 100644 --- a/src/muz_qe/dl_mk_rule_inliner.cpp +++ b/src/muz_qe/dl_mk_rule_inliner.cpp @@ -667,7 +667,7 @@ namespace datalog { return et->get_data().m_value; } - void mk_rule_inliner::add_rule(rule* r, unsigned i) { + void mk_rule_inliner::add_rule(rule_set const& source, rule* r, unsigned i) { svector& can_remove = m_head_visitor.can_remove(); svector& can_expand = m_head_visitor.can_expand(); app* head = r->get_head(); @@ -676,7 +676,7 @@ namespace datalog { m_head_index.insert(head); m_pinned.push_back(r); - if (m_context.get_rules().is_output_predicate(headd) || + if (source.is_output_predicate(headd) || m_preds_with_facts.contains(headd)) { can_remove.set(i, false); TRACE("dl", output_predicate(m_context, head, tout << "cannot remove: " << i << " "); tout << "\n";); @@ -692,7 +692,7 @@ namespace datalog { tl_sz == 1 && r->get_positive_tail_size() == 1 && !m_preds_with_facts.contains(r->get_decl(0)) - && !m_context.get_rules().is_output_predicate(r->get_decl(0)); + && !source.is_output_predicate(r->get_decl(0)); can_expand.set(i, can_exp); } @@ -709,7 +709,7 @@ namespace datalog { #define PRT(_x_) ((_x_)?"T":"F") - bool mk_rule_inliner::inline_linear(scoped_ptr& rules) { + bool mk_rule_inliner::inline_linear(rule_set const& source, scoped_ptr& rules) { scoped_ptr res = alloc(rule_set, m_context); bool done_something = false; unsigned sz = rules->get_num_rules(); @@ -731,7 +731,7 @@ namespace datalog { svector& can_expand = m_head_visitor.can_expand(); for (unsigned i = 0; i < sz; ++i) { - add_rule(acc[i].get(), i); + add_rule(source, acc[i].get(), i); } // initialize substitution. @@ -808,7 +808,7 @@ namespace datalog { TRACE("dl", r->display(m_context, tout); r2->display(m_context, tout); rl_res->display(m_context, tout); ); del_rule(r, i); - add_rule(rl_res.get(), i); + add_rule(source, rl_res.get(), i); r = rl_res; @@ -875,7 +875,7 @@ namespace datalog { TRACE("dl", res->display(tout << "after eager inlining\n");); } - if (m_context.get_params().inline_linear() && inline_linear(res)) { + if (m_context.get_params().inline_linear() && inline_linear(source, res)) { something_done = true; } diff --git a/src/muz_qe/dl_mk_rule_inliner.h b/src/muz_qe/dl_mk_rule_inliner.h index bec788ffe..476c6b4b0 100644 --- a/src/muz_qe/dl_mk_rule_inliner.h +++ b/src/muz_qe/dl_mk_rule_inliner.h @@ -172,9 +172,9 @@ namespace datalog { /** Inline predicates that are known to not be join-points. */ - bool inline_linear(scoped_ptr& rules); + bool inline_linear(rule_set const& source, scoped_ptr& rules); - void add_rule(rule* r, unsigned i); + void add_rule(rule_set const& rule_set, rule* r, unsigned i); void del_rule(rule* r, unsigned i); public: diff --git a/src/muz_qe/dl_mk_similarity_compressor.cpp b/src/muz_qe/dl_mk_similarity_compressor.cpp index 969f1444a..040cbfa78 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.cpp +++ b/src/muz_qe/dl_mk_similarity_compressor.cpp @@ -429,7 +429,7 @@ namespace datalog { m_modified = true; } - void mk_similarity_compressor::process_class(rule_vector::iterator first, + void mk_similarity_compressor::process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last) { SASSERT(first!=after_last); //remove duplicates @@ -487,7 +487,7 @@ namespace datalog { //TODO: compress also rules with pairs (or tuples) of equal constants #if 1 - if (const_cnt>0) { + if (const_cnt>0 && !source.is_output_predicate((*first)->get_decl())) { unsigned rule_cnt = static_cast(after_last-first); if (rule_cnt>m_threshold_count) { merge_class(first, after_last); @@ -521,7 +521,7 @@ namespace datalog { rule_vector::iterator prev = it; ++it; if (it==end || rough_compare(*prev, *it)!=0) { - process_class(cl_begin, it); + process_class(source, cl_begin, it); cl_begin = it; } } diff --git a/src/muz_qe/dl_mk_similarity_compressor.h b/src/muz_qe/dl_mk_similarity_compressor.h index 6e0ca9db5..49704b8cd 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.h +++ b/src/muz_qe/dl_mk_similarity_compressor.h @@ -63,7 +63,7 @@ namespace datalog { ast_ref_vector m_pinned; void merge_class(rule_vector::iterator first, rule_vector::iterator after_last); - void process_class(rule_vector::iterator first, rule_vector::iterator after_last); + void process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last); void reset(); public: diff --git a/src/muz_qe/dl_rule_set.cpp b/src/muz_qe/dl_rule_set.cpp index b0bcdec89..cb129ab37 100644 --- a/src/muz_qe/dl_rule_set.cpp +++ b/src/muz_qe/dl_rule_set.cpp @@ -332,15 +332,12 @@ namespace datalog { void rule_set::inherit_predicates(rule_set const& other) { m_refs.append(other.m_refs); - SASSERT(m_refs.size() < 1000); set_union(m_output_preds, other.m_output_preds); { obj_map::iterator it = other.m_orig2pred.begin(); obj_map::iterator end = other.m_orig2pred.end(); for (; it != end; ++it) { m_orig2pred.insert(it->m_key, it->m_value); - m_refs.push_back(it->m_key); - m_refs.push_back(it->m_value); } } { @@ -348,8 +345,6 @@ namespace datalog { obj_map::iterator end = other.m_pred2orig.end(); for (; it != end; ++it) { m_pred2orig.insert(it->m_key, it->m_value); - m_refs.push_back(it->m_key); - m_refs.push_back(it->m_value); } } } @@ -515,6 +510,11 @@ namespace datalog { void rule_set::display(std::ostream & out) const { out << "; rule count: " << get_num_rules() << "\n"; out << "; predicate count: " << m_head2rules.size() << "\n"; + func_decl_set::iterator pit = m_output_preds.begin(); + func_decl_set::iterator pend = m_output_preds.end(); + for (; pit != pend; ++pit) { + out << "; output: " << (*pit)->get_name() << '\n'; + } decl2rules::iterator it = m_head2rules.begin(); decl2rules::iterator end = m_head2rules.end(); for (; it != end; ++it) { diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index 327e63c0f..00181b18c 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -253,6 +253,8 @@ namespace datalog { m_context.transform_rules(alloc(mk_magic_sets, m_context, query_pred)); } + query_pred = m_context.get_rules().get_pred(query_pred); + lbool res = saturate(); if (res != l_undef) { From f6f59ad6bc675347a227b008459c4c89dfb0d073 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 10 Apr 2013 19:03:25 -0700 Subject: [PATCH 06/91] Fix memory allocation problems in RCF module Signed-off-by: Leonardo de Moura --- src/math/realclosure/realclosure.cpp | 21 ++++++++------------- src/test/rcf.cpp | 2 -- src/util/array.h | 2 +- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 87b1ef8a4..84ba606fd 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -660,8 +660,7 @@ namespace realclosure { return; // interval was already saved. to_restore.push_back(v); inc_ref(v); - void * mem = allocator().allocate(sizeof(mpbqi)); - v->m_old_interval = new (mem) mpbqi(); + v->m_old_interval = new (allocator()) mpbqi(); set_interval(*(v->m_old_interval), v->m_interval); } void save_interval(value * v) { @@ -1237,8 +1236,7 @@ namespace realclosure { } sign_condition * mk_sign_condition(unsigned qidx, int sign, sign_condition * prev_sc) { - void * mem = allocator().allocate(sizeof(sign_condition)); - return new (mem) sign_condition(qidx, sign, prev_sc); + return new (allocator()) sign_condition(qidx, sign, prev_sc); } /** @@ -1246,7 +1244,7 @@ namespace realclosure { This method does not set the interval. It remains (-oo, oo) */ rational_function_value * mk_rational_function_value_core(extension * ext, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { - rational_function_value * r = alloc(rational_function_value, ext); + rational_function_value * r = new (allocator()) rational_function_value(ext); inc_ref(ext); set_p(r->num(), num_sz, num); if (ext->is_algebraic()) { @@ -1283,7 +1281,7 @@ namespace realclosure { */ void mk_infinitesimal(symbol const & n, symbol const & pp_n, numeral & r) { unsigned idx = next_infinitesimal_idx(); - infinitesimal * eps = alloc(infinitesimal, idx, n, pp_n); + infinitesimal * eps = new (allocator()) infinitesimal(idx, n, pp_n); m_extensions[extension::INFINITESIMAL].push_back(eps); set_lower(eps->interval(), mpbq(0)); @@ -1335,7 +1333,7 @@ namespace realclosure { void mk_transcendental(symbol const & n, symbol const & pp_n, mk_interval & proc, numeral & r) { unsigned idx = next_transcendental_idx(); - transcendental * t = alloc(transcendental, idx, n, pp_n, proc); + transcendental * t = new (allocator()) transcendental(idx, n, pp_n, proc); m_extensions[extension::TRANSCENDENTAL].push_back(t); while (contains_zero(t->interval())) { @@ -1798,8 +1796,7 @@ namespace realclosure { M and scs will be empty after this operation. */ sign_det * mk_sign_det(mpz_matrix & M_s, scoped_polynomial_seq const & prs, int_buffer const & taqrs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) { - void * mem = allocator().allocate(sizeof(sign_det)); - sign_det * r = new (mem) sign_det(); + sign_det * r = new (allocator()) sign_det(); r->M_s.swap(M_s); set_array_p(r->m_prs, prs); r->m_taqrs.set(allocator(), taqrs.size(), taqrs.c_ptr()); @@ -1814,8 +1811,7 @@ namespace realclosure { */ algebraic * mk_algebraic(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, sign_det * sd, unsigned sc_idx) { unsigned idx = next_algebraic_idx(); - void * mem = allocator().allocate(sizeof(algebraic)); - algebraic * r = new (mem) algebraic(idx); + algebraic * r = new (allocator()) algebraic(idx); m_extensions[extension::ALGEBRAIC].push_back(r); set_p(r->m_p, p_sz, p); @@ -2561,8 +2557,7 @@ namespace realclosure { } rational_value * mk_rational() { - void * mem = allocator().allocate(sizeof(rational_value)); - return new (mem) rational_value(); + return new (allocator()) rational_value(); } /** diff --git a/src/test/rcf.cpp b/src/test/rcf.cpp index d6cfd8b86..294a7df29 100644 --- a/src/test/rcf.cpp +++ b/src/test/rcf.cpp @@ -137,7 +137,6 @@ static void tst_lin_indep(unsigned m, unsigned n, int _A[], unsigned ex_sz, unsi r.resize(A.n()); scoped_mpz_matrix B(mm); mm.linear_independent_rows(A, r.c_ptr(), B); - SASSERT(r.size() == ex_sz); for (unsigned i = 0; i < ex_sz; i++) { SASSERT(r[i] == ex_r[i]); } @@ -164,7 +163,6 @@ void tst_rcf() { enable_trace("rcf_clean"); enable_trace("rcf_clean_bug"); tst_denominators(); - return; tst1(); tst2(); { int A[] = {0, 1, 1, 1, 0, 1, 1, 1, -1}; int c[] = {10, 4, -4}; int b[] = {-2, 4, 6}; tst_solve(3, A, b, c, true); } diff --git a/src/util/array.h b/src/util/array.h index a3658c354..f983f7dec 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -122,7 +122,7 @@ public: if (m_data) { if (CallDestructors) destroy_elements(); - a.deallocate(size(), raw_ptr()); + a.deallocate(space(size()), raw_ptr()); m_data = 0; } } From dc77141dce86d7049b2e33f0be13f974fda4ca5b Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 10 Apr 2013 19:14:10 -0700 Subject: [PATCH 07/91] Fix warning messages Signed-off-by: Leonardo de Moura --- src/util/small_object_allocator.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/small_object_allocator.h b/src/util/small_object_allocator.h index 538a9e035..957c4f475 100644 --- a/src/util/small_object_allocator.h +++ b/src/util/small_object_allocator.h @@ -21,6 +21,7 @@ Revision History: #define _SMALL_OBJECT_ALLOCATOR_H_ #include"machine.h" +#include"debug.h" class small_object_allocator { static const unsigned CHUNK_SIZE = (8192 - sizeof(void*)*2); @@ -52,8 +53,8 @@ public: inline void * operator new(size_t s, small_object_allocator & r) { return r.allocate(s); } inline void * operator new[](size_t s, small_object_allocator & r) { return r.allocate(s); } -inline void operator delete(void * p, size_t s, small_object_allocator & r) { r.deallocate(s,p); } -inline void operator delete[](void * p, size_t s, small_object_allocator & r) { r.deallocate(s,p); } +inline void operator delete(void * p, small_object_allocator & r) { UNREACHABLE(); } +inline void operator delete[](void * p, small_object_allocator & r) { UNREACHABLE(); } #endif /* _SMALL_OBJECT_ALLOCATOR_H_ */ From cdb90968e31de612747d04e5345ccb056f525c97 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 10 Apr 2013 20:06:21 -0700 Subject: [PATCH 08/91] minor fixes to rel_context Signed-off-by: Nikolaj Bjorner --- src/muz_qe/rel_context.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index 327e63c0f..0e84ce4f6 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -40,7 +40,9 @@ namespace datalog { rule_set m_rules; decl_set m_preds; bool m_was_closed; + public: + scoped_query(context& ctx): m_ctx(ctx), m_rules(ctx.get_rules()), @@ -51,6 +53,7 @@ namespace datalog { ctx.reopen(); } } + ~scoped_query() { m_ctx.reopen(); m_ctx.restrict_predicates(m_preds); @@ -235,7 +238,6 @@ namespace datalog { query_pred = rm.mk_query(query, m_context.get_rules()); } catch (default_exception& exn) { - m_context.close(); m_context.set_status(INPUT_ERROR); throw exn; } From cb31a294c8b5fe4ba4ba1ca372b101def93b537b Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 11 Apr 2013 08:50:04 -0700 Subject: [PATCH 09/91] use unsigned_vector where appropriate Signed-off-by: Nuno Lopes --- src/muz_qe/dl_instruction.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz_qe/dl_instruction.cpp index 401a42e98..478af3127 100644 --- a/src/muz_qe/dl_instruction.cpp +++ b/src/muz_qe/dl_instruction.cpp @@ -452,7 +452,7 @@ namespace datalog { class instr_filter_identical : public instruction { - typedef vector column_vector; + typedef unsigned_vector column_vector; reg_idx m_reg; column_vector m_cols; public: @@ -651,7 +651,7 @@ namespace datalog { class instr_project_rename : public instruction { - typedef vector column_vector; + typedef unsigned_vector column_vector; bool m_projection; reg_idx m_src; column_vector m_cols; @@ -830,7 +830,7 @@ namespace datalog { class instr_filter_by_negation : public instruction { - typedef vector column_vector; + typedef unsigned_vector column_vector; reg_idx m_tgt; reg_idx m_neg_rel; column_vector m_cols1; From 1f5097cdaa40fab3e788412e3ed402fdfe372834 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 15 Apr 2013 16:53:25 -0700 Subject: [PATCH 10/91] [datalog] fix stratum cycle break for rules with multiple looping dependecies e.g. a -> b b-> a a -> a this change makes the cycle breaker quadratic on the number of predicates. This should be revisited later Signed-off-by: Nuno Lopes --- src/muz_qe/dl_compiler.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index 9110c5edb..215a438fc 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -803,17 +803,15 @@ namespace datalog { context& m_context; item_set & m_removed; svector m_stack; - ast_mark m_stack_content; ast_mark m_visited; void traverse(T v) { - SASSERT(!m_stack_content.is_marked(v)); - if(m_visited.is_marked(v) || m_removed.contains(v)) { + SASSERT(!m_visited.is_marked(v)); + if (m_removed.contains(v)) { return; } m_stack.push_back(v); - m_stack_content.mark(v, true); m_visited.mark(v, true); const item_set & deps = m_deps.get_deps(v); @@ -821,33 +819,34 @@ namespace datalog { item_set::iterator end = deps.end(); for(; it!=end; ++it) { T d = *it; - if(m_stack_content.is_marked(d)) { + if (m_visited.is_marked(d)) { //TODO: find the best vertex to remove in the cycle remove_from_stack(); - break; + continue; } traverse(d); } SASSERT(m_stack.back()==v); m_stack.pop_back(); - m_stack_content.mark(v, false); + m_visited.mark(v, false); } void remove_from_stack() { for (unsigned i = 0; i < m_stack.size(); ++i) { func_decl* p = m_stack[i]; - rule_vector const& rules = m_rules.get_predicate_rules(p); - unsigned stratum = m_rules.get_predicate_strat(p); if (m_context.has_facts(p)) { m_removed.insert(p); return; } + + rule_vector const& rules = m_rules.get_predicate_rules(p); + unsigned stratum = m_rules.get_predicate_strat(p); for (unsigned j = 0; j < rules.size(); ++j) { rule const& r = *rules[j]; bool ok = true; for (unsigned k = 0; ok && k < r.get_uninterpreted_tail_size(); ++k) { - ok &= m_rules.get_predicate_strat(r.get_decl(k)) < stratum; + ok = m_rules.get_predicate_strat(r.get_decl(k)) < stratum; } if (ok) { m_removed.insert(p); @@ -858,8 +857,8 @@ namespace datalog { // nothing was found. m_removed.insert(m_stack.back()); - } + public: cycle_breaker(rule_dependencies & deps, rule_set const& rules, context& ctx, item_set & removed) : m_deps(deps), m_rules(rules), m_context(ctx), m_removed(removed) { SASSERT(removed.empty()); } From 38823d6c79b121fca6e6392a2fb4e45ca74deea1 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 16 Apr 2013 08:16:02 -0700 Subject: [PATCH 11/91] [PDR] fix expansion of BV literals Signed-off-by: Nuno Lopes --- src/muz_qe/pdr_prop_solver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/muz_qe/pdr_prop_solver.cpp b/src/muz_qe/pdr_prop_solver.cpp index daa52992f..863e5c03e 100644 --- a/src/muz_qe/pdr_prop_solver.cpp +++ b/src/muz_qe/pdr_prop_solver.cpp @@ -145,7 +145,8 @@ namespace pdr { rational two(2); for (unsigned j = 0; j < bv_size; ++j) { parameter p(j); - expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c); + //expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c); + expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c)); if ((r % two).is_zero()) { e = m.mk_not(e); } From adc8224dbae9ceffd87ef1b179caa1dcb118f777 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 16 Apr 2013 09:02:40 -0700 Subject: [PATCH 12/91] use svector instead of vector where appropriate Signed-off-by: Nuno Lopes --- src/ast/ast.cpp | 2 +- src/util/bit_vector.cpp | 6 ++---- src/util/bit_vector.h | 2 +- src/util/mpff.h | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 8d643a348..fefb400ed 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -2755,7 +2755,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro app const * cls = to_app(f1); unsigned num_args = cls->get_num_args(); #ifdef Z3DEBUG - vector found; + svector found; #endif for (unsigned i = 0; i < num_args; i++) { expr * lit = cls->get_arg(i); diff --git a/src/util/bit_vector.cpp b/src/util/bit_vector.cpp index e3a2bc331..0d67f1f79 100644 --- a/src/util/bit_vector.cpp +++ b/src/util/bit_vector.cpp @@ -208,8 +208,8 @@ void bit_vector::display(std::ostream & out) const { void fr_bit_vector::reset() { unsigned sz = size(); - vector::const_iterator it = m_one_idxs.begin(); - vector::const_iterator end = m_one_idxs.end(); + unsigned_vector::const_iterator it = m_one_idxs.begin(); + unsigned_vector::const_iterator end = m_one_idxs.end(); for (; it != end; ++it) { unsigned idx = *it; if (idx < sz) @@ -217,5 +217,3 @@ void fr_bit_vector::reset() { } m_one_idxs.reset(); } - - diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index 1d6083717..2c641c5a6 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -204,7 +204,7 @@ inline std::ostream & operator<<(std::ostream & out, bit_vector const & b) { This class should be used if the reset is frequently called. */ class fr_bit_vector : private bit_vector { - svector m_one_idxs; + unsigned_vector m_one_idxs; public: void reset(); diff --git a/src/util/mpff.h b/src/util/mpff.h index de71ec75e..8d4c853af 100644 --- a/src/util/mpff.h +++ b/src/util/mpff.h @@ -107,7 +107,7 @@ class mpff_manager { unsigned m_precision; //!< Number of words in the significand. Must be an even number. unsigned m_precision_bits; //!< Number of bits in the significand. Must be 32*m_precision. - vector m_significands; //!< Array containing all significands. + unsigned_vector m_significands; //!< Array containing all significands. unsigned m_capacity; //!< Number of significands that can be stored in m_significands. bool m_to_plus_inf; //!< If True, then round to plus infinity, otherwise to minus infinity id_gen m_id_gen; From 51d3db8105facdbdaf18be56ed3afee09bc1eda9 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 16 Apr 2013 10:48:57 -0700 Subject: [PATCH 13/91] [dl] remove 2 uneeded fields from sparse_table::rename_fn Signed-off-by: Nuno Lopes --- src/muz_qe/dl_sparse_table.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/muz_qe/dl_sparse_table.cpp b/src/muz_qe/dl_sparse_table.cpp index 47f518ee2..52d9618b8 100644 --- a/src/muz_qe/dl_sparse_table.cpp +++ b/src/muz_qe/dl_sparse_table.cpp @@ -1022,19 +1022,16 @@ namespace datalog { class sparse_table_plugin::rename_fn : public convenient_table_rename_fn { - const unsigned m_cycle_len; - const unsigned m_col_cnt; unsigned_vector m_out_of_cycle; public: rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) - : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle), - m_cycle_len(permutation_cycle_len), m_col_cnt(orig_sig.size()) { + : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { SASSERT(permutation_cycle_len>=2); idx_set cycle_cols; - for (unsigned i=0; i Date: Tue, 16 Apr 2013 13:54:41 -0700 Subject: [PATCH 14/91] cleanup front end parameters to datalog engine Signed-off-by: Nikolaj Bjorner --- src/shell/datalog_frontend.cpp | 19 +------------------ src/shell/datalog_frontend.h | 5 ----- src/shell/main.cpp | 2 +- 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index 6cdfe1623..efcab14ea 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -43,17 +43,7 @@ static datalog::context * g_ctx = 0; static datalog::rule_set * g_orig_rules; static datalog::instruction_block * g_code; static datalog::execution_context * g_ectx; -static smt_params * g_params; -datalog_params::datalog_params(): - m_default_table("sparse"), - m_default_table_checked(false) -{} - -// void datalog_params::register_params(ini_params& p) { -// p.register_symbol_param("DEFAULT_TABLE", m_default_table, "Datalog engine: default table (sparse)"); -// p.register_bool_param("DEFAULT_TABLE_CHECKED", m_default_table_checked, "Wrap default table with a sanity checker"); -// } static void display_statistics( std::ostream& out, @@ -61,7 +51,6 @@ static void display_statistics( datalog::rule_set& orig_rules, datalog::instruction_block& code, datalog::execution_context& ex_ctx, - smt_params& params, bool verbose ) { @@ -109,7 +98,7 @@ static void display_statistics( static void display_statistics() { if (g_ctx) { - display_statistics(std::cout, *g_ctx, *g_orig_rules, *g_code, *g_ectx, *g_params, true); + display_statistics(std::cout, *g_ctx, *g_orig_rules, *g_code, *g_ectx, true); } } @@ -127,7 +116,6 @@ static void on_ctrl_c(int) { unsigned read_datalog(char const * file) { IF_VERBOSE(1, verbose_stream() << "Z3 Datalog Engine\n";); - datalog_params dl_params; smt_params s_params; ast_manager m; g_overall_time.start(); @@ -135,8 +123,6 @@ unsigned read_datalog(char const * file) { signal(SIGINT, on_ctrl_c); params_ref params; params.set_sym("engine", symbol("datalog")); - params.set_sym("default_table", dl_params.m_default_table); - params.set_bool("default_table_checked", dl_params.m_default_table_checked); datalog::context ctx(m, s_params, params); datalog::relation_manager & rmgr = ctx.get_rel_context().get_rmanager(); @@ -186,7 +172,6 @@ unsigned read_datalog(char const * file) { g_orig_rules = &original_rules; g_code = &rules_code; g_ectx = &ex_ctx; - g_params = &s_params; try { g_piece_timer.reset(); @@ -254,7 +239,6 @@ unsigned read_datalog(char const * file) { original_rules, rules_code, ex_ctx, - s_params, false); } @@ -266,7 +250,6 @@ unsigned read_datalog(char const * file) { original_rules, rules_code, ex_ctx, - s_params, true); return ERR_MEMOUT; } diff --git a/src/shell/datalog_frontend.h b/src/shell/datalog_frontend.h index e53e35c89..8022d5046 100644 --- a/src/shell/datalog_frontend.h +++ b/src/shell/datalog_frontend.h @@ -19,11 +19,6 @@ Revision History: #ifndef _DATALOG_FRONTEND_H_ #define _DATALOG_FRONTEND_H_ -struct datalog_params { - symbol m_default_table; - bool m_default_table_checked; - datalog_params(); -}; unsigned read_datalog(char const * file); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 63e604719..e0fa4a1f2 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -27,8 +27,8 @@ Revision History: #include"z3_log_frontend.h" #include"warning.h" #include"version.h" -#include"datalog_frontend.h" #include"dimacs_frontend.h" +#include"datalog_frontend.h" #include"timeout.h" #include"z3_exception.h" #include"error_codes.h" From 0b0e5b69121b463a42781a22b43854ff48b9673b Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 16 Apr 2013 15:14:16 -0700 Subject: [PATCH 15/91] add some constness Signed-off-by: Nuno Lopes --- src/ast/dl_decl_plugin.cpp | 4 ++-- src/ast/dl_decl_plugin.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index 1524760e5..d8e2385ec 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -660,9 +660,9 @@ namespace datalog { return 0; } - bool dl_decl_util::is_numeral(expr* e, uint64& v) const { + bool dl_decl_util::is_numeral(const expr* e, uint64& v) const { if (is_numeral(e)) { - app* c = to_app(e); + const app* c = to_app(e); SASSERT(c->get_decl()->get_num_parameters() == 2); parameter const& p = c->get_decl()->get_parameter(0); SASSERT(p.is_rational()); diff --git a/src/ast/dl_decl_plugin.h b/src/ast/dl_decl_plugin.h index 559aff7bd..a662de578 100644 --- a/src/ast/dl_decl_plugin.h +++ b/src/ast/dl_decl_plugin.h @@ -169,11 +169,11 @@ namespace datalog { app* mk_le(expr* a, expr* b); - bool is_lt(expr* a) { return is_app_of(a, m_fid, OP_DL_LT); } + bool is_lt(const expr* a) const { return is_app_of(a, m_fid, OP_DL_LT); } - bool is_numeral(expr* c) const { return is_app_of(c, m_fid, OP_DL_CONSTANT); } + bool is_numeral(const expr* c) const { return is_app_of(c, m_fid, OP_DL_CONSTANT); } - bool is_numeral(expr* e, uint64& v) const; + bool is_numeral(const expr* e, uint64& v) const; // // Utilities for extracting constants From 2afcc493e074afd23d9bf6b4be19ae0c3811a2d0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 18 Apr 2013 10:18:26 -0700 Subject: [PATCH 16/91] remove reference count debugging, add substitution to C++ header Signed-off-by: Nikolaj Bjorner --- src/api/api_util.h | 17 ++++++++--------- src/api/c++/z3++.h | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/api/api_util.h b/src/api/api_util.h index 58abf97bf..b49ef8315 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -29,7 +29,6 @@ Revision History: #define Z3_CATCH_RETURN_NO_HANDLE(VAL) } catch (z3_exception &) { return VAL; } #define CHECK_REF_COUNT(a) (reinterpret_cast(a)->get_ref_count() > 0) -#define VALIDATE(a) SASSERT(!a || CHECK_REF_COUNT(a)) namespace api { // Generic wrapper for ref-count objects exposed by the API @@ -44,30 +43,30 @@ namespace api { }; }; -inline ast * to_ast(Z3_ast a) { VALIDATE(a); return reinterpret_cast(a); } +inline ast * to_ast(Z3_ast a) { return reinterpret_cast(a); } inline Z3_ast of_ast(ast* a) { return reinterpret_cast(a); } -inline expr * to_expr(Z3_ast a) { VALIDATE(a); return reinterpret_cast(a); } +inline expr * to_expr(Z3_ast a) { return reinterpret_cast(a); } inline Z3_ast of_expr(expr* e) { return reinterpret_cast(e); } inline expr * const * to_exprs(Z3_ast const* a) { return reinterpret_cast(a); } inline Z3_ast * const * of_exprs(expr* const* e) { return reinterpret_cast(e); } -inline app * to_app(Z3_app a) { VALIDATE(a); return reinterpret_cast(a); } -inline app * to_app(Z3_ast a) { VALIDATE(a); return reinterpret_cast(a); } +inline app * to_app(Z3_app a) { return reinterpret_cast(a); } +inline app * to_app(Z3_ast a) { return reinterpret_cast(a); } inline Z3_app of_app(app* a) { return reinterpret_cast(a); } -inline app * const* to_apps(Z3_ast const* a) { VALIDATE(a); return reinterpret_cast(a); } +inline app * const* to_apps(Z3_ast const* a) { return reinterpret_cast(a); } inline ast * const * to_asts(Z3_ast const* a) { return reinterpret_cast(a); } -inline sort * to_sort(Z3_sort a) { VALIDATE(a); return reinterpret_cast(a); } +inline sort * to_sort(Z3_sort a) { return reinterpret_cast(a); } inline Z3_sort of_sort(sort* s) { return reinterpret_cast(s); } inline sort * const * to_sorts(Z3_sort const* a) { return reinterpret_cast(a); } inline Z3_sort const * of_sorts(sort* const* s) { return reinterpret_cast(s); } -inline func_decl * to_func_decl(Z3_func_decl a) { VALIDATE(a); return reinterpret_cast(a); } +inline func_decl * to_func_decl(Z3_func_decl a) { return reinterpret_cast(a); } inline Z3_func_decl of_func_decl(func_decl* f) { return reinterpret_cast(f); } inline func_decl * const * to_func_decls(Z3_func_decl const* f) { return reinterpret_cast(f); } @@ -75,7 +74,7 @@ inline func_decl * const * to_func_decls(Z3_func_decl const* f) { return reinter inline symbol to_symbol(Z3_symbol s) { return symbol::mk_symbol_from_c_ptr(reinterpret_cast(s)); } inline Z3_symbol of_symbol(symbol s) { return reinterpret_cast(const_cast(s.c_ptr())); } -inline Z3_pattern of_pattern(ast* a) { VALIDATE(a); return reinterpret_cast(a); } +inline Z3_pattern of_pattern(ast* a) { return reinterpret_cast(a); } inline app* to_pattern(Z3_pattern p) { return reinterpret_cast(p); } inline Z3_lbool of_lbool(lbool b) { return static_cast(b); } diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 43975fd04..a044149e3 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -872,7 +872,18 @@ namespace z3 { \brief Return a simplified version of this expression. The parameter \c p is a set of parameters for the Z3 simplifier. */ expr simplify(params const & p) const { Z3_ast r = Z3_simplify_ex(ctx(), m_ast, p); check_error(); return expr(ctx(), r); } - }; + + /** + \brief Apply substitution. Replace src expressions by dst. + */ + expr substitute(expr_vector const& src, expr_vector const& dst); + + /** + \brief Apply substitution. Replace bound variables by expressions. + */ + expr substitute(expr_vector const& dst); + + }; /** \brief Wraps a Z3_ast as an expr object. It also checks for errors. @@ -1680,6 +1691,30 @@ namespace z3 { d.check_error(); return expr(d.ctx(), r); } + + inline expr expr::substitute(expr_vector const& src, expr_vector const& dst) { + assert(src.size() == dst.size()); + array _src(src.size()); + array _dst(dst.size()); + for (unsigned i = 0; i < src.size(); ++i) { + _src[i] = src[i]; + _dst[i] = dst[i]; + } + Z3_ast r = Z3_substitute(ctx(), m_ast, src.size(), _src.ptr(), _dst.ptr()); + check_error(); + return expr(ctx(), r); + } + + inline expr expr::substitute(expr_vector const& dst) { + array _dst(dst.size()); + for (unsigned i = 0; i < dst.size(); ++i) { + _dst[i] = dst[i]; + } + Z3_ast r = Z3_substitute_vars(ctx(), m_ast, dst.size(), _dst.ptr()); + check_error(); + return expr(ctx(), r); + } + }; From 63ece8278dac7c883984ad11109e18546a6dba47 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 18 Apr 2013 11:23:21 -0700 Subject: [PATCH 17/91] [datalog] improve compilation to reuse total tables, and to reduce cloning/deallocs. this gives up to 40% in memory reduction and 10% speedup in test cases with many rules Signed-off-by: Nuno Lopes --- src/muz_qe/dl_compiler.cpp | 102 +++++++++++++++++++++++++++---------- src/muz_qe/dl_compiler.h | 10 ++-- 2 files changed, 82 insertions(+), 30 deletions(-) diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index 215a438fc..80b9dee5a 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -146,7 +146,7 @@ namespace datalog { } void compiler::make_add_constant_column(func_decl* head_pred, reg_idx src, const relation_sort & s, const relation_element & val, - reg_idx & result, instruction_block & acc) { + reg_idx & result, bool & dealloc, instruction_block & acc) { reg_idx singleton_table; if(!m_constant_registers.find(s, val, singleton_table)) { singleton_table = get_single_column_register(s); @@ -155,16 +155,18 @@ namespace datalog { m_constant_registers.insert(s, val, singleton_table); } if(src==execution_context::void_register) { - make_clone(singleton_table, result, acc); + result = singleton_table; + dealloc = false; } else { variable_intersection empty_vars(m_context.get_manager()); make_join(src, singleton_table, empty_vars, result, acc); + dealloc = true; } } void compiler::make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort & s, reg_idx & result, - instruction_block & acc) { + bool & dealloc, instruction_block & acc) { TRACE("dl", tout << "Adding unbound column " << mk_pp(pred, m_context.get_manager()) << "\n";); IF_VERBOSE(3, { @@ -173,25 +175,35 @@ namespace datalog { verbose_stream() << "Compiling unsafe rule column " << col_idx << "\n" << mk_ismt2_pp(e, m_context.get_manager()) << "\n"; }); - reg_idx total_table = get_single_column_register(s); - relation_signature sig; - sig.push_back(s); - acc.push_back(instruction::mk_total(sig, pred, total_table)); + reg_idx total_table; + if (!m_total_registers.find(s, pred, total_table)) { + total_table = get_single_column_register(s); + relation_signature sig; + sig.push_back(s); + m_top_level_code.push_back(instruction::mk_total(sig, pred, total_table)); + m_total_registers.insert(s, pred, total_table); + } if(src == execution_context::void_register) { result = total_table; + dealloc = false; } else { variable_intersection empty_vars(m_context.get_manager()); make_join(src, total_table, empty_vars, result, acc); - make_dealloc_non_void(total_table, acc); + dealloc = true; } } void compiler::make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc) { + SASSERT(sig.empty()); TRACE("dl", tout << "Adding unbound column " << mk_pp(pred, m_context.get_manager()) << "\n";); + if (m_empty_tables_registers.find(pred, result)) + return; + result = get_fresh_register(sig); - acc.push_back(instruction::mk_total(sig, pred, result)); + m_top_level_code.push_back(instruction::mk_total(sig, pred, result)); + m_empty_tables_registers.insert(pred, result); } @@ -233,6 +245,7 @@ namespace datalog { reg_idx src, const svector & acis0, reg_idx & result, + bool & dealloc, instruction_block & acc) { TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); @@ -277,7 +290,9 @@ namespace datalog { if(!src_cols_to_remove.empty()) { reg_idx new_curr; make_projection(curr, src_cols_to_remove.size(), src_cols_to_remove.c_ptr(), new_curr, acc); - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = true; curr=new_curr; curr_sig = & m_reg_signatures[curr]; @@ -298,16 +313,19 @@ namespace datalog { unsigned bound_column_index; if(acis[i].kind!=ACK_UNBOUND_VAR || !handled_unbound.find(acis[i].var_index,bound_column_index)) { reg_idx new_curr; + bool new_dealloc; bound_column_index=curr_sig->size(); if(acis[i].kind==ACK_CONSTANT) { - make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, new_curr, acc); + make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, new_curr, new_dealloc, acc); } else { SASSERT(acis[i].kind==ACK_UNBOUND_VAR); - make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, new_curr, acc); + make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, new_curr, new_dealloc, acc); handled_unbound.insert(acis[i].var_index,bound_column_index); } - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = new_dealloc; curr=new_curr; curr_sig = & m_reg_signatures[curr]; SASSERT(bound_column_index==curr_sig->size()-1); @@ -328,7 +346,9 @@ namespace datalog { } reg_idx new_curr; make_duplicate_column(curr, col, new_curr, acc); - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = true; curr=new_curr; curr_sig = & m_reg_signatures[curr]; unsigned bound_column_index=curr_sig->size()-1; @@ -356,7 +376,9 @@ namespace datalog { reg_idx new_curr; make_rename(curr, permutation.size(), permutation.c_ptr(), new_curr, acc); - make_dealloc_non_void(curr, acc); + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = true; curr=new_curr; curr_sig = & m_reg_signatures[curr]; } @@ -365,6 +387,7 @@ namespace datalog { SASSERT(src==execution_context::void_register); SASSERT(acis0.size()==0); make_full_relation(head_pred, empty_signature, curr, acc); + dealloc = false; } result=curr; @@ -416,6 +439,9 @@ namespace datalog { //by the join operation unsigned second_tail_arg_ofs; + // whether to dealloc the previous result + bool dealloc = true; + if(pt_len == 2) { reg_idx t1_reg=tail_regs[0]; reg_idx t2_reg=tail_regs[1]; @@ -488,8 +514,7 @@ namespace datalog { } } if(single_res==t_reg) { - //we may be modifying the register later, so we need a local copy - make_clone(t_reg, single_res, acc); + dealloc = false; } } @@ -500,7 +525,7 @@ namespace datalog { single_res=execution_context::void_register; } - add_unbound_columns_for_negation(r, head_pred, single_res, single_res_expr, acc); + add_unbound_columns_for_negation(r, head_pred, single_res, single_res_expr, dealloc, acc); int2ints var_indexes; @@ -515,7 +540,10 @@ namespace datalog { if(is_app(exp)) { SASSERT(m_context.get_decl_util().is_numeral_ext(exp)); relation_element value = to_app(exp); + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_equal(m_context.get_manager(), filtered_res, value, i)); + dealloc = true; } else { SASSERT(is_var(exp)); @@ -542,7 +570,10 @@ namespace datalog { //condition!) continue; } + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_identical(filtered_res, indexes.size(), indexes.c_ptr())); + dealloc = true; } //enforce negative predicates @@ -565,9 +596,12 @@ namespace datalog { relation_sort arg_sort; m_context.get_rel_context().get_rmanager().from_predicate(neg_pred, i, arg_sort); reg_idx new_reg; - make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), new_reg, acc); + bool new_dealloc; + make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), new_reg, new_dealloc, acc); - make_dealloc_non_void(filtered_res, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; filtered_res = new_reg; // here filtered_res value gets changed !! t_cols.push_back(single_res_expr.size()); @@ -577,8 +611,11 @@ namespace datalog { SASSERT(t_cols.size()==neg_cols.size()); reg_idx neg_reg = m_pred_regs.find(neg_pred); + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_by_negation(filtered_res, neg_reg, t_cols.size(), t_cols.c_ptr(), neg_cols.c_ptr())); + dealloc = true; } //enforce interpreted tail predicates @@ -639,9 +676,12 @@ namespace datalog { reg_idx new_reg; TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); - make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, acc); + bool new_dealloc; + make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); - make_dealloc_non_void(filtered_res, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; filtered_res = new_reg; // here filtered_res value gets changed !! unsigned unbound_column_index = single_res_expr.size(); @@ -659,7 +699,10 @@ namespace datalog { expr_ref renamed(m); m_context.get_var_subst()(t, binding.size(), binding.c_ptr(), renamed); app_ref app_renamed(to_app(renamed), m); + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); + dealloc = true; } { @@ -704,11 +747,12 @@ namespace datalog { SASSERT(head_acis.size()==head_len); reg_idx new_head_reg; - make_assembling_code(r, head_pred, filtered_res, head_acis, new_head_reg, acc); + make_assembling_code(r, head_pred, filtered_res, head_acis, new_head_reg, dealloc, acc); //update the head relation make_union(new_head_reg, head_reg, delta_reg, use_widening, acc); - make_dealloc_non_void(new_head_reg, acc); + if (dealloc) + make_dealloc_non_void(new_head_reg, acc); } finish: @@ -716,7 +760,7 @@ namespace datalog { } void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, ptr_vector& single_res_expr, - instruction_block & acc) { + bool & dealloc, instruction_block & acc) { uint_set pos_vars; u_map neg_vars; ast_manager& m = m_context.get_manager(); @@ -750,7 +794,13 @@ namespace datalog { expr* e = it->m_value; if (!pos_vars.contains(v)) { single_res_expr.push_back(e); - make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), single_res, acc); + reg_idx new_single_res; + bool new_dealloc; + make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), new_single_res, new_dealloc, acc); + if (dealloc) + make_dealloc_non_void(single_res, acc); + dealloc = new_dealloc; + single_res = new_single_res; TRACE("dl", tout << "Adding unbound column: " << mk_pp(e, m) << "\n";); } } diff --git a/src/muz_qe/dl_compiler.h b/src/muz_qe/dl_compiler.h index 92fe1d79b..1dfb7c7d8 100644 --- a/src/muz_qe/dl_compiler.h +++ b/src/muz_qe/dl_compiler.h @@ -114,6 +114,8 @@ namespace datalog { reg_idx m_new_reg; vector m_reg_signatures; obj_pair_map m_constant_registers; + obj_pair_map m_total_registers; + obj_map m_empty_tables_registers; instruction_observer m_instruction_observer; /** @@ -163,20 +165,20 @@ namespace datalog { with empty signature. */ void make_assembling_code(rule* compiled_rule, func_decl* head_pred, reg_idx src, const svector & acis0, - reg_idx & result, instruction_block & acc); + reg_idx & result, bool & dealloc, instruction_block & acc); void make_dealloc_non_void(reg_idx r, instruction_block & acc); void make_add_constant_column(func_decl* pred, reg_idx src, const relation_sort & s, const relation_element & val, - reg_idx & result, instruction_block & acc); + reg_idx & result, bool & dealloc, instruction_block & acc); void make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort & s, reg_idx & result, - instruction_block & acc); + bool & dealloc, instruction_block & acc); void make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc); void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, ptr_vector& single_res_expr, - instruction_block& acc); + bool & dealloc, instruction_block& acc); void make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, instruction_block & acc); From 7ce88d4da9d985380bb3cb033a9e3113e8779703 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 21 Apr 2013 14:36:39 -0700 Subject: [PATCH 18/91] fix a few compilation warnings - remove unused variables and class fields - add support for gcc 4.5 & clang's __builtin_unreachable - fix 2 bugs related to strict aliasing - remove a few unused function parameters Signed-off-by: Nuno Lopes --- src/ast/ast.cpp | 2 +- src/ast/ast.h | 1 - src/ast/dl_decl_plugin.cpp | 1 + src/ast/float_decl_plugin.cpp | 1 + src/ast/func_decl_dependencies.cpp | 5 ++--- src/ast/macros/quasi_macros.cpp | 3 +-- src/ast/macros/quasi_macros.h | 3 +-- src/ast/recurse_expr.h | 2 +- src/ast/substitution/matcher.cpp | 4 ---- src/ast/substitution/matcher.h | 3 +-- src/ast/substitution/substitution.cpp | 2 +- src/math/subpaving/subpaving.cpp | 4 ---- src/muz_qe/dl_context.cpp | 1 - src/muz_qe/dl_interval_relation.cpp | 16 ++++++---------- src/muz_qe/dl_mk_bit_blast.cpp | 1 - src/muz_qe/dl_mk_explanations.cpp | 3 +-- src/muz_qe/dl_mk_karr_invariants.cpp | 7 ++----- src/muz_qe/dl_mk_subsumption_checker.cpp | 4 +--- src/muz_qe/dl_relation_manager.cpp | 4 +--- src/muz_qe/dl_relation_manager.h | 8 +++----- src/muz_qe/dl_util.h | 4 +--- src/muz_qe/nlarith_util.cpp | 1 - src/muz_qe/pdr_context.cpp | 2 +- src/muz_qe/pdr_farkas_learner.cpp | 5 ++--- src/muz_qe/qe.cpp | 5 +---- src/muz_qe/qe_dl_plugin.cpp | 2 -- src/muz_qe/tab_context.cpp | 4 ---- src/parsers/smt/smtparser.cpp | 8 +++----- src/parsers/smt2/smt2scanner.cpp | 1 - src/parsers/smt2/smt2scanner.h | 1 - src/smt/asserted_formulas.cpp | 2 +- src/smt/database.h | 2 +- src/smt/expr_context_simplifier.cpp | 2 +- src/smt/expr_context_simplifier.h | 2 -- src/smt/mam.cpp | 2 -- src/smt/theory_dl.cpp | 9 +++------ src/tactic/ufbv/quasi_macros_tactic.cpp | 2 +- src/tactic/ufbv/ufbv_rewriter.cpp | 14 ++++++-------- src/test/matcher.cpp | 2 +- src/util/bit_util.cpp | 14 +++++++++++++- src/util/debug.cpp | 4 ++-- src/util/debug.h | 11 +++++++++++ src/util/gparams.cpp | 4 ++-- src/util/hwf.h | 15 ++++++++++----- src/util/memory_manager.cpp | 16 ++++++++-------- src/util/warning.cpp | 10 +++++----- 46 files changed, 97 insertions(+), 122 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index fefb400ed..0555e5fc8 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -300,7 +300,7 @@ std::ostream & operator<<(std::ostream & out, func_decl_info const & info) { // // ----------------------------------- -char const * g_ast_kind_names[] = {"application", "variable", "quantifier", "sort", "function declaration" }; +static char const * g_ast_kind_names[] = {"application", "variable", "quantifier", "sort", "function declaration" }; char const * get_ast_kind_name(ast_kind k) { return g_ast_kind_names[k]; diff --git a/src/ast/ast.h b/src/ast/ast.h index 4c924691c..ef4ab3b55 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1193,7 +1193,6 @@ enum pattern_op_kind { heurisitic quantifier instantiation. */ class pattern_decl_plugin : public decl_plugin { - sort * m_list; public: virtual decl_plugin * mk_fresh() { return alloc(pattern_decl_plugin); } diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index d8e2385ec..4f0c9a75d 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -326,6 +326,7 @@ namespace datalog { } unsigned index0; sort* last_sort = 0; + SASSERT(num_params > 0); for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (!p.is_int()) { diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index 26131bc28..2a090fc39 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -200,6 +200,7 @@ func_decl * float_decl_plugin::mk_float_const_decl(decl_kind k, unsigned num_par } else { m_manager->raise_exception("sort of floating point constant was not specified"); + UNREACHABLE(); } SASSERT(is_sort_of(s, m_family_id, FLOAT_SORT)); diff --git a/src/ast/func_decl_dependencies.cpp b/src/ast/func_decl_dependencies.cpp index 5e054d5d9..162efb0dd 100644 --- a/src/ast/func_decl_dependencies.cpp +++ b/src/ast/func_decl_dependencies.cpp @@ -79,7 +79,6 @@ void func_decl_dependencies::collect_ng_func_decls(expr * n, func_decl_set * s) */ class func_decl_dependencies::top_sort { enum color { OPEN, IN_PROGRESS, CLOSED }; - ast_manager & m_manager; dependency_graph & m_deps; typedef obj_map color_map; @@ -177,7 +176,7 @@ class func_decl_dependencies::top_sort { } public: - top_sort(ast_manager & m, dependency_graph & deps):m_manager(m), m_deps(deps) {} + top_sort(dependency_graph & deps) : m_deps(deps) {} bool operator()(func_decl * new_decl) { @@ -198,7 +197,7 @@ bool func_decl_dependencies::insert(func_decl * f, func_decl_set * s) { m_deps.insert(f, s); - top_sort cycle_detector(m_manager, m_deps); + top_sort cycle_detector(m_deps); if (cycle_detector(f)) { m_deps.erase(f); dealloc(s); diff --git a/src/ast/macros/quasi_macros.cpp b/src/ast/macros/quasi_macros.cpp index 1b0f0b621..084a33ebf 100644 --- a/src/ast/macros/quasi_macros.cpp +++ b/src/ast/macros/quasi_macros.cpp @@ -22,10 +22,9 @@ Revision History: #include"uint_set.h" #include"var_subst.h" -quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm, basic_simplifier_plugin & p, simplifier & s) : +quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm, simplifier & s) : m_manager(m), m_macro_manager(mm), - m_bsimp(p), m_simplifier(s), m_new_vars(m), m_new_eqs(m), diff --git a/src/ast/macros/quasi_macros.h b/src/ast/macros/quasi_macros.h index 1731774b2..c5e6b6d4f 100644 --- a/src/ast/macros/quasi_macros.h +++ b/src/ast/macros/quasi_macros.h @@ -32,7 +32,6 @@ class quasi_macros { ast_manager & m_manager; macro_manager & m_macro_manager; - basic_simplifier_plugin & m_bsimp; simplifier & m_simplifier; occurrences_map m_occurrences; ptr_vector m_todo; @@ -57,7 +56,7 @@ class quasi_macros { void apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); public: - quasi_macros(ast_manager & m, macro_manager & mm, basic_simplifier_plugin & p, simplifier & s); + quasi_macros(ast_manager & m, macro_manager & mm, simplifier & s); ~quasi_macros(); /** diff --git a/src/ast/recurse_expr.h b/src/ast/recurse_expr.h index 9cb71872b..6b3220d40 100644 --- a/src/ast/recurse_expr.h +++ b/src/ast/recurse_expr.h @@ -30,7 +30,7 @@ class recurse_expr : public Visitor { vector m_results2; bool is_cached(expr * n) const { T c; return m_cache.find(n, c); } - T get_cached(expr * n) const { T c; m_cache.find(n, c); return c; } + T get_cached(expr * n) const { return m_cache.find(n); } void cache_result(expr * n, T c) { m_cache.insert(n, c); } void visit(expr * n, bool & visited); diff --git a/src/ast/substitution/matcher.cpp b/src/ast/substitution/matcher.cpp index a5560c6a2..ce9bdcb3e 100644 --- a/src/ast/substitution/matcher.cpp +++ b/src/ast/substitution/matcher.cpp @@ -18,10 +18,6 @@ Revision History: --*/ #include"matcher.h" -matcher::matcher(ast_manager & m): - m_manager(m) { -} - bool matcher::operator()(expr * e1, expr * e2, substitution & s) { reset(); m_subst = &s; diff --git a/src/ast/substitution/matcher.h b/src/ast/substitution/matcher.h index 1a71a51ed..84a62e874 100644 --- a/src/ast/substitution/matcher.h +++ b/src/ast/substitution/matcher.h @@ -30,7 +30,6 @@ class matcher { typedef pair_hash, obj_ptr_hash > expr_pair_hash; typedef hashtable > cache; - ast_manager & m_manager; substitution * m_subst; // cache m_cache; svector m_todo; @@ -38,7 +37,7 @@ class matcher { void reset(); public: - matcher(ast_manager & m); + matcher() {} /** \brief Return true if e2 is an instance of e1. diff --git a/src/ast/substitution/substitution.cpp b/src/ast/substitution/substitution.cpp index 11bca0133..be293c5a8 100644 --- a/src/ast/substitution/substitution.cpp +++ b/src/ast/substitution/substitution.cpp @@ -148,7 +148,7 @@ void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, e expr * arg = to_app(e)->get_arg(i); expr * new_arg; - m_apply_cache.find(expr_offset(arg, off), new_arg); + VERIFY(m_apply_cache.find(expr_offset(arg, off), new_arg)); new_args.push_back(new_arg); if (arg != new_arg) has_new_args = true; diff --git a/src/math/subpaving/subpaving.cpp b/src/math/subpaving/subpaving.cpp index 0bbabc683..ad0819ad8 100644 --- a/src/math/subpaving/subpaving.cpp +++ b/src/math/subpaving/subpaving.cpp @@ -85,7 +85,6 @@ namespace subpaving { }; class context_mpf_wrapper : public context_wrapper { - f2n & m_fm; unsynch_mpq_manager & m_qm; scoped_mpf m_c; scoped_mpf_vector m_as; @@ -103,7 +102,6 @@ namespace subpaving { public: context_mpf_wrapper(f2n & fm, params_ref const & p, small_object_allocator * a): context_wrapper(fm, p, a), - m_fm(fm), m_qm(fm.m().mpq_manager()), m_c(fm.m()), m_as(fm.m()), @@ -145,7 +143,6 @@ namespace subpaving { }; class context_hwf_wrapper : public context_wrapper { - f2n & m_fm; unsynch_mpq_manager & m_qm; hwf m_c; svector m_as; @@ -166,7 +163,6 @@ namespace subpaving { public: context_hwf_wrapper(f2n & fm, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): context_wrapper(fm, p, a), - m_fm(fm), m_qm(qm) { } diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 73ddd22e5..df293aeba 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -647,7 +647,6 @@ namespace datalog { } } ast_manager& m = get_manager(); - datalog::rule_manager& rm = get_rule_manager(); contains_pred contains_p(*this); check_pred check_pred(contains_p, get_manager()); diff --git a/src/muz_qe/dl_interval_relation.cpp b/src/muz_qe/dl_interval_relation.cpp index 3397f2db0..4c8171bc7 100644 --- a/src/muz_qe/dl_interval_relation.cpp +++ b/src/muz_qe/dl_interval_relation.cpp @@ -99,11 +99,9 @@ namespace datalog { } class interval_relation_plugin::rename_fn : public convenient_relation_rename_fn { - interval_relation_plugin& m_plugin; public: - rename_fn(interval_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) - : convenient_relation_rename_fn(orig_sig, cycle_len, cycle), - m_plugin(p){ + rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { } virtual relation_base * operator()(const relation_base & _r) { @@ -120,7 +118,7 @@ namespace datalog { if(!check_kind(r)) { return 0; } - return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle); + return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); } interval interval_relation_plugin::unite(interval const& src1, interval const& src2) { @@ -194,11 +192,9 @@ namespace datalog { } class interval_relation_plugin::union_fn : public relation_union_fn { - interval_relation_plugin& m_plugin; bool m_is_widen; public: - union_fn(interval_relation_plugin& p, bool is_widen) : - m_plugin(p), + union_fn(bool is_widen) : m_is_widen(is_widen) { } @@ -223,7 +219,7 @@ namespace datalog { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } - return alloc(union_fn, *this, false); + return alloc(union_fn, false); } relation_union_fn * interval_relation_plugin::mk_widen_fn( @@ -232,7 +228,7 @@ namespace datalog { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } - return alloc(union_fn, *this, true); + return alloc(union_fn, true); } class interval_relation_plugin::filter_identical_fn : public relation_mutator_fn { diff --git a/src/muz_qe/dl_mk_bit_blast.cpp b/src/muz_qe/dl_mk_bit_blast.cpp index 3f92bbce8..51a9d5927 100644 --- a/src/muz_qe/dl_mk_bit_blast.cpp +++ b/src/muz_qe/dl_mk_bit_blast.cpp @@ -141,7 +141,6 @@ namespace datalog { func_decl_ref_vector const& new_funcs() const { return m_new_funcs; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - rule_manager& rm = m_context.get_rule_manager(); bool found = false; for (unsigned j = 0; !found && j < num; ++j) { found = m_util.is_mkbv(args[j]); diff --git a/src/muz_qe/dl_mk_explanations.cpp b/src/muz_qe/dl_mk_explanations.cpp index bf3548a7a..004b1823d 100644 --- a/src/muz_qe/dl_mk_explanations.cpp +++ b/src/muz_qe/dl_mk_explanations.cpp @@ -527,11 +527,10 @@ namespace datalog { } class explanation_relation_plugin::intersection_filter_fn : public relation_intersection_filter_fn { - explanation_relation_plugin & m_plugin; func_decl_ref m_union_decl; public: intersection_filter_fn(explanation_relation_plugin & plugin) - : m_plugin(plugin), m_union_decl(plugin.m_union_decl) {} + : m_union_decl(plugin.m_union_decl) {} virtual void operator()(relation_base & tgt0, const relation_base & src0) { explanation_relation & tgt = static_cast(tgt0); diff --git a/src/muz_qe/dl_mk_karr_invariants.cpp b/src/muz_qe/dl_mk_karr_invariants.cpp index c8e350eeb..9109f1bda 100644 --- a/src/muz_qe/dl_mk_karr_invariants.cpp +++ b/src/muz_qe/dl_mk_karr_invariants.cpp @@ -938,11 +938,8 @@ namespace datalog { class karr_relation_plugin::union_fn : public relation_union_fn { - karr_relation_plugin& m_plugin; public: - union_fn(karr_relation_plugin& p) : - m_plugin(p) { - } + union_fn() {} virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { @@ -966,7 +963,7 @@ namespace datalog { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } - return alloc(union_fn, *this); + return alloc(union_fn); } class karr_relation_plugin::filter_identical_fn : public relation_mutator_fn { diff --git a/src/muz_qe/dl_mk_subsumption_checker.cpp b/src/muz_qe/dl_mk_subsumption_checker.cpp index f50bd104c..0e94ba3b9 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.cpp +++ b/src/muz_qe/dl_mk_subsumption_checker.cpp @@ -320,9 +320,7 @@ namespace datalog { if(!m_ground_unconditional_rule_heads.contains(pred)) { m_ground_unconditional_rule_heads.insert(pred, alloc(obj_hashtable)); } - obj_hashtable * head_store; - m_ground_unconditional_rule_heads.find(pred, head_store); - head_store->insert(head); + m_ground_unconditional_rule_heads.find(pred)->insert(head); next_rule:; } diff --git a/src/muz_qe/dl_relation_manager.cpp b/src/muz_qe/dl_relation_manager.cpp index d92ae7dcc..ef8e6ddd4 100644 --- a/src/muz_qe/dl_relation_manager.cpp +++ b/src/muz_qe/dl_relation_manager.cpp @@ -1090,12 +1090,10 @@ namespace datalog { class relation_manager::default_table_rename_fn : public convenient_table_rename_fn, auxiliary_table_transformer_fn { - const unsigned m_cycle_len; public: default_table_rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) - : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle), - m_cycle_len(permutation_cycle_len) { + : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { SASSERT(permutation_cycle_len>=2); } diff --git a/src/muz_qe/dl_relation_manager.h b/src/muz_qe/dl_relation_manager.h index 8715e5cff..26008a830 100644 --- a/src/muz_qe/dl_relation_manager.h +++ b/src/muz_qe/dl_relation_manager.h @@ -661,17 +661,15 @@ namespace datalog { SASSERT(res_idxinsert(m_allocated_kinds[res_idx], spec); } return m_allocated_kinds[res_idx]; } void get_relation_spec(const relation_signature & sig, family_id kind, Spec & spec) { - family_id2spec * idspecs; - VERIFY( m_kind_specs.find(sig, idspecs) ); - VERIFY( idspecs->find(kind, spec) ); + family_id2spec * idspecs = m_kind_specs.find(sig); + spec = idspecs->find(kind); } }; diff --git a/src/muz_qe/dl_util.h b/src/muz_qe/dl_util.h index c3644cd74..974ab5862 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz_qe/dl_util.h @@ -198,8 +198,6 @@ namespace datalog { { bool values_match(const expr * v1, const expr * v2); - ast_manager & m_manager; - unsigned_vector m_args1; unsigned_vector m_args2; @@ -211,7 +209,7 @@ namespace datalog { static unsigned expr_cont_get_size(const ptr_vector & v) { return v.size(); } static expr * expr_cont_get(const ptr_vector & v, unsigned i) { return v[i]; } public: - variable_intersection(ast_manager & m) : m_manager(m), m_consts(m) {} + variable_intersection(ast_manager & m) : m_consts(m) {} unsigned size() const { return m_args1.size(); diff --git a/src/muz_qe/nlarith_util.cpp b/src/muz_qe/nlarith_util.cpp index 5f8a24e99..c555b71f1 100644 --- a/src/muz_qe/nlarith_util.cpp +++ b/src/muz_qe/nlarith_util.cpp @@ -1028,7 +1028,6 @@ namespace nlarith { }; class sqrt_subst : public isubst { - bool m_even; sqrt_form const& m_s; public: sqrt_subst(imp& i, sqrt_form const& s): isubst(i), m_s(s) {} diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index d4027d73d..a240a9aef 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -1285,7 +1285,7 @@ namespace pdr { obj_hashtable::iterator itf = deps.begin(), endf = deps.end(); for (; itf != endf; ++itf) { TRACE("pdr", tout << mk_pp(pred, m) << " " << mk_pp(*itf, m) << "\n";); - VERIFY (rels.find(*itf, pt_user)); + pt_user = rels.find(*itf); pt_user->add_use(pt); } } diff --git a/src/muz_qe/pdr_farkas_learner.cpp b/src/muz_qe/pdr_farkas_learner.cpp index 489b2437e..393090299 100644 --- a/src/muz_qe/pdr_farkas_learner.cpp +++ b/src/muz_qe/pdr_farkas_learner.cpp @@ -418,11 +418,10 @@ namespace pdr { class farkas_learner::constant_replacer_cfg : public default_rewriter_cfg { - ast_manager& m; const obj_map& m_translation; public: - constant_replacer_cfg(ast_manager& m, const obj_map& translation) - : m(m), m_translation(translation) + constant_replacer_cfg(const obj_map& translation) + : m_translation(translation) { } bool get_subst(expr * s, expr * & t, proof * & t_pr) { diff --git a/src/muz_qe/qe.cpp b/src/muz_qe/qe.cpp index bb65f8bf8..894a935b5 100644 --- a/src/muz_qe/qe.cpp +++ b/src/muz_qe/qe.cpp @@ -1093,8 +1093,7 @@ namespace qe { bool has_branch(rational const& branch_id) const { return m_branch_index.contains(branch_id); } search_tree* child(rational const& branch_id) const { - unsigned idx; - VERIFY(m_branch_index.find(branch_id, idx)); + unsigned idx = m_branch_index.find(branch_id); return m_children[idx]; } @@ -1963,7 +1962,6 @@ namespace qe { expr_ref m_assumption; bool m_produce_models; ptr_vector m_plugins; - unsigned m_name_counter; // fresh-id volatile bool m_cancel; bool m_eliminate_variables_as_block; @@ -1973,7 +1971,6 @@ namespace qe { m_fparams(p), m_assumption(m), m_produce_models(m_fparams.m_model), - m_name_counter(0), m_cancel(false), m_eliminate_variables_as_block(true) { diff --git a/src/muz_qe/qe_dl_plugin.cpp b/src/muz_qe/qe_dl_plugin.cpp index 15e972e88..61466795b 100644 --- a/src/muz_qe/qe_dl_plugin.cpp +++ b/src/muz_qe/qe_dl_plugin.cpp @@ -12,14 +12,12 @@ namespace qe { // dl_plugin class eq_atoms { - ast_manager& m; expr_ref_vector m_eqs; expr_ref_vector m_neqs; app_ref_vector m_eq_atoms; app_ref_vector m_neq_atoms; public: eq_atoms(ast_manager& m): - m(m), m_eqs(m), m_neqs(m), m_eq_atoms(m), diff --git a/src/muz_qe/tab_context.cpp b/src/muz_qe/tab_context.cpp index 44d9d94e2..653f5f188 100644 --- a/src/muz_qe/tab_context.cpp +++ b/src/muz_qe/tab_context.cpp @@ -740,7 +740,6 @@ namespace tb { typedef svector double_vector; typedef obj_map score_map; typedef obj_map pred_map; - datalog::context& m_ctx; ast_manager& m; datatype_util dt; score_map m_score_map; @@ -750,19 +749,16 @@ namespace tb { pred_map m_pred_map; expr_ref_vector m_refs; double m_weight_multiply; - unsigned m_num_invocations; unsigned m_update_frequency; unsigned m_next_update; public: selection(datalog::context& ctx): - m_ctx(ctx), m(ctx.get_manager()), dt(m), m_refs(m), m_weight_multiply(1.0), - m_num_invocations(0), m_update_frequency(20), m_next_update(20) { set_strategy(ctx.get_params().tab_selection()); diff --git a/src/parsers/smt/smtparser.cpp b/src/parsers/smt/smtparser.cpp index b6b40c01a..d26190541 100644 --- a/src/parsers/smt/smtparser.cpp +++ b/src/parsers/smt/smtparser.cpp @@ -1472,7 +1472,7 @@ private: SASSERT(sorts.size() > 0); - idbuilder* pop_q = new (region) pop_quantifier(this, (head_symbol == m_forall), weight, qid, skid, patterns, no_patterns, sorts, vars, local_scope, current); + idbuilder* pop_q = new (region) pop_quantifier(this, (head_symbol == m_forall), weight, qid, skid, patterns, no_patterns, sorts, vars, local_scope); expr_ref_vector * empty_v = alloc(expr_ref_vector, m_manager); up.push_back(new (region) parse_frame(current, pop_q, empty_v, 0, m_binding_level)); @@ -2522,7 +2522,7 @@ private: class pop_quantifier : public idbuilder { public: pop_quantifier(smtparser * smt, bool is_forall, int weight, symbol const& qid, symbol const& skid, expr_ref_buffer & patterns, expr_ref_buffer & no_patterns, sort_ref_buffer & sorts, - svector& vars, symbol_table & local_scope, proto_expr* p_expr): + svector& vars, symbol_table & local_scope): m_smt(smt), m_is_forall(is_forall), m_weight(weight), @@ -2531,8 +2531,7 @@ private: m_patterns(m_smt->m_manager), m_no_patterns(m_smt->m_manager), m_sorts(m_smt->m_manager), - m_local_scope(local_scope), - m_p_expr(p_expr) { + m_local_scope(local_scope) { SASSERT(sorts.size() == vars.size()); m_vars.append(vars); @@ -2619,7 +2618,6 @@ private: sort_ref_buffer m_sorts; svector m_vars; symbol_table& m_local_scope; - proto_expr* m_p_expr; }; class builtin_builder : public idbuilder { diff --git a/src/parsers/smt2/smt2scanner.cpp b/src/parsers/smt2/smt2scanner.cpp index 0f6101a93..f653cbb25 100644 --- a/src/parsers/smt2/smt2scanner.cpp +++ b/src/parsers/smt2/smt2scanner.cpp @@ -243,7 +243,6 @@ namespace smt2 { } scanner::scanner(cmd_context & ctx, std::istream& stream, bool interactive): - m_ctx(ctx), m_interactive(interactive), m_spos(0), m_curr(0), // avoid Valgrind warning diff --git a/src/parsers/smt2/smt2scanner.h b/src/parsers/smt2/smt2scanner.h index 7b74c752f..631c71f17 100644 --- a/src/parsers/smt2/smt2scanner.h +++ b/src/parsers/smt2/smt2scanner.h @@ -31,7 +31,6 @@ namespace smt2 { class scanner { private: - cmd_context & m_ctx; bool m_interactive; int m_spos; // position in the current line of the stream char m_curr; // current char; diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index c5d3c08cf..4775e44a4 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -408,7 +408,7 @@ void asserted_formulas::apply_quasi_macros() { TRACE("before_quasi_macros", display(tout);); expr_ref_vector new_exprs(m_manager); proof_ref_vector new_prs(m_manager); - quasi_macros proc(m_manager, m_macro_manager, *m_bsimp, m_simplifier); + quasi_macros proc(m_manager, m_macro_manager, m_simplifier); while (proc(m_asserted_formulas.size() - m_asserted_qhead, m_asserted_formulas.c_ptr() + m_asserted_qhead, m_asserted_formula_prs.c_ptr() + m_asserted_qhead, diff --git a/src/smt/database.h b/src/smt/database.h index 2e71cbae4..1975fe3c3 100644 --- a/src/smt/database.h +++ b/src/smt/database.h @@ -1,4 +1,4 @@ -char const * g_pattern_database = +static char const * g_pattern_database = "(benchmark patterns \n" " :status unknown \n" " :logic ALL \n" diff --git a/src/smt/expr_context_simplifier.cpp b/src/smt/expr_context_simplifier.cpp index b23bb3bdc..66252c3cf 100644 --- a/src/smt/expr_context_simplifier.cpp +++ b/src/smt/expr_context_simplifier.cpp @@ -311,7 +311,7 @@ bool expr_context_simplifier::is_false(expr* e) const { // expr_strong_context_simplifier::expr_strong_context_simplifier(smt_params& p, ast_manager& m): - m_manager(m), m_params(p), m_arith(m), m_id(0), m_fn(0,m), m_solver(m, p) { + m_manager(m), m_arith(m), m_fn(0,m), m_solver(m, p) { sort* i_sort = m_arith.mk_int(); m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } diff --git a/src/smt/expr_context_simplifier.h b/src/smt/expr_context_simplifier.h index 982b65878..a1d01b78c 100644 --- a/src/smt/expr_context_simplifier.h +++ b/src/smt/expr_context_simplifier.h @@ -57,9 +57,7 @@ private: class expr_strong_context_simplifier { ast_manager& m_manager; - smt_params & m_params; arith_util m_arith; - unsigned m_id; func_decl_ref m_fn; smt::kernel m_solver; diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index c9d6ead88..d92eef5b8 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -1849,11 +1849,9 @@ namespace smt { unsigned m_curr_max_generation; // temporary var used to store a copy of m_max_generation unsigned m_num_args; unsigned m_oreg; - unsigned m_ireg; enode * m_n1; enode * m_n2; enode * m_app; - instruction * m_alt; const bind * m_b; ptr_vector m_used_enodes; unsigned m_curr_used_enodes_size; diff --git a/src/smt/theory_dl.cpp b/src/smt/theory_dl.cpp index 758f78c2c..9c3489aec 100644 --- a/src/smt/theory_dl.cpp +++ b/src/smt/theory_dl.cpp @@ -68,14 +68,11 @@ namespace smt { bv_util& b() { return m_bv; } class dl_value_proc : public smt::model_value_proc { - smt::model_generator & m_mg; theory_dl& m_th; smt::enode* m_node; public: - dl_value_proc(smt::model_generator & m, theory_dl& th, smt::enode* n): - m_mg(m), m_th(th), m_node(n) - { } + dl_value_proc(theory_dl& th, smt::enode* n) : m_th(th), m_node(n) {} virtual void get_dependencies(buffer & result) {} @@ -165,8 +162,8 @@ namespace smt { m.register_factory(alloc(dl_factory, m_util, m.get_model())); } - virtual smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator & m) { - return alloc(dl_value_proc, m, *this, n); + virtual smt::model_value_proc * mk_value(smt::enode * n) { + return alloc(dl_value_proc, *this, n); } virtual void apply_sort_cnstr(enode * n, sort * s) { diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp index d7e55379a..393a40603 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.cpp +++ b/src/tactic/ufbv/quasi_macros_tactic.cpp @@ -67,7 +67,7 @@ class quasi_macros_tactic : public tactic { simp.register_plugin(bvsimp); macro_manager mm(m_manager, simp); - quasi_macros qm(m_manager, mm, *bsimp, simp); + quasi_macros qm(m_manager, mm, simp); bool more = true; expr_ref_vector forms(m_manager), new_forms(m_manager); diff --git a/src/tactic/ufbv/ufbv_rewriter.cpp b/src/tactic/ufbv/ufbv_rewriter.cpp index e4ed2ec01..40fdf5e3e 100644 --- a/src/tactic/ufbv/ufbv_rewriter.cpp +++ b/src/tactic/ufbv/ufbv_rewriter.cpp @@ -442,11 +442,10 @@ expr * ufbv_rewriter::rewrite(expr * n) { } class ufbv_rewriter::add_back_idx_proc { - ast_manager & m_manager; back_idx_map & m_back_idx; expr * m_expr; public: - add_back_idx_proc(ast_manager & m, back_idx_map & bi, expr * e):m_manager(m),m_back_idx(bi),m_expr(e) {} + add_back_idx_proc(back_idx_map & bi, expr * e):m_back_idx(bi),m_expr(e) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { @@ -469,11 +468,10 @@ public: }; class ufbv_rewriter::remove_back_idx_proc { - ast_manager & m_manager; back_idx_map & m_back_idx; expr * m_expr; public: - remove_back_idx_proc(ast_manager & m, back_idx_map & bi, expr * e):m_manager(m),m_back_idx(bi),m_expr(e) {} + remove_back_idx_proc(back_idx_map & bi, expr * e):m_back_idx(bi),m_expr(e) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { @@ -511,7 +509,7 @@ void ufbv_rewriter::reschedule_processed(func_decl * f) { expr * p = *sit; // remove p from m_processed and m_back_idx m_processed.remove(p); - remove_back_idx_proc proc(m_manager, m_back_idx, p); // this could change it->m_value, thus we need the `temp' set. + remove_back_idx_proc proc(m_back_idx, p); // this could change it->m_value, thus we need the `temp' set. for_each_expr(proc, p); // insert p into m_todo m_todo.push_back(p); @@ -619,7 +617,7 @@ void ufbv_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { // remove d from m_back_idx // just remember it here, because otherwise it and/or esit might become invalid? // to_remove.insert(d); - remove_back_idx_proc proc(m_manager, m_back_idx, d); + remove_back_idx_proc proc(m_back_idx, d); for_each_expr(proc, d); // insert d into m_todo m_todo.push_back(d); @@ -674,7 +672,7 @@ void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * // insert n' into m_processed m_processed.insert(np); // update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx) - add_back_idx_proc proc(m_manager, m_back_idx, np); + add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); } else { // np is a demodulator that allows us to replace 'large' with 'small'. @@ -702,7 +700,7 @@ void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * insert_fwd_idx(large, small, to_quantifier(np)); // update m_back_idx - add_back_idx_proc proc(m_manager, m_back_idx, np); + add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); } } diff --git a/src/test/matcher.cpp b/src/test/matcher.cpp index 05d971e24..598a82ae3 100644 --- a/src/test/matcher.cpp +++ b/src/test/matcher.cpp @@ -26,7 +26,7 @@ void tst_match(ast_manager & m, app * t, app * i) { substitution s(m); s.reserve(2, 10); // reserving a big number of variables to be safe. - matcher match(m); + matcher match; std::cout << "Is " << mk_pp(i, m) << " an instance of " << mk_pp(t, m) << "\n"; if (match(t, i, s)) { std::cout << "yes\n"; diff --git a/src/util/bit_util.cpp b/src/util/bit_util.cpp index 32861f754..0f1fd294d 100644 --- a/src/util/bit_util.cpp +++ b/src/util/bit_util.cpp @@ -19,6 +19,7 @@ Revision History: #include"bit_util.h" #include"util.h" #include"debug.h" +#include /** \brief (Debugging version) Return the position of the most significant (set) bit of a @@ -67,7 +68,11 @@ unsigned msb_pos(unsigned v) { */ unsigned nlz_core(unsigned x) { SASSERT(x != 0); +#ifdef __GNUC__ + return __builtin_clz(x); +#else return 31 - msb_pos(x); +#endif } /** @@ -92,8 +97,15 @@ unsigned nlz(unsigned sz, unsigned const * data) { */ unsigned ntz_core(unsigned x) { SASSERT(x != 0); +#ifdef __GNUC__ + return __builtin_ctz(x); +#else float f = static_cast(x & static_cast(-static_cast(x))); - return (*reinterpret_cast(&f) >> 23) - 0x7f; + unsigned u; + SASSERT(sizeof(u) == sizeof(f)); + memcpy(&u, &f, sizeof(u)); + return (u >> 23) - 0x7f; +#endif } /** diff --git a/src/util/debug.cpp b/src/util/debug.cpp index 75ea8586a..c336ddb73 100644 --- a/src/util/debug.cpp +++ b/src/util/debug.cpp @@ -24,7 +24,7 @@ Revision History: #include"str_hashtable.h" #include"z3_exception.h" -volatile bool g_enable_assertions = true; +static volatile bool g_enable_assertions = true; void enable_assertions(bool f) { g_enable_assertions = f; @@ -41,7 +41,7 @@ void notify_assertion_violation(const char * fileName, int line, const char * co std::cerr << condition << "\n"; } -str_hashtable* g_enabled_debug_tags = 0; +static str_hashtable* g_enabled_debug_tags = 0; static void init_debug_table() { if (!g_enabled_debug_tags) { diff --git a/src/util/debug.h b/src/util/debug.h index c45ee5aa6..9e519982f 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -29,6 +29,10 @@ bool assertions_enabled(); #include #endif +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + #include"error_codes.h" #include"warning.h" @@ -53,7 +57,14 @@ bool is_debug_enabled(const char * tag); #define SASSERT(COND) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define CASSERT(TAG, COND) DEBUG_CODE(if (assertions_enabled() && is_debug_enabled(TAG) && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define XASSERT(COND, EXTRA_CODE) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); { EXTRA_CODE } INVOKE_DEBUGGER(); }) + +#if (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 405)) || __has_builtin(__builtin_unreachable) +// only available in gcc >= 4.5 and in newer versions of clang +# define UNREACHABLE() __builtin_unreachable() +#else #define UNREACHABLE() DEBUG_CODE(notify_assertion_violation(__FILE__, __LINE__, "UNREACHABLE CODE WAS REACHED."); INVOKE_DEBUGGER();) +#endif + #define NOT_IMPLEMENTED_YET() { std::cerr << "NOT IMPLEMENTED YET!\n"; UNREACHABLE(); exit(ERR_NOT_IMPLEMENTED_YET); } ((void) 0) #ifdef Z3DEBUG diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 3b2e8edc1..1d9426390 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -22,7 +22,7 @@ Notes: extern void gparams_register_modules(); -char const * g_old_params_names[] = { +static char const * g_old_params_names[] = { "arith_adaptive","arith_adaptive_assertion_threshold","arith_adaptive_gcd","arith_adaptive_propagation_threshold","arith_add_binary_bounds","arith_blands_rule_threshold","arith_branch_cut_ratio","arith_dump_lemmas","arith_eager_eq_axioms","arith_eager_gcd","arith_eq_bounds","arith_euclidean_solver","arith_expand_eqs","arith_force_simplex","arith_gcd_test","arith_ignore_int","arith_lazy_adapter","arith_lazy_pivoting","arith_max_lemma_size","arith_process_all_eqs","arith_propagate_eqs","arith_propagation_mode","arith_propagation_threshold","arith_prop_strategy","arith_random_initial_value","arith_random_lower","arith_random_seed","arith_random_upper","arith_reflect","arith_skip_big_coeffs","arith_small_lemma_size","arith_solver","arith_stronger_lemmas","array_always_prop_upward","array_canonize","array_cg","array_delay_exp_axiom","array_extensional","array_laziness","array_lazy_ieq","array_lazy_ieq_delay","array_solver","array_weak","async_commands","at_labels_cex","auto_config","bb_eager","bb_ext_gates","bb_quantifiers","bin_clauses","bit2int","bv2int_distribute","bv_blast_max_size","bv_cc","bv_enable_int2bv_propagation","bv_lazy_le","bv_max_sharing","bv_reflect","bv_solver","case_split","check_at_labels","check_proof","cnf_factor","cnf_mode","context_simplifier","dack","dack_eq","dack_factor","dack_gc","dack_gc_inv_decay","dack_threshold","default_qid","default_table","default_table_checked","delay_units","delay_units_threshold","der","display_config","display_dot_proof","display_error_for_visual_studio","display_features","display_proof","display_unsat_core","distribute_forall","dt_lazy_splits","dump_goal_as_smt","elim_and","elim_bounds","elim_nlarith_quantifiers","elim_quantifiers","elim_term_ite","ematching","engine","eq_propagation","hi_div0","ignore_bad_patterns","ignore_setparameter","instruction_max","inst_gen","interactive","internalizer_nnf","lemma_gc_factor","lemma_gc_half","lemma_gc_initial","lemma_gc_new_clause_activity","lemma_gc_new_clause_relevancy","lemma_gc_new_old_ratio","lemma_gc_old_clause_activity","lemma_gc_old_clause_relevancy","lemma_gc_strategy","lift_ite","lookahead_diseq","macro_finder","max_conflicts","max_counterexamples","mbqi","mbqi_force_template","mbqi_max_cexs","mbqi_max_cexs_incr","mbqi_max_iterations","mbqi_trace","minimize_lemmas","model","model_compact","model_completion","model_display_arg_sort","model_hide_unused_partitions","model_on_final_check","model_on_timeout","model_partial","model_v1","model_v2","model_validate","new_core2th_eq","ng_lift_ite","nl_arith","nl_arith_branching","nl_arith_gb","nl_arith_gb_eqs","nl_arith_gb_perturbate","nl_arith_gb_threshold","nl_arith_max_degree","nl_arith_rounds","nnf_factor","nnf_ignore_labels","nnf_mode","nnf_sk_hack","order","order_var_weight","order_weights","phase_selection","pi_arith","pi_arith_weight","pi_avoid_skolems","pi_block_looop_patterns","pi_max_multi_patterns","pi_non_nested_arith_weight","pi_nopat_weight","pi_pull_quantifiers","pi_use_database","pi_warnings","pp_bounded","pp_bv_literals","pp_bv_neg","pp_decimal","pp_decimal_precision","pp_fixed_indent","pp_flat_assoc","pp_max_depth","pp_max_indent","pp_max_num_lines","pp_max_ribbon","pp_max_width","pp_min_alias_size","pp_simplify_implies","pp_single_line","precedence","precedence_gen","pre_demodulator","pre_simplifier","pre_simplify_expr","profile_res_sub","progress_sampling_freq","proof_mode","propagate_booleans","propagate_values","pull_cheap_ite_trees","pull_nested_quantifiers","qi_conservative_final_check","qi_cost","qi_eager_threshold","qi_lazy_instantiation","qi_lazy_quick_checker","qi_lazy_threshold","qi_max_eager_multi_patterns","qi_max_instances","qi_max_lazy_multi_pattern_matching","qi_new_gen","qi_profile","qi_profile_freq","qi_promote_unsat","qi_quick_checker","quasi_macros","random_case_split_freq","random_initial_activity","random_seed","recent_lemma_threshold","reduce_args","refine_inj_axiom","relevancy","relevancy_lemma","rel_case_split_order","restart_adaptive","restart_agility_threshold","restart_factor","restart_initial","restart_strategy","restricted_quasi_macros","simplify_clauses","smtlib2_compliant","smtlib_category","smtlib_dump_lemmas","smtlib_logic","smtlib_source_info","smtlib_trace_path","soft_timeout","solver","spc_bs","spc_es","spc_factor_subsumption_index_opt","spc_initial_subsumption_index_opt","spc_max_subsumption_index_features","spc_min_func_freq_subsumption_index","spc_num_iterations","spc_trace","statistics","strong_context_simplifier","tick","trace","trace_file_name","type_check","user_theory_persist_axioms","user_theory_preprocess_axioms","verbose","warning","well_sorted_check","z3_solver_ll_pp","z3_solver_smt_pp", 0 }; bool is_old_param_name(symbol const & name) { @@ -35,7 +35,7 @@ bool is_old_param_name(symbol const & name) { return false; } -char const * g_params_renames[] = { +static char const * g_params_renames[] = { "proof_mode", "proof", "soft_timeout", "timeout", "mbqi", "smt.mbqi", diff --git a/src/util/hwf.h b/src/util/hwf.h index 3b7a0e94b..9059869a0 100644 --- a/src/util/hwf.h +++ b/src/util/hwf.h @@ -28,6 +28,12 @@ class hwf { friend class hwf_manager; double value; hwf & operator=(hwf const & other) { UNREACHABLE(); return *this; } + uint64 get_raw() const { + uint64 n; + SASSERT(sizeof(n) == sizeof(value)); + memcpy(&n, &value, sizeof(value)); + return n; + } public: hwf() {} @@ -122,16 +128,15 @@ public: bool sgn(hwf const & x) const { - uint64 raw = *reinterpret_cast(&x.value); - return (raw & 0x8000000000000000ull) != 0; + return (x.get_raw() & 0x8000000000000000ull) != 0; } const uint64 sig(hwf const & x) const { - return *reinterpret_cast(&x.value) & 0x000FFFFFFFFFFFFFull; + return x.get_raw() & 0x000FFFFFFFFFFFFFull; } const int exp(hwf const & x) const { - return ((*reinterpret_cast(&x.value) & 0x7FF0000000000000ull) >> 52) - 1023; + return ((x.get_raw() & 0x7FF0000000000000ull) >> 52) - 1023; } bool is_nan(hwf const & x); @@ -151,7 +156,7 @@ public: void mk_pinf(hwf & o); void mk_ninf(hwf & o); - unsigned hash(hwf const & a) { return hash_ull(*reinterpret_cast(&a.value)); } + unsigned hash(hwf const & a) { return hash_ull(a.get_raw()); } inline void set_rounding_mode(mpf_rounding_mode rm); diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index de4e760d7..f5e5fa9fa 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -27,14 +27,14 @@ void mem_finalize(); out_of_memory_error::out_of_memory_error():z3_error(ERR_MEMOUT) { } -volatile bool g_memory_out_of_memory = false; -bool g_memory_initialized = false; -long long g_memory_alloc_size = 0; -long long g_memory_max_size = 0; -long long g_memory_max_used_size = 0; -long long g_memory_watermark = 0; -bool g_exit_when_out_of_memory = false; -char const * g_out_of_memory_msg = "ERROR: out of memory"; +static volatile bool g_memory_out_of_memory = false; +static bool g_memory_initialized = false; +static long long g_memory_alloc_size = 0; +static long long g_memory_max_size = 0; +static long long g_memory_max_used_size = 0; +static long long g_memory_watermark = 0; +static bool g_exit_when_out_of_memory = false; +static char const * g_out_of_memory_msg = "ERROR: out of memory"; void memory::exit_when_out_of_memory(bool flag, char const * msg) { g_exit_when_out_of_memory = flag; diff --git a/src/util/warning.cpp b/src/util/warning.cpp index 0a1ac9bbc..88f807e3c 100644 --- a/src/util/warning.cpp +++ b/src/util/warning.cpp @@ -60,11 +60,11 @@ void myInvalidParameterHandler( #define END_ERR_HANDLER() {} #endif -bool g_warning_msgs = true; -bool g_use_std_stdout = false; -std::ostream* g_error_stream = 0; -std::ostream* g_warning_stream = 0; -bool g_show_error_msg_prefix = true; +static bool g_warning_msgs = true; +static bool g_use_std_stdout = false; +static std::ostream* g_error_stream = 0; +static std::ostream* g_warning_stream = 0; +static bool g_show_error_msg_prefix = true; void send_warnings_to_stdout(bool flag) { g_use_std_stdout = flag; From 0fbdd37e8947c0c8a5c09db182b2447ba668b318 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 21 Apr 2013 18:17:49 -0700 Subject: [PATCH 19/91] working on horn difference logic Signed-off-by: Nikolaj Bjorner --- src/api/api_ast.cpp | 2 +- src/ast/arith_decl_plugin.h | 2 + src/muz_qe/dl_bmc_engine.cpp | 1 - src/muz_qe/dl_context.cpp | 1 - src/muz_qe/dl_mk_bit_blast.cpp | 1 - src/muz_qe/dl_mk_karr_invariants.cpp | 47 +++++++++++++++++----- src/muz_qe/dl_mk_karr_invariants.h | 2 +- src/muz_qe/dl_mk_loop_counter.cpp | 60 +++++++++++++++++++++++----- src/muz_qe/dl_mk_loop_counter.h | 5 ++- src/muz_qe/dl_rule_set.cpp | 7 ++-- src/smt/diff_logic.h | 4 -- src/smt/theory_diff_logic_def.h | 9 +---- src/util/inf_rational.h | 5 +++ 13 files changed, 105 insertions(+), 41 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 680b59c68..c4b5c97d7 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -46,7 +46,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_int_symbol(c, i); RESET_ERROR_CODE(); - if (i < 0 || (unsigned)i >= (SIZE_MAX >> PTR_ALIGNMENT)) { + if (i < 0 || (size_t)i >= (SIZE_MAX >> PTR_ALIGNMENT)) { SET_ERROR_CODE(Z3_IOB); return 0; } diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index d048bb2f7..cd33c7782 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -268,6 +268,8 @@ public: bool is_int_real(expr const * n) const { return is_int_real(get_sort(n)); } MATCH_UNARY(is_uminus); + MATCH_UNARY(is_to_real); + MATCH_UNARY(is_to_int); MATCH_BINARY(is_sub); MATCH_BINARY(is_add); MATCH_BINARY(is_mul); diff --git a/src/muz_qe/dl_bmc_engine.cpp b/src/muz_qe/dl_bmc_engine.cpp index 15876b631..c313f7d7b 100644 --- a/src/muz_qe/dl_bmc_engine.cpp +++ b/src/muz_qe/dl_bmc_engine.cpp @@ -1004,7 +1004,6 @@ namespace datalog { symbol is_name(_name.str().c_str()); std::stringstream _name2; _name2 << "get_succ#" << i; - symbol acc_name(_name2.str().c_str()); ptr_vector accs; type_ref tr(0); accs.push_back(mk_accessor_decl(name, tr)); diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 73ddd22e5..df293aeba 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -647,7 +647,6 @@ namespace datalog { } } ast_manager& m = get_manager(); - datalog::rule_manager& rm = get_rule_manager(); contains_pred contains_p(*this); check_pred check_pred(contains_p, get_manager()); diff --git a/src/muz_qe/dl_mk_bit_blast.cpp b/src/muz_qe/dl_mk_bit_blast.cpp index 3f92bbce8..51a9d5927 100644 --- a/src/muz_qe/dl_mk_bit_blast.cpp +++ b/src/muz_qe/dl_mk_bit_blast.cpp @@ -141,7 +141,6 @@ namespace datalog { func_decl_ref_vector const& new_funcs() const { return m_new_funcs; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - rule_manager& rm = m_context.get_rule_manager(); bool found = false; for (unsigned j = 0; !found && j < num; ++j) { found = m_util.is_mkbv(args[j]); diff --git a/src/muz_qe/dl_mk_karr_invariants.cpp b/src/muz_qe/dl_mk_karr_invariants.cpp index c8e350eeb..0a85f9abf 100644 --- a/src/muz_qe/dl_mk_karr_invariants.cpp +++ b/src/muz_qe/dl_mk_karr_invariants.cpp @@ -35,6 +35,8 @@ Revision History: #include"dl_mk_karr_invariants.h" #include"expr_safe_replace.h" #include"bool_rewriter.h" +#include"dl_mk_backwards.h" +#include"dl_mk_loop_counter.h" namespace datalog { @@ -199,6 +201,29 @@ namespace datalog { return 0; } } + + mk_loop_counter lc(m_ctx); + mk_backwards bwd(m_ctx); + + scoped_ptr src_loop = lc(source); + TRACE("dl", src_loop->display(tout << "source loop\n");); + + // run propagation forwards, then backwards + scoped_ptr src_annot = update_using_propagation(*src_loop, *src_loop); + TRACE("dl", src_annot->display(tout << "updated using propagation\n");); + +#if 0 + // figure out whether to update same rules as used for saturation. + scoped_ptr rev_source = bwd(*src_annot); + src_annot = update_using_propagation(*src_annot, *rev_source); +#endif + rule_set* rules = lc.revert(*src_annot); + rules->inherit_predicates(source); + TRACE("dl", rules->display(tout);); + return rules; + } + + rule_set* mk_karr_invariants::update_using_propagation(rule_set const& src, rule_set const& srcref) { m_inner_ctx.reset(); rel_context& rctx = m_inner_ctx.get_rel_context(); ptr_vector heads; @@ -207,24 +232,24 @@ namespace datalog { m_inner_ctx.register_predicate(*fit, false); } m_inner_ctx.ensure_opened(); - m_inner_ctx.replace_rules(source); + m_inner_ctx.replace_rules(srcref); m_inner_ctx.close(); - rule_set::decl2rules::iterator dit = source.begin_grouped_rules(); - rule_set::decl2rules::iterator dend = source.end_grouped_rules(); + rule_set::decl2rules::iterator dit = srcref.begin_grouped_rules(); + rule_set::decl2rules::iterator dend = srcref.end_grouped_rules(); for (; dit != dend; ++dit) { heads.push_back(dit->m_key); } m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); - rule_set* rules = alloc(rule_set, m_ctx); - it = source.begin(); + rule_set* dst = alloc(rule_set, m_ctx); + rule_set::iterator it = src.begin(), end = src.end(); for (; it != end; ++it) { - update_body(rctx, *rules, **it); + update_body(rctx, *dst, **it); } if (m_ctx.get_model_converter()) { add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m); - rule_set::decl2rules::iterator git = source.begin_grouped_rules(); - rule_set::decl2rules::iterator gend = source.end_grouped_rules(); + rule_set::decl2rules::iterator git = src.begin_grouped_rules(); + rule_set::decl2rules::iterator gend = src.end_grouped_rules(); for (; git != gend; ++git) { func_decl* p = git->m_key; expr_ref fml(m); @@ -236,9 +261,9 @@ namespace datalog { } m_ctx.add_model_converter(kmc); } - TRACE("dl", rules->display(tout);); - rules->inherit_predicates(source); - return rules; + + dst->inherit_predicates(src); + return dst; } void mk_karr_invariants::update_body(rel_context& rctx, rule_set& rules, rule& r) { diff --git a/src/muz_qe/dl_mk_karr_invariants.h b/src/muz_qe/dl_mk_karr_invariants.h index 414953e4f..330260671 100644 --- a/src/muz_qe/dl_mk_karr_invariants.h +++ b/src/muz_qe/dl_mk_karr_invariants.h @@ -56,7 +56,7 @@ namespace datalog { context m_inner_ctx; arith_util a; void update_body(rel_context& rctx, rule_set& result, rule& r); - + rule_set* update_using_propagation(rule_set const& src, rule_set const& srcref); public: mk_karr_invariants(context & ctx, unsigned priority); diff --git a/src/muz_qe/dl_mk_loop_counter.cpp b/src/muz_qe/dl_mk_loop_counter.cpp index 144826639..678bfc5a3 100644 --- a/src/muz_qe/dl_mk_loop_counter.cpp +++ b/src/muz_qe/dl_mk_loop_counter.cpp @@ -32,7 +32,7 @@ namespace datalog { mk_loop_counter::~mk_loop_counter() { } - app_ref mk_loop_counter::add_arg(app* fn, unsigned idx) { + app_ref mk_loop_counter::add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx) { expr_ref_vector args(m); func_decl* new_fn, *old_fn = fn->get_decl(); args.append(fn->get_num_args(), fn->get_args()); @@ -46,17 +46,29 @@ namespace datalog { m_old2new.insert(old_fn, new_fn); m_new2old.insert(new_fn, old_fn); m_refs.push_back(new_fn); + m_ctx.register_predicate(new_fn, false); + if (src.is_output_predicate(old_fn)) { + dst.set_output_predicate(new_fn); + } } return app_ref(m.mk_app(new_fn, args.size(), args.c_ptr()), m); } + + app_ref mk_loop_counter::del_arg(app* fn) { + expr_ref_vector args(m); + func_decl* old_fn, *new_fn = fn->get_decl(); + SASSERT(fn->get_num_args() > 0); + args.append(fn->get_num_args()-1, fn->get_args()); + VERIFY (m_new2old.find(new_fn, old_fn)); + return app_ref(m.mk_app(old_fn, args.size(), args.c_ptr()), m); + } rule_set * mk_loop_counter::operator()(rule_set const & source) { m_refs.reset(); m_old2new.reset(); m_new2old.reset(); - context& ctx = source.get_context(); rule_manager& rm = source.get_rule_manager(); - rule_set * result = alloc(rule_set, ctx); + rule_set * result = alloc(rule_set, m_ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); @@ -71,16 +83,14 @@ namespace datalog { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j, ++cnt) { - tail.push_back(add_arg(r.get_tail(j), cnt)); + tail.push_back(add_arg(source, *result, r.get_tail(j), cnt)); neg.push_back(r.is_neg_tail(j)); - m_ctx.register_predicate(tail.back()->get_decl(), false); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } - head = add_arg(r.get_head(), cnt); - m_ctx.register_predicate(head->get_decl(), false); + head = add_arg(source, *result, r.get_head(), cnt); // set the loop counter to be an increment of the previous bool found = false; unsigned last = head->get_num_args()-1; @@ -108,9 +118,41 @@ namespace datalog { // model converter: remove references to extra argument. // proof converter: remove references to extra argument as well. - result->inherit_predicates(source); - return result; } + rule_set * mk_loop_counter::revert(rule_set const & source) { + context& ctx = source.get_context(); + rule_manager& rm = source.get_rule_manager(); + rule_set * result = alloc(rule_set, ctx); + unsigned sz = source.get_num_rules(); + rule_ref new_rule(rm); + app_ref_vector tail(m); + app_ref head(m); + svector neg; + for (unsigned i = 0; i < sz; ++i) { + tail.reset(); + neg.reset(); + rule & r = *source.get_rule(i); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + for (unsigned j = 0; j < utsz; ++j) { + tail.push_back(del_arg(r.get_tail(j))); + neg.push_back(r.is_neg_tail(j)); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + neg.push_back(false); + } + head = del_arg(r.get_head()); + new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + } + + // model converter: ... + // proof converter: ... + + return result; + + } }; diff --git a/src/muz_qe/dl_mk_loop_counter.h b/src/muz_qe/dl_mk_loop_counter.h index fc4d7e32f..d67c88e0e 100644 --- a/src/muz_qe/dl_mk_loop_counter.h +++ b/src/muz_qe/dl_mk_loop_counter.h @@ -32,7 +32,8 @@ namespace datalog { obj_map m_new2old; obj_map m_old2new; - app_ref add_arg(app* fn, unsigned idx); + app_ref add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx); + app_ref del_arg(app* fn); public: mk_loop_counter(context & ctx, unsigned priority = 33000); ~mk_loop_counter(); @@ -40,6 +41,8 @@ namespace datalog { rule_set * operator()(rule_set const & source); func_decl* get_old(func_decl* f) const { return m_new2old.find(f); } + + rule_set * revert(rule_set const& source); }; }; diff --git a/src/muz_qe/dl_rule_set.cpp b/src/muz_qe/dl_rule_set.cpp index cb129ab37..ad3b512a3 100644 --- a/src/muz_qe/dl_rule_set.cpp +++ b/src/muz_qe/dl_rule_set.cpp @@ -409,9 +409,10 @@ namespace datalog { } void rule_set::reopen() { - SASSERT(is_closed()); - m_stratifier = 0; - m_deps.reset(); + if (is_closed()) { + m_stratifier = 0; + m_deps.reset(); + } } /** diff --git a/src/smt/diff_logic.h b/src/smt/diff_logic.h index 49d7313d1..6d5101a80 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.h @@ -1643,7 +1643,3 @@ public: #endif /* _DIFF_LOGIC_H_ */ -#if 0 - - -#endif diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 9f2c97a84..7493f81c2 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -318,7 +318,7 @@ template void theory_diff_logic::assign_eh(bool_var v, bool is_true) { m_stats.m_num_assertions++; atom * a = 0; - m_bool_var2atom.find(v, a); + VERIFY (m_bool_var2atom.find(v, a)); SASSERT(a); SASSERT(get_context().get_assignment(v) != l_undef); SASSERT((get_context().get_assignment(v) == l_true) == is_true); @@ -376,13 +376,6 @@ final_check_status theory_diff_logic::final_check_eh() { SASSERT(is_consistent()); -#if 0 - TBD: - if (propagate_cheap_equalities()) { - return FC_CONTINUE; - } -#endif - if (m_non_diff_logic_exprs) { return FC_GIVEUP; } diff --git a/src/util/inf_rational.h b/src/util/inf_rational.h index 9e1753484..5cdfe9e93 100644 --- a/src/util/inf_rational.h +++ b/src/util/inf_rational.h @@ -223,6 +223,7 @@ class inf_rational { } friend inline inf_rational operator*(const rational & r1, const inf_rational & r2); + friend inline inf_rational operator*(const inf_rational & r1, const rational & r2); friend inline inf_rational operator/(const inf_rational & r1, const rational & r2); inf_rational & operator++() { @@ -426,6 +427,10 @@ inline inf_rational operator*(const rational & r1, const inf_rational & r2) { return result; } +inline inf_rational operator*(const inf_rational & r1, const rational & r2) { + return r2 * r1; +} + inline inf_rational operator/(const inf_rational & r1, const rational & r2) { inf_rational result(r1); result.m_first /= r2; From db653a6e686da6cd0ca8bf7f291a099c976ccced Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 22 Apr 2013 09:05:27 -0700 Subject: [PATCH 20/91] [datalog] merge changes from the hassel branch Signed-off-by: Nuno Lopes --- src/muz_qe/dl_check_table.h | 9 ----- src/muz_qe/dl_compiler.cpp | 53 ++++++++++-------------------- src/muz_qe/dl_compiler.h | 2 +- src/muz_qe/dl_context.cpp | 9 ++++- src/muz_qe/dl_relation_manager.cpp | 1 - src/muz_qe/dl_util.h | 2 ++ 6 files changed, 29 insertions(+), 47 deletions(-) diff --git a/src/muz_qe/dl_check_table.h b/src/muz_qe/dl_check_table.h index 7126bde66..40a3d5207 100644 --- a/src/muz_qe/dl_check_table.h +++ b/src/muz_qe/dl_check_table.h @@ -89,15 +89,6 @@ namespace datalog { class check_table : public table_base { friend class check_table_plugin; - friend class check_table_plugin::join_fn; - friend class check_table_plugin::union_fn; - friend class check_table_plugin::transformer_fn; - friend class check_table_plugin::rename_fn; - friend class check_table_plugin::project_fn; - friend class check_table_plugin::filter_equal_fn; - friend class check_table_plugin::filter_identical_fn; - friend class check_table_plugin::filter_interpreted_fn; - friend class check_table_plugin::filter_by_negation_fn; table_base* m_checker; table_base* m_tocheck; diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index 80b9dee5a..34dd94214 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -421,6 +421,7 @@ namespace datalog { void compiler::compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs, reg_idx delta_reg, bool use_widening, instruction_block & acc) { + ast_manager & m = m_context.get_manager(); m_instruction_observer.start_rule(r); const app * h = r->get_head(); @@ -433,7 +434,7 @@ namespace datalog { SASSERT(pt_len<=2); //we require rules to be processed by the mk_simple_joins rule transformer plugin reg_idx single_res; - ptr_vector single_res_expr; + expr_ref_vector single_res_expr(m); //used to save on filter_identical instructions where the check is already done //by the join operation @@ -536,7 +537,7 @@ namespace datalog { unsigned srlen=single_res_expr.size(); SASSERT((single_res==execution_context::void_register) ? (srlen==0) : (srlen==m_reg_signatures[single_res].size())); for(unsigned i=0; iget_tail_size(); //full tail for(unsigned tail_index=ut_len; tail_indexget_tail(tail_index); - var_idx_set t_vars; - ast_manager & m = m_context.get_manager(); - collect_vars(m, t, t_vars); - + ptr_vector t_vars; + ::get_free_vars(t, t_vars); + if(t_vars.empty()) { expr_ref simplified(m); m_context.get_rewriter()(t, simplified); @@ -639,40 +639,23 @@ namespace datalog { } //determine binding size - unsigned max_var=0; - var_idx_set::iterator vit = t_vars.begin(); - var_idx_set::iterator vend = t_vars.end(); - for(; vit!=vend; ++vit) { - unsigned v = *vit; - if(v>max_var) { max_var = v; } + while (!t_vars.back()) { + t_vars.pop_back(); } + unsigned max_var = t_vars.size(); //create binding expr_ref_vector binding(m); binding.resize(max_var+1); - vit = t_vars.begin(); - for(; vit!=vend; ++vit) { - unsigned v = *vit; + + for(unsigned v = 0; v < t_vars.size(); ++v) { + if (!t_vars[v]) { + continue; + } int2ints::entry * e = var_indexes.find_core(v); if(!e) { //we have an unbound variable, so we add an unbound column for it - relation_sort unbound_sort = 0; - - for(unsigned hindex = 0; hindexget_arg(hindex); - if(!is_var(harg) || to_var(harg)->get_idx()!=v) { - continue; - } - unbound_sort = to_var(harg)->get_sort(); - } - if(!unbound_sort) { - // the variable in the interpreted tail is neither bound in the - // uninterpreted tail nor present in the head - std::stringstream sstm; - sstm << "rule with unbound variable #" << v << " in interpreted tail: "; - r->display(m_context, sstm); - throw default_exception(sstm.str()); - } + relation_sort unbound_sort = t_vars[v]; reg_idx new_reg; TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); @@ -759,7 +742,7 @@ namespace datalog { m_instruction_observer.finish_rule(); } - void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, ptr_vector& single_res_expr, + void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, bool & dealloc, instruction_block & acc) { uint_set pos_vars; u_map neg_vars; @@ -782,7 +765,7 @@ namespace datalog { } // populate positive variables: for (unsigned i = 0; i < single_res_expr.size(); ++i) { - expr* e = single_res_expr[i]; + expr* e = single_res_expr[i].get(); if (is_var(e)) { pos_vars.insert(to_var(e)->get_idx()); } diff --git a/src/muz_qe/dl_compiler.h b/src/muz_qe/dl_compiler.h index 1dfb7c7d8..e5b62f54b 100644 --- a/src/muz_qe/dl_compiler.h +++ b/src/muz_qe/dl_compiler.h @@ -177,7 +177,7 @@ namespace datalog { void make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc); - void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, ptr_vector& single_res_expr, + void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, bool & dealloc, instruction_block& acc); void make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, instruction_block & acc); diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index df293aeba..8ac32f4a7 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -823,7 +823,14 @@ namespace datalog { if (similarity_compressor()) { m_transf.register_plugin(alloc(mk_similarity_compressor, *this)); } - m_transf.register_plugin(alloc(datalog::mk_partial_equivalence_transformer, *this)); + m_transf.register_plugin(alloc(mk_partial_equivalence_transformer, *this)); + m_transf.register_plugin(alloc(mk_rule_inliner, *this)); + m_transf.register_plugin(alloc(mk_interp_tail_simplifier, *this)); + + if (get_params().bit_blast()) { + m_transf.register_plugin(alloc(mk_bit_blast, *this, 22000)); + m_transf.register_plugin(alloc(mk_interp_tail_simplifier, *this, 21000)); + } transform_rules(m_transf); } diff --git a/src/muz_qe/dl_relation_manager.cpp b/src/muz_qe/dl_relation_manager.cpp index ef8e6ddd4..986a1f2c4 100644 --- a/src/muz_qe/dl_relation_manager.cpp +++ b/src/muz_qe/dl_relation_manager.cpp @@ -740,7 +740,6 @@ namespace datalog { relation_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col) { relation_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); - TRACE("dl", tout << t.get_plugin().get_name() << " " << value << " " << col << "\n";); if(!res) { relation_mutator_fn * selector = mk_filter_equal_fn(t, value, col); if(selector) { diff --git a/src/muz_qe/dl_util.h b/src/muz_qe/dl_util.h index 974ab5862..2da0463e8 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz_qe/dl_util.h @@ -207,7 +207,9 @@ namespace datalog { static unsigned expr_cont_get_size(app * a) { return a->get_num_args(); } static expr * expr_cont_get(app * a, unsigned i) { return a->get_arg(i); } static unsigned expr_cont_get_size(const ptr_vector & v) { return v.size(); } + static unsigned expr_cont_get_size(const expr_ref_vector & v) { return v.size(); } static expr * expr_cont_get(const ptr_vector & v, unsigned i) { return v[i]; } + static expr * expr_cont_get(const expr_ref_vector & v, unsigned i) { return v[i]; } public: variable_intersection(ast_manager & m) : m_consts(m) {} From e1d5f484f15df705cf05d98c92d9ef11e5142bb1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 23 Apr 2013 08:46:46 -0700 Subject: [PATCH 21/91] simplify result from tactics, remove unused features from difference logic solver Signed-off-by: Nikolaj Bjorner --- src/smt/theory_diff_logic.h | 15 +--- src/smt/theory_diff_logic_def.h | 97 +------------------------ src/tactic/core/ctx_simplify_tactic.cpp | 6 ++ 3 files changed, 11 insertions(+), 107 deletions(-) diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 4140f683c..9c80d8c34 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -259,8 +259,6 @@ namespace smt { theory_var m_zero_int; // cache the variable representing the zero variable. theory_var m_zero_real; // cache the variable representing the zero variable. int_vector m_scc_id; // Cheap equality propagation - bool m_modified_since_eq_prop; // true if new constraints were asserted - // since last eq propagation. eq_prop_info_set m_eq_prop_info_set; // set of existing equality prop infos ptr_vector m_eq_prop_infos; @@ -289,18 +287,14 @@ namespace smt { virtual theory_var mk_var(enode* n); virtual theory_var mk_var(app* n); - - void mark_as_modified_since_eq_prop(); - - void unmark_as_modified_since_eq_prop(); - - bool propagate_cheap_equalities(); - + void compute_delta(); void found_non_diff_logic_expr(expr * n); - bool is_interpreted(app* n) const; + bool is_interpreted(app* n) const { + return get_family_id() == n->get_family_id(); + } void del_clause_eh(clause* cls); @@ -312,7 +306,6 @@ namespace smt { m_arith_eq_adapter(*this, params, m_util), m_zero_int(null_theory_var), m_zero_real(null_theory_var), - m_modified_since_eq_prop(false), m_asserted_qhead(0), m_num_core_conflicts(0), m_num_propagation_calls(0), diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 9f2c97a84..28c14b18f 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -374,15 +374,7 @@ final_check_status theory_diff_logic::final_check_eh() { // either will already be zero (as we don't do mixed constraints). m_graph.set_to_zero(m_zero_int, m_zero_real); SASSERT(is_consistent()); - - -#if 0 - TBD: - if (propagate_cheap_equalities()) { - return FC_CONTINUE; - } -#endif - + if (m_non_diff_logic_exprs) { return FC_GIVEUP; } @@ -540,22 +532,6 @@ bool theory_diff_logic::propagate_atom(atom* a) { return true; } - - -template -void theory_diff_logic::mark_as_modified_since_eq_prop() { - if (!m_modified_since_eq_prop) { - get_context().push_trail(value_trail(m_modified_since_eq_prop)); - m_modified_since_eq_prop = true; - } -} - -template -void theory_diff_logic::unmark_as_modified_since_eq_prop() { - get_context().push_trail(value_trail(m_modified_since_eq_prop)); - m_modified_since_eq_prop = false; -} - template void theory_diff_logic::del_clause_eh(clause* cls) { @@ -802,7 +778,6 @@ theory_var theory_diff_logic::mk_num(app* n, rational const& r) { template theory_var theory_diff_logic::mk_var(enode* n) { - mark_as_modified_since_eq_prop(); theory_var v = theory::mk_var(n); TRACE("diff_logic_vars", tout << "mk_var: " << v << "\n";); m_graph.init_var(v); @@ -810,10 +785,6 @@ theory_var theory_diff_logic::mk_var(enode* n) { return v; } -template -bool theory_diff_logic::is_interpreted(app* n) const { - return n->get_family_id() == get_family_id(); -} template theory_var theory_diff_logic::mk_var(app* n) { @@ -854,7 +825,6 @@ void theory_diff_logic::reset_eh() { m_asserted_atoms .reset(); m_stats .reset(); m_scopes .reset(); - m_modified_since_eq_prop = false; m_asserted_qhead = 0; m_num_core_conflicts = 0; m_num_propagation_calls = 0; @@ -865,70 +835,6 @@ void theory_diff_logic::reset_eh() { } -template -bool theory_diff_logic::propagate_cheap_equalities() { - bool result = false; - TRACE("dl_new_eq", get_context().display(tout);); - context& ctx = get_context(); - region& reg = ctx.get_region(); - SASSERT(m_eq_prop_info_set.empty()); - SASSERT(m_eq_prop_infos.empty()); - if (m_modified_since_eq_prop) { - m_graph.compute_zero_edge_scc(m_scc_id); - int n = get_num_vars(); - for (theory_var v = 0; v < n; v++) { - rational delta_r; - theory_var x_v = expand(true, v, delta_r); - numeral delta(delta_r); - int scc_id = m_scc_id[x_v]; - if (scc_id != -1) { - delta += m_graph.get_assignment(x_v); - TRACE("eq_scc", tout << v << " " << x_v << " " << scc_id << " " << delta << "\n";); - eq_prop_info info(scc_id, delta); - typename eq_prop_info_set::entry * entry = m_eq_prop_info_set.find_core(&info); - if (entry == 0) { - eq_prop_info * new_info = alloc(eq_prop_info, scc_id, delta, v); - m_eq_prop_info_set.insert(new_info); - m_eq_prop_infos.push_back(new_info); - } - else { - // new equality found - theory_var r = entry->get_data()->get_root(); - - enode * n1 = get_enode(v); - enode * n2 = get_enode(r); - if (n1->get_root() != n2->get_root()) { - // r may be an alias (i.e., it is not realy in the graph). So, I should expand it. - // nsb: ?? - rational r_delta; - theory_var x_r = expand(true, r, r_delta); - - justification* j = new (reg) implied_eq_justification(*this, x_v, x_r, m_graph.get_timestamp()); - (void)j; - - m_stats.m_num_th2core_eqs++; - // TBD: get equality into core. - - NOT_IMPLEMENTED_YET(); - // new_eq_eh(x_v, x_r, *j); - result = true; - } - } - } - } - m_eq_prop_info_set.reset(); - std::for_each(m_eq_prop_infos.begin(), m_eq_prop_infos.end(), delete_proc()); - m_eq_prop_infos.reset(); - unmark_as_modified_since_eq_prop(); - } - - TRACE("dl_new_eq", get_context().display(tout);); - SASSERT(!m_modified_since_eq_prop); - - return result; -} - - template void theory_diff_logic::compute_delta() { m_delta = rational(1); @@ -1087,7 +993,6 @@ void theory_diff_logic::new_eq_or_diseq(bool is_eq, theory_var v1, theory_v // assign the corresponding equality literal. // - mark_as_modified_since_eq_prop(); app_ref eq(m), s2(m), t2(m); app* s1 = get_enode(s)->get_owner(); diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 831efa087..be4dfd516 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -371,6 +371,12 @@ struct ctx_simplify_tactic::imp { if (!modified) { r = t; } + if (new_new_args.empty()) { + r = OR?m.mk_false():m.mk_true(); + } + else if (new_new_args.size() == 1) { + r = new_new_args[0]; + } else { std::reverse(new_new_args.c_ptr(), new_new_args.c_ptr() + new_new_args.size()); m_mk_app(t->get_decl(), new_new_args.size(), new_new_args.c_ptr(), r); From eead1bbc4878ffd5362cb465a8cb565734c0aca3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 23 Apr 2013 09:24:39 -0700 Subject: [PATCH 22/91] missing else Signed-off-by: Nikolaj Bjorner --- src/tactic/core/ctx_simplify_tactic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index be4dfd516..2c2afab6f 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -371,7 +371,7 @@ struct ctx_simplify_tactic::imp { if (!modified) { r = t; } - if (new_new_args.empty()) { + else if (new_new_args.empty()) { r = OR?m.mk_false():m.mk_true(); } else if (new_new_args.size() == 1) { From d849dbf21f218663e9c9ffa01eea168c7b7765c9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 23 Apr 2013 00:22:14 -0700 Subject: [PATCH 23/91] remove pointer comparisons/hash Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_base.h | 11 +++++--- src/muz_qe/dl_compiler.cpp | 2 +- src/muz_qe/dl_compiler.h | 3 ++- src/muz_qe/dl_context.cpp | 4 +-- src/muz_qe/dl_finite_product_relation.h | 2 +- src/muz_qe/dl_mk_filter_rules.h | 8 ++++-- src/muz_qe/dl_mk_magic_sets.h | 7 +++++- src/muz_qe/dl_mk_similarity_compressor.cpp | 10 +++++--- src/muz_qe/dl_product_relation.h | 6 ++++- src/muz_qe/dl_relation_manager.h | 2 +- src/muz_qe/dl_rule_transformer.cpp | 3 +++ src/muz_qe/dl_sieve_relation.h | 2 +- src/muz_qe/dl_sparse_table.h | 2 +- src/muz_qe/dl_table.h | 2 +- src/muz_qe/dl_util.h | 29 ++++++++++++++-------- src/muz_qe/rel_context.cpp | 2 ++ src/util/hash.h | 6 +++++ src/util/vector.h | 17 ++++++++----- 18 files changed, 81 insertions(+), 37 deletions(-) diff --git a/src/muz_qe/dl_base.h b/src/muz_qe/dl_base.h index e7243cb70..491bb261d 100644 --- a/src/muz_qe/dl_base.h +++ b/src/muz_qe/dl_base.h @@ -656,6 +656,7 @@ namespace datalog { typedef sort * relation_sort; typedef ptr_vector relation_signature_base0; + typedef ptr_hash relation_sort_hash; typedef app * relation_element; typedef app_ref relation_element_ref; @@ -739,8 +740,8 @@ namespace datalog { struct hash { unsigned operator()(relation_signature const& s) const { - relation_sort const* sorts = s.c_ptr(); - return string_hash(reinterpret_cast(sorts), sizeof(*sorts)*s.size(), 12); } + return obj_vector_hash(s); + } }; struct eq { @@ -816,9 +817,11 @@ namespace datalog { typedef uint64 table_sort; typedef svector table_signature_base0; + typedef uint64_hash table_sort_hash; typedef uint64 table_element; typedef svector table_fact; + typedef uint64_hash table_element_hash; struct table_traits { typedef table_plugin plugin; @@ -881,8 +884,8 @@ namespace datalog { public: struct hash { unsigned operator()(table_signature const& s) const { - table_sort const* sorts = s.c_ptr(); - return string_hash(reinterpret_cast(sorts), sizeof(*sorts)*s.size(), 12); } + return svector_hash()(s); + } }; struct eq { diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index 34dd94214..e4528426a 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -36,7 +36,7 @@ namespace datalog { } void compiler::ensure_predicate_loaded(func_decl * pred, instruction_block & acc) { - pred2idx::entry * e = m_pred_regs.insert_if_not_there2(pred, UINT_MAX); + pred2idx::obj_map_entry * e = m_pred_regs.insert_if_not_there2(pred, UINT_MAX); if(e->get_data().m_value!=UINT_MAX) { //predicate is already loaded return; diff --git a/src/muz_qe/dl_compiler.h b/src/muz_qe/dl_compiler.h index e5b62f54b..8f40f814a 100644 --- a/src/muz_qe/dl_compiler.h +++ b/src/muz_qe/dl_compiler.h @@ -41,7 +41,8 @@ namespace datalog { typedef hashtable int_set; typedef u_map int2int; typedef u_map int2ints; - typedef map,ptr_eq > pred2idx; + typedef obj_map pred2idx; +// typedef map,ptr_eq > pred2idx; typedef unsigned_vector var_vector; typedef ptr_vector func_decl_vector; diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 8ac32f4a7..50915946a 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -813,9 +813,7 @@ namespace datalog { void context::transform_rules() { m_transf.reset(); - if (get_params().filter_rules()) { - m_transf.register_plugin(alloc(mk_filter_rules, *this)); - } + m_transf.register_plugin(alloc(mk_filter_rules, *this)); m_transf.register_plugin(alloc(mk_simple_joins, *this)); if (unbound_compressor()) { m_transf.register_plugin(alloc(mk_unbound_compressor, *this)); diff --git a/src/muz_qe/dl_finite_product_relation.h b/src/muz_qe/dl_finite_product_relation.h index 165422661..d5181d122 100644 --- a/src/muz_qe/dl_finite_product_relation.h +++ b/src/muz_qe/dl_finite_product_relation.h @@ -47,7 +47,7 @@ namespace datalog { } struct hash { unsigned operator()(const rel_spec & o) const { - return o.m_inner_kind^int_vector_hash(o.m_table_cols); + return o.m_inner_kind^svector_hash()(o.m_table_cols); } }; }; diff --git a/src/muz_qe/dl_mk_filter_rules.h b/src/muz_qe/dl_mk_filter_rules.h index 4a247fdb5..91751f9b8 100644 --- a/src/muz_qe/dl_mk_filter_rules.h +++ b/src/muz_qe/dl_mk_filter_rules.h @@ -45,14 +45,18 @@ namespace datalog { filter_key(ast_manager & m) : new_pred(m), filter_args(m) {} unsigned hash() const { - return new_pred->hash() ^ int_vector_hash(filter_args); + unsigned r = new_pred->hash(); + for (unsigned i = 0; i < filter_args.size(); ++i) { + r ^= filter_args[i]->hash(); + } + return r; } bool operator==(const filter_key & o) const { return o.new_pred==new_pred && vectors_equal(o.filter_args, filter_args); } }; - typedef map, deref_eq > filter_cache; + typedef obj_map filter_cache; context & m_context; ast_manager & m_manager; diff --git a/src/muz_qe/dl_mk_magic_sets.h b/src/muz_qe/dl_mk_magic_sets.h index 507f6c2bf..dfc66e7ea 100644 --- a/src/muz_qe/dl_mk_magic_sets.h +++ b/src/muz_qe/dl_mk_magic_sets.h @@ -47,6 +47,11 @@ namespace datalog { AD_BOUND }; + struct a_flag_hash { + typedef a_flag data; + unsigned operator()(a_flag x) const { return x; } + }; + struct adornment : public svector { void populate(app * lit, const var_idx_set & bound_vars); @@ -71,7 +76,7 @@ namespace datalog { return m_pred==o.m_pred && m_adornment==o.m_adornment; } unsigned hash() const { - return m_pred->hash()^int_vector_hash(m_adornment); + return m_pred->hash()^svector_hash()(m_adornment); } }; diff --git a/src/muz_qe/dl_mk_similarity_compressor.cpp b/src/muz_qe/dl_mk_similarity_compressor.cpp index 8e77785b8..b600811f0 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.cpp +++ b/src/muz_qe/dl_mk_similarity_compressor.cpp @@ -29,6 +29,7 @@ namespace datalog { m_manager(ctx.get_manager()), m_threshold_count(ctx.similarity_compressor_threshold()), m_result_rules(ctx.get_rule_manager()), + m_modified(false), m_pinned(m_manager) { SASSERT(m_threshold_count>1); } @@ -55,6 +56,9 @@ namespace datalog { return (a>b) ? 1 : ( (a==b) ? 0 : -1); } + template + static int aux_compare(T* a, T* b); + static int compare_var_args(app* t1, app* t2) { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; @@ -88,7 +92,7 @@ namespace datalog { if ((skip_countdown--) == 0) { continue; } - res = aux_compare(t1->get_arg(i), t2->get_arg(i)); + res = aux_compare(t1->get_arg(i)->get_id(), t2->get_arg(i)->get_id()); if (res!=0) { return res; } } return 0; @@ -113,7 +117,7 @@ namespace datalog { for (int i=-1; iget_decl(), t2->get_decl()); + res = aux_compare(t1->get_decl()->get_id(), t2->get_decl()->get_id()); if (res!=0) { return res; } res = compare_var_args(t1, t2); if (res!=0) { return res; } @@ -121,7 +125,7 @@ namespace datalog { unsigned tail_sz = r1->get_tail_size(); for (unsigned i=pos_tail_sz; iget_tail(i), r2->get_tail(i)); + res = aux_compare(r1->get_tail(i)->get_id(), r2->get_tail(i)->get_id()); if (res!=0) { return res; } } diff --git a/src/muz_qe/dl_product_relation.h b/src/muz_qe/dl_product_relation.h index c5e755939..91c2286a7 100644 --- a/src/muz_qe/dl_product_relation.h +++ b/src/muz_qe/dl_product_relation.h @@ -40,8 +40,12 @@ namespace datalog { class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; + struct fid_hash { + typedef family_id data; + unsigned operator()(data x) const { return static_cast(x); } + }; - rel_spec_store m_spec_store; + rel_spec_store > m_spec_store; family_id get_relation_kind(const product_relation & r); diff --git a/src/muz_qe/dl_relation_manager.h b/src/muz_qe/dl_relation_manager.h index 26008a830..f22ae7923 100644 --- a/src/muz_qe/dl_relation_manager.h +++ b/src/muz_qe/dl_relation_manager.h @@ -605,7 +605,7 @@ namespace datalog { /** This is a helper class for relation_plugins whose relations can be of various kinds. */ - template, class Eq=vector_eq_proc > + template > class rel_spec_store { typedef relation_signature::hash r_hash; typedef relation_signature::eq r_eq; diff --git a/src/muz_qe/dl_rule_transformer.cpp b/src/muz_qe/dl_rule_transformer.cpp index 0cad08cb4..21021ca43 100644 --- a/src/muz_qe/dl_rule_transformer.cpp +++ b/src/muz_qe/dl_rule_transformer.cpp @@ -107,6 +107,9 @@ namespace datalog { tout << typeid(p).name()<<":\n"; new_rules->display(tout); ); + IF_VERBOSE(1, new_rules->get_rule(0)->display(m_context, verbose_stream() << typeid(p).name() <<"\n");); + IF_VERBOSE(1, verbose_stream() << new_rules->get_dependencies().begin()->m_key->get_name() << "\n";); + } if (modified) { rules.replace_rules(*new_rules); diff --git a/src/muz_qe/dl_sieve_relation.h b/src/muz_qe/dl_sieve_relation.h index d6df3af55..551f5d705 100644 --- a/src/muz_qe/dl_sieve_relation.h +++ b/src/muz_qe/dl_sieve_relation.h @@ -52,7 +52,7 @@ namespace datalog { struct hash { unsigned operator()(const rel_spec & s) const { - return int_vector_hash(s.m_inner_cols)^s.m_inner_kind; + return svector_hash()(s.m_inner_cols)^s.m_inner_kind; } }; }; diff --git a/src/muz_qe/dl_sparse_table.h b/src/muz_qe/dl_sparse_table.h index 3920836e6..010277b6b 100644 --- a/src/muz_qe/dl_sparse_table.h +++ b/src/muz_qe/dl_sparse_table.h @@ -359,7 +359,7 @@ namespace datalog { typedef svector key_spec; //sequence of columns in a key typedef svector key_value; //values of key columns - typedef map, + typedef map, vector_eq_proc > key_index_map; static const store_offset NO_RESERVE = UINT_MAX; diff --git a/src/muz_qe/dl_table.h b/src/muz_qe/dl_table.h index 8dc0a355b..3a240c337 100644 --- a/src/muz_qe/dl_table.h +++ b/src/muz_qe/dl_table.h @@ -73,7 +73,7 @@ namespace datalog { class our_iterator_core; - typedef hashtable, + typedef hashtable, vector_eq_proc > storage; storage m_data; diff --git a/src/muz_qe/dl_util.h b/src/muz_qe/dl_util.h index 2da0463e8..96bc8c326 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz_qe/dl_util.h @@ -587,17 +587,31 @@ namespace datalog { } template - unsigned int_vector_hash(const T & cont) { - return string_hash(reinterpret_cast(cont.c_ptr()), - cont.size()*sizeof(typename T::data), 0); + struct default_obj_chash { + unsigned operator()(T const& cont, unsigned i) const { + return cont[i]->hash(); + } + }; + template + unsigned obj_vector_hash(const T & cont) { + return get_composite_hash(cont, cont.size(),default_kind_hash_proc(), default_obj_chash()); } template - struct int_vector_hash_proc { + struct obj_vector_hash_proc { unsigned operator()(const T & cont) const { - return int_vector_hash(cont); + return obj_vector_hash(cont); } }; + + template + struct svector_hash_proc { + unsigned operator()(const svector & cont) const { + return svector_hash()(cont); + } + }; + + template struct vector_eq_proc { bool operator()(const T & c1, const T & c2) const { return vectors_equal(c1, c2); } @@ -765,11 +779,6 @@ namespace datalog { // // ----------------------------------- - struct uint64_hash { - typedef uint64 data; - unsigned operator()(uint64 x) const { return hash_ull(x); } - }; - template void universal_delete(T* ptr) { dealloc(ptr); diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index ef5639279..be6d999b6 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -113,10 +113,12 @@ namespace datalog { TRACE("dl", m_context.display(tout);); while (true) { + m_ectx.reset(); m_code.reset(); termination_code.reset(); m_context.ensure_closed(); + IF_VERBOSE(1, verbose_stream() << "num rules: " << m_context.get_rules().get_num_rules() << "\n";); m_context.transform_rules(); if (m_context.canceled()) { result = l_undef; diff --git a/src/util/hash.h b/src/util/hash.h index 3c7e50a6c..4232dd2af 100644 --- a/src/util/hash.h +++ b/src/util/hash.h @@ -20,6 +20,7 @@ Revision History: #define _HASH_H_ #include +#include"util.h" #ifndef __fallthrough #define __fallthrough @@ -142,6 +143,11 @@ struct size_t_hash { unsigned operator()(size_t x) const { return static_cast(x); } }; +struct uint64_hash { + typedef uint64 data; + unsigned operator()(uint64 x) const { return static_cast(x); } +}; + struct bool_hash { typedef bool data; unsigned operator()(bool x) const { return static_cast(x); } diff --git a/src/util/vector.h b/src/util/vector.h index 704452d0f..c9ed900a9 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -432,24 +432,29 @@ typedef svector unsigned_vector; typedef svector char_vector; typedef svector double_vector; -template -struct vector_hash { +template +struct vector_hash_tpl { Hash m_hash; - typedef vector data; + typedef Vec data; unsigned operator()(data const& v, unsigned idx) const { return m_hash(v[idx]); } - vector_hash(Hash const& h = Hash()):m_hash(h) {} + vector_hash_tpl(Hash const& h = Hash()):m_hash(h) {} unsigned operator()(data const& v) const { if (v.empty()) { return 778; } - return get_composite_hash, vector_hash>(v, v.size()); + return get_composite_hash, vector_hash_tpl>(v, v.size()); } - }; +template +struct vector_hash : public vector_hash_tpl > {}; + +template +struct svector_hash : public vector_hash_tpl > {}; + #endif /* _VECTOR_H_ */ From 6250a29602026b8ecd79ebecb90c8951cb8a3dcf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 23 Apr 2013 10:02:37 -0700 Subject: [PATCH 24/91] resolved conflicts Signed-off-by: Nikolaj Bjorner --- src/smt/theory_diff_logic_def.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 6700b4c37..aeb4f73d6 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -374,12 +374,6 @@ final_check_status theory_diff_logic::final_check_eh() { // either will already be zero (as we don't do mixed constraints). m_graph.set_to_zero(m_zero_int, m_zero_real); SASSERT(is_consistent()); -<<<<<<< HEAD - - -======= - ->>>>>>> d849dbf21f218663e9c9ffa01eea168c7b7765c9 if (m_non_diff_logic_exprs) { return FC_GIVEUP; } From 08eb85fe3def5ab5d2ded01d1a14192119bf6996 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 23 Apr 2013 10:02:44 -0700 Subject: [PATCH 25/91] minor cleanup Signed-off-by: Nuno Lopes --- src/muz_qe/dl_compiler.h | 1 - src/muz_qe/dl_rule_transformer.cpp | 3 --- src/muz_qe/fixedpoint_params.pyg | 1 - src/muz_qe/rel_context.cpp | 2 -- 4 files changed, 7 deletions(-) diff --git a/src/muz_qe/dl_compiler.h b/src/muz_qe/dl_compiler.h index 8f40f814a..78b4623de 100644 --- a/src/muz_qe/dl_compiler.h +++ b/src/muz_qe/dl_compiler.h @@ -42,7 +42,6 @@ namespace datalog { typedef u_map int2int; typedef u_map int2ints; typedef obj_map pred2idx; -// typedef map,ptr_eq > pred2idx; typedef unsigned_vector var_vector; typedef ptr_vector func_decl_vector; diff --git a/src/muz_qe/dl_rule_transformer.cpp b/src/muz_qe/dl_rule_transformer.cpp index 21021ca43..0cad08cb4 100644 --- a/src/muz_qe/dl_rule_transformer.cpp +++ b/src/muz_qe/dl_rule_transformer.cpp @@ -107,9 +107,6 @@ namespace datalog { tout << typeid(p).name()<<":\n"; new_rules->display(tout); ); - IF_VERBOSE(1, new_rules->get_rule(0)->display(m_context, verbose_stream() << typeid(p).name() <<"\n");); - IF_VERBOSE(1, verbose_stream() << new_rules->get_dependencies().begin()->m_key->get_name() << "\n";); - } if (modified) { rules.replace_rules(*new_rules); diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index fd7dbdcae..774559cdb 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -13,7 +13,6 @@ def_module_params('fixedpoint', ('unbound_compressor', BOOL, True, "auxiliary relations will be introduced to avoid unbound variables in rule heads"), ('similarity_compressor', BOOL, True, "(DATALOG) rules that differ only in values of constants will be merged into a single rule"), ('similarity_compressor_threshold', UINT, 11, "(DATALOG) if similarity_compressor is on, this value determines how many similar rules there must be in order for them to be merged"), - ('filter_rules', BOOL, True, "(DATALOG) apply filter compression on rules"), ('all_or_nothing_deltas', BOOL, False, "(DATALOG) compile rules so that it is enough for the delta relation in union and widening operations to determine only whether the updated relation was modified or not"), ('compile_with_widening', BOOL, False, "(DATALOG) widening will be used to compile recursive rules"), ('eager_emptiness_checking', BOOL, True, "(DATALOG) emptiness of affected relations will be checked after each instruction, so that we may ommit unnecessary instructions"), diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index be6d999b6..ef5639279 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -113,12 +113,10 @@ namespace datalog { TRACE("dl", m_context.display(tout);); while (true) { - m_ectx.reset(); m_code.reset(); termination_code.reset(); m_context.ensure_closed(); - IF_VERBOSE(1, verbose_stream() << "num rules: " << m_context.get_rules().get_num_rules() << "\n";); m_context.transform_rules(); if (m_context.canceled()) { result = l_undef; From 12b092c45fdde206bbc12e2446cb786d021ef1a3 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 23 Apr 2013 11:27:27 -0700 Subject: [PATCH 26/91] [datalog] restore the old (linear) cycle breaker force the compiler to use all preds as global deltas for correctness. This is a temporary fix. Signed-off-by: Nuno Lopes --- src/muz_qe/dl_compiler.cpp | 58 +- src/muz_qe/dl_compiler.cpp.orig | 1245 +++++++++++++++++++++++++++++++ 2 files changed, 1263 insertions(+), 40 deletions(-) create mode 100644 src/muz_qe/dl_compiler.cpp.orig diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index e4528426a..57d9880c0 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -832,19 +832,19 @@ namespace datalog { typedef rule_dependencies::item_set item_set; //set of T rule_dependencies & m_deps; - rule_set const& m_rules; - context& m_context; item_set & m_removed; svector m_stack; + ast_mark m_stack_content; ast_mark m_visited; void traverse(T v) { - SASSERT(!m_visited.is_marked(v)); - if (m_removed.contains(v)) { + SASSERT(!m_stack_content.is_marked(v)); + if(m_visited.is_marked(v) || m_removed.contains(v)) { return; } m_stack.push_back(v); + m_stack_content.mark(v, true); m_visited.mark(v, true); const item_set & deps = m_deps.get_deps(v); @@ -852,49 +852,22 @@ namespace datalog { item_set::iterator end = deps.end(); for(; it!=end; ++it) { T d = *it; - if (m_visited.is_marked(d)) { + if(m_stack_content.is_marked(d)) { //TODO: find the best vertex to remove in the cycle - remove_from_stack(); - continue; + m_removed.insert(v); + break; } traverse(d); } SASSERT(m_stack.back()==v); m_stack.pop_back(); - m_visited.mark(v, false); - } - - void remove_from_stack() { - for (unsigned i = 0; i < m_stack.size(); ++i) { - func_decl* p = m_stack[i]; - if (m_context.has_facts(p)) { - m_removed.insert(p); - return; - } - - rule_vector const& rules = m_rules.get_predicate_rules(p); - unsigned stratum = m_rules.get_predicate_strat(p); - for (unsigned j = 0; j < rules.size(); ++j) { - rule const& r = *rules[j]; - bool ok = true; - for (unsigned k = 0; ok && k < r.get_uninterpreted_tail_size(); ++k) { - ok = m_rules.get_predicate_strat(r.get_decl(k)) < stratum; - } - if (ok) { - m_removed.insert(p); - return; - } - } - } - - // nothing was found. - m_removed.insert(m_stack.back()); + m_stack_content.mark(v, false); } public: - cycle_breaker(rule_dependencies & deps, rule_set const& rules, context& ctx, item_set & removed) - : m_deps(deps), m_rules(rules), m_context(ctx), m_removed(removed) { SASSERT(removed.empty()); } + cycle_breaker(rule_dependencies & deps, item_set & removed) + : m_deps(deps), m_removed(removed) { SASSERT(removed.empty()); } void operator()() { rule_dependencies::iterator it = m_deps.begin(); @@ -916,7 +889,7 @@ namespace datalog { rule_dependencies deps(m_rule_set.get_dependencies()); deps.restrict(preds); - cycle_breaker(deps, m_rule_set, m_context, global_deltas)(); + cycle_breaker(deps, global_deltas)(); VERIFY( deps.sort_deps(ordered_preds) ); //the predicates that were removed to get acyclic induced subgraph are put last @@ -1052,12 +1025,17 @@ namespace datalog { } func_decl_vector preds_vector; - func_decl_set global_deltas; + func_decl_set global_deltas_dummy; - detect_chains(head_preds, preds_vector, global_deltas); + detect_chains(head_preds, preds_vector, global_deltas_dummy); + /* + FIXME: right now we use all preds as global deltas for correctness purposes func_decl_set local_deltas(head_preds); set_difference(local_deltas, global_deltas); + */ + func_decl_set local_deltas; + func_decl_set global_deltas(head_preds); pred2idx d_global_src; //these deltas serve as sources of tuples for rule evaluation inside the loop get_fresh_registers(global_deltas, d_global_src); diff --git a/src/muz_qe/dl_compiler.cpp.orig b/src/muz_qe/dl_compiler.cpp.orig new file mode 100644 index 000000000..e4528426a --- /dev/null +++ b/src/muz_qe/dl_compiler.cpp.orig @@ -0,0 +1,1245 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + dl_compiler.cpp + +Abstract: + + + +Author: + + Krystof Hoder (t-khoder) 2010-09-14. + +Revision History: + +--*/ + + +#include +#include"ref_vector.h" +#include"dl_context.h" +#include"dl_rule.h" +#include"dl_util.h" +#include"dl_compiler.h" +#include"ast_pp.h" +#include"ast_smt2_pp.h" + + +namespace datalog { + + void compiler::reset() { + m_pred_regs.reset(); + m_new_reg = 0; + } + + void compiler::ensure_predicate_loaded(func_decl * pred, instruction_block & acc) { + pred2idx::obj_map_entry * e = m_pred_regs.insert_if_not_there2(pred, UINT_MAX); + if(e->get_data().m_value!=UINT_MAX) { + //predicate is already loaded + return; + } + relation_signature sig; + m_context.get_rel_context().get_rmanager().from_predicate(pred, sig); + reg_idx reg = get_fresh_register(sig); + e->get_data().m_value=reg; + + acc.push_back(instruction::mk_load(m_context.get_manager(), pred, reg)); + } + + void compiler::make_join(reg_idx t1, reg_idx t2, const variable_intersection & vars, reg_idx & result, + instruction_block & acc) { + relation_signature res_sig; + relation_signature::from_join(m_reg_signatures[t1], m_reg_signatures[t2], vars.size(), + vars.get_cols1(), vars.get_cols2(), res_sig); + result = get_fresh_register(res_sig); + acc.push_back(instruction::mk_join(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), result)); + } + + void compiler::make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, + const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc) { + relation_signature aux_sig; + relation_signature sig1 = m_reg_signatures[t1]; + relation_signature sig2 = m_reg_signatures[t2]; + relation_signature::from_join(sig1, sig2, vars.size(), vars.get_cols1(), vars.get_cols2(), aux_sig); + relation_signature res_sig; + relation_signature::from_project(aux_sig, removed_cols.size(), removed_cols.c_ptr(), + res_sig); + result = get_fresh_register(res_sig); + + acc.push_back(instruction::mk_join_project(t1, t2, vars.size(), vars.get_cols1(), + vars.get_cols2(), removed_cols.size(), removed_cols.c_ptr(), result)); + } + + void compiler::make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col, + reg_idx & result, instruction_block & acc) { + relation_signature res_sig; + relation_signature::from_project(m_reg_signatures[src], 1, &col, res_sig); + result = get_fresh_register(res_sig); + acc.push_back(instruction::mk_select_equal_and_project(m_context.get_manager(), + src, val, col, result)); + } + + void compiler::make_clone(reg_idx src, reg_idx & result, instruction_block & acc) { + relation_signature sig = m_reg_signatures[src]; + result = get_fresh_register(sig); + acc.push_back(instruction::mk_clone(src, result)); + } + + void compiler::make_union(reg_idx src, reg_idx tgt, reg_idx delta, bool use_widening, + instruction_block & acc) { + SASSERT(m_reg_signatures[src]==m_reg_signatures[tgt]); + SASSERT(delta==execution_context::void_register || m_reg_signatures[src]==m_reg_signatures[delta]); + + if (use_widening) { + acc.push_back(instruction::mk_widen(src, tgt, delta)); + } + else { + acc.push_back(instruction::mk_union(src, tgt, delta)); + } + } + + void compiler::make_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, + reg_idx & result, instruction_block & acc) { + SASSERT(col_cnt>0); + + relation_signature res_sig; + relation_signature::from_project(m_reg_signatures[src], col_cnt, removed_cols, res_sig); + result = get_fresh_register(res_sig); + acc.push_back(instruction::mk_projection(src, col_cnt, removed_cols, result)); + } + + compiler::reg_idx compiler::get_fresh_register(const relation_signature & sig) { + //since we might be resizing the m_reg_signatures vector, the argument must not point inside it + SASSERT((&sig>=m_reg_signatures.end()) || (&sigto_formula(e); + verbose_stream() << "Compiling unsafe rule column " << col_idx << "\n" + << mk_ismt2_pp(e, m_context.get_manager()) << "\n"; + }); + reg_idx total_table; + if (!m_total_registers.find(s, pred, total_table)) { + total_table = get_single_column_register(s); + relation_signature sig; + sig.push_back(s); + m_top_level_code.push_back(instruction::mk_total(sig, pred, total_table)); + m_total_registers.insert(s, pred, total_table); + } + if(src == execution_context::void_register) { + result = total_table; + dealloc = false; + } + else { + variable_intersection empty_vars(m_context.get_manager()); + make_join(src, total_table, empty_vars, result, acc); + dealloc = true; + } + } + + void compiler::make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, + instruction_block & acc) { + SASSERT(sig.empty()); + TRACE("dl", tout << "Adding unbound column " << mk_pp(pred, m_context.get_manager()) << "\n";); + if (m_empty_tables_registers.find(pred, result)) + return; + + result = get_fresh_register(sig); + m_top_level_code.push_back(instruction::mk_total(sig, pred, result)); + m_empty_tables_registers.insert(pred, result); + } + + + void compiler::make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, + instruction_block & acc) { + + relation_signature & src_sig = m_reg_signatures[src]; + reg_idx single_col_reg; + unsigned src_col_cnt = src_sig.size(); + if(src_col_cnt==1) { + single_col_reg = src; + } + else { + unsigned_vector removed_cols; + for(unsigned i=0; i & acis0, + reg_idx & result, + bool & dealloc, + instruction_block & acc) { + + TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); + + unsigned col_cnt = acis0.size(); + reg_idx curr = src; + + relation_signature empty_signature; + + relation_signature * curr_sig; + if(curr!=execution_context::void_register) { + curr_sig = & m_reg_signatures[curr]; + } + else { + curr_sig = & empty_signature; + } + unsigned src_col_cnt=curr_sig->size(); + + svector acis(acis0); + int2int handled_unbound; + + //first remove unused source columns + int_set referenced_src_cols; + for(unsigned i=0; isize(); + if(acis[i].kind==ACK_CONSTANT) { + make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, new_curr, new_dealloc, acc); + } + else { + SASSERT(acis[i].kind==ACK_UNBOUND_VAR); + make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, new_curr, new_dealloc, acc); + handled_unbound.insert(acis[i].var_index,bound_column_index); + } + if (dealloc) + make_dealloc_non_void(curr, acc); + dealloc = new_dealloc; + curr=new_curr; + curr_sig = & m_reg_signatures[curr]; + SASSERT(bound_column_index==curr_sig->size()-1); + } + SASSERT((*curr_sig)[bound_column_index]==acis[i].domain); + acis[i].kind=ACK_BOUND_VAR; + acis[i].source_column=bound_column_index; + } + + //duplicate needed source columns + int_set used_cols; + for(unsigned i=0; isize()-1; + SASSERT((*curr_sig)[bound_column_index]==acis[i].domain); + acis[i].source_column=bound_column_index; + } + + //reorder source columns to match target + SASSERT(curr_sig->size()==col_cnt); //now the intermediate table is a permutation + for(unsigned i=0; i=i); //columns below i are already reordered + SASSERT(nextget_num_args(); + for(unsigned i = 0; iget_arg(i); + if(!is_var(e) || globals.get(to_var(e)->get_idx())!=0) { + continue; + } + res.push_back(i+ofs); + } + } + + void compiler::get_local_indexes_for_projection(rule * r, unsigned_vector & res) { + SASSERT(r->get_positive_tail_size()==2); + ast_manager & m = m_context.get_manager(); + rule_counter counter; + counter.count_rule_vars(m, r); + app * t1 = r->get_tail(0); + app * t2 = r->get_tail(1); + counter.count_vars(m, t1, -1); + counter.count_vars(m, t2, -1); + get_local_indexes_for_projection(t1, counter, 0, res); + get_local_indexes_for_projection(t2, counter, t1->get_num_args(), res); + } + + void compiler::compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs, + reg_idx delta_reg, bool use_widening, instruction_block & acc) { + + ast_manager & m = m_context.get_manager(); + m_instruction_observer.start_rule(r); + + const app * h = r->get_head(); + unsigned head_len = h->get_num_args(); + func_decl * head_pred = h->get_decl(); + + TRACE("dl", r->display(m_context, tout); ); + + unsigned pt_len = r->get_positive_tail_size(); + SASSERT(pt_len<=2); //we require rules to be processed by the mk_simple_joins rule transformer plugin + + reg_idx single_res; + expr_ref_vector single_res_expr(m); + + //used to save on filter_identical instructions where the check is already done + //by the join operation + unsigned second_tail_arg_ofs; + + // whether to dealloc the previous result + bool dealloc = true; + + if(pt_len == 2) { + reg_idx t1_reg=tail_regs[0]; + reg_idx t2_reg=tail_regs[1]; + app * a1 = r->get_tail(0); + app * a2 = r->get_tail(1); + SASSERT(m_reg_signatures[t1_reg].size()==a1->get_num_args()); + SASSERT(m_reg_signatures[t2_reg].size()==a2->get_num_args()); + + variable_intersection a1a2(m_context.get_manager()); + a1a2.populate(a1,a2); + + unsigned_vector removed_cols; + get_local_indexes_for_projection(r, removed_cols); + + if(removed_cols.empty()) { + make_join(t1_reg, t2_reg, a1a2, single_res, acc); + } + else { + make_join_project(t1_reg, t2_reg, a1a2, removed_cols, single_res, acc); + } + + unsigned rem_index = 0; + unsigned rem_sz = removed_cols.size(); + unsigned a1len=a1->get_num_args(); + for(unsigned i=0; i=i); + if(rem_indexget_arg(i)); + } + second_tail_arg_ofs = single_res_expr.size(); + unsigned a2len=a2->get_num_args(); + for(unsigned i=0; i=i+a1len); + if(rem_indexget_arg(i)); + } + SASSERT(rem_index==rem_sz); + } + else if(pt_len==1) { + reg_idx t_reg=tail_regs[0]; + app * a = r->get_tail(0); + SASSERT(m_reg_signatures[t_reg].size()==a->get_num_args()); + + single_res = t_reg; + + unsigned n=a->get_num_args(); + for(unsigned i=0; iget_arg(i); + if(is_app(arg)) { + app * c = to_app(arg); //argument is a constant + SASSERT(c->get_num_args()==0); + SASSERT(m_context.get_decl_util().is_numeral_ext(arg)); + reg_idx new_reg; + make_select_equal_and_project(single_res, c, single_res_expr.size(), new_reg, acc); + if(single_res!=t_reg) { + //since single_res is a local register, we deallocate it + make_dealloc_non_void(single_res, acc); + } + single_res = new_reg; + } + else { + SASSERT(is_var(arg)); + single_res_expr.push_back(arg); + } + } + if(single_res==t_reg) { + dealloc = false; + } + + } + else { + SASSERT(pt_len==0); + + //single_res register should never be used in this case + single_res=execution_context::void_register; + } + + add_unbound_columns_for_negation(r, head_pred, single_res, single_res_expr, dealloc, acc); + + int2ints var_indexes; + + reg_idx filtered_res = single_res; + + { + //enforce equality to constants + unsigned srlen=single_res_expr.size(); + SASSERT((single_res==execution_context::void_register) ? (srlen==0) : (srlen==m_reg_signatures[single_res].size())); + for(unsigned i=0; iget_idx(); + int2ints::entry * e = var_indexes.insert_if_not_there2(var_num, unsigned_vector()); + e->get_data().m_value.push_back(i); + } + } + } + + //enforce equality of columns + int2ints::iterator vit=var_indexes.begin(); + int2ints::iterator vend=var_indexes.end(); + for(; vit!=vend; ++vit) { + int2ints::key_data & k = *vit; + unsigned_vector & indexes = k.m_value; + if(indexes.size()==1) { + continue; + } + SASSERT(indexes.size()>1); + if(pt_len==2 && indexes[0]=second_tail_arg_ofs) { + //If variable appears in multiple tails, the identicity will already be enforced by join. + //(If behavior the join changes so that it is not enforced anymore, remove this + //condition!) + continue; + } + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); + acc.push_back(instruction::mk_filter_identical(filtered_res, indexes.size(), indexes.c_ptr())); + dealloc = true; + } + + //enforce negative predicates + unsigned ut_len=r->get_uninterpreted_tail_size(); + for(unsigned i=pt_len; iget_tail(i); + func_decl * neg_pred = neg_tail->get_decl(); + variable_intersection neg_intersection(m_context.get_manager()); + neg_intersection.populate(single_res_expr, neg_tail); + unsigned_vector t_cols(neg_intersection.size(), neg_intersection.get_cols1()); + unsigned_vector neg_cols(neg_intersection.size(), neg_intersection.get_cols2()); + + unsigned neg_len = neg_tail->get_num_args(); + for(unsigned i=0; iget_arg(i); + if(is_var(e)) { + continue; + } + SASSERT(is_app(e)); + relation_sort arg_sort; + m_context.get_rel_context().get_rmanager().from_predicate(neg_pred, i, arg_sort); + reg_idx new_reg; + bool new_dealloc; + make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), new_reg, new_dealloc, acc); + + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; + filtered_res = new_reg; // here filtered_res value gets changed !! + + t_cols.push_back(single_res_expr.size()); + neg_cols.push_back(i); + single_res_expr.push_back(e); + } + SASSERT(t_cols.size()==neg_cols.size()); + + reg_idx neg_reg = m_pred_regs.find(neg_pred); + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); + acc.push_back(instruction::mk_filter_by_negation(filtered_res, neg_reg, t_cols.size(), + t_cols.c_ptr(), neg_cols.c_ptr())); + dealloc = true; + } + + // enforce interpreted tail predicates + unsigned ft_len=r->get_tail_size(); //full tail + for(unsigned tail_index=ut_len; tail_indexget_tail(tail_index); + ptr_vector t_vars; + ::get_free_vars(t, t_vars); + + if(t_vars.empty()) { + expr_ref simplified(m); + m_context.get_rewriter()(t, simplified); + if(m.is_true(simplified)) { + //this tail element is always true + continue; + } + //the tail of this rule is never satisfied + SASSERT(m.is_false(simplified)); + goto finish; + } + + //determine binding size + while (!t_vars.back()) { + t_vars.pop_back(); + } + unsigned max_var = t_vars.size(); + + //create binding + expr_ref_vector binding(m); + binding.resize(max_var+1); + + for(unsigned v = 0; v < t_vars.size(); ++v) { + if (!t_vars[v]) { + continue; + } + int2ints::entry * e = var_indexes.find_core(v); + if(!e) { + //we have an unbound variable, so we add an unbound column for it + relation_sort unbound_sort = t_vars[v]; + + reg_idx new_reg; + TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); + bool new_dealloc; + make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); + + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; + filtered_res = new_reg; // here filtered_res value gets changed !! + + unsigned unbound_column_index = single_res_expr.size(); + single_res_expr.push_back(m.mk_var(v, unbound_sort)); + + e = var_indexes.insert_if_not_there2(v, unsigned_vector()); + e->get_data().m_value.push_back(unbound_column_index); + } + unsigned src_col=e->get_data().m_value.back(); + relation_sort var_sort = m_reg_signatures[filtered_res][src_col]; + binding[max_var-v]=m.mk_var(src_col, var_sort); + } + + + expr_ref renamed(m); + m_context.get_var_subst()(t, binding.size(), binding.c_ptr(), renamed); + app_ref app_renamed(to_app(renamed), m); + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); + acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); + dealloc = true; + } + + { + //put together the columns of head relation + relation_signature & head_sig = m_reg_signatures[head_reg]; + svector head_acis; + unsigned_vector head_src_cols; + for(unsigned i=0; iget_arg(i); + if(is_var(exp)) { + unsigned var_num=to_var(exp)->get_idx(); + int2ints::entry * e = var_indexes.find_core(var_num); + if(e) { + unsigned_vector & binding_indexes = e->get_data().m_value; + aci.kind=ACK_BOUND_VAR; + aci.source_column=binding_indexes.back(); + SASSERT(aci.source_column1) { + //if possible, we do not want multiple head columns + //point to a single column in the intermediate table, + //since then we would have to duplicate the column + //(and remove columns we did not point to at all) + binding_indexes.pop_back(); + } + } + else { + aci.kind=ACK_UNBOUND_VAR; + aci.var_index=var_num; + } + } + else { + SASSERT(is_app(exp)); + SASSERT(m_context.get_decl_util().is_numeral_ext(exp)); + aci.kind=ACK_CONSTANT; + aci.constant=to_app(exp); + } + head_acis.push_back(aci); + } + SASSERT(head_acis.size()==head_len); + + reg_idx new_head_reg; + make_assembling_code(r, head_pred, filtered_res, head_acis, new_head_reg, dealloc, acc); + + //update the head relation + make_union(new_head_reg, head_reg, delta_reg, use_widening, acc); + if (dealloc) + make_dealloc_non_void(new_head_reg, acc); + } + + finish: + m_instruction_observer.finish_rule(); + } + + void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, + bool & dealloc, instruction_block & acc) { + uint_set pos_vars; + u_map neg_vars; + ast_manager& m = m_context.get_manager(); + unsigned pt_len = r->get_positive_tail_size(); + unsigned ut_len = r->get_uninterpreted_tail_size(); + if (pt_len == ut_len || pt_len == 0) { + return; + } + // populate negative variables: + for (unsigned i = pt_len; i < ut_len; ++i) { + app * neg_tail = r->get_tail(i); + unsigned neg_len = neg_tail->get_num_args(); + for (unsigned j = 0; j < neg_len; ++j) { + expr * e = neg_tail->get_arg(j); + if (is_var(e)) { + neg_vars.insert(to_var(e)->get_idx(), e); + } + } + } + // populate positive variables: + for (unsigned i = 0; i < single_res_expr.size(); ++i) { + expr* e = single_res_expr[i].get(); + if (is_var(e)) { + pos_vars.insert(to_var(e)->get_idx()); + } + } + // add negative variables that are not in positive: + u_map::iterator it = neg_vars.begin(), end = neg_vars.end(); + for (; it != end; ++it) { + unsigned v = it->m_key; + expr* e = it->m_value; + if (!pos_vars.contains(v)) { + single_res_expr.push_back(e); + reg_idx new_single_res; + bool new_dealloc; + make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), new_single_res, new_dealloc, acc); + if (dealloc) + make_dealloc_non_void(single_res, acc); + dealloc = new_dealloc; + single_res = new_single_res; + TRACE("dl", tout << "Adding unbound column: " << mk_pp(e, m) << "\n";); + } + } + } + + void compiler::compile_rule_evaluation(rule * r, const pred2idx * input_deltas, + reg_idx output_delta, bool use_widening, instruction_block & acc) { + typedef std::pair tail_delta_info; //(delta register, tail index) + typedef svector tail_delta_infos; + + unsigned rule_len = r->get_uninterpreted_tail_size(); + reg_idx head_reg = m_pred_regs.find(r->get_decl()); + + svector tail_regs; + tail_delta_infos tail_deltas; + for(unsigned j=0;jget_tail(j)->get_decl(); + reg_idx tail_reg = m_pred_regs.find(tail_pred); + tail_regs.push_back(tail_reg); + + if(input_deltas && !all_or_nothing_deltas()) { + reg_idx tail_delta_idx; + if(input_deltas->find(tail_pred, tail_delta_idx)) { + tail_deltas.push_back(tail_delta_info(tail_delta_idx, j)); + } + } + } + + if(!input_deltas || all_or_nothing_deltas()) { + compile_rule_evaluation_run(r, head_reg, tail_regs.c_ptr(), output_delta, use_widening, acc); + } + else { + tail_delta_infos::iterator tdit = tail_deltas.begin(); + tail_delta_infos::iterator tdend = tail_deltas.end(); + for(; tdit!=tdend; ++tdit) { + tail_delta_info tdinfo = *tdit; + flet flet_tail_reg(tail_regs[tdinfo.second], tdinfo.first); + compile_rule_evaluation_run(r, head_reg, tail_regs.c_ptr(), output_delta, use_widening, acc); + } + } + } + + class cycle_breaker + { + typedef func_decl * T; + typedef rule_dependencies::item_set item_set; //set of T + + rule_dependencies & m_deps; + rule_set const& m_rules; + context& m_context; + item_set & m_removed; + svector m_stack; + ast_mark m_visited; + + void traverse(T v) { + SASSERT(!m_visited.is_marked(v)); + if (m_removed.contains(v)) { + return; + } + + m_stack.push_back(v); + m_visited.mark(v, true); + + const item_set & deps = m_deps.get_deps(v); + item_set::iterator it = deps.begin(); + item_set::iterator end = deps.end(); + for(; it!=end; ++it) { + T d = *it; + if (m_visited.is_marked(d)) { + //TODO: find the best vertex to remove in the cycle + remove_from_stack(); + continue; + } + traverse(d); + } + SASSERT(m_stack.back()==v); + + m_stack.pop_back(); + m_visited.mark(v, false); + } + + void remove_from_stack() { + for (unsigned i = 0; i < m_stack.size(); ++i) { + func_decl* p = m_stack[i]; + if (m_context.has_facts(p)) { + m_removed.insert(p); + return; + } + + rule_vector const& rules = m_rules.get_predicate_rules(p); + unsigned stratum = m_rules.get_predicate_strat(p); + for (unsigned j = 0; j < rules.size(); ++j) { + rule const& r = *rules[j]; + bool ok = true; + for (unsigned k = 0; ok && k < r.get_uninterpreted_tail_size(); ++k) { + ok = m_rules.get_predicate_strat(r.get_decl(k)) < stratum; + } + if (ok) { + m_removed.insert(p); + return; + } + } + } + + // nothing was found. + m_removed.insert(m_stack.back()); + } + + public: + cycle_breaker(rule_dependencies & deps, rule_set const& rules, context& ctx, item_set & removed) + : m_deps(deps), m_rules(rules), m_context(ctx), m_removed(removed) { SASSERT(removed.empty()); } + + void operator()() { + rule_dependencies::iterator it = m_deps.begin(); + rule_dependencies::iterator end = m_deps.end(); + for(; it!=end; ++it) { + T v = it->m_key; + traverse(v); + } + m_deps.remove(m_removed); + } + }; + + void compiler::detect_chains(const func_decl_set & preds, func_decl_vector & ordered_preds, + func_decl_set & global_deltas) { + typedef obj_map pred2pred; + + SASSERT(ordered_preds.empty()); + SASSERT(global_deltas.empty()); + + rule_dependencies deps(m_rule_set.get_dependencies()); + deps.restrict(preds); + cycle_breaker(deps, m_rule_set, m_context, global_deltas)(); + VERIFY( deps.sort_deps(ordered_preds) ); + + //the predicates that were removed to get acyclic induced subgraph are put last + //so that all their local input deltas are already populated + func_decl_set::iterator gdit = global_deltas.begin(); + func_decl_set::iterator gend = global_deltas.end(); + for(; gdit!=gend; ++gdit) { + ordered_preds.push_back(*gdit); + } + } + + void compiler::compile_preds(const func_decl_vector & head_preds, const func_decl_set & widened_preds, + const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc) { + func_decl_vector::const_iterator hpit = head_preds.begin(); + func_decl_vector::const_iterator hpend = head_preds.end(); + for(; hpit!=hpend; ++hpit) { + func_decl * head_pred = *hpit; + + bool widen_predicate_in_loop = widened_preds.contains(head_pred); + + reg_idx d_head_reg; //output delta for the initial rule execution + if(!output_deltas.find(head_pred, d_head_reg)) { + d_head_reg = execution_context::void_register; + } + + const rule_vector & pred_rules = m_rule_set.get_predicate_rules(head_pred); + rule_vector::const_iterator rit = pred_rules.begin(); + rule_vector::const_iterator rend = pred_rules.end(); + for(; rit!=rend; ++rit) { + rule * r = *rit; + SASSERT(head_pred==r->get_decl()); + + compile_rule_evaluation(r, input_deltas, d_head_reg, widen_predicate_in_loop, acc); + } + } + } + + void compiler::compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds, + const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc) { + func_decl_vector::const_iterator hpit = head_preds.begin(); + func_decl_vector::const_iterator hpend = head_preds.end(); + reg_idx void_reg = execution_context::void_register; + for(; hpit!=hpend; ++hpit) { + func_decl * head_pred = *hpit; + const rule_vector & pred_rules = m_rule_set.get_predicate_rules(head_pred); + rule_vector::const_iterator rit = pred_rules.begin(); + rule_vector::const_iterator rend = pred_rules.end(); + unsigned stratum = m_rule_set.get_predicate_strat(head_pred); + + for(; rit != rend; ++rit) { + rule * r = *rit; + SASSERT(head_pred==r->get_decl()); + + for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { + unsigned stratum1 = m_rule_set.get_predicate_strat(r->get_decl(i)); + if (stratum1 >= stratum) { + goto next_loop; + } + } + compile_rule_evaluation(r, input_deltas, void_reg, false, acc); + next_loop: + ; + } + + reg_idx d_head_reg; + if (output_deltas.find(head_pred, d_head_reg)) { + acc.push_back(instruction::mk_clone(m_pred_regs.find(head_pred), d_head_reg)); + } + } + } + + void compiler::make_inloop_delta_transition(const pred2idx & global_head_deltas, + const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc) { + //move global head deltas into tail ones + pred2idx::iterator gdit = global_head_deltas.begin(); + pred2idx::iterator gend = global_head_deltas.end(); + for(; gdit!=gend; ++gdit) { + func_decl * pred = gdit->m_key; + reg_idx head_reg = gdit->m_value; + reg_idx tail_reg = global_tail_deltas.find(pred); + acc.push_back(instruction::mk_move(head_reg, tail_reg)); + } + //empty local deltas + pred2idx::iterator lit = local_deltas.begin(); + pred2idx::iterator lend = local_deltas.end(); + for(; lit!=lend; ++lit) { + acc.push_back(instruction::mk_dealloc(lit->m_value)); + } + } + + void compiler::compile_loop(const func_decl_vector & head_preds, const func_decl_set & widened_preds, + const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, + const pred2idx & local_deltas, instruction_block & acc) { + instruction_block * loop_body = alloc(instruction_block); + loop_body->set_observer(&m_instruction_observer); + + pred2idx all_head_deltas(global_head_deltas); + unite_disjoint_maps(all_head_deltas, local_deltas); + pred2idx all_tail_deltas(global_tail_deltas); + unite_disjoint_maps(all_tail_deltas, local_deltas); + + //generate code for the iterative fixpoint search + //The order in which we iterate the preds_vector matters, since rules can depend on + //deltas generated earlier in the same iteration. + compile_preds(head_preds, widened_preds, &all_tail_deltas, all_head_deltas, *loop_body); + + svector loop_control_regs; //loop is controlled by global src regs + collect_map_range(loop_control_regs, global_tail_deltas); + //move target deltas into source deltas at the end of the loop + //and clear local deltas + make_inloop_delta_transition(global_head_deltas, global_tail_deltas, local_deltas, *loop_body); + + loop_body->set_observer(0); + acc.push_back(instruction::mk_while_loop(loop_control_regs.size(), + loop_control_regs.c_ptr(), loop_body)); + } + + void compiler::compile_dependent_rules(const func_decl_set & head_preds, + const pred2idx * input_deltas, const pred2idx & output_deltas, + bool add_saturation_marks, instruction_block & acc) { + + if (!output_deltas.empty()) { + func_decl_set::iterator hpit = head_preds.begin(); + func_decl_set::iterator hpend = head_preds.end(); + for(; hpit!=hpend; ++hpit) { + if(output_deltas.contains(*hpit)) { + //we do not support retrieving deltas for rules that are inside a recursive + //stratum, since we would have to maintain this 'global' delta through the loop + //iterations + NOT_IMPLEMENTED_YET(); + } + } + } + + func_decl_vector preds_vector; + func_decl_set global_deltas; + + detect_chains(head_preds, preds_vector, global_deltas); + + func_decl_set local_deltas(head_preds); + set_difference(local_deltas, global_deltas); + + pred2idx d_global_src; //these deltas serve as sources of tuples for rule evaluation inside the loop + get_fresh_registers(global_deltas, d_global_src); + pred2idx d_global_tgt; //these deltas are targets for new tuples in rule evaluation inside the loop + get_fresh_registers(global_deltas, d_global_tgt); + pred2idx d_local; + get_fresh_registers(local_deltas, d_local); + + pred2idx d_all_src(d_global_src); //src together with local deltas + unite_disjoint_maps(d_all_src, d_local); + pred2idx d_all_tgt(d_global_tgt); //tgt together with local deltas + unite_disjoint_maps(d_all_tgt, d_local); + + + func_decl_set empty_func_decl_set; + + //generate code for the initial run + // compile_preds(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); + compile_preds_init(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); + + if (compile_with_widening()) { + compile_loop(preds_vector, global_deltas, d_global_tgt, d_global_src, d_local, acc); + } + else { + compile_loop(preds_vector, empty_func_decl_set, d_global_tgt, d_global_src, d_local, acc); + } + + + if(add_saturation_marks) { + //after the loop finishes, all predicates in the group are saturated, + //so we may mark them as such + func_decl_set::iterator fdit = head_preds.begin(); + func_decl_set::iterator fdend = head_preds.end(); + for(; fdit!=fdend; ++fdit) { + acc.push_back(instruction::mk_mark_saturated(m_context.get_manager(), *fdit)); + } + } + } + + bool compiler::is_nonrecursive_stratum(const func_decl_set & preds) const { + SASSERT(preds.size()>0); + if(preds.size()>1) { + return false; + } + func_decl * head_pred = *preds.begin(); + const rule_vector & rules = m_rule_set.get_predicate_rules(head_pred); + rule_vector::const_iterator it = rules.begin(); + rule_vector::const_iterator end = rules.end(); + for(; it!=end; ++it) { + //it is sufficient to check just for presence of the first head predicate, + //since if the rules are recursive and their heads are strongly connected by dependence, + //this predicate must appear in some tail + if((*it)->is_in_tail(head_pred)) { + return false; + } + } + return true; + } + + void compiler::compile_nonrecursive_stratum(const func_decl_set & preds, + const pred2idx * input_deltas, const pred2idx & output_deltas, + bool add_saturation_marks, instruction_block & acc) { + //non-recursive stratum always has just one head predicate + SASSERT(preds.size()==1); + SASSERT(is_nonrecursive_stratum(preds)); + func_decl * head_pred = *preds.begin(); + const rule_vector & rules = m_rule_set.get_predicate_rules(head_pred); + + + + reg_idx output_delta; + if (!output_deltas.find(head_pred, output_delta)) { + output_delta = execution_context::void_register; + } + + rule_vector::const_iterator it = rules.begin(); + rule_vector::const_iterator end = rules.end(); + for (; it != end; ++it) { + rule * r = *it; + SASSERT(r->get_decl()==head_pred); + + compile_rule_evaluation(r, input_deltas, output_delta, false, acc); + } + + if (add_saturation_marks) { + //now the predicate is saturated, so we may mark it as such + acc.push_back(instruction::mk_mark_saturated(m_context.get_manager(), head_pred)); + } + } + + bool compiler::all_saturated(const func_decl_set & preds) const { + func_decl_set::iterator fdit = preds.begin(); + func_decl_set::iterator fdend = preds.end(); + for(; fdit!=fdend; ++fdit) { + if(!m_context.get_rel_context().get_rmanager().is_saturated(*fdit)) { + return false; + } + } + return true; + } + + void compiler::compile_strats(const rule_stratifier & stratifier, + const pred2idx * input_deltas, const pred2idx & output_deltas, + bool add_saturation_marks, instruction_block & acc) { + rule_set::pred_set_vector strats = stratifier.get_strats(); + rule_set::pred_set_vector::const_iterator sit = strats.begin(); + rule_set::pred_set_vector::const_iterator send = strats.end(); + for(; sit!=send; ++sit) { + func_decl_set & strat_preds = **sit; + + if (all_saturated(strat_preds)) { + //all predicates in stratum are saturated, so no need to compile rules for them + continue; + } + + TRACE("dl", + tout << "Stratum: "; + func_decl_set::iterator pit = strat_preds.begin(); + func_decl_set::iterator pend = strat_preds.end(); + for(; pit!=pend; ++pit) { + func_decl * pred = *pit; + tout << pred->get_name() << " "; + } + tout << "\n"; + ); + + if (is_nonrecursive_stratum(strat_preds)) { + //this stratum contains just a single non-recursive rule + compile_nonrecursive_stratum(strat_preds, input_deltas, output_deltas, add_saturation_marks, acc); + } + else { + compile_dependent_rules(strat_preds, input_deltas, output_deltas, + add_saturation_marks, acc); + } + } + } + + void compiler::do_compilation(instruction_block & execution_code, + instruction_block & termination_code) { + + unsigned rule_cnt=m_rule_set.get_num_rules(); + if(rule_cnt==0) { + return; + } + + instruction_block & acc = execution_code; + acc.set_observer(&m_instruction_observer); + + + //load predicate data + for(unsigned i=0;iget_decl(), acc); + + unsigned rule_len = r->get_uninterpreted_tail_size(); + for(unsigned j=0;jget_tail(j)->get_decl(), acc); + } + } + + pred2idx empty_pred2idx_map; + + compile_strats(m_rule_set.get_stratifier(), static_cast(0), + empty_pred2idx_map, true, execution_code); + + + + //store predicate data + pred2idx::iterator pit = m_pred_regs.begin(); + pred2idx::iterator pend = m_pred_regs.end(); + for(; pit!=pend; ++pit) { + pred2idx::key_data & e = *pit; + func_decl * pred = e.m_key; + reg_idx reg = e.m_value; + termination_code.push_back(instruction::mk_store(m_context.get_manager(), pred, reg)); + } + + acc.set_observer(0); + + TRACE("dl", execution_code.display(m_context.get_rel_context(), tout);); + } + + +} + From 1917c909d8ba44a15d6e06b3e13541751bff6d9c Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 23 Apr 2013 11:28:09 -0700 Subject: [PATCH 27/91] delete garbage Signed-off-by: Nuno Lopes --- src/muz_qe/dl_compiler.cpp.orig | 1245 ------------------------------- 1 file changed, 1245 deletions(-) delete mode 100644 src/muz_qe/dl_compiler.cpp.orig diff --git a/src/muz_qe/dl_compiler.cpp.orig b/src/muz_qe/dl_compiler.cpp.orig deleted file mode 100644 index e4528426a..000000000 --- a/src/muz_qe/dl_compiler.cpp.orig +++ /dev/null @@ -1,1245 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - dl_compiler.cpp - -Abstract: - - - -Author: - - Krystof Hoder (t-khoder) 2010-09-14. - -Revision History: - ---*/ - - -#include -#include"ref_vector.h" -#include"dl_context.h" -#include"dl_rule.h" -#include"dl_util.h" -#include"dl_compiler.h" -#include"ast_pp.h" -#include"ast_smt2_pp.h" - - -namespace datalog { - - void compiler::reset() { - m_pred_regs.reset(); - m_new_reg = 0; - } - - void compiler::ensure_predicate_loaded(func_decl * pred, instruction_block & acc) { - pred2idx::obj_map_entry * e = m_pred_regs.insert_if_not_there2(pred, UINT_MAX); - if(e->get_data().m_value!=UINT_MAX) { - //predicate is already loaded - return; - } - relation_signature sig; - m_context.get_rel_context().get_rmanager().from_predicate(pred, sig); - reg_idx reg = get_fresh_register(sig); - e->get_data().m_value=reg; - - acc.push_back(instruction::mk_load(m_context.get_manager(), pred, reg)); - } - - void compiler::make_join(reg_idx t1, reg_idx t2, const variable_intersection & vars, reg_idx & result, - instruction_block & acc) { - relation_signature res_sig; - relation_signature::from_join(m_reg_signatures[t1], m_reg_signatures[t2], vars.size(), - vars.get_cols1(), vars.get_cols2(), res_sig); - result = get_fresh_register(res_sig); - acc.push_back(instruction::mk_join(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), result)); - } - - void compiler::make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, - const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc) { - relation_signature aux_sig; - relation_signature sig1 = m_reg_signatures[t1]; - relation_signature sig2 = m_reg_signatures[t2]; - relation_signature::from_join(sig1, sig2, vars.size(), vars.get_cols1(), vars.get_cols2(), aux_sig); - relation_signature res_sig; - relation_signature::from_project(aux_sig, removed_cols.size(), removed_cols.c_ptr(), - res_sig); - result = get_fresh_register(res_sig); - - acc.push_back(instruction::mk_join_project(t1, t2, vars.size(), vars.get_cols1(), - vars.get_cols2(), removed_cols.size(), removed_cols.c_ptr(), result)); - } - - void compiler::make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col, - reg_idx & result, instruction_block & acc) { - relation_signature res_sig; - relation_signature::from_project(m_reg_signatures[src], 1, &col, res_sig); - result = get_fresh_register(res_sig); - acc.push_back(instruction::mk_select_equal_and_project(m_context.get_manager(), - src, val, col, result)); - } - - void compiler::make_clone(reg_idx src, reg_idx & result, instruction_block & acc) { - relation_signature sig = m_reg_signatures[src]; - result = get_fresh_register(sig); - acc.push_back(instruction::mk_clone(src, result)); - } - - void compiler::make_union(reg_idx src, reg_idx tgt, reg_idx delta, bool use_widening, - instruction_block & acc) { - SASSERT(m_reg_signatures[src]==m_reg_signatures[tgt]); - SASSERT(delta==execution_context::void_register || m_reg_signatures[src]==m_reg_signatures[delta]); - - if (use_widening) { - acc.push_back(instruction::mk_widen(src, tgt, delta)); - } - else { - acc.push_back(instruction::mk_union(src, tgt, delta)); - } - } - - void compiler::make_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, - reg_idx & result, instruction_block & acc) { - SASSERT(col_cnt>0); - - relation_signature res_sig; - relation_signature::from_project(m_reg_signatures[src], col_cnt, removed_cols, res_sig); - result = get_fresh_register(res_sig); - acc.push_back(instruction::mk_projection(src, col_cnt, removed_cols, result)); - } - - compiler::reg_idx compiler::get_fresh_register(const relation_signature & sig) { - //since we might be resizing the m_reg_signatures vector, the argument must not point inside it - SASSERT((&sig>=m_reg_signatures.end()) || (&sigto_formula(e); - verbose_stream() << "Compiling unsafe rule column " << col_idx << "\n" - << mk_ismt2_pp(e, m_context.get_manager()) << "\n"; - }); - reg_idx total_table; - if (!m_total_registers.find(s, pred, total_table)) { - total_table = get_single_column_register(s); - relation_signature sig; - sig.push_back(s); - m_top_level_code.push_back(instruction::mk_total(sig, pred, total_table)); - m_total_registers.insert(s, pred, total_table); - } - if(src == execution_context::void_register) { - result = total_table; - dealloc = false; - } - else { - variable_intersection empty_vars(m_context.get_manager()); - make_join(src, total_table, empty_vars, result, acc); - dealloc = true; - } - } - - void compiler::make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, - instruction_block & acc) { - SASSERT(sig.empty()); - TRACE("dl", tout << "Adding unbound column " << mk_pp(pred, m_context.get_manager()) << "\n";); - if (m_empty_tables_registers.find(pred, result)) - return; - - result = get_fresh_register(sig); - m_top_level_code.push_back(instruction::mk_total(sig, pred, result)); - m_empty_tables_registers.insert(pred, result); - } - - - void compiler::make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, - instruction_block & acc) { - - relation_signature & src_sig = m_reg_signatures[src]; - reg_idx single_col_reg; - unsigned src_col_cnt = src_sig.size(); - if(src_col_cnt==1) { - single_col_reg = src; - } - else { - unsigned_vector removed_cols; - for(unsigned i=0; i & acis0, - reg_idx & result, - bool & dealloc, - instruction_block & acc) { - - TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); - - unsigned col_cnt = acis0.size(); - reg_idx curr = src; - - relation_signature empty_signature; - - relation_signature * curr_sig; - if(curr!=execution_context::void_register) { - curr_sig = & m_reg_signatures[curr]; - } - else { - curr_sig = & empty_signature; - } - unsigned src_col_cnt=curr_sig->size(); - - svector acis(acis0); - int2int handled_unbound; - - //first remove unused source columns - int_set referenced_src_cols; - for(unsigned i=0; isize(); - if(acis[i].kind==ACK_CONSTANT) { - make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, new_curr, new_dealloc, acc); - } - else { - SASSERT(acis[i].kind==ACK_UNBOUND_VAR); - make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, new_curr, new_dealloc, acc); - handled_unbound.insert(acis[i].var_index,bound_column_index); - } - if (dealloc) - make_dealloc_non_void(curr, acc); - dealloc = new_dealloc; - curr=new_curr; - curr_sig = & m_reg_signatures[curr]; - SASSERT(bound_column_index==curr_sig->size()-1); - } - SASSERT((*curr_sig)[bound_column_index]==acis[i].domain); - acis[i].kind=ACK_BOUND_VAR; - acis[i].source_column=bound_column_index; - } - - //duplicate needed source columns - int_set used_cols; - for(unsigned i=0; isize()-1; - SASSERT((*curr_sig)[bound_column_index]==acis[i].domain); - acis[i].source_column=bound_column_index; - } - - //reorder source columns to match target - SASSERT(curr_sig->size()==col_cnt); //now the intermediate table is a permutation - for(unsigned i=0; i=i); //columns below i are already reordered - SASSERT(nextget_num_args(); - for(unsigned i = 0; iget_arg(i); - if(!is_var(e) || globals.get(to_var(e)->get_idx())!=0) { - continue; - } - res.push_back(i+ofs); - } - } - - void compiler::get_local_indexes_for_projection(rule * r, unsigned_vector & res) { - SASSERT(r->get_positive_tail_size()==2); - ast_manager & m = m_context.get_manager(); - rule_counter counter; - counter.count_rule_vars(m, r); - app * t1 = r->get_tail(0); - app * t2 = r->get_tail(1); - counter.count_vars(m, t1, -1); - counter.count_vars(m, t2, -1); - get_local_indexes_for_projection(t1, counter, 0, res); - get_local_indexes_for_projection(t2, counter, t1->get_num_args(), res); - } - - void compiler::compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs, - reg_idx delta_reg, bool use_widening, instruction_block & acc) { - - ast_manager & m = m_context.get_manager(); - m_instruction_observer.start_rule(r); - - const app * h = r->get_head(); - unsigned head_len = h->get_num_args(); - func_decl * head_pred = h->get_decl(); - - TRACE("dl", r->display(m_context, tout); ); - - unsigned pt_len = r->get_positive_tail_size(); - SASSERT(pt_len<=2); //we require rules to be processed by the mk_simple_joins rule transformer plugin - - reg_idx single_res; - expr_ref_vector single_res_expr(m); - - //used to save on filter_identical instructions where the check is already done - //by the join operation - unsigned second_tail_arg_ofs; - - // whether to dealloc the previous result - bool dealloc = true; - - if(pt_len == 2) { - reg_idx t1_reg=tail_regs[0]; - reg_idx t2_reg=tail_regs[1]; - app * a1 = r->get_tail(0); - app * a2 = r->get_tail(1); - SASSERT(m_reg_signatures[t1_reg].size()==a1->get_num_args()); - SASSERT(m_reg_signatures[t2_reg].size()==a2->get_num_args()); - - variable_intersection a1a2(m_context.get_manager()); - a1a2.populate(a1,a2); - - unsigned_vector removed_cols; - get_local_indexes_for_projection(r, removed_cols); - - if(removed_cols.empty()) { - make_join(t1_reg, t2_reg, a1a2, single_res, acc); - } - else { - make_join_project(t1_reg, t2_reg, a1a2, removed_cols, single_res, acc); - } - - unsigned rem_index = 0; - unsigned rem_sz = removed_cols.size(); - unsigned a1len=a1->get_num_args(); - for(unsigned i=0; i=i); - if(rem_indexget_arg(i)); - } - second_tail_arg_ofs = single_res_expr.size(); - unsigned a2len=a2->get_num_args(); - for(unsigned i=0; i=i+a1len); - if(rem_indexget_arg(i)); - } - SASSERT(rem_index==rem_sz); - } - else if(pt_len==1) { - reg_idx t_reg=tail_regs[0]; - app * a = r->get_tail(0); - SASSERT(m_reg_signatures[t_reg].size()==a->get_num_args()); - - single_res = t_reg; - - unsigned n=a->get_num_args(); - for(unsigned i=0; iget_arg(i); - if(is_app(arg)) { - app * c = to_app(arg); //argument is a constant - SASSERT(c->get_num_args()==0); - SASSERT(m_context.get_decl_util().is_numeral_ext(arg)); - reg_idx new_reg; - make_select_equal_and_project(single_res, c, single_res_expr.size(), new_reg, acc); - if(single_res!=t_reg) { - //since single_res is a local register, we deallocate it - make_dealloc_non_void(single_res, acc); - } - single_res = new_reg; - } - else { - SASSERT(is_var(arg)); - single_res_expr.push_back(arg); - } - } - if(single_res==t_reg) { - dealloc = false; - } - - } - else { - SASSERT(pt_len==0); - - //single_res register should never be used in this case - single_res=execution_context::void_register; - } - - add_unbound_columns_for_negation(r, head_pred, single_res, single_res_expr, dealloc, acc); - - int2ints var_indexes; - - reg_idx filtered_res = single_res; - - { - //enforce equality to constants - unsigned srlen=single_res_expr.size(); - SASSERT((single_res==execution_context::void_register) ? (srlen==0) : (srlen==m_reg_signatures[single_res].size())); - for(unsigned i=0; iget_idx(); - int2ints::entry * e = var_indexes.insert_if_not_there2(var_num, unsigned_vector()); - e->get_data().m_value.push_back(i); - } - } - } - - //enforce equality of columns - int2ints::iterator vit=var_indexes.begin(); - int2ints::iterator vend=var_indexes.end(); - for(; vit!=vend; ++vit) { - int2ints::key_data & k = *vit; - unsigned_vector & indexes = k.m_value; - if(indexes.size()==1) { - continue; - } - SASSERT(indexes.size()>1); - if(pt_len==2 && indexes[0]=second_tail_arg_ofs) { - //If variable appears in multiple tails, the identicity will already be enforced by join. - //(If behavior the join changes so that it is not enforced anymore, remove this - //condition!) - continue; - } - if (!dealloc) - make_clone(filtered_res, filtered_res, acc); - acc.push_back(instruction::mk_filter_identical(filtered_res, indexes.size(), indexes.c_ptr())); - dealloc = true; - } - - //enforce negative predicates - unsigned ut_len=r->get_uninterpreted_tail_size(); - for(unsigned i=pt_len; iget_tail(i); - func_decl * neg_pred = neg_tail->get_decl(); - variable_intersection neg_intersection(m_context.get_manager()); - neg_intersection.populate(single_res_expr, neg_tail); - unsigned_vector t_cols(neg_intersection.size(), neg_intersection.get_cols1()); - unsigned_vector neg_cols(neg_intersection.size(), neg_intersection.get_cols2()); - - unsigned neg_len = neg_tail->get_num_args(); - for(unsigned i=0; iget_arg(i); - if(is_var(e)) { - continue; - } - SASSERT(is_app(e)); - relation_sort arg_sort; - m_context.get_rel_context().get_rmanager().from_predicate(neg_pred, i, arg_sort); - reg_idx new_reg; - bool new_dealloc; - make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), new_reg, new_dealloc, acc); - - if (dealloc) - make_dealloc_non_void(filtered_res, acc); - dealloc = new_dealloc; - filtered_res = new_reg; // here filtered_res value gets changed !! - - t_cols.push_back(single_res_expr.size()); - neg_cols.push_back(i); - single_res_expr.push_back(e); - } - SASSERT(t_cols.size()==neg_cols.size()); - - reg_idx neg_reg = m_pred_regs.find(neg_pred); - if (!dealloc) - make_clone(filtered_res, filtered_res, acc); - acc.push_back(instruction::mk_filter_by_negation(filtered_res, neg_reg, t_cols.size(), - t_cols.c_ptr(), neg_cols.c_ptr())); - dealloc = true; - } - - // enforce interpreted tail predicates - unsigned ft_len=r->get_tail_size(); //full tail - for(unsigned tail_index=ut_len; tail_indexget_tail(tail_index); - ptr_vector t_vars; - ::get_free_vars(t, t_vars); - - if(t_vars.empty()) { - expr_ref simplified(m); - m_context.get_rewriter()(t, simplified); - if(m.is_true(simplified)) { - //this tail element is always true - continue; - } - //the tail of this rule is never satisfied - SASSERT(m.is_false(simplified)); - goto finish; - } - - //determine binding size - while (!t_vars.back()) { - t_vars.pop_back(); - } - unsigned max_var = t_vars.size(); - - //create binding - expr_ref_vector binding(m); - binding.resize(max_var+1); - - for(unsigned v = 0; v < t_vars.size(); ++v) { - if (!t_vars[v]) { - continue; - } - int2ints::entry * e = var_indexes.find_core(v); - if(!e) { - //we have an unbound variable, so we add an unbound column for it - relation_sort unbound_sort = t_vars[v]; - - reg_idx new_reg; - TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); - bool new_dealloc; - make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); - - if (dealloc) - make_dealloc_non_void(filtered_res, acc); - dealloc = new_dealloc; - filtered_res = new_reg; // here filtered_res value gets changed !! - - unsigned unbound_column_index = single_res_expr.size(); - single_res_expr.push_back(m.mk_var(v, unbound_sort)); - - e = var_indexes.insert_if_not_there2(v, unsigned_vector()); - e->get_data().m_value.push_back(unbound_column_index); - } - unsigned src_col=e->get_data().m_value.back(); - relation_sort var_sort = m_reg_signatures[filtered_res][src_col]; - binding[max_var-v]=m.mk_var(src_col, var_sort); - } - - - expr_ref renamed(m); - m_context.get_var_subst()(t, binding.size(), binding.c_ptr(), renamed); - app_ref app_renamed(to_app(renamed), m); - if (!dealloc) - make_clone(filtered_res, filtered_res, acc); - acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); - dealloc = true; - } - - { - //put together the columns of head relation - relation_signature & head_sig = m_reg_signatures[head_reg]; - svector head_acis; - unsigned_vector head_src_cols; - for(unsigned i=0; iget_arg(i); - if(is_var(exp)) { - unsigned var_num=to_var(exp)->get_idx(); - int2ints::entry * e = var_indexes.find_core(var_num); - if(e) { - unsigned_vector & binding_indexes = e->get_data().m_value; - aci.kind=ACK_BOUND_VAR; - aci.source_column=binding_indexes.back(); - SASSERT(aci.source_column1) { - //if possible, we do not want multiple head columns - //point to a single column in the intermediate table, - //since then we would have to duplicate the column - //(and remove columns we did not point to at all) - binding_indexes.pop_back(); - } - } - else { - aci.kind=ACK_UNBOUND_VAR; - aci.var_index=var_num; - } - } - else { - SASSERT(is_app(exp)); - SASSERT(m_context.get_decl_util().is_numeral_ext(exp)); - aci.kind=ACK_CONSTANT; - aci.constant=to_app(exp); - } - head_acis.push_back(aci); - } - SASSERT(head_acis.size()==head_len); - - reg_idx new_head_reg; - make_assembling_code(r, head_pred, filtered_res, head_acis, new_head_reg, dealloc, acc); - - //update the head relation - make_union(new_head_reg, head_reg, delta_reg, use_widening, acc); - if (dealloc) - make_dealloc_non_void(new_head_reg, acc); - } - - finish: - m_instruction_observer.finish_rule(); - } - - void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, - bool & dealloc, instruction_block & acc) { - uint_set pos_vars; - u_map neg_vars; - ast_manager& m = m_context.get_manager(); - unsigned pt_len = r->get_positive_tail_size(); - unsigned ut_len = r->get_uninterpreted_tail_size(); - if (pt_len == ut_len || pt_len == 0) { - return; - } - // populate negative variables: - for (unsigned i = pt_len; i < ut_len; ++i) { - app * neg_tail = r->get_tail(i); - unsigned neg_len = neg_tail->get_num_args(); - for (unsigned j = 0; j < neg_len; ++j) { - expr * e = neg_tail->get_arg(j); - if (is_var(e)) { - neg_vars.insert(to_var(e)->get_idx(), e); - } - } - } - // populate positive variables: - for (unsigned i = 0; i < single_res_expr.size(); ++i) { - expr* e = single_res_expr[i].get(); - if (is_var(e)) { - pos_vars.insert(to_var(e)->get_idx()); - } - } - // add negative variables that are not in positive: - u_map::iterator it = neg_vars.begin(), end = neg_vars.end(); - for (; it != end; ++it) { - unsigned v = it->m_key; - expr* e = it->m_value; - if (!pos_vars.contains(v)) { - single_res_expr.push_back(e); - reg_idx new_single_res; - bool new_dealloc; - make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), new_single_res, new_dealloc, acc); - if (dealloc) - make_dealloc_non_void(single_res, acc); - dealloc = new_dealloc; - single_res = new_single_res; - TRACE("dl", tout << "Adding unbound column: " << mk_pp(e, m) << "\n";); - } - } - } - - void compiler::compile_rule_evaluation(rule * r, const pred2idx * input_deltas, - reg_idx output_delta, bool use_widening, instruction_block & acc) { - typedef std::pair tail_delta_info; //(delta register, tail index) - typedef svector tail_delta_infos; - - unsigned rule_len = r->get_uninterpreted_tail_size(); - reg_idx head_reg = m_pred_regs.find(r->get_decl()); - - svector tail_regs; - tail_delta_infos tail_deltas; - for(unsigned j=0;jget_tail(j)->get_decl(); - reg_idx tail_reg = m_pred_regs.find(tail_pred); - tail_regs.push_back(tail_reg); - - if(input_deltas && !all_or_nothing_deltas()) { - reg_idx tail_delta_idx; - if(input_deltas->find(tail_pred, tail_delta_idx)) { - tail_deltas.push_back(tail_delta_info(tail_delta_idx, j)); - } - } - } - - if(!input_deltas || all_or_nothing_deltas()) { - compile_rule_evaluation_run(r, head_reg, tail_regs.c_ptr(), output_delta, use_widening, acc); - } - else { - tail_delta_infos::iterator tdit = tail_deltas.begin(); - tail_delta_infos::iterator tdend = tail_deltas.end(); - for(; tdit!=tdend; ++tdit) { - tail_delta_info tdinfo = *tdit; - flet flet_tail_reg(tail_regs[tdinfo.second], tdinfo.first); - compile_rule_evaluation_run(r, head_reg, tail_regs.c_ptr(), output_delta, use_widening, acc); - } - } - } - - class cycle_breaker - { - typedef func_decl * T; - typedef rule_dependencies::item_set item_set; //set of T - - rule_dependencies & m_deps; - rule_set const& m_rules; - context& m_context; - item_set & m_removed; - svector m_stack; - ast_mark m_visited; - - void traverse(T v) { - SASSERT(!m_visited.is_marked(v)); - if (m_removed.contains(v)) { - return; - } - - m_stack.push_back(v); - m_visited.mark(v, true); - - const item_set & deps = m_deps.get_deps(v); - item_set::iterator it = deps.begin(); - item_set::iterator end = deps.end(); - for(; it!=end; ++it) { - T d = *it; - if (m_visited.is_marked(d)) { - //TODO: find the best vertex to remove in the cycle - remove_from_stack(); - continue; - } - traverse(d); - } - SASSERT(m_stack.back()==v); - - m_stack.pop_back(); - m_visited.mark(v, false); - } - - void remove_from_stack() { - for (unsigned i = 0; i < m_stack.size(); ++i) { - func_decl* p = m_stack[i]; - if (m_context.has_facts(p)) { - m_removed.insert(p); - return; - } - - rule_vector const& rules = m_rules.get_predicate_rules(p); - unsigned stratum = m_rules.get_predicate_strat(p); - for (unsigned j = 0; j < rules.size(); ++j) { - rule const& r = *rules[j]; - bool ok = true; - for (unsigned k = 0; ok && k < r.get_uninterpreted_tail_size(); ++k) { - ok = m_rules.get_predicate_strat(r.get_decl(k)) < stratum; - } - if (ok) { - m_removed.insert(p); - return; - } - } - } - - // nothing was found. - m_removed.insert(m_stack.back()); - } - - public: - cycle_breaker(rule_dependencies & deps, rule_set const& rules, context& ctx, item_set & removed) - : m_deps(deps), m_rules(rules), m_context(ctx), m_removed(removed) { SASSERT(removed.empty()); } - - void operator()() { - rule_dependencies::iterator it = m_deps.begin(); - rule_dependencies::iterator end = m_deps.end(); - for(; it!=end; ++it) { - T v = it->m_key; - traverse(v); - } - m_deps.remove(m_removed); - } - }; - - void compiler::detect_chains(const func_decl_set & preds, func_decl_vector & ordered_preds, - func_decl_set & global_deltas) { - typedef obj_map pred2pred; - - SASSERT(ordered_preds.empty()); - SASSERT(global_deltas.empty()); - - rule_dependencies deps(m_rule_set.get_dependencies()); - deps.restrict(preds); - cycle_breaker(deps, m_rule_set, m_context, global_deltas)(); - VERIFY( deps.sort_deps(ordered_preds) ); - - //the predicates that were removed to get acyclic induced subgraph are put last - //so that all their local input deltas are already populated - func_decl_set::iterator gdit = global_deltas.begin(); - func_decl_set::iterator gend = global_deltas.end(); - for(; gdit!=gend; ++gdit) { - ordered_preds.push_back(*gdit); - } - } - - void compiler::compile_preds(const func_decl_vector & head_preds, const func_decl_set & widened_preds, - const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc) { - func_decl_vector::const_iterator hpit = head_preds.begin(); - func_decl_vector::const_iterator hpend = head_preds.end(); - for(; hpit!=hpend; ++hpit) { - func_decl * head_pred = *hpit; - - bool widen_predicate_in_loop = widened_preds.contains(head_pred); - - reg_idx d_head_reg; //output delta for the initial rule execution - if(!output_deltas.find(head_pred, d_head_reg)) { - d_head_reg = execution_context::void_register; - } - - const rule_vector & pred_rules = m_rule_set.get_predicate_rules(head_pred); - rule_vector::const_iterator rit = pred_rules.begin(); - rule_vector::const_iterator rend = pred_rules.end(); - for(; rit!=rend; ++rit) { - rule * r = *rit; - SASSERT(head_pred==r->get_decl()); - - compile_rule_evaluation(r, input_deltas, d_head_reg, widen_predicate_in_loop, acc); - } - } - } - - void compiler::compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds, - const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc) { - func_decl_vector::const_iterator hpit = head_preds.begin(); - func_decl_vector::const_iterator hpend = head_preds.end(); - reg_idx void_reg = execution_context::void_register; - for(; hpit!=hpend; ++hpit) { - func_decl * head_pred = *hpit; - const rule_vector & pred_rules = m_rule_set.get_predicate_rules(head_pred); - rule_vector::const_iterator rit = pred_rules.begin(); - rule_vector::const_iterator rend = pred_rules.end(); - unsigned stratum = m_rule_set.get_predicate_strat(head_pred); - - for(; rit != rend; ++rit) { - rule * r = *rit; - SASSERT(head_pred==r->get_decl()); - - for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { - unsigned stratum1 = m_rule_set.get_predicate_strat(r->get_decl(i)); - if (stratum1 >= stratum) { - goto next_loop; - } - } - compile_rule_evaluation(r, input_deltas, void_reg, false, acc); - next_loop: - ; - } - - reg_idx d_head_reg; - if (output_deltas.find(head_pred, d_head_reg)) { - acc.push_back(instruction::mk_clone(m_pred_regs.find(head_pred), d_head_reg)); - } - } - } - - void compiler::make_inloop_delta_transition(const pred2idx & global_head_deltas, - const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc) { - //move global head deltas into tail ones - pred2idx::iterator gdit = global_head_deltas.begin(); - pred2idx::iterator gend = global_head_deltas.end(); - for(; gdit!=gend; ++gdit) { - func_decl * pred = gdit->m_key; - reg_idx head_reg = gdit->m_value; - reg_idx tail_reg = global_tail_deltas.find(pred); - acc.push_back(instruction::mk_move(head_reg, tail_reg)); - } - //empty local deltas - pred2idx::iterator lit = local_deltas.begin(); - pred2idx::iterator lend = local_deltas.end(); - for(; lit!=lend; ++lit) { - acc.push_back(instruction::mk_dealloc(lit->m_value)); - } - } - - void compiler::compile_loop(const func_decl_vector & head_preds, const func_decl_set & widened_preds, - const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, - const pred2idx & local_deltas, instruction_block & acc) { - instruction_block * loop_body = alloc(instruction_block); - loop_body->set_observer(&m_instruction_observer); - - pred2idx all_head_deltas(global_head_deltas); - unite_disjoint_maps(all_head_deltas, local_deltas); - pred2idx all_tail_deltas(global_tail_deltas); - unite_disjoint_maps(all_tail_deltas, local_deltas); - - //generate code for the iterative fixpoint search - //The order in which we iterate the preds_vector matters, since rules can depend on - //deltas generated earlier in the same iteration. - compile_preds(head_preds, widened_preds, &all_tail_deltas, all_head_deltas, *loop_body); - - svector loop_control_regs; //loop is controlled by global src regs - collect_map_range(loop_control_regs, global_tail_deltas); - //move target deltas into source deltas at the end of the loop - //and clear local deltas - make_inloop_delta_transition(global_head_deltas, global_tail_deltas, local_deltas, *loop_body); - - loop_body->set_observer(0); - acc.push_back(instruction::mk_while_loop(loop_control_regs.size(), - loop_control_regs.c_ptr(), loop_body)); - } - - void compiler::compile_dependent_rules(const func_decl_set & head_preds, - const pred2idx * input_deltas, const pred2idx & output_deltas, - bool add_saturation_marks, instruction_block & acc) { - - if (!output_deltas.empty()) { - func_decl_set::iterator hpit = head_preds.begin(); - func_decl_set::iterator hpend = head_preds.end(); - for(; hpit!=hpend; ++hpit) { - if(output_deltas.contains(*hpit)) { - //we do not support retrieving deltas for rules that are inside a recursive - //stratum, since we would have to maintain this 'global' delta through the loop - //iterations - NOT_IMPLEMENTED_YET(); - } - } - } - - func_decl_vector preds_vector; - func_decl_set global_deltas; - - detect_chains(head_preds, preds_vector, global_deltas); - - func_decl_set local_deltas(head_preds); - set_difference(local_deltas, global_deltas); - - pred2idx d_global_src; //these deltas serve as sources of tuples for rule evaluation inside the loop - get_fresh_registers(global_deltas, d_global_src); - pred2idx d_global_tgt; //these deltas are targets for new tuples in rule evaluation inside the loop - get_fresh_registers(global_deltas, d_global_tgt); - pred2idx d_local; - get_fresh_registers(local_deltas, d_local); - - pred2idx d_all_src(d_global_src); //src together with local deltas - unite_disjoint_maps(d_all_src, d_local); - pred2idx d_all_tgt(d_global_tgt); //tgt together with local deltas - unite_disjoint_maps(d_all_tgt, d_local); - - - func_decl_set empty_func_decl_set; - - //generate code for the initial run - // compile_preds(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); - compile_preds_init(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); - - if (compile_with_widening()) { - compile_loop(preds_vector, global_deltas, d_global_tgt, d_global_src, d_local, acc); - } - else { - compile_loop(preds_vector, empty_func_decl_set, d_global_tgt, d_global_src, d_local, acc); - } - - - if(add_saturation_marks) { - //after the loop finishes, all predicates in the group are saturated, - //so we may mark them as such - func_decl_set::iterator fdit = head_preds.begin(); - func_decl_set::iterator fdend = head_preds.end(); - for(; fdit!=fdend; ++fdit) { - acc.push_back(instruction::mk_mark_saturated(m_context.get_manager(), *fdit)); - } - } - } - - bool compiler::is_nonrecursive_stratum(const func_decl_set & preds) const { - SASSERT(preds.size()>0); - if(preds.size()>1) { - return false; - } - func_decl * head_pred = *preds.begin(); - const rule_vector & rules = m_rule_set.get_predicate_rules(head_pred); - rule_vector::const_iterator it = rules.begin(); - rule_vector::const_iterator end = rules.end(); - for(; it!=end; ++it) { - //it is sufficient to check just for presence of the first head predicate, - //since if the rules are recursive and their heads are strongly connected by dependence, - //this predicate must appear in some tail - if((*it)->is_in_tail(head_pred)) { - return false; - } - } - return true; - } - - void compiler::compile_nonrecursive_stratum(const func_decl_set & preds, - const pred2idx * input_deltas, const pred2idx & output_deltas, - bool add_saturation_marks, instruction_block & acc) { - //non-recursive stratum always has just one head predicate - SASSERT(preds.size()==1); - SASSERT(is_nonrecursive_stratum(preds)); - func_decl * head_pred = *preds.begin(); - const rule_vector & rules = m_rule_set.get_predicate_rules(head_pred); - - - - reg_idx output_delta; - if (!output_deltas.find(head_pred, output_delta)) { - output_delta = execution_context::void_register; - } - - rule_vector::const_iterator it = rules.begin(); - rule_vector::const_iterator end = rules.end(); - for (; it != end; ++it) { - rule * r = *it; - SASSERT(r->get_decl()==head_pred); - - compile_rule_evaluation(r, input_deltas, output_delta, false, acc); - } - - if (add_saturation_marks) { - //now the predicate is saturated, so we may mark it as such - acc.push_back(instruction::mk_mark_saturated(m_context.get_manager(), head_pred)); - } - } - - bool compiler::all_saturated(const func_decl_set & preds) const { - func_decl_set::iterator fdit = preds.begin(); - func_decl_set::iterator fdend = preds.end(); - for(; fdit!=fdend; ++fdit) { - if(!m_context.get_rel_context().get_rmanager().is_saturated(*fdit)) { - return false; - } - } - return true; - } - - void compiler::compile_strats(const rule_stratifier & stratifier, - const pred2idx * input_deltas, const pred2idx & output_deltas, - bool add_saturation_marks, instruction_block & acc) { - rule_set::pred_set_vector strats = stratifier.get_strats(); - rule_set::pred_set_vector::const_iterator sit = strats.begin(); - rule_set::pred_set_vector::const_iterator send = strats.end(); - for(; sit!=send; ++sit) { - func_decl_set & strat_preds = **sit; - - if (all_saturated(strat_preds)) { - //all predicates in stratum are saturated, so no need to compile rules for them - continue; - } - - TRACE("dl", - tout << "Stratum: "; - func_decl_set::iterator pit = strat_preds.begin(); - func_decl_set::iterator pend = strat_preds.end(); - for(; pit!=pend; ++pit) { - func_decl * pred = *pit; - tout << pred->get_name() << " "; - } - tout << "\n"; - ); - - if (is_nonrecursive_stratum(strat_preds)) { - //this stratum contains just a single non-recursive rule - compile_nonrecursive_stratum(strat_preds, input_deltas, output_deltas, add_saturation_marks, acc); - } - else { - compile_dependent_rules(strat_preds, input_deltas, output_deltas, - add_saturation_marks, acc); - } - } - } - - void compiler::do_compilation(instruction_block & execution_code, - instruction_block & termination_code) { - - unsigned rule_cnt=m_rule_set.get_num_rules(); - if(rule_cnt==0) { - return; - } - - instruction_block & acc = execution_code; - acc.set_observer(&m_instruction_observer); - - - //load predicate data - for(unsigned i=0;iget_decl(), acc); - - unsigned rule_len = r->get_uninterpreted_tail_size(); - for(unsigned j=0;jget_tail(j)->get_decl(), acc); - } - } - - pred2idx empty_pred2idx_map; - - compile_strats(m_rule_set.get_stratifier(), static_cast(0), - empty_pred2idx_map, true, execution_code); - - - - //store predicate data - pred2idx::iterator pit = m_pred_regs.begin(); - pred2idx::iterator pend = m_pred_regs.end(); - for(; pit!=pend; ++pit) { - pred2idx::key_data & e = *pit; - func_decl * pred = e.m_key; - reg_idx reg = e.m_value; - termination_code.push_back(instruction::mk_store(m_context.get_manager(), pred, reg)); - } - - acc.set_observer(0); - - TRACE("dl", execution_code.display(m_context.get_rel_context(), tout);); - } - - -} - From 9c230941cc4179ec5aa9a73b3b928bce16f47d4d Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 23 Apr 2013 12:01:50 -0700 Subject: [PATCH 28/91] [datalog] improve performance of smt2 frontend - delay calls to make_annotations and process_costs untill needed - remove debug exception handler in join() Signed-off-by: Nuno Lopes --- src/muz_qe/dl_instruction.cpp | 16 +--------------- src/muz_qe/rel_context.cpp | 9 +++++---- src/muz_qe/rel_context.h | 2 +- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz_qe/dl_instruction.cpp index 296fa95f3..b103d743e 100644 --- a/src/muz_qe/dl_instruction.cpp +++ b/src/muz_qe/dl_instruction.cpp @@ -359,21 +359,7 @@ namespace datalog { r2.get_signature().output(ctx.get_rel_context().get_manager(), tout); tout<<":"<\n";); - try { - ctx.set_reg(m_res, (*fn)(r1, r2)); - } - catch(...) - { - std::string annotation; - unsigned sz; - ctx.get_register_annotation(m_rel1, annotation); - sz = ctx.reg(m_rel1)?ctx.reg(m_rel1)->get_size_estimate_rows():0; - std::cout << m_rel1 << "\t" << sz << "\t" << annotation << "\n"; - ctx.get_register_annotation(m_rel2, annotation); - sz = ctx.reg(m_rel2)?ctx.reg(m_rel2)->get_size_estimate_rows():0; - std::cout << m_rel2 << "\t" << sz << "\t" << annotation << "\n"; - throw; - } + ctx.set_reg(m_res, (*fn)(r1, r2)); TRACE("dl", ctx.reg(m_res)->get_signature().output(ctx.get_rel_context().get_manager(), tout); diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index ef5639279..731ab8b63 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -179,9 +179,7 @@ namespace datalog { scoped_query.reset(); } m_context.record_transformed_rules(); - TRACE("dl", m_ectx.report_big_relations(100, tout);); - m_code.process_all_costs(); - m_code.make_annotations(m_ectx); + TRACE("dl", display_profile(tout);); return result; } @@ -480,7 +478,10 @@ namespace datalog { get_rmanager().display(out); } - void rel_context::display_profile(std::ostream& out) const { + void rel_context::display_profile(std::ostream& out) { + m_code.make_annotations(m_ectx); + m_code.process_all_costs(); + out << "\n--------------\n"; out << "Instructions\n"; m_code.display(*this, out); diff --git a/src/muz_qe/rel_context.h b/src/muz_qe/rel_context.h index 3e019cb50..8e4c6f2de 100644 --- a/src/muz_qe/rel_context.h +++ b/src/muz_qe/rel_context.h @@ -105,7 +105,7 @@ namespace datalog { void display_output_facts(rule_set const& rules, std::ostream & out) const; void display_facts(std::ostream & out) const; - void display_profile(std::ostream& out) const; + void display_profile(std::ostream& out); lbool saturate(); From f58e8e961dfd09181407d7be90c8498678c04859 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 23 Apr 2013 14:59:19 -0700 Subject: [PATCH 29/91] fix the build Signed-off-by: Nuno Lopes --- src/muz_qe/dl_mk_filter_rules.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/muz_qe/dl_mk_filter_rules.cpp b/src/muz_qe/dl_mk_filter_rules.cpp index 8ab8412c0..932a644ab 100644 --- a/src/muz_qe/dl_mk_filter_rules.cpp +++ b/src/muz_qe/dl_mk_filter_rules.cpp @@ -152,9 +152,6 @@ namespace datalog { } rule_set * mk_filter_rules::operator()(rule_set const & source) { - if (!m_context.get_params().filter_rules()) { - return 0; - } m_tail2filter.reset(); m_result = alloc(rule_set, m_context); m_modified = false; From 780ad7cc179aa38648a2aca7c6a1d8a99ff42763 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 25 Apr 2013 09:30:51 -0700 Subject: [PATCH 30/91] fix seg-fault caused by neglecting to inherit output predicate in slice Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_slice.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/muz_qe/dl_mk_slice.cpp b/src/muz_qe/dl_mk_slice.cpp index 21c3486b3..89804447b 100644 --- a/src/muz_qe/dl_mk_slice.cpp +++ b/src/muz_qe/dl_mk_slice.cpp @@ -725,6 +725,9 @@ namespace datalog { m_mc->add_predicate(p, f); } } + else if (src.is_output_predicate(p)) { + dst.set_output_predicate(p); + } } } From 83add2bd9bfcd3dab8917b0baf25a75c33f0b343 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 25 Apr 2013 13:39:11 -0700 Subject: [PATCH 31/91] fix bugs reported by Filip Konecny in PDR Signed-off-by: Nikolaj Bjorner --- src/ast/ast_smt_pp.cpp | 5 +- src/muz_qe/dl_mk_rule_inliner.cpp | 26 ++++-- src/muz_qe/dl_mk_rule_inliner.h | 2 +- src/muz_qe/dl_mk_subsumption_checker.cpp | 6 +- src/muz_qe/dl_rule.cpp | 8 +- src/muz_qe/horn_tactic.cpp | 16 ++-- src/muz_qe/pdr_context.cpp | 14 ++- src/muz_qe/pdr_context.h | 1 + src/muz_qe/pdr_util.cpp | 30 +++++- src/muz_qe/pdr_util.h | 2 + src/muz_qe/proof_utils.cpp | 91 +++++++++++++++++++ src/muz_qe/proof_utils.h | 6 ++ src/parsers/smt2/smt2parser.cpp | 2 +- src/tactic/portfolio/smt_strategic_solver.cpp | 1 - 14 files changed, 176 insertions(+), 34 deletions(-) diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 5819e3930..f684879ae 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -260,6 +260,7 @@ class smt_printer { else { m_out << sym << "["; } + for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (p.is_ast()) { @@ -642,9 +643,7 @@ class smt_printer { m_out << m_var_names[m_num_var_names - idx - 1]; } else { - if (!m_is_smt2) { - m_out << "?" << idx; - } + m_out << "?" << idx; } } diff --git a/src/muz_qe/dl_mk_rule_inliner.cpp b/src/muz_qe/dl_mk_rule_inliner.cpp index 8428d9049..5bbeb2378 100644 --- a/src/muz_qe/dl_mk_rule_inliner.cpp +++ b/src/muz_qe/dl_mk_rule_inliner.cpp @@ -709,8 +709,7 @@ namespace datalog { #define PRT(_x_) ((_x_)?"T":"F") - bool mk_rule_inliner::inline_linear(rule_set const& source, scoped_ptr& rules) { - scoped_ptr res = alloc(rule_set, m_context); + bool mk_rule_inliner::inline_linear(scoped_ptr& rules) { bool done_something = false; unsigned sz = rules->get_num_rules(); @@ -731,7 +730,7 @@ namespace datalog { svector& can_expand = m_head_visitor.can_expand(); for (unsigned i = 0; i < sz; ++i) { - add_rule(source, acc[i].get(), i); + add_rule(*rules, acc[i].get(), i); } // initialize substitution. @@ -808,7 +807,7 @@ namespace datalog { TRACE("dl", r->display(m_context, tout); r2->display(m_context, tout); rl_res->display(m_context, tout); ); del_rule(r, i); - add_rule(source, rl_res.get(), i); + add_rule(*rules, rl_res.get(), i); r = rl_res; @@ -828,13 +827,15 @@ namespace datalog { } } if (done_something) { - rules = alloc(rule_set, m_context); + scoped_ptr res = alloc(rule_set, m_context); for (unsigned i = 0; i < sz; ++i) { if (valid.get(i)) { - rules->add_rule(acc[i].get()); + res->add_rule(acc[i].get()); } } - TRACE("dl", rules->display(tout);); + res->inherit_predicates(*rules); + TRACE("dl", res->display(tout);); + rules = res.detach(); } return done_something; } @@ -871,11 +872,17 @@ namespace datalog { // try eager inlining if (do_eager_inlining(res)) { something_done = true; - } + } TRACE("dl", res->display(tout << "after eager inlining\n");); + } + if (something_done) { + res->inherit_predicates(source); + } + else { + res = alloc(rule_set, source); } - if (m_context.get_params().inline_linear() && inline_linear(source, res)) { + if (m_context.get_params().inline_linear() && inline_linear(res)) { something_done = true; } @@ -883,7 +890,6 @@ namespace datalog { res = 0; } else { - res->inherit_predicates(source); m_context.add_model_converter(hsmc.get()); } diff --git a/src/muz_qe/dl_mk_rule_inliner.h b/src/muz_qe/dl_mk_rule_inliner.h index 476c6b4b0..3a933f990 100644 --- a/src/muz_qe/dl_mk_rule_inliner.h +++ b/src/muz_qe/dl_mk_rule_inliner.h @@ -172,7 +172,7 @@ namespace datalog { /** Inline predicates that are known to not be join-points. */ - bool inline_linear(rule_set const& source, scoped_ptr& rules); + bool inline_linear(scoped_ptr& rules); void add_rule(rule_set const& rule_set, rule* r, unsigned i); void del_rule(rule* r, unsigned i); diff --git a/src/muz_qe/dl_mk_subsumption_checker.cpp b/src/muz_qe/dl_mk_subsumption_checker.cpp index 0e94ba3b9..8c9e7e69f 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.cpp +++ b/src/muz_qe/dl_mk_subsumption_checker.cpp @@ -241,6 +241,7 @@ namespace datalog { tgt.add_rule(new_rule); subs_index.add(new_rule); } + tgt.inherit_predicates(orig); TRACE("dl", tout << "original set size: "<inherit_predicates(source); return res; } diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index 92f504bcc..32f3a5fe8 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -1004,16 +1004,14 @@ namespace datalog { } svector names; used_symbols<> us; - - us(fml); - sorts.reverse(); - for (unsigned i = 0; i < sorts.size(); ++i) { if (!sorts[i]) { sorts[i] = m.mk_bool_sort(); } } - + + us(fml); + sorts.reverse(); for (unsigned j = 0, i = 0; i < sorts.size(); ++j) { for (char c = 'A'; i < sorts.size() && c <= 'Z'; ++c) { func_decl_ref f(m); diff --git a/src/muz_qe/horn_tactic.cpp b/src/muz_qe/horn_tactic.cpp index 1a8f562d9..5db4f1a00 100644 --- a/src/muz_qe/horn_tactic.cpp +++ b/src/muz_qe/horn_tactic.cpp @@ -125,12 +125,13 @@ class horn_tactic : public tactic { enum formula_kind { IS_RULE, IS_QUERY, IS_NONE }; formula_kind get_formula_kind(expr_ref& f) { - normalize(f); + expr_ref tmp(f); + normalize(tmp); ast_mark mark; expr_ref_vector args(m), body(m); expr_ref head(m); expr* a = 0, *a1 = 0; - datalog::flatten_or(f, args); + datalog::flatten_or(tmp, args); for (unsigned i = 0; i < args.size(); ++i) { a = args[i].get(); check_predicate(mark, a); @@ -147,12 +148,12 @@ class horn_tactic : public tactic { body.push_back(m.mk_not(a)); } } - f = m.mk_and(body.size(), body.c_ptr()); if (head) { - f = m.mk_implies(f, head); + // f = m.mk_implies(f, head); return IS_RULE; } else { + f = m.mk_and(body.size(), body.c_ptr()); return IS_QUERY; } } @@ -171,7 +172,7 @@ class horn_tactic : public tactic { tactic_report report("horn", *g); bool produce_proofs = g->proofs_enabled(); - if (produce_proofs) { + if (produce_proofs) { if (!m_ctx.get_params().generate_proof_trace()) { params_ref params = m_ctx.get_params().p; params.set_bool("generate_proof_trace", true); @@ -239,10 +240,13 @@ class horn_tactic : public tactic { switch (is_reachable) { case l_true: { // goal is unsat - g->assert_expr(m.mk_false()); if (produce_proofs) { proof_ref proof = m_ctx.get_proof(); pc = proof2proof_converter(m, proof); + g->assert_expr(m.mk_false(), proof, 0); + } + else { + g->assert_expr(m.mk_false()); } break; } diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index a240a9aef..62b119538 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -43,6 +43,7 @@ Notes: #include "ast_ll_pp.h" #include "proof_checker.h" #include "smt_value_sort.h" +#include "proof_utils.h" namespace pdr { @@ -275,7 +276,7 @@ namespace pdr { src.pop_back(); } else if (is_invariant(tgt_level, curr, false, assumes_level)) { - + add_property(curr, assumes_level?tgt_level:infty_level); TRACE("pdr", tout << "is invariant: "<< pp_level(tgt_level) << " " << mk_pp(curr, m) << "\n";); src[i] = src.back(); @@ -1225,6 +1226,7 @@ namespace pdr { m_search(m_params.bfs_model_search()), m_last_result(l_undef), m_inductive_lvl(0), + m_expanded_lvl(0), m_cancel(false) { } @@ -1680,6 +1682,9 @@ namespace pdr { proof = m_search.get_proof_trace(*this); TRACE("pdr", tout << "PDR trace: " << mk_pp(proof, m) << "\n";); apply(m, m_pc.get(), proof); + TRACE("pdr", tout << "PDR trace: " << mk_pp(proof, m) << "\n";); + // proof_utils::push_instantiations_up(proof); + // TRACE("pdr", tout << "PDR up: " << mk_pp(proof, m) << "\n";); return proof; } @@ -1711,6 +1716,7 @@ namespace pdr { bool reachable; while (true) { checkpoint(); + m_expanded_lvl = lvl; reachable = check_reachability(lvl); if (reachable) { throw model_exception(); @@ -1769,6 +1775,10 @@ namespace pdr { void context::expand_node(model_node& n) { SASSERT(n.is_open()); expr_ref_vector cube(m); + + if (n.level() < m_expanded_lvl) { + m_expanded_lvl = n.level(); + } if (n.pt().is_reachable(n.state())) { TRACE("pdr", tout << "reachable\n";); @@ -1835,7 +1845,7 @@ namespace pdr { if (m_params.simplify_formulas_pre()) { simplify_formulas(); } - for (unsigned lvl = 0; lvl <= max_prop_lvl; lvl++) { + for (unsigned lvl = m_expanded_lvl; lvl <= max_prop_lvl; lvl++) { checkpoint(); bool all_propagated = true; decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); diff --git a/src/muz_qe/pdr_context.h b/src/muz_qe/pdr_context.h index 6aa02ef10..b5652d1b6 100644 --- a/src/muz_qe/pdr_context.h +++ b/src/muz_qe/pdr_context.h @@ -303,6 +303,7 @@ namespace pdr { mutable model_search m_search; lbool m_last_result; unsigned m_inductive_lvl; + unsigned m_expanded_lvl; ptr_vector m_core_generalizers; stats m_stats; volatile bool m_cancel; diff --git a/src/muz_qe/pdr_util.cpp b/src/muz_qe/pdr_util.cpp index 237cf9415..62185f1d2 100644 --- a/src/muz_qe/pdr_util.cpp +++ b/src/muz_qe/pdr_util.cpp @@ -1081,6 +1081,7 @@ namespace pdr { arith_util a; bv_util bv; bool m_is_dl; + bool m_test_for_utvpi; bool is_numeric(expr* e) const { if (a.is_numeral(e)) { @@ -1115,6 +1116,16 @@ namespace pdr { } return false; } + if (m_test_for_utvpi) { + if (a.is_mul(e, e1, e2)) { + if (is_minus_one(e1)) { + return is_offset(e2); + } + if (is_minus_one(e2)) { + return is_offset(e1); + } + } + } return !is_arith_expr(e); } @@ -1140,6 +1151,9 @@ namespace pdr { if (!a.is_add(lhs, arg1, arg2)) return false; // x + if (m_test_for_utvpi) { + return is_offset(arg1) && is_offset(arg2); + } if (is_arith_expr(arg1)) std::swap(arg1, arg2); if (is_arith_expr(arg1)) @@ -1209,8 +1223,10 @@ namespace pdr { } public: - test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true) {} - + test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} + + void test_for_utvpi() { m_test_for_utvpi = true; } + void operator()(expr* e) { if (!m_is_dl) { return; @@ -1248,6 +1264,16 @@ namespace pdr { return test.is_dl(); } + bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { + test_diff_logic test(m); + test.test_for_utvpi(); + expr_fast_mark1 mark; + for (unsigned i = 0; i < num_fmls; ++i) { + quick_for_each_expr(test, mark, fmls[i]); + } + return test.is_dl(); + } + } template class rewriter_tpl; diff --git a/src/muz_qe/pdr_util.h b/src/muz_qe/pdr_util.h index ddbf0d122..5f2d22b76 100644 --- a/src/muz_qe/pdr_util.h +++ b/src/muz_qe/pdr_util.h @@ -151,6 +151,8 @@ namespace pdr { bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); + bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); + } #endif diff --git a/src/muz_qe/proof_utils.cpp b/src/muz_qe/proof_utils.cpp index 369c3aae6..36e721b5c 100644 --- a/src/muz_qe/proof_utils.cpp +++ b/src/muz_qe/proof_utils.cpp @@ -1,6 +1,7 @@ #include "dl_util.h" #include "proof_utils.h" #include "ast_smt2_pp.h" +#include "var_subst.h" class reduce_hypotheses { typedef obj_hashtable expr_set; @@ -517,3 +518,93 @@ void proof_utils::permute_unit_resolution(proof_ref& pr) { obj_map cache; ::permute_unit_resolution(refs, cache, pr); } + +class push_instantiations_up_cl { + ast_manager& m; +public: + push_instantiations_up_cl(ast_manager& m): m(m) {} + + void operator()(proof_ref& p) { + expr_ref_vector s0(m); + p = push(p, s0); + } + +private: + + proof* push(proof* p, expr_ref_vector const& sub) { + proof_ref_vector premises(m); + expr_ref conclusion(m); + svector > positions; + vector substs; + + if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { + for (unsigned i = 0; i < premises.size(); ++i) { + compose(substs[i], sub); + premises[i] = push(premises[i].get(), substs[i]); + substs[i].reset(); + } + instantiate(sub, conclusion); + return + m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, + positions, + substs); + } + if (sub.empty()) { + return p; + } + if (m.is_modus_ponens(p)) { + SASSERT(m.get_num_parents(p) == 2); + proof* p0 = m.get_parent(p, 0); + proof* p1 = m.get_parent(p, 1); + if (m.get_fact(p0) == m.get_fact(p)) { + return push(p0, sub); + } + expr* e1, *e2; + if (m.is_rewrite(p1, e1, e2) && + is_quantifier(e1) && is_quantifier(e2) && + to_quantifier(e1)->get_num_decls() == to_quantifier(e2)->get_num_decls()) { + expr_ref r1(e1,m), r2(e2,m); + instantiate(sub, r1); + instantiate(sub, r2); + p1 = m.mk_rewrite(r1, r2); + return m.mk_modus_ponens(push(p0, sub), p1); + } + } + premises.push_back(p); + substs.push_back(sub); + conclusion = m.get_fact(p); + instantiate(sub, conclusion); + return m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); + } + + void compose(expr_ref_vector& sub, expr_ref_vector const& s0) { + for (unsigned i = 0; i < sub.size(); ++i) { + expr_ref e(m); + var_subst(m, false)(sub[i].get(), s0.size(), s0.c_ptr(), e); + sub[i] = e; + } + } + + void instantiate(expr_ref_vector const& sub, expr_ref& fml) { + if (sub.empty()) { + return; + } + if (!is_forall(fml)) { + return; + } + quantifier* q = to_quantifier(fml); + if (q->get_num_decls() != sub.size()) { + TRACE("proof_utils", tout << "quantifier has different number of variables than substitution"; + tout << mk_pp(q, m) << "\n"; + tout << sub.size() << "\n";); + return; + } + var_subst(m, false)(q->get_expr(), sub.size(), sub.c_ptr(), fml); + } + +}; + +void proof_utils::push_instantiations_up(proof_ref& pr) { + push_instantiations_up_cl push(pr.get_manager()); + push(pr); +} diff --git a/src/muz_qe/proof_utils.h b/src/muz_qe/proof_utils.h index 53a38592e..dc3cdc3ef 100644 --- a/src/muz_qe/proof_utils.h +++ b/src/muz_qe/proof_utils.h @@ -36,6 +36,12 @@ public: */ static void permute_unit_resolution(proof_ref& pr); + /** + \brief Push instantiations created in hyper-resolutions up to leaves. + This produces a "ground" proof where leaves are annotated by instantiations. + */ + static void push_instantiations_up(proof_ref& pr); + }; #endif diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 0698f4eaf..b5af6353e 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -1159,7 +1159,7 @@ namespace smt2 { m_num_expr_frames++; unsigned num_vars = parse_sorted_vars(); if (num_vars == 0) - throw parser_exception("invalied quantifier, list of sorted variables is empty"); + throw parser_exception("invalid quantifier, list of sorted variables is empty"); } symbol parse_indexed_identifier_core() { diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 55512f677..ae79446e3 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -94,7 +94,6 @@ public: smt_strategic_solver_factory(symbol const & logic):m_logic(logic) {} virtual ~smt_strategic_solver_factory() {} - virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { symbol l; if (m_logic != symbol::null) From c58b4f9a53e2fae636568157bdbfbc958596ce23 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 Apr 2013 11:43:06 -0700 Subject: [PATCH 32/91] optimize rule processing Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/ast_counter.cpp | 4 +- src/ast/rewriter/ast_counter.h | 2 + src/ast/rewriter/var_subst.cpp | 35 ++++--- src/ast/rewriter/var_subst.h | 12 +++ src/muz_qe/dl_context.cpp | 3 +- src/muz_qe/dl_context.h | 1 + src/muz_qe/dl_finite_product_relation.cpp | 4 +- src/muz_qe/dl_mk_array_blast.cpp | 7 +- src/muz_qe/dl_mk_array_blast.h | 1 + src/muz_qe/dl_mk_filter_rules.cpp | 28 +++--- src/muz_qe/dl_mk_filter_rules.h | 3 +- src/muz_qe/dl_mk_interp_tail_simplifier.cpp | 45 ++++++--- src/muz_qe/dl_mk_interp_tail_simplifier.h | 27 +++--- src/muz_qe/dl_mk_magic_sets.cpp | 3 +- src/muz_qe/dl_mk_magic_sets.h | 1 + src/muz_qe/dl_mk_rule_inliner.cpp | 4 - src/muz_qe/dl_mk_simple_joins.cpp | 27 +++--- src/muz_qe/dl_mk_simple_joins.h | 3 +- src/muz_qe/dl_mk_unbound_compressor.cpp | 28 +++--- src/muz_qe/dl_mk_unbound_compressor.h | 1 + src/muz_qe/dl_rule.cpp | 101 +++++++++++++++----- src/muz_qe/dl_rule.h | 41 ++++++++ src/muz_qe/dl_sieve_relation.cpp | 3 +- src/muz_qe/dl_util.cpp | 50 +--------- src/muz_qe/dl_util.h | 20 ---- src/muz_qe/qe_lite.cpp | 8 +- src/smt/theory_diff_logic.h | 2 +- src/util/memory_manager.cpp | 3 +- 28 files changed, 267 insertions(+), 200 deletions(-) diff --git a/src/ast/rewriter/ast_counter.cpp b/src/ast/rewriter/ast_counter.cpp index c542abb60..099bdedec 100644 --- a/src/ast/rewriter/ast_counter.cpp +++ b/src/ast/rewriter/ast_counter.cpp @@ -93,7 +93,9 @@ void var_counter::count_vars(ast_manager & m, const app * pred, int coef) { unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { m_sorts.reset(); - ::get_free_vars(pred->get_arg(i), m_sorts); + m_todo.reset(); + m_mark.reset(); + ::get_free_vars(m_mark, m_todo, pred->get_arg(i), m_sorts); for (unsigned j = 0; j < m_sorts.size(); ++j) { if (m_sorts[j]) { update(j, coef); diff --git a/src/ast/rewriter/ast_counter.h b/src/ast/rewriter/ast_counter.h index 2a581c302..e7251079f 100644 --- a/src/ast/rewriter/ast_counter.h +++ b/src/ast/rewriter/ast_counter.h @@ -38,6 +38,7 @@ public: counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} + void reset() { m_data.reset(); } iterator begin() const { return m_data.begin(); } iterator end() const { return m_data.end(); } void update(unsigned el, int delta); @@ -71,6 +72,7 @@ protected: ptr_vector m_sorts; expr_fast_mark1 m_visited; ptr_vector m_todo; + ast_mark m_mark; unsigned_vector m_scopes; unsigned get_max_var(bool & has_var); public: diff --git a/src/ast/rewriter/var_subst.cpp b/src/ast/rewriter/var_subst.cpp index 3ebc0b573..f7f0c8aef 100644 --- a/src/ast/rewriter/var_subst.cpp +++ b/src/ast/rewriter/var_subst.cpp @@ -17,7 +17,6 @@ Notes: --*/ #include"var_subst.h" -#include"used_vars.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"ast_smt2_pp.h" @@ -40,7 +39,7 @@ void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, exp tout << mk_ismt2_pp(result, m_reducer.m()) << "\n";); } -void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { +void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { SASSERT(is_well_sorted(m, q)); if (is_ground(q->get_expr())) { // ignore patterns if the body is a ground formula. @@ -51,11 +50,11 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { result = q; return; } - used_vars used; - used.process(q->get_expr()); + m_used.reset(); + m_used.process(q->get_expr()); unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) - used.process(q->get_pattern(i)); + m_used.process(q->get_pattern(i)); unsigned num_no_patterns = q->get_num_no_patterns(); for (unsigned i = 0; i < num_no_patterns; i++) used.process(q->get_no_pattern(i)); @@ -110,9 +109,8 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { std::reverse(var_mapping.c_ptr(), var_mapping.c_ptr() + var_mapping.size()); expr_ref new_expr(m); - var_subst subst(m); - subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr(), new_expr); + m_subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr(), new_expr); if (num_removed == num_decls) { result = new_expr; @@ -145,7 +143,12 @@ void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { num_no_patterns, new_no_patterns.c_ptr()); to_quantifier(result)->set_no_unused_vars(); - SASSERT(is_well_sorted(m, result)); + SASSERT(is_well_sorted(m, result)); +} + +void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { + unused_vars_eliminator el(m); + el(q, result); } void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result) { @@ -161,9 +164,7 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";); } -static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector& sorts) { - ast_mark mark; - ptr_vector todo; +static void get_free_vars_offset(ast_mark& mark, ptr_vector& todo, unsigned offset, expr* e, ptr_vector& sorts) { todo.push_back(e); while (!todo.empty()) { e = todo.back(); @@ -175,7 +176,9 @@ static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector& sor switch(e->get_kind()) { case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); - get_free_vars_offset(q->get_expr(), offset+q->get_num_decls(), sorts); + ast_mark mark1; + ptr_vector todo1; + get_free_vars_offset(mark1, todo1, offset+q->get_num_decls(), q->get_expr(), sorts); break; } case AST_VAR: { @@ -207,5 +210,11 @@ static void get_free_vars_offset(expr* e, unsigned offset, ptr_vector& sor void get_free_vars(expr* e, ptr_vector& sorts) { - get_free_vars_offset(e, 0, sorts); + ast_mark mark; + ptr_vector todo; + get_free_vars_offset(mark, todo, 0, e, sorts); +} + +void get_free_vars(ast_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts) { + get_free_vars_offset(mark, todo, 0, e, sorts); } diff --git a/src/ast/rewriter/var_subst.h b/src/ast/rewriter/var_subst.h index 9f3c13c0c..ffc21e691 100644 --- a/src/ast/rewriter/var_subst.h +++ b/src/ast/rewriter/var_subst.h @@ -20,6 +20,7 @@ Notes: #define _VAR_SUBST_H_ #include"rewriter.h" +#include"used_vars.h" /** \brief Alias for var_shifter class. @@ -53,6 +54,15 @@ public: /** \brief Eliminate the unused variables from \c q. Store the result in \c r. */ +class unused_vars_eliminator { + ast_manager& m; + var_subst m_subst; + used_vars m_used; +public: + unused_vars_eliminator(ast_manager& m): m(m), m_subst(m) {} + void operator()(quantifier* q, expr_ref& r); +}; + void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & r); /** @@ -73,6 +83,8 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref */ void get_free_vars(expr* e, ptr_vector& sorts); +void get_free_vars(ast_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts); + #endif diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 50915946a..f283bd456 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -226,6 +226,7 @@ namespace datalog { m_rewriter(m), m_var_subst(m), m_rule_manager(*this), + m_elim_unused_vars(m), m_transf(*this), m_trail(*this), m_pinned(m), @@ -332,7 +333,7 @@ namespace datalog { quantifier_ref q(m); sorts.reverse(); q = m.mk_quantifier(is_forall, sorts.size(), sorts.c_ptr(), names.c_ptr(), result); - elim_unused_vars(m, q, result); + m_elim_unused_vars(q, result); } } return result; diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index 74449940c..2798ba8df 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -84,6 +84,7 @@ namespace datalog { th_rewriter m_rewriter; var_subst m_var_subst; rule_manager m_rule_manager; + unused_vars_eliminator m_elim_unused_vars; rule_transformer m_transf; trail_stack m_trail; ast_ref_vector m_pinned; diff --git a/src/muz_qe/dl_finite_product_relation.cpp b/src/muz_qe/dl_finite_product_relation.cpp index 4880e6068..86fef433b 100644 --- a/src/muz_qe/dl_finite_product_relation.cpp +++ b/src/muz_qe/dl_finite_product_relation.cpp @@ -1291,8 +1291,8 @@ namespace datalog { m_renaming_for_inner_rel(m_manager) { relation_manager & rmgr = r.get_manager(); - idx_set cond_columns; - collect_vars(m_manager, m_cond, cond_columns); + rule_manager& rm = r.get_context().get_rule_manager(); + idx_set& cond_columns = rm.collect_vars(m_cond); unsigned sig_sz = r.get_signature().size(); for(unsigned i=0; i m_vars; mk_interp_tail_simplifier m_simplifier; typedef obj_map defs_t; diff --git a/src/muz_qe/dl_mk_filter_rules.cpp b/src/muz_qe/dl_mk_filter_rules.cpp index 932a644ab..ef89c9ffa 100644 --- a/src/muz_qe/dl_mk_filter_rules.cpp +++ b/src/muz_qe/dl_mk_filter_rules.cpp @@ -27,9 +27,10 @@ namespace datalog { mk_filter_rules::mk_filter_rules(context & ctx): plugin(2000), m_context(ctx), - m_manager(ctx.get_manager()), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), m_result(0), - m_pinned(m_manager) { + m_pinned(m) { } mk_filter_rules::~mk_filter_rules() { @@ -52,14 +53,14 @@ namespace datalog { */ bool mk_filter_rules::is_candidate(app * pred) { if (!m_context.is_predicate(pred)) { - TRACE("mk_filter_rules", tout << mk_pp(pred, m_manager) << "\nis not a candidate because it is interpreted.\n";); + TRACE("mk_filter_rules", tout << mk_pp(pred, m) << "\nis not a candidate because it is interpreted.\n";); return false; } var_idx_set used_vars; unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = pred->get_arg(i); - if (m_manager.is_value(arg)) + if (m.is_value(arg)) return true; SASSERT(is_var(arg)); unsigned vidx = to_var(arg)->get_idx(); @@ -74,10 +75,10 @@ namespace datalog { \brief Create a "filter" (if it doesn't exist already) for the given predicate. */ func_decl * mk_filter_rules::mk_filter_decl(app * pred, var_idx_set const & non_local_vars) { - sort_ref_buffer filter_domain(m_manager); + sort_ref_buffer filter_domain(m); - filter_key * key = alloc(filter_key, m_manager); - mk_new_rule_tail(m_manager, pred, non_local_vars, filter_domain, key->filter_args, key->new_pred); + filter_key * key = alloc(filter_key, m); + mk_new_rule_tail(m, pred, non_local_vars, filter_domain, key->filter_args, key->new_pred); func_decl * filter_decl = 0; if (!m_tail2filter.find(key, filter_decl)) { filter_decl = m_context.mk_fresh_head_predicate(pred->get_decl()->get_name(), symbol("filter"), @@ -85,8 +86,8 @@ namespace datalog { m_pinned.push_back(filter_decl); m_tail2filter.insert(key, filter_decl); - app_ref filter_head(m_manager); - filter_head = m_manager.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr()); + app_ref filter_head(m); + filter_head = m.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr()); app * filter_tail = key->new_pred; rule * filter_rule = m_context.get_rule_manager().mk(filter_head, 1, &filter_tail, (const bool *)0); filter_rule->set_accounting_parent_object(m_context, m_current); @@ -104,16 +105,15 @@ namespace datalog { void mk_filter_rules::process(rule * r) { m_current = r; app * new_head = r->get_head(); - app_ref_vector new_tail(m_manager); + app_ref_vector new_tail(m); svector new_is_negated; unsigned sz = r->get_tail_size(); bool rule_modified = false; for (unsigned i = 0; i < sz; i++) { app * tail = r->get_tail(i); if (is_candidate(tail)) { - TRACE("mk_filter_rules", tout << "is_candidate: " << mk_pp(tail, m_manager) << "\n";); - var_idx_set non_local_vars; - collect_non_local_vars(m_manager, r, tail, non_local_vars); + TRACE("mk_filter_rules", tout << "is_candidate: " << mk_pp(tail, m) << "\n";); + var_idx_set non_local_vars = rm.collect_rule_vars_ex(r, tail); func_decl * filter_decl = mk_filter_decl(tail, non_local_vars); ptr_buffer new_args; var_idx_set used_vars; @@ -129,7 +129,7 @@ namespace datalog { } } SASSERT(new_args.size() == filter_decl->get_arity()); - new_tail.push_back(m_manager.mk_app(filter_decl, new_args.size(), new_args.c_ptr())); + new_tail.push_back(m.mk_app(filter_decl, new_args.size(), new_args.c_ptr())); rule_modified = true; } else { diff --git a/src/muz_qe/dl_mk_filter_rules.h b/src/muz_qe/dl_mk_filter_rules.h index 91751f9b8..b51cb8e24 100644 --- a/src/muz_qe/dl_mk_filter_rules.h +++ b/src/muz_qe/dl_mk_filter_rules.h @@ -59,7 +59,8 @@ namespace datalog { typedef obj_map filter_cache; context & m_context; - ast_manager & m_manager; + ast_manager & m; + rule_manager & rm; filter_cache m_tail2filter; rule_set * m_result; rule * m_current; diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp index d0b94f582..8a9b1257a 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp @@ -67,24 +67,23 @@ namespace datalog { void mk_interp_tail_simplifier::rule_substitution::get_result(rule_ref & res) { SASSERT(m_rule); - app_ref new_head(m); - apply(m_rule->get_head(), new_head); + apply(m_rule->get_head(), m_head); - app_ref_vector tail(m); - svector tail_neg; + m_tail.reset(); + m_neg.reset(); unsigned tail_len = m_rule->get_tail_size(); for (unsigned i=0; iget_tail(i), new_tail_el); - tail.push_back(new_tail_el); - tail_neg.push_back(m_rule->is_neg_tail(i)); + m_tail.push_back(new_tail_el); + m_neg.push_back(m_rule->is_neg_tail(i)); } - mk_rule_inliner::remove_duplicate_tails(tail, tail_neg); + mk_rule_inliner::remove_duplicate_tails(m_tail, m_neg); - SASSERT(tail.size() == tail_neg.size()); - res = m_context.get_rule_manager().mk(new_head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); + SASSERT(m_tail.size() == m_neg.size()); + res = m_context.get_rule_manager().mk(m_head, m_tail.size(), m_tail.c_ptr(), m_neg.c_ptr()); res->set_accounting_parent_object(m_context, m_rule); res->norm_vars(res.get_manager()); } @@ -362,14 +361,34 @@ namespace datalog { } }; + class mk_interp_tail_simplifier::normalizer_rw : public rewriter_tpl { + public: + normalizer_rw(ast_manager& m, normalizer_cfg& cfg): rewriter_tpl(m, false, cfg) {} + }; + + + mk_interp_tail_simplifier::mk_interp_tail_simplifier(context & ctx, unsigned priority) + : plugin(priority), + m(ctx.get_manager()), + m_context(ctx), + m_simp(ctx.get_rewriter()), + a(m), + m_rule_subst(ctx) { + m_cfg = alloc(normalizer_cfg, m); + m_rw = alloc(normalizer_rw, m, *m_cfg); + } + + mk_interp_tail_simplifier::~mk_interp_tail_simplifier() { + dealloc(m_rw); + dealloc(m_cfg); + } + + void mk_interp_tail_simplifier::simplify_expr(app * a, expr_ref& res) { expr_ref simp1_res(m); m_simp(a, simp1_res); - normalizer_cfg r_cfg(m); - rewriter_tpl rwr(m, false, r_cfg); - expr_ref dl_form_e(m); - rwr(simp1_res.get(), res); + (*m_rw)(simp1_res.get(), res); /*if (simp1_res.get()!=res.get()) { std::cout<<"pre norm:\n"< m_neg; + rule * m_rule; void apply(app * a, app_ref& res); public: rule_substitution(context & ctx) - : m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_rule(0) {} + : m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_head(m), m_tail(m), m_rule(0) {} /** Reset substitution and get it ready for working with rule r. @@ -61,13 +63,17 @@ namespace datalog { } }; + class normalizer_cfg; + class normalizer_rw; + ast_manager & m; context & m_context; th_rewriter & m_simp; arith_util a; rule_substitution m_rule_subst; + normalizer_cfg* m_cfg; + normalizer_rw* m_rw; - class normalizer_cfg; void simplify_expr(app * a, expr_ref& res); @@ -77,13 +83,8 @@ namespace datalog { /** Return true if something was modified */ bool transform_rules(const rule_set & orig, rule_set & tgt); public: - mk_interp_tail_simplifier(context & ctx, unsigned priority=40000) - : plugin(priority), - m(ctx.get_manager()), - m_context(ctx), - m_simp(ctx.get_rewriter()), - a(m), - m_rule_subst(ctx) {} + mk_interp_tail_simplifier(context & ctx, unsigned priority=40000); + virtual ~mk_interp_tail_simplifier(); /**If rule should be retained, assign transformed version to res and return true; if rule can be deleted, return false. diff --git a/src/muz_qe/dl_mk_magic_sets.cpp b/src/muz_qe/dl_mk_magic_sets.cpp index 54ffdc805..f6f79f348 100644 --- a/src/muz_qe/dl_mk_magic_sets.cpp +++ b/src/muz_qe/dl_mk_magic_sets.cpp @@ -28,6 +28,7 @@ namespace datalog { plugin(10000, true), m_context(ctx), m(ctx.get_manager()), + rm(ctx.get_rule_manager()), m_pinned(m), m_goal(goal, m) { } @@ -259,7 +260,7 @@ namespace datalog { } new_tail.push_back(curr); negations.push_back(r->is_neg_tail(curr_index)); - collect_vars(m, curr, bound_vars); + bound_vars |= rm.collect_vars(curr); } diff --git a/src/muz_qe/dl_mk_magic_sets.h b/src/muz_qe/dl_mk_magic_sets.h index dfc66e7ea..3496a5967 100644 --- a/src/muz_qe/dl_mk_magic_sets.h +++ b/src/muz_qe/dl_mk_magic_sets.h @@ -95,6 +95,7 @@ namespace datalog { context & m_context; ast_manager & m; + rule_manager& rm; ast_ref_vector m_pinned; /** \brief Predicates from the original set that appear in a head of a rule diff --git a/src/muz_qe/dl_mk_rule_inliner.cpp b/src/muz_qe/dl_mk_rule_inliner.cpp index 5bbeb2378..4afc1d323 100644 --- a/src/muz_qe/dl_mk_rule_inliner.cpp +++ b/src/muz_qe/dl_mk_rule_inliner.cpp @@ -505,9 +505,6 @@ namespace datalog { unsigned head_arity = head_pred->get_arity(); - //var_idx_set head_vars; - //var_idx_set same_strat_vars; - //collect_vars(m, r->get_head(), head_vars); unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti=0; tiget_head(), same_strat_vars); if (pred->get_arity()>head_arity || (pred->get_arity()==head_arity && pred->get_id()>=head_pred->get_id()) ) { return false; diff --git a/src/muz_qe/dl_mk_simple_joins.cpp b/src/muz_qe/dl_mk_simple_joins.cpp index 2fec78066..990125475 100644 --- a/src/muz_qe/dl_mk_simple_joins.cpp +++ b/src/muz_qe/dl_mk_simple_joins.cpp @@ -29,7 +29,8 @@ namespace datalog { mk_simple_joins::mk_simple_joins(context & ctx): plugin(1000), - m_context(ctx) { + m_context(ctx), + rm(ctx.get_rule_manager()) { } class join_planner { @@ -120,6 +121,7 @@ namespace datalog { context & m_context; ast_manager & m; + rule_manager & rm; var_subst & m_var_subst; rule_set & m_rs_aux_copy; //reference to a rule_set that will allow to ask for stratum levels @@ -130,10 +132,13 @@ namespace datalog { ptr_hashtable, ptr_eq > m_modified_rules; ast_ref_vector m_pinned; + mutable ptr_vector m_vars; public: join_planner(context & ctx, rule_set & rs_aux_copy) - : m_context(ctx), m(ctx.get_manager()), m_var_subst(ctx.get_var_subst()), + : m_context(ctx), m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_var_subst(ctx.get_var_subst()), m_rs_aux_copy(rs_aux_copy), m_introduced_rules(ctx.get_rule_manager()), m_pinned(ctx.get_manager()) @@ -175,9 +180,7 @@ namespace datalog { unsigned max_var_idx = 0; { - var_idx_set orig_var_set; - collect_vars(m, t1, orig_var_set); - collect_vars(m, t2, orig_var_set); + var_idx_set& orig_var_set = rm.collect_vars(t1, t2); var_idx_set::iterator ovit = orig_var_set.begin(); var_idx_set::iterator ovend = orig_var_set.end(); for(; ovit!=ovend; ++ovit) { @@ -323,14 +326,13 @@ namespace datalog { } for(unsigned i=0; iget_tail(i); - var_idx_set t1_vars; - collect_vars(m, t1, t1_vars); + var_idx_set t1_vars = rm.collect_vars(t1); counter.count_vars(m, t1, -1); //temporarily remove t1 variables from counter for(unsigned j=i+1; jget_tail(j); counter.count_vars(m, t2, -1); //temporarily remove t2 variables from counter - var_idx_set scope_vars(t1_vars); - collect_vars(m, t2, scope_vars); + var_idx_set scope_vars = rm.collect_vars(t2); + scope_vars |= t1_vars; var_idx_set non_local_vars; counter.collect_positive(non_local_vars); counter.count_vars(m, t2, 1); //restore t2 variables in counter @@ -472,8 +474,7 @@ namespace datalog { while(!added_tails.empty()) { app * a_tail = added_tails.back(); //added tail - var_idx_set a_tail_vars; - collect_vars(m, a_tail, a_tail_vars); + var_idx_set a_tail_vars = rm.collect_vars(a_tail); counter.count_vars(m, a_tail, -1); //temporarily remove a_tail variables from counter for(unsigned i=0; iget_idx(); - var_idx_set tail_vars; - collect_tail_vars(m, r, tail_vars); - - return tail_vars.contains(var_idx); + return rm.collect_tail_vars(r).contains(var_idx); } void mk_unbound_compressor::add_task(func_decl * pred, unsigned arg_index) { @@ -83,8 +81,7 @@ namespace datalog { void mk_unbound_compressor::detect_tasks(rule_set const& source, unsigned rule_index) { rule * r = m_rules.get(rule_index); - var_idx_set tail_vars; - collect_tail_vars(m, r, tail_vars); + var_idx_set& tail_vars = rm.collect_tail_vars(r); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); @@ -94,9 +91,9 @@ namespace datalog { } unsigned n = head_pred->get_arity(); - - var_counter head_var_counter; - head_var_counter.count_vars(m, head, 1); + + rm.get_counter().reset(); + rm.get_counter().count_vars(m, head, 1); for (unsigned i=0; iget_arg(i); @@ -107,7 +104,7 @@ namespace datalog { if (!tail_vars.contains(var_idx)) { //unbound - unsigned occurence_cnt = head_var_counter.get(var_idx); + unsigned occurence_cnt = rm.get_counter().get(var_idx); SASSERT(occurence_cnt>0); if (occurence_cnt == 1) { TRACE("dl", r->display(m_context, tout << "Compress: ");); @@ -121,15 +118,14 @@ namespace datalog { void mk_unbound_compressor::try_compress(rule_set const& source, unsigned rule_index) { start: rule * r = m_rules.get(rule_index); - var_idx_set tail_vars; - collect_tail_vars(m, r, tail_vars); + var_idx_set& tail_vars = rm.collect_tail_vars(r); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); unsigned head_arity = head_pred->get_arity(); - var_counter head_var_counter; - head_var_counter.count_vars(m, head); + rm.get_counter().reset(); + rm.get_counter().count_vars(m, head); unsigned arg_index; for (arg_index = 0; arg_index < head_arity; arg_index++) { @@ -140,7 +136,7 @@ namespace datalog { unsigned var_idx = to_var(arg)->get_idx(); if (!tail_vars.contains(var_idx)) { //unbound - unsigned occurence_cnt = head_var_counter.get(var_idx); + unsigned occurence_cnt = rm.get_counter().get(var_idx); SASSERT(occurence_cnt>0); if ( occurence_cnt==1 && m_in_progress.contains(c_info(head_pred, arg_index)) ) { //we have found what to compress diff --git a/src/muz_qe/dl_mk_unbound_compressor.h b/src/muz_qe/dl_mk_unbound_compressor.h index 4e56a74fc..4e2ff0b3c 100644 --- a/src/muz_qe/dl_mk_unbound_compressor.h +++ b/src/muz_qe/dl_mk_unbound_compressor.h @@ -52,6 +52,7 @@ namespace datalog { context & m_context; ast_manager & m; + rule_manager & rm; rule_ref_vector m_rules; bool m_modified; todo_stack m_todo; diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index 32f3a5fe8..59d316ae7 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -48,7 +48,9 @@ namespace datalog { rule_manager::rule_manager(context& ctx) : m(ctx.get_manager()), - m_ctx(ctx) {} + m_ctx(ctx), + m_cfg(m), + m_rwr(m, false, m_cfg) {} void rule_manager::inc_ref(rule * r) { if (r) { @@ -67,29 +69,20 @@ namespace datalog { } } - class remove_label_cfg : public default_rewriter_cfg { - family_id m_label_fid; - public: - remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {} - virtual ~remove_label_cfg() {} - - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, - proof_ref & result_pr) - { - if (is_decl_of(f, m_label_fid, OP_LABEL)) { - SASSERT(num == 1); - result = args[0]; - return BR_DONE; - } - return BR_FAILED; + br_status rule_manager::remove_label_cfg::reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, + proof_ref & result_pr) + { + if (is_decl_of(f, m_label_fid, OP_LABEL)) { + SASSERT(num == 1); + result = args[0]; + return BR_DONE; } - }; + return BR_FAILED; + } void rule_manager::remove_labels(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); - remove_label_cfg r_cfg(m); - rewriter_tpl rwr(m, false, r_cfg); - rwr(fml, tmp); + m_rwr(fml, tmp); if (pr && fml != tmp) { pr = m.mk_modus_ponens(pr, m.mk_rewrite(fml, tmp)); @@ -97,6 +90,67 @@ namespace datalog { fml = tmp; } + var_idx_set& rule_manager::collect_vars(expr* e) { + return collect_vars(e, 0); + } + + var_idx_set& rule_manager::collect_vars(expr* e1, expr* e2) { + reset_collect_vars(); + if (e1) accumulate_vars(e1); + if (e2) accumulate_vars(e2); + return finalize_collect_vars(); + } + + void rule_manager::reset_collect_vars() { + m_vars.reset(); + m_var_idx.reset(); + m_todo.reset(); + m_mark.reset(); + } + + var_idx_set& rule_manager::finalize_collect_vars() { + unsigned sz = m_vars.size(); + for (unsigned i=0; iget_tail_size(); + for (unsigned i=0;iget_tail(i)); + } + return finalize_collect_vars(); + } + + var_idx_set& rule_manager::collect_rule_vars_ex(rule * r, app* t) { + reset_collect_vars(); + unsigned n = r->get_tail_size(); + accumulate_vars(r->get_head()); + for (unsigned i=0;iget_tail(i) != t) { + accumulate_vars(r->get_tail(i)); + } + } + return finalize_collect_vars(); + } + + var_idx_set& rule_manager::collect_rule_vars(rule * r) { + reset_collect_vars(); + unsigned n = r->get_tail_size(); + accumulate_vars(r->get_head()); + for (unsigned i=0;iget_tail(i)); + } + return finalize_collect_vars(); + } + + void rule_manager::accumulate_vars(expr* e) { + ::get_free_vars(m_mark, m_todo, e, m_vars); + } + void rule_manager::mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); @@ -570,15 +624,14 @@ namespace datalog { return; } - ptr_vector free_rule_vars; var_counter vctr; app_ref_vector tail(m); svector tail_neg; app_ref head(r->get_head(), m); - get_free_vars(r, free_rule_vars); + collect_rule_vars(r); vctr.count_vars(m, head); - + ptr_vector& free_rule_vars = m_vars; for (unsigned i = 0; i < ut_len; i++) { app * t = r->get_tail(i); @@ -906,7 +959,7 @@ namespace datalog { } void rule::norm_vars(rule_manager & rm) { - used_vars used; + used_vars& used = rm.reset_used(); get_used_vars(used); unsigned first_unsused = used.get_max_found_var_idx_plus_1(); diff --git a/src/muz_qe/dl_rule.h b/src/muz_qe/dl_rule.h index 666ddbd50..5abb64624 100644 --- a/src/muz_qe/dl_rule.h +++ b/src/muz_qe/dl_rule.h @@ -27,6 +27,7 @@ Revision History: #include"proof_converter.h" #include"model_converter.h" #include"ast_counter.h" +#include"rewriter.h" namespace datalog { @@ -47,9 +48,27 @@ namespace datalog { */ class rule_manager { + class remove_label_cfg : public default_rewriter_cfg { + family_id m_label_fid; + public: + remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {} + virtual ~remove_label_cfg() {} + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, + proof_ref & result_pr); + }; + ast_manager& m; context& m_ctx; rule_counter m_counter; + used_vars m_used; + ptr_vector m_vars; + var_idx_set m_var_idx; + ptr_vector m_todo; + ast_mark m_mark; + remove_label_cfg m_cfg; + rewriter_tpl m_rwr; + // only the context can create a rule_manager friend class context; @@ -90,6 +109,10 @@ namespace datalog { */ void reduce_unbound_vars(rule_ref& r); + void reset_collect_vars(); + + var_idx_set& finalize_collect_vars(); + public: ast_manager& get_manager() const { return m; } @@ -98,6 +121,24 @@ namespace datalog { void dec_ref(rule * r); + used_vars& reset_used() { m_used.reset(); return m_used; } + + var_idx_set& collect_vars(expr * pred); + + var_idx_set& collect_vars(expr * e1, expr* e2); + + var_idx_set& collect_rule_vars(rule * r); + + var_idx_set& collect_rule_vars_ex(rule * r, app* t); + + var_idx_set& collect_tail_vars(rule * r); + + void accumulate_vars(expr* pred); + + ptr_vector& get_var_sorts() { return m_vars; } + + var_idx_set& get_var_idx() { return m_var_idx; } + /** \brief Create a Datalog rule from a Horn formula. The formula is of the form (forall (...) (forall (...) (=> (and ...) head))) diff --git a/src/muz_qe/dl_sieve_relation.cpp b/src/muz_qe/dl_sieve_relation.cpp index c3ea5a3d0..e80462900 100644 --- a/src/muz_qe/dl_sieve_relation.cpp +++ b/src/muz_qe/dl_sieve_relation.cpp @@ -567,8 +567,7 @@ namespace datalog { const relation_signature sig = r.get_signature(); unsigned sz = sig.size(); - var_idx_set cond_vars; - collect_vars(m, condition, cond_vars); + var_idx_set& cond_vars = get_context().get_rule_manager().collect_vars(condition); expr_ref_vector subst_vect(m); subst_vect.resize(sz); unsigned subst_ofs = sz-1; diff --git a/src/muz_qe/dl_util.cpp b/src/muz_qe/dl_util.cpp index 95d268510..1b1042345 100644 --- a/src/muz_qe/dl_util.cpp +++ b/src/muz_qe/dl_util.cpp @@ -158,36 +158,7 @@ namespace datalog { ::get_free_vars(trm, vars); return var_idx < vars.size() && vars[var_idx] != 0; } - - - void collect_vars(ast_manager & m, expr * e, var_idx_set & result) { - ptr_vector vars; - ::get_free_vars(e, vars); - unsigned sz = vars.size(); - for(unsigned i=0; iget_tail_size(); - for(unsigned i=0;iget_tail(i), result); - } - } - - void get_free_tail_vars(rule * r, ptr_vector& sorts) { - unsigned n = r->get_tail_size(); - for(unsigned i=0;iget_tail(i), sorts); - } - } - - void get_free_vars(rule * r, ptr_vector& sorts) { - get_free_vars(r->get_head(), sorts); - get_free_tail_vars(r, sorts); - } - unsigned count_variable_arguments(app * pred) { SASSERT(is_uninterp(pred)); @@ -202,26 +173,6 @@ namespace datalog { return res; } - void collect_non_local_vars(ast_manager & m, rule const * r, app * t, var_idx_set & result) { - collect_vars(m, r->get_head(), result); - unsigned sz = r->get_tail_size(); - for (unsigned i = 0; i < sz; i++) { - app * curr = r->get_tail(i); - if (curr != t) - collect_vars(m, curr, result); - } - } - - void collect_non_local_vars(ast_manager & m, rule const * r, app * t_1, app * t_2, var_idx_set & result) { - collect_vars(m, r->get_head(), result); - unsigned sz = r->get_tail_size(); - for (unsigned i = 0; i < sz; i++) { - app * curr = r->get_tail(i); - if (curr != t_1 && curr != t_2) - collect_vars(m, curr, result); - } - } - void mk_new_rule_tail(ast_manager & m, app * pred, var_idx_set const & non_local_vars, unsigned & next_idx, varidx2var_map & varidx2var, sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args, app_ref & new_pred) { expr_ref_buffer new_args(m); @@ -404,6 +355,7 @@ namespace datalog { void rule_counter::count_rule_vars(ast_manager & m, const rule * r, int coef) { + reset(); count_vars(m, r->get_head(), 1); unsigned n = r->get_tail_size(); for (unsigned i = 0; i < n; i++) { diff --git a/src/muz_qe/dl_util.h b/src/muz_qe/dl_util.h index 96bc8c326..70e34f91c 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz_qe/dl_util.h @@ -81,33 +81,13 @@ namespace datalog { void flatten_or(expr* fml, expr_ref_vector& result); - - bool contains_var(expr * trm, unsigned var_idx); - /** - \brief Collect the variables in \c pred. - \pre \c pred must be a valid head or tail. - */ - void collect_vars(ast_manager & m, expr * pred, var_idx_set & result); - void collect_tail_vars(ast_manager & m, rule * r, var_idx_set & result); - - void get_free_vars(rule * r, ptr_vector& sorts); - /** \brief Return number of arguments of \c pred that are variables */ unsigned count_variable_arguments(app * pred); - /** - \brief Store in \c result the set of variables used by \c r when ignoring the tail \c t. - */ - void collect_non_local_vars(ast_manager & m, rule const * r, app * t, var_idx_set & result); - - /** - \brief Store in \c result the set of variables used by \c r when ignoring the tail elements \c t_1 and \c t_2. - */ - void collect_non_local_vars(ast_manager & m, rule const * r, app * t_1, app * t_2, var_idx_set & result); template void copy_nonvariables(app * src, T& tgt) diff --git a/src/muz_qe/qe_lite.cpp b/src/muz_qe/qe_lite.cpp index ff49584ff..50c3347ee 100644 --- a/src/muz_qe/qe_lite.cpp +++ b/src/muz_qe/qe_lite.cpp @@ -2525,15 +2525,15 @@ public: m_params(p) { m_imp = alloc(imp, m, p); } - - virtual tactic * translate(ast_manager & m) { - return alloc(qe_lite_tactic, m, m_params); - } virtual ~qe_lite_tactic() { dealloc(m_imp); } + virtual tactic * translate(ast_manager & m) { + return alloc(qe_lite_tactic, m, m_params); + } + virtual void updt_params(params_ref const & p) { m_params = p; // m_imp->updt_params(p); diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 9c80d8c34..3e1fef906 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -316,7 +316,7 @@ namespace smt { m_nc_functor(*this) { } - ~theory_diff_logic() { + virtual ~theory_diff_logic() { reset_eh(); } diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index f5e5fa9fa..3f2e224d9 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -231,9 +231,8 @@ void * memory::allocate(size_t s) { return 0; s = s + sizeof(size_t); // we allocate an extra field! void * r = malloc(s); - if (r == 0) { + if (r == 0) throw_out_of_memory(); - } *(static_cast(r)) = s; g_memory_thread_alloc_size += s; if (g_memory_thread_alloc_size > SYNCH_THRESHOLD) { From b644fb987511b8b878c7150c5df73f1657fa26c0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 Apr 2013 12:02:19 -0700 Subject: [PATCH 33/91] optimize rule processing Signed-off-by: Nikolaj Bjorner --- src/ast/expr_abstract.cpp | 59 ++++++++++++++++++---------------- src/ast/expr_abstract.h | 11 +++++++ src/ast/rewriter/var_subst.cpp | 16 ++++----- src/muz_qe/dl_context.cpp | 4 +-- src/muz_qe/dl_context.h | 2 ++ src/muz_qe/dl_mk_array_blast.h | 1 - 6 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/ast/expr_abstract.cpp b/src/ast/expr_abstract.cpp index 6deb4bf45..0569eb360 100644 --- a/src/ast/expr_abstract.cpp +++ b/src/ast/expr_abstract.cpp @@ -20,52 +20,50 @@ Notes: #include "expr_abstract.h" #include "map.h" -void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { - ast_ref_vector pinned(m); - ptr_vector stack; - obj_map map; +void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { + expr * curr = 0, *b = 0; SASSERT(n->get_ref_count() > 0); - stack.push_back(n); + m_stack.push_back(n); for (unsigned i = 0; i < num_bound; ++i) { b = bound[i]; expr* v = m.mk_var(base + num_bound - i - 1, m.get_sort(b)); - pinned.push_back(v); - map.insert(b, v); + m_pinned.push_back(v); + m_map.insert(b, v); } - while(!stack.empty()) { - curr = stack.back(); - if (map.contains(curr)) { - stack.pop_back(); + while(!m_stack.empty()) { + curr = m_stack.back(); + if (m_map.contains(curr)) { + m_stack.pop_back(); continue; } switch(curr->get_kind()) { case AST_VAR: { - map.insert(curr, curr); - stack.pop_back(); + m_map.insert(curr, curr); + m_stack.pop_back(); break; } case AST_APP: { app* a = to_app(curr); bool all_visited = true; - ptr_vector args; + m_args.reset(); for (unsigned i = 0; i < a->get_num_args(); ++i) { - if (!map.find(a->get_arg(i), b)) { - stack.push_back(a->get_arg(i)); + if (!m_map.find(a->get_arg(i), b)) { + m_stack.push_back(a->get_arg(i)); all_visited = false; } else { - args.push_back(b); + m_args.push_back(b); } } if (all_visited) { - b = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); - pinned.push_back(b); - map.insert(curr, b); - stack.pop_back(); + b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); + m_pinned.push_back(b); + m_map.insert(curr, b); + m_stack.pop_back(); } break; } @@ -81,17 +79,24 @@ void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* cons } expr_abstract(m, new_base, num_bound, bound, q->get_expr(), result1); b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get()); - pinned.push_back(b); - map.insert(curr, b); - stack.pop_back(); + m_pinned.push_back(b); + m_map.insert(curr, b); + m_stack.pop_back(); break; } default: UNREACHABLE(); } } - if (!map.find(n, b)) { - UNREACHABLE(); - } + VERIFY (m_map.find(n, b)); result = b; + m_pinned.reset(); + m_map.reset(); + m_stack.reset(); + m_args.reset(); +} + +void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { + expr_abstractor abs(m); + abs(base, num_bound, bound, n, result); } diff --git a/src/ast/expr_abstract.h b/src/ast/expr_abstract.h index c6ec7973b..3d9f3960f 100644 --- a/src/ast/expr_abstract.h +++ b/src/ast/expr_abstract.h @@ -21,6 +21,17 @@ Notes: #include"ast.h" +class expr_abstractor { + ast_manager& m; + expr_ref_vector m_pinned; + ptr_vector m_stack, m_args; + obj_map m_map; + +public: + expr_abstractor(ast_manager& m): m(m), m_pinned(m) {} + void operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); +}; + void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); #endif diff --git a/src/ast/rewriter/var_subst.cpp b/src/ast/rewriter/var_subst.cpp index f7f0c8aef..930267dad 100644 --- a/src/ast/rewriter/var_subst.cpp +++ b/src/ast/rewriter/var_subst.cpp @@ -57,10 +57,10 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { m_used.process(q->get_pattern(i)); unsigned num_no_patterns = q->get_num_no_patterns(); for (unsigned i = 0; i < num_no_patterns; i++) - used.process(q->get_no_pattern(i)); + m_used.process(q->get_no_pattern(i)); unsigned num_decls = q->get_num_decls(); - if (used.uses_all_vars(num_decls)) { + if (m_used.uses_all_vars(num_decls)) { q->set_no_unused_vars(); result = q; return; @@ -69,7 +69,7 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { ptr_buffer used_decl_sorts; buffer used_decl_names; for (unsigned i = 0; i < num_decls; ++i) { - if (used.contains(num_decls - i - 1)) { + if (m_used.contains(num_decls - i - 1)) { used_decl_sorts.push_back(q->get_decl_sort(i)); used_decl_names.push_back(q->get_decl_name(i)); } @@ -78,10 +78,10 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { unsigned num_removed = 0; expr_ref_buffer var_mapping(m); int next_idx = 0; - unsigned sz = used.get_max_found_var_idx_plus_1(); + unsigned sz = m_used.get_max_found_var_idx_plus_1(); for (unsigned i = 0; i < num_decls; ++i) { - sort * s = used.contains(i); + sort * s = m_used.contains(i); if (s) { var_mapping.push_back(m.mk_var(next_idx, s)); next_idx++; @@ -94,7 +94,7 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { // (VAR 0) is in the first position of var_mapping. for (unsigned i = num_decls; i < sz; i++) { - sort * s = used.contains(i); + sort * s = m_used.contains(i); if (s) var_mapping.push_back(m.mk_var(i - num_removed, s)); else @@ -122,11 +122,11 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { expr_ref_buffer new_no_patterns(m); for (unsigned i = 0; i < num_patterns; i++) { - subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); + m_subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); new_patterns.push_back(tmp); } for (unsigned i = 0; i < num_no_patterns; i++) { - subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); + m_subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); new_no_patterns.push_back(tmp); } diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index f283bd456..592c1ad4b 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -49,7 +49,6 @@ Revision History: #include"dl_mk_quantifier_abstraction.h" #include"dl_mk_quantifier_instantiation.h" #include"datatype_decl_plugin.h" -#include"expr_abstract.h" namespace datalog { @@ -227,6 +226,7 @@ namespace datalog { m_var_subst(m), m_rule_manager(*this), m_elim_unused_vars(m), + m_abstractor(m), m_transf(*this), m_trail(*this), m_pinned(m), @@ -307,7 +307,7 @@ namespace datalog { } else { ptr_vector sorts; - expr_abstract(m, 0, vars.size(), reinterpret_cast(vars.c_ptr()), fml, result); + m_abstractor(0, vars.size(), reinterpret_cast(vars.c_ptr()), fml, result); get_free_vars(result, sorts); if (sorts.empty()) { result = fml; diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index 2798ba8df..aba8577e1 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -45,6 +45,7 @@ Revision History: #include"model2expr.h" #include"smt_params.h" #include"dl_rule_transformer.h" +#include"expr_abstract.h" namespace datalog { @@ -85,6 +86,7 @@ namespace datalog { var_subst m_var_subst; rule_manager m_rule_manager; unused_vars_eliminator m_elim_unused_vars; + expr_abstractor m_abstractor; rule_transformer m_transf; trail_stack m_trail; ast_ref_vector m_pinned; diff --git a/src/muz_qe/dl_mk_array_blast.h b/src/muz_qe/dl_mk_array_blast.h index 74ac9ec97..21f2a0bf7 100644 --- a/src/muz_qe/dl_mk_array_blast.h +++ b/src/muz_qe/dl_mk_array_blast.h @@ -38,7 +38,6 @@ namespace datalog { rule_manager& rm; params_ref m_params; th_rewriter m_rewriter; - ptr_vector m_vars; mk_interp_tail_simplifier m_simplifier; typedef obj_map defs_t; From 8038c719fb2d4fce1c3841f0fa6bbe74e4455d10 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 Apr 2013 14:40:20 -0700 Subject: [PATCH 34/91] optimize rule preprocessing Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/ast_counter.cpp | 18 +++--- src/muz_qe/dl_context.cpp | 30 ++++------ src/muz_qe/dl_context.h | 17 ++++++ src/muz_qe/dl_mk_interp_tail_simplifier.cpp | 63 +++++++++++---------- src/muz_qe/dl_mk_interp_tail_simplifier.h | 6 ++ src/muz_qe/dl_rule.cpp | 63 ++++++++++----------- src/muz_qe/dl_rule.h | 8 +++ src/muz_qe/hnf.cpp | 33 ++++++----- src/muz_qe/horn_subsume_model_converter.cpp | 17 ++++-- src/muz_qe/horn_subsume_model_converter.h | 6 +- 10 files changed, 149 insertions(+), 112 deletions(-) diff --git a/src/ast/rewriter/ast_counter.cpp b/src/ast/rewriter/ast_counter.cpp index 099bdedec..a807237c5 100644 --- a/src/ast/rewriter/ast_counter.cpp +++ b/src/ast/rewriter/ast_counter.cpp @@ -110,24 +110,27 @@ unsigned var_counter::get_max_var(bool& has_var) { unsigned max_var = 0; while (!m_todo.empty()) { expr* e = m_todo.back(); - unsigned scope = m_scopes.back(); m_todo.pop_back(); - m_scopes.pop_back(); if (m_visited.is_marked(e)) { continue; } m_visited.mark(e, true); switch(e->get_kind()) { case AST_QUANTIFIER: { + var_counter aux_counter; quantifier* q = to_quantifier(e); - m_todo.push_back(q->get_expr()); - m_scopes.push_back(scope + q->get_num_decls()); + bool has_var1 = false; + unsigned max_v = aux_counter.get_max_var(has_var1); + if (max_v > max_var + q->get_num_decls()) { + max_var = max_v - q->get_num_decls(); + has_var = true; + } break; } case AST_VAR: { - if (to_var(e)->get_idx() >= scope + max_var) { + if (to_var(e)->get_idx() >= max_var) { has_var = true; - max_var = to_var(e)->get_idx() - scope; + max_var = to_var(e)->get_idx(); } break; } @@ -135,7 +138,6 @@ unsigned var_counter::get_max_var(bool& has_var) { app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { m_todo.push_back(a->get_arg(i)); - m_scopes.push_back(scope); } break; } @@ -152,14 +154,12 @@ unsigned var_counter::get_max_var(bool& has_var) { unsigned var_counter::get_max_var(expr* e) { bool has_var = false; m_todo.push_back(e); - m_scopes.push_back(0); return get_max_var(has_var); } unsigned var_counter::get_next_var(expr* e) { bool has_var = false; m_todo.push_back(e); - m_scopes.push_back(0); unsigned mv = get_max_var(has_var); if (has_var) mv++; return mv; diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 592c1ad4b..6aebf6e43 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -41,7 +41,6 @@ Revision History: #include"for_each_expr.h" #include"ast_smt_pp.h" #include"ast_smt2_pp.h" -#include"expr_functors.h" #include"dl_mk_partial_equiv.h" #include"dl_mk_bit_blast.h" #include"dl_mk_array_blast.h" @@ -227,6 +226,8 @@ namespace datalog { m_rule_manager(*this), m_elim_unused_vars(m), m_abstractor(m), + m_contains_p(*this), + m_check_pred(m_contains_p, m), m_transf(*this), m_trail(*this), m_pinned(m), @@ -302,18 +303,19 @@ namespace datalog { expr_ref context::bind_variables(expr* fml, bool is_forall) { expr_ref result(m); app_ref_vector const & vars = m_vars; + rule_manager& rm = get_rule_manager(); if (vars.empty()) { result = fml; } else { - ptr_vector sorts; + m_names.reset(); m_abstractor(0, vars.size(), reinterpret_cast(vars.c_ptr()), fml, result); - get_free_vars(result, sorts); + rm.collect_vars(result); + ptr_vector& sorts = rm.get_var_sorts(); if (sorts.empty()) { result = fml; } else { - svector names; for (unsigned i = 0; i < sorts.size(); ++i) { if (!sorts[i]) { if (i < vars.size()) { @@ -324,15 +326,15 @@ namespace datalog { } } if (i < vars.size()) { - names.push_back(vars[i]->get_decl()->get_name()); + m_names.push_back(vars[i]->get_decl()->get_name()); } else { - names.push_back(symbol(i)); + m_names.push_back(symbol(i)); } } quantifier_ref q(m); sorts.reverse(); - q = m.mk_quantifier(is_forall, sorts.size(), sorts.c_ptr(), names.c_ptr(), result); + q = m.mk_quantifier(is_forall, sorts.size(), sorts.c_ptr(), m_names.c_ptr(), result); m_elim_unused_vars(q, result); } } @@ -608,28 +610,16 @@ namespace datalog { } } - class context::contains_pred : public i_expr_pred { - context const& ctx; - public: - contains_pred(context& ctx): ctx(ctx) {} - virtual ~contains_pred() {} - - virtual bool operator()(expr* e) { - return ctx.is_predicate(e); - } - }; void context::check_existential_tail(rule_ref& r) { unsigned ut_size = r->get_uninterpreted_tail_size(); unsigned t_size = r->get_tail_size(); - contains_pred contains_p(*this); - check_pred check_pred(contains_p, get_manager()); TRACE("dl", r->display_smt2(get_manager(), tout); tout << "\n";); for (unsigned i = ut_size; i < t_size; ++i) { app* t = r->get_tail(i); TRACE("dl", tout << "checking: " << mk_ismt2_pp(t, get_manager()) << "\n";); - if (check_pred(t)) { + if (m_check_pred(t)) { std::ostringstream out; out << "interpreted body " << mk_ismt2_pp(t, get_manager()) << " contains recursive predicate"; throw default_exception(out.str()); diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index aba8577e1..55b9f3010 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -46,6 +46,8 @@ Revision History: #include"smt_params.h" #include"dl_rule_transformer.h" #include"expr_abstract.h" +#include"expr_functors.h" + namespace datalog { @@ -77,6 +79,18 @@ namespace datalog { typedef obj_map > pred2syms; typedef obj_map sort_domain_map; + class contains_pred : public i_expr_pred { + context const& ctx; + public: + contains_pred(context& ctx): ctx(ctx) {} + virtual ~contains_pred() {} + + virtual bool operator()(expr* e) { + return ctx.is_predicate(e); + } + }; + + ast_manager & m; smt_params & m_fparams; params_ref m_params_ref; @@ -87,10 +101,13 @@ namespace datalog { rule_manager m_rule_manager; unused_vars_eliminator m_elim_unused_vars; expr_abstractor m_abstractor; + contains_pred m_contains_p; + check_pred m_check_pred; rule_transformer m_transf; trail_stack m_trail; ast_ref_vector m_pinned; app_ref_vector m_vars; + svector m_names; sort_domain_map m_sorts; func_decl_set m_preds; sym2decl m_preds_by_name; diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp index 8a9b1257a..afd586627 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp @@ -369,11 +369,14 @@ namespace datalog { mk_interp_tail_simplifier::mk_interp_tail_simplifier(context & ctx, unsigned priority) : plugin(priority), - m(ctx.get_manager()), - m_context(ctx), - m_simp(ctx.get_rewriter()), - a(m), - m_rule_subst(ctx) { + m(ctx.get_manager()), + m_context(ctx), + m_simp(ctx.get_rewriter()), + a(m), + m_rule_subst(ctx), + m_tail(m), + m_itail_members(m), + m_conj(m) { m_cfg = alloc(normalizer_cfg, m); m_rw = alloc(normalizer_rw, m, *m_cfg); } @@ -404,15 +407,15 @@ namespace datalog { return false; } - ptr_vector todo; + m_todo.reset(); + m_leqs.reset(); for (unsigned i = u_len; i < len; i++) { - todo.push_back(r->get_tail(i)); + m_todo.push_back(r->get_tail(i)); SASSERT(!r->is_neg_tail(i)); } m_rule_subst.reset(r); - obj_hashtable leqs; expr_ref_vector trail(m); expr_ref tmp1(m), tmp2(m); bool found_something = false; @@ -420,10 +423,10 @@ namespace datalog { #define TRY_UNIFY(_x,_y) if (m_rule_subst.unify(_x,_y)) { found_something = true; } #define IS_FLEX(_x) (is_var(_x) || m.is_value(_x)) - while (!todo.empty()) { + while (!m_todo.empty()) { expr * arg1, *arg2; - expr * t0 = todo.back(); - todo.pop_back(); + expr * t0 = m_todo.back(); + m_todo.pop_back(); expr* t = t0; bool neg = m.is_not(t, t); if (is_var(t)) { @@ -431,7 +434,7 @@ namespace datalog { } else if (!neg && m.is_and(t)) { app* a = to_app(t); - todo.append(a->get_num_args(), a->get_args()); + m_todo.append(a->get_num_args(), a->get_args()); } else if (!neg && m.is_eq(t, arg1, arg2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); @@ -459,12 +462,12 @@ namespace datalog { else if (!neg && (a.is_le(t, arg1, arg2) || a.is_ge(t, arg2, arg1))) { tmp1 = a.mk_sub(arg1, arg2); tmp2 = a.mk_sub(arg2, arg1); - if (false && leqs.contains(tmp2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { + if (false && m_leqs.contains(tmp2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); } else { trail.push_back(tmp1); - leqs.insert(tmp1); + m_leqs.insert(tmp1); } } } @@ -504,12 +507,12 @@ namespace datalog { } app_ref head(r->get_head(), m); - app_ref_vector tail(m); - svector tail_neg; + m_tail.reset(); + m_tail_neg.reset(); for (unsigned i=0; iget_tail(i)); - tail_neg.push_back(r->is_neg_tail(i)); + m_tail.push_back(r->get_tail(i)); + m_tail_neg.push_back(r->is_neg_tail(i)); } bool modified = false; @@ -521,12 +524,12 @@ namespace datalog { SASSERT(!r->is_neg_tail(u_len)); } else { - expr_ref_vector itail_members(m); + m_itail_members.reset(); for (unsigned i=u_len; iget_tail(i)); + m_itail_members.push_back(r->get_tail(i)); SASSERT(!r->is_neg_tail(i)); } - itail = m.mk_and(itail_members.size(), itail_members.c_ptr()); + itail = m.mk_and(m_itail_members.size(), m_itail_members.c_ptr()); modified = true; } @@ -542,21 +545,21 @@ namespace datalog { SASSERT(m.is_bool(simp_res)); if (modified) { - expr_ref_vector conjs(m); - flatten_and(simp_res, conjs); - for (unsigned i = 0; i < conjs.size(); ++i) { - expr* e = conjs[i].get(); + m_conj.reset(); + flatten_and(simp_res, m_conj); + for (unsigned i = 0; i < m_conj.size(); ++i) { + expr* e = m_conj[i].get(); if (is_app(e)) { - tail.push_back(to_app(e)); + m_tail.push_back(to_app(e)); } else { - tail.push_back(m.mk_eq(e, m.mk_true())); + m_tail.push_back(m.mk_eq(e, m.mk_true())); } - tail_neg.push_back(false); + m_tail_neg.push_back(false); } - SASSERT(tail.size() == tail_neg.size()); - res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); + SASSERT(m_tail.size() == m_tail_neg.size()); + res = m_context.get_rule_manager().mk(head, m_tail.size(), m_tail.c_ptr(), m_tail_neg.c_ptr()); res->set_accounting_parent_object(m_context, r); } else { diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.h b/src/muz_qe/dl_mk_interp_tail_simplifier.h index 99e0b575a..5047e1c6e 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.h +++ b/src/muz_qe/dl_mk_interp_tail_simplifier.h @@ -71,6 +71,12 @@ namespace datalog { th_rewriter & m_simp; arith_util a; rule_substitution m_rule_subst; + ptr_vector m_todo; + obj_hashtable m_leqs; + app_ref_vector m_tail; + expr_ref_vector m_itail_members; + expr_ref_vector m_conj; + svector m_tail_neg; normalizer_cfg* m_cfg; normalizer_rw* m_rw; diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index 59d316ae7..e43445396 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -40,15 +40,18 @@ Revision History: #include"quant_hoist.h" #include"expr_replacer.h" #include"bool_rewriter.h" -#include"qe_lite.h" #include"expr_safe_replace.h" -#include"hnf.h" namespace datalog { rule_manager::rule_manager(context& ctx) : m(ctx.get_manager()), m_ctx(ctx), + m_body(m), + m_head(m), + m_args(m), + m_hnf(m), + m_qe(m), m_cfg(m), m_rwr(m, false, m_cfg) {} @@ -179,13 +182,13 @@ namespace datalog { } void rule_manager::mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name) { - hnf h(m); expr_ref_vector fmls(m); proof_ref_vector prs(m); - h.set_name(name); - h(fml, p, fmls, prs); - for (unsigned i = 0; i < h.get_fresh_predicates().size(); ++i) { - m_ctx.register_predicate(h.get_fresh_predicates()[i], false); + m_hnf.reset(); + m_hnf.set_name(name); + m_hnf(fml, p, fmls, prs); + for (unsigned i = 0; i < m_hnf.get_fresh_predicates().size(); ++i) { + m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false); } for (unsigned i = 0; i < fmls.size(); ++i) { mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name); @@ -194,24 +197,23 @@ namespace datalog { void rule_manager::mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { - app_ref_vector body(m); - app_ref head(m); - svector is_negated; - unsigned index = extract_horn(fml, body, head); - hoist_compound_predicates(index, head, body); + m_body.reset(); + m_neg.reset(); + unsigned index = extract_horn(fml, m_body, m_head); + hoist_compound_predicates(index, m_head, m_body); TRACE("dl_rule", tout << mk_pp(head, m) << " :- "; - for (unsigned i = 0; i < body.size(); ++i) { - tout << mk_pp(body[i].get(), m) << " "; + for (unsigned i = 0; i < m_body.size(); ++i) { + tout << mk_pp(m_body[i].get(), m) << " "; } tout << "\n";); - mk_negations(body, is_negated); - check_valid_rule(head, body.size(), body.c_ptr()); + mk_negations(m_body, m_neg); + check_valid_rule(m_head, m_body.size(), m_body.c_ptr()); rule_ref r(*this); - r = mk(head.get(), body.size(), body.c_ptr(), is_negated.c_ptr(), name); + r = mk(m_head.get(), m_body.size(), m_body.c_ptr(), m_neg.c_ptr(), name); expr_ref fml1(m); if (p) { @@ -380,28 +382,28 @@ namespace datalog { fml = m.mk_not(fml); return; } - expr_ref_vector args(m); if (!m_ctx.is_predicate(fml)) { return; } + m_args.reset(); for (unsigned i = 0; i < fml->get_num_args(); ++i) { e = fml->get_arg(i); if (!is_app(e)) { - args.push_back(e); + m_args.push_back(e); continue; } app* b = to_app(e); if (m.is_value(b)) { - args.push_back(e); + m_args.push_back(e); } else { var* v = m.mk_var(num_bound++, m.get_sort(b)); - args.push_back(v); + m_args.push_back(v); body.push_back(m.mk_eq(v, b)); } } - fml = m.mk_app(fml->get_decl(), args.size(), args.c_ptr()); + fml = m.mk_app(fml->get_decl(), m_args.size(), m_args.c_ptr()); TRACE("dl_rule", tout << mk_pp(fml.get(), m) << "\n";); } @@ -565,29 +567,22 @@ namespace datalog { void rule_manager::reduce_unbound_vars(rule_ref& r) { unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned t_len = r->get_tail_size(); - ptr_vector vars; - uint_set index_set; - qe_lite qe(m); expr_ref_vector conjs(m); if (ut_len == t_len) { return; } - get_free_vars(r->get_head(), vars); + reset_collect_vars(); + accumulate_vars(r->get_head()); for (unsigned i = 0; i < ut_len; ++i) { - get_free_vars(r->get_tail(i), vars); + accumulate_vars(r->get_tail(i)); } + var_idx_set& index_set = finalize_collect_vars(); for (unsigned i = ut_len; i < t_len; ++i) { conjs.push_back(r->get_tail(i)); } - - for (unsigned i = 0; i < vars.size(); ++i) { - if (vars[i]) { - index_set.insert(i); - } - } - qe(index_set, false, conjs); + m_qe(index_set, false, conjs); bool change = conjs.size() != t_len - ut_len; for (unsigned i = 0; !change && i < conjs.size(); ++i) { change = r->get_tail(ut_len+i) != conjs[i].get(); diff --git a/src/muz_qe/dl_rule.h b/src/muz_qe/dl_rule.h index 5abb64624..6335c506f 100644 --- a/src/muz_qe/dl_rule.h +++ b/src/muz_qe/dl_rule.h @@ -28,6 +28,8 @@ Revision History: #include"model_converter.h" #include"ast_counter.h" #include"rewriter.h" +#include"hnf.h" +#include"qe_lite.h" namespace datalog { @@ -66,6 +68,12 @@ namespace datalog { var_idx_set m_var_idx; ptr_vector m_todo; ast_mark m_mark; + app_ref_vector m_body; + app_ref m_head; + expr_ref_vector m_args; + svector m_neg; + hnf m_hnf; + qe_lite m_qe; remove_label_cfg m_cfg; rewriter_tpl m_rwr; diff --git a/src/muz_qe/hnf.cpp b/src/muz_qe/hnf.cpp index 5a7d1c4ba..764d31bb6 100644 --- a/src/muz_qe/hnf.cpp +++ b/src/muz_qe/hnf.cpp @@ -71,6 +71,9 @@ class hnf::imp { obj_map m_memoize_disj; obj_map m_memoize_proof; func_decl_ref_vector m_fresh_predicates; + expr_ref_vector m_body; + proof_ref_vector m_defs; + public: imp(ast_manager & m): @@ -82,7 +85,9 @@ public: m_refs(m), m_name("P"), m_qh(m), - m_fresh_predicates(m) { + m_fresh_predicates(m), + m_body(m), + m_defs(m) { } void operator()(expr * n, @@ -182,13 +187,13 @@ private: void mk_horn(expr_ref& fml, proof_ref& premise) { expr* e1, *e2; - expr_ref_vector body(m); - proof_ref_vector defs(m); expr_ref fml0(m), fml1(m), fml2(m), head(m); proof_ref p(m); fml0 = fml; m_names.reset(); m_sorts.reset(); + m_body.reset(); + m_defs.reset(); m_qh.pull_quantifier(true, fml0, &m_sorts, &m_names); if (premise){ fml1 = bind_variables(fml0); @@ -199,12 +204,12 @@ private: } head = fml0; while (m.is_implies(head, e1, e2)) { - body.push_back(e1); + m_body.push_back(e1); head = e2; } - datalog::flatten_and(body); + datalog::flatten_and(m_body); if (premise) { - p = m.mk_rewrite(fml0, mk_implies(body, head)); + p = m.mk_rewrite(fml0, mk_implies(m_body, head)); } // @@ -214,8 +219,8 @@ private: // A -> C // B -> C // - if (body.size() == 1 && m.is_or(body[0].get()) && contains_predicate(body[0].get())) { - app* _or = to_app(body[0].get()); + if (m_body.size() == 1 && m.is_or(m_body[0].get()) && contains_predicate(m_body[0].get())) { + app* _or = to_app(m_body[0].get()); unsigned sz = _or->get_num_args(); expr* const* args = _or->get_args(); for (unsigned i = 0; i < sz; ++i) { @@ -224,7 +229,7 @@ private: } if (premise) { - expr_ref f1 = bind_variables(mk_implies(body, head)); + expr_ref f1 = bind_variables(mk_implies(m_body, head)); expr* f2 = m.mk_and(sz, m_todo.c_ptr()+m_todo.size()-sz); proof_ref p2(m), p3(m); p2 = m.mk_def_axiom(m.mk_iff(f1, f2)); @@ -240,13 +245,13 @@ private: } - eliminate_disjunctions(body, defs); - p = mk_congruence(p, body, head, defs); + eliminate_disjunctions(m_body, m_defs); + p = mk_congruence(p, m_body, head, m_defs); - eliminate_quantifier_body(body, defs); - p = mk_congruence(p, body, head, defs); + eliminate_quantifier_body(m_body, m_defs); + p = mk_congruence(p, m_body, head, m_defs); - fml2 = mk_implies(body, head); + fml2 = mk_implies(m_body, head); fml = bind_variables(fml2); diff --git a/src/muz_qe/horn_subsume_model_converter.cpp b/src/muz_qe/horn_subsume_model_converter.cpp index 374333a9c..ced4e657b 100644 --- a/src/muz_qe/horn_subsume_model_converter.cpp +++ b/src/muz_qe/horn_subsume_model_converter.cpp @@ -28,10 +28,8 @@ Revision History: #include "well_sorted.h" void horn_subsume_model_converter::insert(app* head, expr* body) { - func_decl_ref pred(m); - expr_ref body_res(m); - VERIFY(mk_horn(head, body, pred, body_res)); - insert(pred.get(), body_res.get()); + m_delay_head.push_back(head); + m_delay_body.push_back(body); } void horn_subsume_model_converter::insert(app* head, unsigned sz, expr* const* body) { @@ -148,6 +146,7 @@ bool horn_subsume_model_converter::mk_horn( } void horn_subsume_model_converter::add_default_proc::operator()(app* n) { + // // predicates that have not been assigned values // in the Horn model are assumed false. @@ -174,6 +173,16 @@ void horn_subsume_model_converter::add_default_false_interpretation(expr* e, mod void horn_subsume_model_converter::operator()(model_ref& mr) { + + func_decl_ref pred(m); + expr_ref body_res(m); + for (unsigned i = 0; i < m_delay_head.size(); ++i) { + VERIFY(mk_horn(m_delay_head[i].get(), m_delay_body[i].get(), pred, body_res)); + insert(pred.get(), body_res.get()); + } + m_delay_head.reset(); + m_delay_body.reset(); + TRACE("mc", tout << m_funcs.size() << "\n"; model_smt2_pp(tout, m, *mr, 0);); for (unsigned i = m_funcs.size(); i > 0; ) { --i; diff --git a/src/muz_qe/horn_subsume_model_converter.h b/src/muz_qe/horn_subsume_model_converter.h index edde02b19..993f29cc9 100644 --- a/src/muz_qe/horn_subsume_model_converter.h +++ b/src/muz_qe/horn_subsume_model_converter.h @@ -43,6 +43,8 @@ class horn_subsume_model_converter : public model_converter { func_decl_ref_vector m_funcs; expr_ref_vector m_bodies; th_rewriter m_rewrite; + app_ref_vector m_delay_head; + expr_ref_vector m_delay_body; void add_default_false_interpretation(expr* e, model_ref& md); @@ -56,7 +58,9 @@ class horn_subsume_model_converter : public model_converter { public: - horn_subsume_model_converter(ast_manager& m): m(m), m_funcs(m), m_bodies(m), m_rewrite(m) {} + horn_subsume_model_converter(ast_manager& m): + m(m), m_funcs(m), m_bodies(m), m_rewrite(m), + m_delay_head(m), m_delay_body(m) {} bool mk_horn(expr* clause, func_decl_ref& pred, expr_ref& body); From 65b52ba3e918db250ebdac7ee11836b8ddde6e81 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 Apr 2013 16:10:46 -0700 Subject: [PATCH 35/91] add simple bounded CLP backend Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_context.cpp | 34 ++++++++++++++++++++++++++++++++++ src/muz_qe/dl_context.h | 7 ++++++- src/muz_qe/dl_util.h | 1 + 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 6aebf6e43..0099b16f9 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -547,6 +547,8 @@ namespace datalog { throw default_exception("get_num_levels is not supported for bmc"); case TAB_ENGINE: throw default_exception("get_num_levels is not supported for tab"); + case CLP_ENGINE: + throw default_exception("get_num_levels is not supported for clp"); default: throw default_exception("unknown engine"); } @@ -565,6 +567,8 @@ namespace datalog { throw default_exception("operation is not supported for BMC engine"); case TAB_ENGINE: throw default_exception("operation is not supported for TAB engine"); + case CLP_ENGINE: + throw default_exception("operation is not supported for CLP engine"); default: throw default_exception("unknown engine"); } @@ -584,6 +588,8 @@ namespace datalog { throw default_exception("operation is not supported for BMC engine"); case TAB_ENGINE: throw default_exception("operation is not supported for TAB engine"); + case CLP_ENGINE: + throw default_exception("operation is not supported for CLP engine"); default: throw default_exception("unknown engine"); } @@ -711,6 +717,10 @@ namespace datalog { check_existential_tail(r); check_positive_predicates(r); break; + case CLP_ENGINE: + check_existential_tail(r); + check_positive_predicates(r); + break; default: UNREACHABLE(); break; @@ -984,6 +994,9 @@ namespace datalog { else if (e == symbol("tab")) { m_engine = TAB_ENGINE; } + else if (e == symbol("clp")) { + m_engine = CLP_ENGINE; + } if (m_engine == LAST_ENGINE) { expr_fast_mark1 mark; @@ -1019,6 +1032,8 @@ namespace datalog { return bmc_query(query); case TAB_ENGINE: return tab_query(query); + case CLP_ENGINE: + return clp_query(query); default: UNREACHABLE(); return rel_query(query); @@ -1083,11 +1098,22 @@ namespace datalog { } } + void context::ensure_clp() { + if (!m_clp.get()) { + m_clp = alloc(clp, *this); + } + } + lbool context::tab_query(expr* query) { ensure_tab(); return m_tab->query(query); } + lbool context::clp_query(expr* query) { + ensure_clp(); + return m_clp->query(query); + } + void context::ensure_rel() { if (!m_rel.get()) { m_rel = alloc(rel_context, *this); @@ -1128,6 +1154,10 @@ namespace datalog { ensure_tab(); m_last_answer = m_tab->get_answer(); return m_last_answer.get(); + case CLP_ENGINE: + ensure_clp(); + m_last_answer = m_clp->get_answer(); + return m_last_answer.get(); default: UNREACHABLE(); } @@ -1153,6 +1183,10 @@ namespace datalog { ensure_tab(); m_tab->display_certificate(out); return true; + case CLP_ENGINE: + ensure_clp(); + m_clp->display_certificate(out); + return true; default: return false; } diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index 55b9f3010..0a01b3e01 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -47,7 +47,7 @@ Revision History: #include"dl_rule_transformer.h" #include"expr_abstract.h" #include"expr_functors.h" - +#include"clp_context.h" namespace datalog { @@ -124,6 +124,7 @@ namespace datalog { scoped_ptr m_bmc; scoped_ptr m_rel; scoped_ptr m_tab; + scoped_ptr m_clp; bool m_closed; bool m_saturation_was_run; @@ -477,6 +478,8 @@ namespace datalog { void ensure_tab(); + void ensure_clp(); + void ensure_rel(); void new_query(); @@ -489,6 +492,8 @@ namespace datalog { lbool tab_query(expr* query); + lbool clp_query(expr* query); + void check_quantifier_free(rule_ref& r); void check_uninterpreted_free(rule_ref& r); void check_existential_tail(rule_ref& r); diff --git a/src/muz_qe/dl_util.h b/src/muz_qe/dl_util.h index 70e34f91c..ea2def025 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz_qe/dl_util.h @@ -54,6 +54,7 @@ namespace datalog { BMC_ENGINE, QBMC_ENGINE, TAB_ENGINE, + CLP_ENGINE, LAST_ENGINE }; From d1938ce972c2be03486fbb3a56e87aece9fb016d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 Apr 2013 16:11:07 -0700 Subject: [PATCH 36/91] add simple bounded CLP backend Signed-off-by: Nikolaj Bjorner --- src/muz_qe/clp_context.cpp | 230 +++++++++++++++++++++++++++++++++++++ src/muz_qe/clp_context.h | 45 ++++++++ 2 files changed, 275 insertions(+) create mode 100644 src/muz_qe/clp_context.cpp create mode 100644 src/muz_qe/clp_context.h diff --git a/src/muz_qe/clp_context.cpp b/src/muz_qe/clp_context.cpp new file mode 100644 index 000000000..5e683a5eb --- /dev/null +++ b/src/muz_qe/clp_context.cpp @@ -0,0 +1,230 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + tab_context.cpp + +Abstract: + + Tabulation/subsumption/cyclic proof context. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-01-15 + +Revision History: + +--*/ + +#include "clp_context.h" +#include "dl_context.h" +#include "unifier.h" +#include "var_subst.h" +#include "substitution.h" + +namespace datalog { + + class clp::imp { + struct stats { + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + unsigned m_num_unfold; + unsigned m_num_no_unfold; + unsigned m_num_subsumed; + }; + + context& m_ctx; + ast_manager& m; + rule_manager& rm; + smt_params m_fparams; + smt::kernel m_solver; + unifier m_unify; + substitution m_subst; + var_subst m_var_subst; + expr_ref_vector m_ground; + app_ref_vector m_goals; + volatile bool m_cancel; + unsigned m_deltas[2]; + unsigned m_var_cnt; + stats m_stats; + public: + imp(context& ctx): + m_ctx(ctx), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_solver(m, m_fparams), + m_unify(m), + m_subst(m), + m_var_subst(m, false), + m_ground(m), + m_goals(m), + m_cancel(false) + { + // m_fparams.m_relevancy_lvl = 0; + m_fparams.m_mbqi = false; + m_fparams.m_soft_timeout = 1000; + m_deltas[0] = 0; + m_deltas[1] = m_var_cnt; + } + + ~imp() {} + + lbool query(expr* query) { + m_ctx.ensure_opened(); + m_solver.reset(); + m_goals.reset(); + rm.mk_query(query, m_ctx.get_rules()); + expr_ref head(m); + head = m_ctx.get_rules().last()->get_head(); + ground(head); + m_goals.push_back(to_app(head)); + return search(20, 0); + } + + void cancel() { + m_cancel = true; + m_solver.cancel(); + } + + void cleanup() { + m_cancel = false; + m_goals.reset(); + m_solver.reset_cancel(); + } + + void reset_statistics() { + m_stats.reset(); + } + + void collect_statistics(statistics& st) const { + //st.update("tab.num_unfold", m_stats.m_num_unfold); + //st.update("tab.num_unfold_fail", m_stats.m_num_no_unfold); + //st.update("tab.num_subsumed", m_stats.m_num_subsumed); + } + + void display_certificate(std::ostream& out) const { + expr_ref ans = get_answer(); + out << mk_pp(ans, m) << "\n"; + + } + + expr_ref get_answer() const { + return expr_ref(m.mk_true(), m); + } + + private: + + void reset_ground() { + m_ground.reset(); + } + + void ground(expr_ref& e) { + ptr_vector sorts; + get_free_vars(e, sorts); + if (m_ground.size() < sorts.size()) { + m_ground.resize(sorts.size()); + } + for (unsigned i = 0; i < sorts.size(); ++i) { + if (sorts[i] && !m_ground[i].get()) { + m_ground[i] = m.mk_fresh_const("c",sorts[i]); + } + } + m_var_subst(e, m_ground.size(), m_ground.c_ptr(), e); + } + + lbool search(unsigned depth, unsigned index) { + IF_VERBOSE(1, verbose_stream() << "search " << depth << " " << index << "\n";); + if (depth == 0) { + return l_undef; + } + if (index == m_goals.size()) { + return l_true; + } + unsigned num_goals = m_goals.size(); + app* head = m_goals[index].get(); + rule_vector const& rules = m_ctx.get_rules().get_predicate_rules(head->get_decl()); + lbool status = l_false; + for (unsigned i = 0; i < rules.size(); ++i) { + IF_VERBOSE(2, verbose_stream() << index << " " << mk_pp(head, m) << "\n";); + rule* r = rules[i]; + m_solver.push(); + reset_ground(); + expr_ref tmp(m); + tmp = r->get_head(); + ground(tmp); + for (unsigned j = 0; j < head->get_num_args(); ++j) { + expr_ref eq(m); + eq = m.mk_eq(head->get_arg(j), to_app(tmp)->get_arg(j)); + m_solver.assert_expr(eq); + } + for (unsigned j = r->get_uninterpreted_tail_size(); j < r->get_tail_size(); ++j) { + tmp = r->get_tail(j); + ground(tmp); + m_solver.assert_expr(tmp); + } + lbool is_sat = m_solver.check(); + switch (is_sat) { + case l_false: + break; + case l_true: + for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { + tmp = r->get_tail(j); + ground(tmp); + m_goals.push_back(to_app(tmp)); + } + switch(search(depth-1, index+1)) { + case l_undef: + status = l_undef; + // fallthrough + case l_false: + m_goals.resize(num_goals); + break; + case l_true: + return l_true; + } + break; + case l_undef: + status = l_undef; + throw default_exception("undef"); + } + m_solver.pop(1); + } + return status; + } + + + proof_ref get_proof() const { + return proof_ref(0, m); + } + }; + + clp::clp(context& ctx): + m_imp(alloc(imp, ctx)) { + } + clp::~clp() { + dealloc(m_imp); + } + lbool clp::query(expr* query) { + return m_imp->query(query); + } + void clp::cancel() { + m_imp->cancel(); + } + void clp::cleanup() { + m_imp->cleanup(); + } + void clp::reset_statistics() { + m_imp->reset_statistics(); + } + void clp::collect_statistics(statistics& st) const { + m_imp->collect_statistics(st); + } + void clp::display_certificate(std::ostream& out) const { + m_imp->display_certificate(out); + } + expr_ref clp::get_answer() { + return m_imp->get_answer(); + } + +}; diff --git a/src/muz_qe/clp_context.h b/src/muz_qe/clp_context.h new file mode 100644 index 000000000..cd9117553 --- /dev/null +++ b/src/muz_qe/clp_context.h @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + clp_context.h + +Abstract: + + Bounded CLP (symbolic simulation using Z3) context. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-01-15 + +Revision History: + +--*/ +#ifndef _CLP_CONTEXT_H_ +#define _CLP_CONTEXT_H_ + +#include "ast.h" +#include "lbool.h" +#include "statistics.h" + +namespace datalog { + class context; + + class clp { + class imp; + imp* m_imp; + public: + clp(context& ctx); + ~clp(); + lbool query(expr* query); + void cancel(); + void cleanup(); + void reset_statistics(); + void collect_statistics(statistics& st) const; + void display_certificate(std::ostream& out) const; + expr_ref get_answer(); + }; +}; + +#endif From 80f2b70e787325de8f80463bf7693051371927f1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 Apr 2013 16:12:52 -0700 Subject: [PATCH 37/91] fix header information Signed-off-by: Nikolaj Bjorner --- src/muz_qe/clp_context.cpp | 6 +++--- src/muz_qe/clp_context.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/muz_qe/clp_context.cpp b/src/muz_qe/clp_context.cpp index 5e683a5eb..73b065f7d 100644 --- a/src/muz_qe/clp_context.cpp +++ b/src/muz_qe/clp_context.cpp @@ -3,15 +3,15 @@ Copyright (c) 2013 Microsoft Corporation Module Name: - tab_context.cpp + clp_context.cpp Abstract: - Tabulation/subsumption/cyclic proof context. + Bounded CLP (symbolic simulation using Z3) context. Author: - Nikolaj Bjorner (nbjorner) 2013-01-15 + Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: diff --git a/src/muz_qe/clp_context.h b/src/muz_qe/clp_context.h index cd9117553..635891205 100644 --- a/src/muz_qe/clp_context.h +++ b/src/muz_qe/clp_context.h @@ -11,7 +11,7 @@ Abstract: Author: - Nikolaj Bjorner (nbjorner) 2013-01-15 + Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: From 3f45782814387106bef3bfaf80840ce746471a65 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 Apr 2013 17:22:06 -0700 Subject: [PATCH 38/91] tidy up clp_context a bit Signed-off-by: Nikolaj Bjorner --- src/muz_qe/clp_context.cpp | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/muz_qe/clp_context.cpp b/src/muz_qe/clp_context.cpp index 73b065f7d..25ea1455b 100644 --- a/src/muz_qe/clp_context.cpp +++ b/src/muz_qe/clp_context.cpp @@ -39,23 +39,17 @@ namespace datalog { rule_manager& rm; smt_params m_fparams; smt::kernel m_solver; - unifier m_unify; - substitution m_subst; var_subst m_var_subst; expr_ref_vector m_ground; app_ref_vector m_goals; volatile bool m_cancel; - unsigned m_deltas[2]; - unsigned m_var_cnt; stats m_stats; public: imp(context& ctx): m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), - m_solver(m, m_fparams), - m_unify(m), - m_subst(m), + m_solver(m, m_fparams), // TBD: can be replaced by efficient BV solver. m_var_subst(m, false), m_ground(m), m_goals(m), @@ -64,8 +58,6 @@ namespace datalog { // m_fparams.m_relevancy_lvl = 0; m_fparams.m_mbqi = false; m_fparams.m_soft_timeout = 1000; - m_deltas[0] = 0; - m_deltas[1] = m_var_cnt; } ~imp() {} @@ -134,24 +126,24 @@ namespace datalog { } lbool search(unsigned depth, unsigned index) { - IF_VERBOSE(1, verbose_stream() << "search " << depth << " " << index << "\n";); - if (depth == 0) { - return l_undef; - } if (index == m_goals.size()) { return l_true; } + if (depth == 0) { + return l_undef; + } + IF_VERBOSE(1, verbose_stream() << "search " << depth << " " << index << "\n";); unsigned num_goals = m_goals.size(); app* head = m_goals[index].get(); rule_vector const& rules = m_ctx.get_rules().get_predicate_rules(head->get_decl()); lbool status = l_false; for (unsigned i = 0; i < rules.size(); ++i) { - IF_VERBOSE(2, verbose_stream() << index << " " << mk_pp(head, m) << "\n";); rule* r = rules[i]; m_solver.push(); reset_ground(); expr_ref tmp(m); tmp = r->get_head(); + IF_VERBOSE(2, verbose_stream() << index << " " << mk_pp(tmp, m) << "\n";); ground(tmp); for (unsigned j = 0; j < head->get_num_args(); ++j) { expr_ref eq(m); @@ -168,6 +160,10 @@ namespace datalog { case l_false: break; case l_true: + if (depth == 1 && (index+1 > m_goals.size() || r->get_uninterpreted_tail_size() > 0)) { + status = l_undef; + break; + } for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { tmp = r->get_tail(j); ground(tmp); From 9158fb17c1490a5c0b5fbcbd38f1551855390825 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 28 Apr 2013 12:47:55 -0700 Subject: [PATCH 39/91] add special procedures for UTVPI and horn arithmetic Signed-off-by: Nikolaj Bjorner --- src/smt/diff_logic.h | 34 +- src/smt/params/theory_arith_params.h | 4 +- src/smt/smt_setup.cpp | 14 + src/smt/theory_diff_logic.h | 115 +-- src/smt/theory_diff_logic_def.h | 101 +-- src/smt/theory_horn_ineq.cpp | 236 ++++++ src/smt/theory_horn_ineq.h | 342 ++++++++ src/smt/theory_horn_ineq_def.h | 1166 ++++++++++++++++++++++++++ src/smt/theory_utvpi.cpp | 159 ++++ src/smt/theory_utvpi.h | 331 ++++++++ src/smt/theory_utvpi_def.h | 694 +++++++++++++++ src/util/inf_eps_rational.h | 409 +++++++++ 12 files changed, 3397 insertions(+), 208 deletions(-) create mode 100644 src/smt/theory_horn_ineq.cpp create mode 100644 src/smt/theory_horn_ineq.h create mode 100644 src/smt/theory_horn_ineq_def.h create mode 100644 src/smt/theory_utvpi.cpp create mode 100644 src/smt/theory_utvpi.h create mode 100644 src/smt/theory_utvpi_def.h create mode 100644 src/util/inf_eps_rational.h diff --git a/src/smt/diff_logic.h b/src/smt/diff_logic.h index 6d5101a80..b32a74c2c 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.h @@ -118,7 +118,7 @@ const edge_id null_edge_id = -1; template class dl_graph { - struct statistics { + struct stats { unsigned m_propagation_cost; unsigned m_implied_literal_cost; unsigned m_num_implied_literals; @@ -131,16 +131,16 @@ class dl_graph { m_num_helpful_implied_literals = 0; m_num_relax = 0; } - statistics() { reset(); } - void display(std::ostream& out) const { - out << "num. prop. steps. " << m_propagation_cost << "\n"; - out << "num. impl. steps. " << m_implied_literal_cost << "\n"; - out << "num. impl. lits. " << m_num_implied_literals << "\n"; - out << "num. impl. conf lits. " << m_num_helpful_implied_literals << "\n"; - out << "num. bound relax. " << m_num_relax << "\n"; + stats() { reset(); } + void collect_statistics(::statistics& st) const { + st.update("dl prop steps", m_propagation_cost); + st.update("dl impl steps", m_implied_literal_cost); + st.update("dl impl lits", m_num_implied_literals); + st.update("dl impl conf lits", m_num_helpful_implied_literals); + st.update("dl bound relax", m_num_relax); } }; - statistics m_stats; + stats m_stats; typedef typename Ext::numeral numeral; typedef typename Ext::explanation explanation; typedef vector assignment; @@ -264,6 +264,12 @@ class dl_graph { m_assignment[e.get_target()] - m_assignment[e.get_source()] <= e.get_weight(); } + bool is_tight(edge_id e) const { + edge const& edge = m_edges[e]; + return edge.is_enabled() && + m_assignment[edge.get_target()] - m_assignment[e.get_source()] == e.get_weight(); + } + public: // An assignment is feasible if all edges are feasible. @@ -472,8 +478,9 @@ public: m_bw(m_mark) { } - void display_statistics(std::ostream& out) const { - m_stats.display(out); + + void collect_statistics(::statistics& st) const { + m_stats.collect_statistics(st); } // Create/Initialize a variable with the given id. @@ -655,10 +662,8 @@ public: throw default_exception("edges are not inconsistent"); } -#if 1 - // experimental feature: + // allow theory to introduce shortcut lemmas. prune_edges(edges, f); -#endif for (unsigned i = 0; i < edges.size(); ++i) { edge const& e = m_edges[edges[i]]; @@ -752,7 +757,6 @@ public: f.new_edge(src, dst, idx2-idx1+1, edges.begin()+idx1); } - // Create a new scope. // That is, save the number of edges in the graph. void push() { diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index 52fef8ca4..30bc65b6d 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -26,7 +26,9 @@ enum arith_solver_id { AS_NO_ARITH, AS_DIFF_LOGIC, AS_ARITH, - AS_DENSE_DIFF_LOGIC + AS_DENSE_DIFF_LOGIC, + AS_UTVPI, + AS_HORN }; enum bound_prop_mode { diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 1f020cdd3..f91a58f87 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -22,6 +22,8 @@ Revision History: #include"theory_arith.h" #include"theory_dense_diff_logic.h" #include"theory_diff_logic.h" +#include"theory_horn_ineq.h" +#include"theory_utvpi.h" #include"theory_array.h" #include"theory_array_full.h" #include"theory_bv.h" @@ -723,6 +725,18 @@ namespace smt { m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params)); } break; + case AS_HORN: + if (m_params.m_arith_int_only) + m_context.register_plugin(alloc(smt::theory_ihi, m_manager)); + else + m_context.register_plugin(alloc(smt::theory_rhi, m_manager)); + break; + case AS_UTVPI: + if (m_params.m_arith_int_only) + m_context.register_plugin(alloc(smt::theory_iutvpi, m_manager)); + else + m_context.register_plugin(alloc(smt::theory_rutvpi, m_manager)); + break; default: if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 9c80d8c34..3bfd33b1e 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -59,109 +59,30 @@ namespace smt { } }; - class dl_conflict : public simple_justification { - public: - dl_conflict(region & r, unsigned nls, literal const * lits): simple_justification(r, nls, lits) { } - - virtual proof * mk_proof(conflict_resolution & cr) { - NOT_IMPLEMENTED_YET(); - return 0; - } - }; - - template class theory_diff_logic : public theory, private Ext { typedef typename Ext::numeral numeral; - class implied_eq_justification : public justification { - theory_diff_logic & m_theory; - theory_var m_v1; - theory_var m_v2; - unsigned m_timestamp; - public: - implied_eq_justification(theory_diff_logic & theory, theory_var v1, theory_var v2, unsigned ts): - m_theory(theory), - m_v1(v1), - m_v2(v2), - m_timestamp(ts) { - } - - virtual void get_antecedents(conflict_resolution & cr) { - m_theory.get_eq_antecedents(m_v1, m_v2, m_timestamp, cr); - } - - virtual proof * mk_proof(conflict_resolution & cr) { NOT_IMPLEMENTED_YET(); return 0; } - }; - - class implied_bound_justification : public justification { - theory_diff_logic& m_theory; - edge_id m_subsumed_edge; - edge_id m_bridge_edge; - public: - implied_bound_justification(theory_diff_logic & theory, edge_id se, edge_id be): - m_theory(theory), - m_subsumed_edge(se), - m_bridge_edge(be) { - } - - virtual void get_antecedents(conflict_resolution & cr) { - m_theory.get_implied_bound_antecedents(m_bridge_edge, m_subsumed_edge, cr); - } - - virtual proof * mk_proof(conflict_resolution & cr) { NOT_IMPLEMENTED_YET(); return 0; } - }; - - enum atom_kind { - LE_ATOM, - EQ_ATOM - }; - class atom { - protected: - atom_kind m_kind; bool_var m_bvar; bool m_true; + int m_pos; + int m_neg; public: - atom(atom_kind k, bool_var bv) : m_kind(k), m_bvar(bv), m_true(false) {} - virtual ~atom() {} - atom_kind kind() const { return m_kind; } - bool_var get_bool_var() const { return m_bvar; } - bool is_true() const { return m_true; } - void assign_eh(bool is_true) { m_true = is_true; } - virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; - }; - - class le_atom : public atom { - int m_pos; - int m_neg; - public: - le_atom(bool_var bv, int pos, int neg): - atom(LE_ATOM, bv), + atom(bool_var bv, int pos, int neg): + m_bvar(bv), m_true(false), m_pos(pos), m_neg(neg) { } - virtual ~le_atom() {} + ~atom() {} + bool_var get_bool_var() const { return m_bvar; } + bool is_true() const { return m_true; } + void assign_eh(bool is_true) { m_true = is_true; } int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } int get_pos() const { return m_pos; } int get_neg() const { return m_neg; } - virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; - }; - - class eq_atom : public atom { - app_ref m_le; - app_ref m_ge; - public: - eq_atom(bool_var bv, app_ref& le, app_ref& ge): - atom(EQ_ATOM, bv), - m_le(le), - m_ge(ge) - {} - virtual ~eq_atom() {} - virtual std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; - app* get_le() const { return m_le.get(); } - app* get_ge() const { return m_ge.get(); } + std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; }; typedef ptr_vector atoms; @@ -239,19 +160,7 @@ namespace smt { unsigned m_asserted_qhead_old; }; - class theory_diff_logic_del_eh : public clause_del_eh { - theory_diff_logic& m_super; - public: - theory_diff_logic_del_eh(theory_diff_logic& s) : m_super(s) {} - virtual ~theory_diff_logic_del_eh() {} - virtual void operator()(ast_manager&, clause* cls) { - TRACE("dl_activity", tout << "deleting " << cls << "\n";); - m_super.del_clause_eh(cls); - dealloc(this); - } - }; - - smt_params & m_params; + smt_params & m_params; arith_util m_util; arith_eq_adapter m_arith_eq_adapter; theory_diff_logic_statistics m_stats; @@ -296,8 +205,6 @@ namespace smt { return get_family_id() == n->get_family_id(); } - void del_clause_eh(clause* cls); - public: theory_diff_logic(ast_manager& m, smt_params & params): theory(m.mk_family_id("arith")), @@ -316,7 +223,7 @@ namespace smt { m_nc_functor(*this) { } - ~theory_diff_logic() { + virtual ~theory_diff_logic() { reset_eh(); } diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index aeb4f73d6..362962620 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -31,34 +31,15 @@ Revision History: using namespace smt; + template -std::ostream& theory_diff_logic::atom::display(theory_diff_logic const& th, std::ostream& out) const { +std::ostream& theory_diff_logic::atom::display(theory_diff_logic const& th, std::ostream& out) const { context& ctx = th.get_context(); lbool asgn = ctx.get_assignment(m_bvar); //SASSERT(asgn == l_undef || ((asgn == l_true) == m_true)); bool sign = (l_undef == asgn) || m_true; return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; -} - -template -std::ostream& theory_diff_logic::eq_atom::display(theory_diff_logic const& th, std::ostream& out) const { - atom::display(th, out); - lbool asgn = th.get_context().get_assignment(this->m_bvar); - if (l_undef == asgn) { - out << "unassigned\n"; - } - else { - out << mk_pp(m_le.get(), m_le.get_manager()) << " " - << mk_pp(m_ge.get(), m_ge.get_manager()) << "\n"; - } - return out; -} - -template -std::ostream& theory_diff_logic::le_atom::display(theory_diff_logic const& th, std::ostream& out) const { - atom::display(th, out); - lbool asgn = th.get_context().get_assignment(this->m_bvar); if (l_undef == asgn) { out << "unassigned\n"; } @@ -94,7 +75,6 @@ void theory_diff_logic::init(context * ctx) { e = ctx->mk_enode(zero, false, false, true); SASSERT(!is_attached_to_var(e)); m_zero_real = mk_var(e); - } @@ -277,7 +257,7 @@ bool theory_diff_logic::internalize_atom(app * n, bool gate_ctx) { k -= this->m_epsilon; } edge_id neg = m_graph.add_edge(target, source, k, ~l); - le_atom * a = alloc(le_atom, bv, pos, neg); + atom * a = alloc(atom, bv, pos, neg); m_atoms.push_back(a); m_bool_var2atom.insert(bv, a); @@ -334,6 +314,7 @@ void theory_diff_logic::collect_statistics(::statistics & st) const { st.update("dl asserts", m_stats.m_num_assertions); st.update("core->dl eqs", m_stats.m_num_core2th_eqs); m_arith_eq_adapter.collect_statistics(st); + m_graph.collect_statistics(st); } template @@ -497,45 +478,14 @@ bool theory_diff_logic::propagate_atom(atom* a) { if (ctx.inconsistent()) { return false; } - switch(a->kind()) { - case LE_ATOM: { - int edge_id = dynamic_cast(a)->get_asserted_edge(); - if (!m_graph.enable_edge(edge_id)) { - set_neg_cycle_conflict(); - return false; - } -#if 0 - if (m_params.m_arith_bound_prop != BP_NONE) { - svector subsumed; - m_graph.find_subsumed1(edge_id, subsumed); - for (unsigned i = 0; i < subsumed.size(); ++i) { - int subsumed_edge_id = subsumed[i]; - literal l = m_graph.get_explanation(subsumed_edge_id); - context & ctx = get_context(); - region& r = ctx.get_region(); - ++m_stats.m_num_th2core_prop; - ctx.assign(l, new (r) implied_bound_justification(*this, subsumed_edge_id, edge_id)); - } - - } -#endif - break; - } - case EQ_ATOM: - if (!a->is_true()) { - SASSERT(ctx.get_assignment(a->get_bool_var()) == l_false); - // eq_atom * ea = dynamic_cast(a); - } - break; + int edge_id = a->get_asserted_edge(); + if (!m_graph.enable_edge(edge_id)) { + set_neg_cycle_conflict(); + return false; } return true; } -template -void theory_diff_logic::del_clause_eh(clause* cls) { - -} - template void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { @@ -584,7 +534,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges atom* a = 0; m_bool_var2atom.find(bv, a); SASSERT(a); - edge_id e_id = static_cast(a)->get_pos(); + edge_id e_id = a->get_pos(); literal_vector lits; for (unsigned i = 0; i < num_edges; ++i) { @@ -608,11 +558,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges lits.size(), lits.c_ptr(), params.size(), params.c_ptr()); } - clause_del_eh* del_eh = alloc(theory_diff_logic_del_eh, *this); - clause* cls = ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, del_eh); - if (!cls) { - dealloc(del_eh); - } + clause* cls = ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); if (dump_lemmas()) { char const * logic = m_is_lia ? "QF_LIA" : "QF_LRA"; ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); @@ -906,30 +852,9 @@ bool theory_diff_logic::is_consistent() const { lbool asgn = ctx.get_assignment(bv); if (ctx.is_relevant(ctx.bool_var2expr(bv)) && asgn != l_undef) { SASSERT((asgn == l_true) == a->is_true()); - switch(a->kind()) { - case LE_ATOM: { - le_atom* le = dynamic_cast(a); - int edge_id = le->get_asserted_edge(); - SASSERT(m_graph.is_enabled(edge_id)); - SASSERT(m_graph.is_feasible(edge_id)); - break; - } - case EQ_ATOM: { - eq_atom* ea = dynamic_cast(a); - bool_var bv1 = ctx.get_bool_var(ea->get_le()); - bool_var bv2 = ctx.get_bool_var(ea->get_ge()); - lbool val1 = ctx.get_assignment(bv1); - lbool val2 = ctx.get_assignment(bv2); - if (asgn == l_true) { - SASSERT(val1 == l_true); - SASSERT(val2 == l_true); - } - else { - SASSERT(val1 == l_false || val2 == l_false); - } - break; - } - } + int edge_id = a->get_asserted_edge(); + SASSERT(m_graph.is_enabled(edge_id)); + SASSERT(m_graph.is_feasible(edge_id)); } } return m_graph.is_feasible(); diff --git a/src/smt/theory_horn_ineq.cpp b/src/smt/theory_horn_ineq.cpp new file mode 100644 index 000000000..978b5b003 --- /dev/null +++ b/src/smt/theory_horn_ineq.cpp @@ -0,0 +1,236 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_horn_ineq.h + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-18 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ +#include "theory_horn_ineq.h" +#include "theory_horn_ineq_def.h" + +namespace smt { + + template class theory_horn_ineq; + template class theory_horn_ineq; + + // similar to test_diff_logic: + + horn_ineq_tester::horn_ineq_tester(ast_manager& m): m(m), a(m) {} + + bool horn_ineq_tester::operator()(expr* e) { + m_todo.reset(); + m_pols.reset(); + pos_mark.reset(); + neg_mark.reset(); + m_todo.push_back(e); + m_pols.push_back(l_true); + while (!m_todo.empty()) { + expr* e = m_todo.back(); + lbool p = m_pols.back(); + m_todo.pop_back(); + m_pols.pop_back(); + switch (p) { + case l_true: + if (pos_mark.is_marked(e)) { + continue; + } + pos_mark.mark(e, true); + break; + case l_false: + if (neg_mark.is_marked(e)) { + continue; + } + neg_mark.mark(e, true); + break; + case l_undef: + if (pos_mark.is_marked(e) && neg_mark.is_marked(e)) { + continue; + } + pos_mark.mark(e, true); + neg_mark.mark(e, true); + break; + } + if (!test_expr(p, e)) { + return false; + } + } + return true; + } + + vector > const& horn_ineq_tester::get_linearization() const { + return m_terms; + } + + bool horn_ineq_tester::test_expr(lbool p, expr* e) { + expr* e1, *e2, *e3; + if (is_var(e)) { + return true; + } + if (!is_app(e)) { + return false; + } + app* ap = to_app(e); + if (m.is_and(ap) || m.is_or(ap)) { + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + m_todo.push_back(ap->get_arg(i)); + m_pols.push_back(p); + } + } + else if (m.is_not(e, e1)) { + m_todo.push_back(e); + m_pols.push_back(~p); + } + else if (m.is_ite(e, e1, e2, e3)) { + m_todo.push_back(e1); + m_pols.push_back(l_undef); + m_todo.push_back(e2); + m_pols.push_back(p); + m_todo.push_back(e3); + m_pols.push_back(p); + } + else if (m.is_iff(e, e1, e2)) { + m_todo.push_back(e1); + m_pols.push_back(l_undef); + m_todo.push_back(e2); + m_pols.push_back(l_undef); + m_todo.push_back(e2); + } + else if (m.is_implies(e, e1, e2)) { + m_todo.push_back(e1); + m_pols.push_back(~p); + m_todo.push_back(e2); + m_pols.push_back(p); + } + else if (m.is_eq(e, e1, e2)) { + return linearize(e1, e2) == diff; + } + else if (m.is_true(e) || m.is_false(e)) { + // no-op + } + else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) || + a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { + if (p == l_false) { + std::swap(e2, e1); + } + classify_t cl = linearize(e1, e2); + switch(p) { + case l_undef: + return cl == diff; + case l_true: + case l_false: + return cl == horn || cl == diff; + } + } + else if (!is_uninterp_const(e)) { + return false; + } + return true; + } + + bool horn_ineq_tester::operator()(unsigned num_fmls, expr* const* fmls) { + for (unsigned i = 0; i < num_fmls; ++i) { + if (!(*this)(fmls[i])) { + return false; + } + } + return true; + } + + horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e, rational(1))); + return linearize(); + } + + horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e1, expr* e2) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e1, rational(1))); + m_terms.push_back(std::make_pair(e2, rational(-1))); + return linearize(); + } + + horn_ineq_tester::classify_t horn_ineq_tester::linearize() { + + m_weight.reset(); + m_coeff_map.reset(); + + while (!m_terms.empty()) { + expr* e1, *e2; + rational num; + rational mul = m_terms.back().second; + expr* e = m_terms.back().first; + m_terms.pop_back(); + if (a.is_add(e)) { + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul)); + } + } + else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_sub(e, e1, e2)) { + m_terms.push_back(std::make_pair(e1, mul)); + m_terms.push_back(std::make_pair(e2, -mul)); + } + else if (a.is_uminus(e, e1)) { + m_terms.push_back(std::make_pair(e1, -mul)); + } + else if (a.is_numeral(e, num)) { + m_weight += num*mul; + } + else if (a.is_to_real(e, e1)) { + m_terms.push_back(std::make_pair(e1, mul)); + } + else if (!is_uninterp_const(e)) { + return non_horn; + } + else { + m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul; + } + } + unsigned num_negative = 0; + unsigned num_positive = 0; + bool is_unit_pos = true, is_unit_neg = true; + obj_map::iterator it = m_coeff_map.begin(); + obj_map::iterator end = m_coeff_map.end(); + for (; it != end; ++it) { + rational r = it->m_value; + if (r.is_zero()) { + continue; + } + m_terms.push_back(std::make_pair(it->m_key, r)); + if (r.is_pos()) { + is_unit_pos = is_unit_pos && r.is_one(); + num_positive++; + } + else { + is_unit_neg = is_unit_neg && r.is_minus_one(); + num_negative++; + } + } + if (num_negative <= 1 && is_unit_pos && is_unit_neg && num_positive <= 1) { + return diff; + } + if (num_positive <= 1 && is_unit_pos) { + return horn; + } + if (num_negative <= 1 && is_unit_neg) { + return co_horn; + } + return non_horn; + } + + +} diff --git a/src/smt/theory_horn_ineq.h b/src/smt/theory_horn_ineq.h new file mode 100644 index 000000000..f5fb41263 --- /dev/null +++ b/src/smt/theory_horn_ineq.h @@ -0,0 +1,342 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_horn_ineq.h + +Abstract: + + + A*x <= b + D*x, coefficients to A and D are non-negative, + D is a diagonal matrix. + Coefficients to b may have both signs. + + + Ford-Fulkerson variant: + Label variables by weight. + Select inequality that is not satisfied. + Set gamma(LHS) := 0 + Set gamma(RHS(x)) := weight(x) - b + Propagate gamma through inequalities. + Gamma is the increment. + Maintain Heap of variables weighted by gamma. + When processing inequality, + then update gamma of variables by gamma := A(gamma + weight) - b + If some variable in the premise of the original rule gets + relabeled (assignment is increased), then the set of + inequalities is unsatisfiable. + + Propagation updates lower bounds on gamma by taking into + account integer inequalities. The greatest lower bound + is computable by taking integer floor/ceilings. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-18 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ + +#ifndef _THEORY_HORN_INEQ_H_ +#define _THEORY_HORN_INEQ_H_ + +#include"rational.h" +#include"inf_rational.h" +#include"inf_int_rational.h" +#include"inf_eps_rational.h" +#include"smt_theory.h" +#include"arith_decl_plugin.h" +#include"smt_justification.h" +#include"map.h" +#include"smt_params.h" +#include"arith_eq_adapter.h" +#include"smt_model_generator.h" +#include"numeral_factory.h" +#include"smt_clause.h" + +namespace smt { + + class horn_ineq_tester { + ast_manager& m; + arith_util a; + ptr_vector m_todo; + svector m_pols; + ast_mark pos_mark, neg_mark; + obj_map m_coeff_map; + rational m_weight; + vector > m_terms; + + public: + enum classify_t { + co_horn, + horn, + diff, + non_horn + }; + horn_ineq_tester(ast_manager& m); + + // test if formula is in the Horn inequality fragment: + bool operator()(expr* fml); + bool operator()(unsigned num_fmls, expr* const* fmls); + + // linearize inequality/equality + classify_t linearize(expr* e); + classify_t linearize(expr* e1, expr* e2); + + // retrieve linearization + vector > const& get_linearization() const; + rational const& get_weight() const { return m_weight; } + private: + bool test_expr(lbool p, expr* e); + classify_t linearize(); + }; + + template + class theory_horn_ineq : public theory, private Ext { + + typedef typename Ext::numeral numeral; + typedef literal explanation; + typedef theory_var th_var; + typedef svector th_var_vector; + typedef unsigned clause_id; + typedef vector > coeffs; + static const clause_id null_clause_id = UINT_MAX; + + class clause; + class graph; + class assignment_trail; + class parent_trail; + + class atom { + protected: + bool_var m_bvar; + bool m_true; + int m_pos; + int m_neg; + public: + atom(bool_var bv, int pos, int neg) : + m_bvar(bv), m_true(false), + m_pos(pos), m_neg(neg) {} + virtual ~atom() {} + bool_var get_bool_var() const { return m_bvar; } + bool is_true() const { return m_true; } + void assign_eh(bool is_true) { m_true = is_true; } + int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } + int get_pos() const { return m_pos; } + int get_neg() const { return m_neg; } + std::ostream& display(theory_horn_ineq const& th, std::ostream& out) const; + }; + typedef svector atoms; + + struct scope { + unsigned m_atoms_lim; + unsigned m_asserted_atoms_lim; + unsigned m_asserted_qhead_old; + }; + + struct stats { + unsigned m_num_conflicts; + unsigned m_num_assertions; + unsigned m_num_core2th_eqs; + unsigned m_num_core2th_diseqs; + + void reset() { + memset(this, 0, sizeof(*this)); + } + + stats() { + reset(); + } + }; + + stats m_stats; + smt_params m_params; + arith_util a; + arith_eq_adapter m_arith_eq_adapter; + th_var m_zero_int; // cache the variable representing the zero variable. + th_var m_zero_real; // cache the variable representing the zero variable. + + graph* m_graph; + atoms m_atoms; + unsigned_vector m_asserted_atoms; // set of asserted atoms + unsigned m_asserted_qhead; + u_map m_bool_var2atom; + svector m_scopes; + + double m_agility; + bool m_lia; + bool m_lra; + bool m_non_horn_ineq_exprs; + + horn_ineq_tester m_test; + + + arith_factory * m_factory; + rational m_delta; + rational m_lambda; + + + // Set a conflict due to a negative cycle. + void set_neg_cycle_conflict(); + + // Create a new theory variable. + virtual th_var mk_var(enode* n); + + virtual th_var mk_var(expr* n); + + void compute_delta(); + + void found_non_horn_ineq_expr(expr * n); + + bool is_interpreted(app* n) const { + return n->get_family_id() == get_family_id(); + } + + public: + theory_horn_ineq(ast_manager& m); + + virtual ~theory_horn_ineq(); + + virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_horn_ineq, get_manager()); } + + virtual char const * get_name() const { return "horn-inequality-logic"; } + + /** + \brief See comment in theory::mk_eq_atom + */ + virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); } + + virtual void init(context * ctx); + + virtual bool internalize_atom(app * atom, bool gate_ctx); + + virtual bool internalize_term(app * term); + + virtual void internalize_eq_eh(app * atom, bool_var v); + + virtual void assign_eh(bool_var v, bool is_true); + + virtual void new_eq_eh(th_var v1, th_var v2) { + m_arith_eq_adapter.new_eq_eh(v1, v2); + } + + virtual bool use_diseqs() const { return true; } + + virtual void new_diseq_eh(th_var v1, th_var v2) { + m_arith_eq_adapter.new_diseq_eh(v1, v2); + } + + virtual void push_scope_eh(); + + virtual void pop_scope_eh(unsigned num_scopes); + + virtual void restart_eh() { + m_arith_eq_adapter.restart_eh(); + } + + virtual void relevant_eh(app* e) {} + + virtual void init_search_eh() { + m_arith_eq_adapter.init_search_eh(); + } + + virtual final_check_status final_check_eh(); + + virtual bool is_shared(th_var v) const { + return false; + } + + virtual bool can_propagate() { + SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); + return m_asserted_qhead != m_asserted_atoms.size(); + } + + virtual void propagate(); + + virtual justification * why_is_diseq(th_var v1, th_var v2) { + UNREACHABLE(); + return 0; + } + + virtual void reset_eh(); + + virtual void init_model(model_generator & m); + + virtual model_value_proc * mk_value(enode * n, model_generator & mg); + + virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const { + return true; + } + + virtual void display(std::ostream & out) const; + + virtual void collect_statistics(::statistics & st) const; + + private: + + virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_eqs++; + new_eq_or_diseq(true, v1, v2, j); + } + + virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_diseqs++; + new_eq_or_diseq(false, v1, v2, j); + } + + void negate(coeffs& coeffs, rational& weight); + numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; + void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); + + void del_atoms(unsigned old_size); + + void propagate_core(); + + bool propagate_atom(atom const& a); + + th_var mk_term(app* n); + + th_var mk_num(app* n, rational const& r); + + bool is_consistent() const; + + th_var expand(bool pos, th_var v, rational & k); + + void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); + + th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; } + + th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } + + void inc_conflicts(); + + }; + + struct rhi_ext { + typedef inf_rational inf_numeral; + typedef inf_eps_rational numeral; + numeral m_epsilon; + numeral m_minus_infty; + rhi_ext() : m_epsilon(inf_rational(rational(), true)), m_minus_infty(rational(-1),inf_rational()) {} + }; + + struct ihi_ext { + typedef rational inf_numeral; + typedef inf_eps_rational numeral; + numeral m_epsilon; + numeral m_minus_infty; + ihi_ext() : m_epsilon(rational(1)), m_minus_infty(rational(-1),rational(0)) {} + }; + + typedef theory_horn_ineq theory_rhi; + typedef theory_horn_ineq theory_ihi; +}; + + + + +#endif /* _THEORY_HORN_INEQ_H_ */ diff --git a/src/smt/theory_horn_ineq_def.h b/src/smt/theory_horn_ineq_def.h new file mode 100644 index 000000000..b86d45819 --- /dev/null +++ b/src/smt/theory_horn_ineq_def.h @@ -0,0 +1,1166 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_horn_ineq_def.h + +Abstract: + + A*x <= b + D*x, coefficients to A and D are non-negative, + D is a diagonal matrix. + Coefficients to b may have both signs. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-18 + +Revision History: + +--*/ + +#ifndef _THEORY_HORN_INEQ_DEF_H_ +#define _THEORY_HORN_INEQ_DEF_H_ +#include "theory_horn_ineq.h" +#include "ast_pp.h" +#include "smt_context.h" + +namespace smt { + + /** + A clause represents an inequality of the form + + v1*c1 + v2*c2 + .. + v_n*c_n + w <= v*c + + where + - m_vars: [v1,v2,...,v_n] + - m_coeffs: [c1,c2,...,c_n] + - m_var: v + - m_coeff: c + - m_weight: w + + */ + template + class theory_horn_ineq::clause { + vector m_coeffs; // coefficients of body. + svector m_vars; // variables of body. + rational m_coeff; // coefficient of head. + th_var m_var; // head variable. + numeral m_weight; // constant to add + literal m_explanation; + bool m_enabled; + public: + clause(unsigned sz, rational const* coeffs, th_var const* vars, + rational const& coeff, th_var var, numeral const& w, + const literal& ex): + m_coeffs(sz, coeffs), + m_vars(sz, vars), + m_coeff(coeff), + m_var(var), + m_weight(w), + m_explanation(ex), + m_enabled(false) { + DEBUG_CODE( + { + for (unsigned i = 0; i < size(); ++i) { + SASSERT(coeffs[i].is_pos()); + } + SASSERT(coeff.is_pos()); + }); + } + + th_var vars(unsigned i) const { return m_vars[i]; } + rational const& coeffs(unsigned i) const { return m_coeffs[i]; } + th_var var() const { return m_var; } + rational const& coeff() const { return m_coeff; } + const numeral & get_weight() const { return m_weight; } + const literal & get_explanation() const { return m_explanation; } + bool is_enabled() const { return m_enabled; } + unsigned size() const { return m_vars.size(); } + + void enable() { m_enabled = true; } + void disable() { m_enabled = false; } + + void display(std::ostream& out) const { + out << (is_enabled()?"+ ":"- "); + for (unsigned i = 0; i < size(); ++i) { + if (i > 0 && coeffs(i).is_pos()) { + out << " + "; + } + display(out, coeffs(i), vars(i)); + } + if (!get_weight().is_zero()) { + out << " + " << get_weight(); + } + display(out << " <= ", coeff(), var()); + out << "\n"; + } + + private: + + void display(std::ostream& out, rational const& c, th_var v) const { + if (!c.is_one()) { + out << c << "*"; + } + out << "v" << v; + } + }; + + template + class theory_horn_ineq::assignment_trail { + th_var m_var; + numeral m_old_value; + public: + assignment_trail(th_var v, const numeral & val): + m_var(v), + m_old_value(val) {} + th_var get_var() const { return m_var; } + const numeral & get_old_value() const { return m_old_value; } + }; + + template + class theory_horn_ineq::parent_trail { + th_var m_var; + clause_id m_old_value; + public: + parent_trail(th_var v, clause_id val): + m_var(v), + m_old_value(val) {} + th_var get_var() const { return m_var; } + clause_id get_old_value() const { return m_old_value; } + }; + + + template + class theory_horn_ineq::graph : private Ext { + + typedef vector assignment_stack; + typedef vector parent_stack; + typedef unsigned_vector clause_id_vector; + + struct stats { + unsigned m_propagation_cost; + + void reset() { + memset(this, 0, sizeof(*this)); + } + }; + + struct scope { + unsigned m_clauses_lim; + unsigned m_enabled_clauses_lim; + unsigned m_assignment_lim; + unsigned m_parent_lim; + scope(unsigned e, unsigned enabled, unsigned alim, unsigned plim): + m_clauses_lim(e), + m_enabled_clauses_lim(enabled), + m_assignment_lim(alim), + m_parent_lim(plim) { + } + }; + + stats m_stats; + vector m_clauses; + vector m_assignment; // per var + clause_id_vector m_parent; // per var + assignment_stack m_assignment_stack; // stack for restoring the assignment + parent_stack m_parent_stack; // stack for restoring parents + clause_id_vector m_enabled_clauses; + vector m_out_clauses; // use-list for clauses. + vector m_in_clauses; // clauses that have variable in head. + // forward reachability + unsigned_vector m_onstack; + unsigned m_ts; + unsigned_vector m_todo; + literal_vector m_lits; + vector m_coeffs; + th_var m_zero; + clause_id m_unsat_clause; + svector m_trail_stack; + + + public: + + graph(): m_ts(0), m_zero(null_theory_var), m_unsat_clause(null_clause_id) {} + + void reset() { + m_clauses .reset(); + m_assignment .reset(); + m_parent .reset(); + m_assignment_stack .reset(); + m_parent_stack .reset(); + m_out_clauses .reset(); + m_in_clauses .reset(); + m_enabled_clauses .reset(); + m_onstack .reset(); + m_ts = 0; + m_lits .reset(); + m_trail_stack .reset(); + m_unsat_clause = null_clause_id; + } + + + void traverse_neg_cycle1(bool /*stronger_lemmas*/) { + TRACE("horn_ineq", display(tout);); + SASSERT(!m_enabled_clauses.empty()); + clause_id id = m_unsat_clause; + SASSERT(id != null_clause_id); + SASSERT(!is_feasible(m_clauses[id])); + clause_id_vector todo; + vector muls; + todo.push_back(id); + muls.push_back(rational(1)); + u_map lits; + while (!todo.empty()) { + id = todo.back(); + rational mul = muls.back(); + todo.pop_back(); + muls.pop_back(); + clause const& cl = m_clauses[id]; + literal lit = cl.get_explanation(); + if (lit != null_literal) { + lits.insert_if_not_there2(id, rational(0))->get_data().m_value += mul; + } + for (unsigned i = 0; i < cl.size(); ++i) { + id = m_parent[cl.vars(i)]; + if (id != null_clause_id) { + todo.push_back(id); + muls.push_back(mul*cl.coeffs(i)); + } + } + } + u_map::iterator it = lits.begin(), end = lits.end(); + m_lits.reset(); + m_coeffs.reset(); + for (; it != end; ++it) { + m_lits.push_back(m_clauses[it->m_key].get_explanation()); + m_coeffs.push_back(it->m_value); + } + + // TODO: use topological sort to tune traversal of parents to linear. + // (current traversal can be exponential). + // TODO: negative cycle traversal with inline resolution to find + // stronger conflict clauses. + // follow heuristic used in theory_diff_logic_def.h: + } + + unsigned get_num_clauses() const { + return m_clauses.size(); + } + + literal_vector const& get_lits() const { + return m_lits; + } + + vector const& get_coeffs() const { + return m_coeffs; + } + + numeral get_assignment(th_var v) const { + return m_assignment[v]; + } + + numeral eval_body(clause const& cl) const { + numeral v(0); + for (unsigned i = 0; i < cl.size(); ++i) { + v += cl.coeffs(i)*m_assignment[cl.vars(i)]; + } + v += cl.get_weight(); + return v; + } + + numeral eval_body(clause_id id) const { + return eval_body(m_clauses[id]); + } + + numeral eval_head(clause_id id) const { + return eval_head(m_clauses[id]); + } + + numeral eval_head(clause const& cl) const { + return cl.coeff()*m_assignment[cl.var()]; + } + + clause const& get_clause(clause_id id) const { + return m_clauses[id]; + } + + void display_clause(std::ostream& out, clause_id id) const { + if (id == null_clause_id) { + out << "null\n"; + } + else { + m_clauses[id].display(out); + } + } + + void display(std::ostream& out) const { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + display_clause(out, i); + } + for (unsigned i = 0; i < m_assignment.size(); ++i) { + out << m_assignment[i] << "\n"; + } + } + + void collect_statistics(::statistics& st) const { + st.update("hi_propagation_cst", m_stats.m_propagation_cost); + } + + void push() { + m_trail_stack.push_back(scope(m_clauses.size(), m_enabled_clauses.size(), + m_assignment_stack.size(), m_parent_stack.size())); + } + + void pop(unsigned num_scopes) { + unsigned lvl = m_trail_stack.size(); + SASSERT(num_scopes <= lvl); + unsigned new_lvl = lvl - num_scopes; + scope & s = m_trail_stack[new_lvl]; + // restore enabled clauses + for (unsigned i = m_enabled_clauses.size(); i > s.m_enabled_clauses_lim; ) { + --i; + m_clauses[m_enabled_clauses[i]].disable(); + } + m_enabled_clauses.shrink(s.m_enabled_clauses_lim); + + // restore assignment stack + for (unsigned i = m_assignment_stack.size(); i > s.m_assignment_lim; ) { + --i; + m_assignment[m_assignment_stack[i].get_var()] = m_assignment_stack[i].get_old_value(); + } + m_assignment_stack.shrink(s.m_assignment_lim); + + // restore parent stack + for (unsigned i = m_parent_stack.size(); i > s.m_parent_lim; ) { + --i; + m_parent[m_parent_stack[i].get_var()] = m_parent_stack[i].get_old_value(); + } + m_assignment_stack.shrink(s.m_assignment_lim); + + // restore clauses + unsigned old_num_clauses = s.m_clauses_lim; + unsigned num_clauses = m_clauses.size(); + SASSERT(old_num_clauses <= num_clauses); + unsigned to_delete = num_clauses - old_num_clauses; + for (unsigned i = 0; i < to_delete; i++) { + const clause & cl = m_clauses.back(); + TRACE("horn_ineq", tout << "deleting clause:\n"; cl.display(tout);); + for (unsigned j = 0; j < cl.size(); ++j) { + m_out_clauses[cl.vars(j)].pop_back(); + } + m_in_clauses[cl.var()].pop_back(); + m_clauses.pop_back(); + } + m_trail_stack.shrink(new_lvl); + SASSERT(check_invariant()); + } + + /** + \brief Add clause z <= z and the assignment z := 0 + Then z cannot be incremented without causing a loop (and therefore a contradiction). + */ + void set_to_zero(th_var z) { + m_zero = z; + } + + bool enable_clause(clause_id id) { + if (id == null_clause_id) { + return true; + } + clause& cl = m_clauses[id]; + bool r = true; + if (!cl.is_enabled()) { + cl.enable(); + if (!is_feasible(cl)) { + r = make_feasible(id); + } + m_enabled_clauses.push_back(id); + } + return r; + } + + void init_var(th_var v) { + unsigned sz = static_cast(v); + while (m_assignment.size() <= sz) { + m_assignment.push_back(Ext::m_minus_infty); + m_out_clauses.push_back(clause_id_vector()); + m_in_clauses.push_back(clause_id_vector()); + m_parent.push_back(null_clause_id); + m_onstack.push_back(0); + } + m_assignment[v] = Ext::m_minus_infty; + SASSERT(m_out_clauses[v].empty()); + SASSERT(m_in_clauses[v].empty()); + SASSERT(check_invariant()); + } + + clause_id add_ineq(vector > const& terms, numeral const& weight, literal l) { + vector coeffs; + svector vars; + rational coeff(1); + th_var var = null_theory_var; + bool found_negative = false; + for (unsigned i = 0; i < terms.size(); ++i) { + rational const& r = terms[i].second; + if (r.is_pos()) { + coeffs.push_back(terms[i].second); + vars.push_back(terms[i].first); + } + else if (found_negative) { + return null_clause_id; + } + else { + SASSERT(r.is_neg()); + found_negative = true; + coeff = -r; + var = terms[i].first; + } + } + if (!found_negative) { + coeff = rational(1); + var = m_zero; + } + if (!coeff.is_one()) { + // so far just support unit coefficients on right. + return null_clause_id; + } + clause_id id = m_clauses.size(); + m_clauses.push_back(clause(coeffs.size(), coeffs.c_ptr(), vars.c_ptr(), coeff, var, weight, l)); + for (unsigned i = 0; i < vars.size(); ++i) { + m_out_clauses[vars[i]].push_back(id); + } + m_in_clauses[var].push_back(id); + + return id; + } + + bool is_feasible() const { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + if (!is_feasible(m_clauses[i])) { + return false; + } + } + return true; + } + + private: + + bool check_invariant() { + return true; + } + + /** + assignments are fully retraced on backtracking. + This is not always necessary. + */ + + void acc_assignment(th_var v, const numeral & inc) { + m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); + m_assignment[v] += inc; + } + + void acc_parent(th_var v, clause_id parent) { + m_parent[v] = parent; + m_parent_stack.push_back(parent_trail(v, parent)); + } + + numeral get_delta(const clause & cl) const { + SASSERT(cl.coeff().is_one() && "Not yet support for non-units"); + return eval_body(cl) - eval_head(cl); + } + + void set_onstack(th_var v) { + SASSERT(m_ts != 0); + m_onstack[v] = m_ts; + } + + void reset_onstack(th_var v) { + m_onstack[v] = 0; + } + + bool is_onstack(th_var v) const { + return m_onstack[v] == m_ts; + } + + void inc_ts() { + m_ts++; + if (m_ts == 0) { + m_ts++; + m_onstack.reset(); + m_onstack.resize(m_assignment.size(), 0); + } + } + + // Make the assignment feasible. An assignment is feasible if + // Forall clause cl. eval_body(cl) <= eval_head(cl) + // + // This method assumes that if the assignment is not feasible, + // then the only infeasible clause is the last added clause. + // + // Traversal is by naive DFS search. + // + bool make_feasible(clause_id id) { + SASSERT(is_almost_feasible(id)); + SASSERT(!m_clauses.empty()); + SASSERT(!is_feasible(m_clauses[id])); + const clause & cl0 = m_clauses[id]; + inc_ts(); + for (unsigned i = 0; i < cl0.size(); ++i) { + set_onstack(cl0.vars(i)); + } + th_var source = cl0.var(); + numeral delta = get_delta(cl0); + acc_parent(source, id); + SASSERT(delta.is_pos()); + acc_assignment(source, delta); + m_todo.reset(); + m_todo.push_back(source); + + TRACE("horn_ineq", cl0.display(tout);); + + do { + ++m_stats.m_propagation_cost; + + typename clause_id_vector::iterator it = m_out_clauses[source].begin(); + typename clause_id_vector::iterator end = m_out_clauses[source].end(); + for (; it != end; ++it) { + clause & cl = m_clauses[*it]; + if (!cl.is_enabled()) { + continue; + } + delta = get_delta(cl); + + if (delta.is_pos()) { + TRACE("horn_ineq", cl.display(tout);); + th_var target = cl.var(); + if (is_onstack(target)) { + m_unsat_clause = *it; + return false; + } + else { + acc_assignment(target, delta); + acc_parent(target, *it); + m_todo.push_back(target); + } + } + } + set_onstack(source); + source = m_todo.back(); + // pop stack until there is a new variable to process. + while (is_onstack(source)) { + m_todo.pop_back(); + reset_onstack(source); + if (m_todo.empty()) { + break; + } + source = m_todo.back(); + } + } + while (!m_todo.empty()); + return true; + } + + bool is_almost_feasible(clause_id id) const { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + if (id != static_cast(i) && !is_feasible(m_clauses[i])) { + return false; + } + } + return true; + } + + bool is_feasible(const clause & cl) const { + return !cl.is_enabled() || get_delta(cl).is_nonpos(); + } + + }; + + template + theory_horn_ineq::theory_horn_ineq(ast_manager& m): + theory(m.mk_family_id("arith")), + a(m), + m_arith_eq_adapter(*this, m_params, a), + m_zero_int(null_theory_var), + m_zero_real(null_theory_var), + m_graph(0), + m_asserted_qhead(0), + m_agility(0.5), + m_lia(false), + m_lra(false), + m_non_horn_ineq_exprs(false), + m_test(m), + m_factory(0) { + m_graph = alloc(graph); + } + + template + theory_horn_ineq::~theory_horn_ineq() { + reset_eh(); + dealloc(m_graph); + } + + template + std::ostream& theory_horn_ineq::atom::display(theory_horn_ineq const& th, std::ostream& out) const { + context& ctx = th.get_context(); + lbool asgn = ctx.get_assignment(m_bvar); + bool sign = (l_undef == asgn) || m_true; + return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; + if (l_undef == asgn) { + out << "unassigned\n"; + } + else { + th.m_graph->display_clause(out, get_asserted_edge()); + } + return out; + } + + template + theory_var theory_horn_ineq::mk_var(enode* n) { + th_var v = theory::mk_var(n); + m_graph->init_var(v); + get_context().attach_th_var(n, this, v); + return v; + } + + template + theory_var theory_horn_ineq::mk_var(expr* n) { + context & ctx = get_context(); + enode* e = 0; + th_var v = null_theory_var; + m_lia |= a.is_int(n); + m_lra |= a.is_real(n); + if (!is_app(n)) { + return v; + } + if (ctx.e_internalized(n)) { + e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + } + else { + ctx.internalize(n, false); + e = ctx.get_enode(n); + } + if (v == null_theory_var) { + v = mk_var(e); + } + if (is_interpreted(to_app(n))) { + found_non_horn_ineq_expr(n); + } + return v; + } + + template + void theory_horn_ineq::reset_eh() { + m_graph ->reset(); + m_zero_int = null_theory_var; + m_zero_real = null_theory_var; + m_atoms .reset(); + m_asserted_atoms .reset(); + m_stats .reset(); + m_scopes .reset(); + m_asserted_qhead = 0; + m_agility = 0.5; + m_lia = false; + m_lra = false; + m_non_horn_ineq_exprs = false; + theory::reset_eh(); + } + + + + template + void theory_horn_ineq::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) { + rational k; + th_var s = expand(true, v1, k); + th_var t = expand(false, v2, k); + context& ctx = get_context(); + ast_manager& m = get_manager(); + + if (s == t) { + if (is_eq != k.is_zero()) { + // conflict 0 /= k; + inc_conflicts(); + ctx.set_conflict(&eq_just); + } + } + else { + // + // Create equality ast, internalize_atom + // assign the corresponding equality literal. + // + + app_ref eq(m), s2(m), t2(m); + app* s1 = get_enode(s)->get_owner(); + app* t1 = get_enode(t)->get_owner(); + s2 = a.mk_sub(t1, s1); + t2 = a.mk_numeral(k, m.get_sort(s2.get())); + // t1 - s1 = k + eq = m.mk_eq(s2.get(), t2.get()); + + TRACE("horn_ineq", + tout << v1 << " .. " << v2 << "\n"; + tout << mk_pp(eq.get(), m) <<"\n";); + + if (!internalize_atom(eq.get(), false)) { + UNREACHABLE(); + } + + literal l(ctx.get_literal(eq.get())); + if (!is_eq) { + l = ~l; + } + + ctx.assign(l, b_justification(&eq_just), false); + } + } + + template + void theory_horn_ineq::inc_conflicts() { + m_stats.m_num_conflicts++; + if (m_params.m_arith_adaptive) { + double g = m_params.m_arith_adaptive_propagation_threshold; + m_agility = m_agility*g + 1 - g; + } + } + + template + void theory_horn_ineq::set_neg_cycle_conflict() { + m_graph->traverse_neg_cycle1(m_params.m_arith_stronger_lemmas); + inc_conflicts(); + literal_vector const& lits = m_graph->get_lits(); + context & ctx = get_context(); + TRACE("horn_ineq", + tout << "conflict: "; + for (unsigned i = 0; i < lits.size(); ++i) { + ctx.display_literal_info(tout, lits[i]); + } + tout << "\n"; + ); + + if (m_params.m_arith_dump_lemmas) { + char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"; + ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); + } + + vector params; + if (get_manager().proofs_enabled()) { + params.push_back(parameter(symbol("farkas"))); + vector const& coeffs = m_graph->get_coeffs(); + for (unsigned i = 0; i < coeffs.size(); ++i) { + params.push_back(parameter(coeffs[i])); + } + } + + ctx.set_conflict( + ctx.mk_justification( + ext_theory_conflict_justification( + get_id(), ctx.get_region(), + lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); + } + + template + void theory_horn_ineq::found_non_horn_ineq_expr(expr* n) { + if (!m_non_horn_ineq_exprs) { + TRACE("horn_ineq", tout << "found non horn logic expression:\n" << mk_pp(n, get_manager()) << "\n";); + get_context().push_trail(value_trail(m_non_horn_ineq_exprs)); + m_non_horn_ineq_exprs = true; + } + } + + template + void theory_horn_ineq::init(context* ctx) { + theory::init(ctx); + m_zero_int = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); + m_zero_real = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), false), false, false, true)); + m_graph->set_to_zero(m_zero_int); + m_graph->set_to_zero(m_zero_real); + } + + /** + \brief Create negated literal. + + The negation of: E <= 0 + + -E + epsilon <= 0 + or + -E + 1 <= 0 + */ + template + void theory_horn_ineq::negate(coeffs& coeffs, rational& weight) { + for (unsigned i = 0; i < coeffs.size(); ++i) { + coeffs[i].second.neg(); + } + weight.neg(); + } + + template + typename theory_horn_ineq::numeral theory_horn_ineq::mk_weight(bool is_real, bool is_strict, rational const& w) const { + if (is_strict) { + return numeral(inf_numeral(w)) + (is_real?m_epsilon:numeral(1)); + } + else { + return numeral(inf_numeral(w)); + } + } + + template + void theory_horn_ineq::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) { + coeffs.reset(); + w = m_test.get_weight(); + for (unsigned i = 0; i < terms.size(); ++i) { + coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second)); + } + } + + template + bool theory_horn_ineq::internalize_atom(app * n, bool) { + context & ctx = get_context(); + if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) { + found_non_horn_ineq_expr(n); + return false; + } + SASSERT(!ctx.b_internalized(n)); + expr* e1 = n->get_arg(0), *e2 = n->get_arg(1); + if (a.is_ge(n) || a.is_gt(n)) { + std::swap(e1, e2); + } + bool is_strict = a.is_gt(n) || a.is_lt(n); + + horn_ineq_tester::classify_t cl = m_test.linearize(e1, e2); + if (cl == horn_ineq_tester::non_horn) { + found_non_horn_ineq_expr(n); + return false; + } + + rational w; + coeffs coeffs; + mk_coeffs(m_test.get_linearization(), coeffs, w); + bool_var bv = ctx.mk_bool_var(n); + ctx.set_var_theory(bv, get_id()); + literal l(bv); + numeral w1 = mk_weight(a.is_real(e1), is_strict, w); + clause_id pos = m_graph->add_ineq(coeffs, w1, l); + negate(coeffs, w); + numeral w2 = mk_weight(a.is_real(e1), !is_strict, w); + clause_id neg = m_graph->add_ineq(coeffs, w2, ~l); + m_bool_var2atom.insert(bv, m_atoms.size()); + m_atoms.push_back(atom(bv, pos, neg)); + + TRACE("horn_ineq", + tout << mk_pp(n, get_manager()) << "\n"; + m_graph->display_clause(tout << "pos: ", pos); + m_graph->display_clause(tout << "neg: ", neg); + ); + + return true; + } + + template + bool theory_horn_ineq::internalize_term(app * term) { + bool result = null_theory_var != mk_term(term); + CTRACE("horn_ineq", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); + TRACE("horn_ineq", tout << "Terms may not be internalized " << mk_pp(term, get_manager()) << "\n";); + found_non_horn_ineq_expr(term); + return result; + } + + template + void theory_horn_ineq::internalize_eq_eh(app * atom, bool_var) { + // noop + } + + template + void theory_horn_ineq::assign_eh(bool_var v, bool is_true) { + m_stats.m_num_assertions++; + unsigned idx = m_bool_var2atom.find(v); + SASSERT(get_context().get_assignment(v) != l_undef); + SASSERT((get_context().get_assignment(v) == l_true) == is_true); + m_atoms[idx].assign_eh(is_true); + m_asserted_atoms.push_back(idx); + } + + template + void theory_horn_ineq::push_scope_eh() { + theory::push_scope_eh(); + m_graph->push(); + m_scopes.push_back(scope()); + scope & s = m_scopes.back(); + s.m_atoms_lim = m_atoms.size(); + s.m_asserted_atoms_lim = m_asserted_atoms.size(); + s.m_asserted_qhead_old = m_asserted_qhead; + } + + template + void theory_horn_ineq::pop_scope_eh(unsigned num_scopes) { + unsigned lvl = m_scopes.size(); + SASSERT(num_scopes <= lvl); + unsigned new_lvl = lvl - num_scopes; + scope & s = m_scopes[new_lvl]; + del_atoms(s.m_atoms_lim); + m_asserted_atoms.shrink(s.m_asserted_atoms_lim); + m_asserted_qhead = s.m_asserted_qhead_old; + m_scopes.shrink(new_lvl); + m_graph->pop(num_scopes); + theory::pop_scope_eh(num_scopes); + } + + template + final_check_status theory_horn_ineq::final_check_eh() { + SASSERT(is_consistent()); + TRACE("horn_ineq", display(tout);); + if (can_propagate()) { + propagate_core(); + return FC_CONTINUE; + } + else if (m_non_horn_ineq_exprs) { + return FC_GIVEUP; + } + else { + return FC_DONE; + } + } + + template + void theory_horn_ineq::propagate() { + propagate_core(); + } + + template + void theory_horn_ineq::display(std::ostream& out) const { + for (unsigned i = 0; i < m_atoms.size(); ++i) { + m_atoms[i].display(*this, out); + } + out << "\n"; + m_graph->display(out); + } + + template + void theory_horn_ineq::collect_statistics(::statistics& st) const { + st.update("hi conflicts", m_stats.m_num_conflicts); +// st.update("hi propagations", m_stats.m_num_th2core_prop); +// st.update("hi asserts", m_stats.m_num_assertions); +// st.update("core->hi eqs", m_stats.m_num_core2th_eqs); + m_arith_eq_adapter.collect_statistics(st); + m_graph->collect_statistics(st); + } + + template + void theory_horn_ineq::del_atoms(unsigned old_size) { + typename atoms::iterator begin = m_atoms.begin() + old_size; + typename atoms::iterator it = m_atoms.end(); + while (it != begin) { + --it; + m_bool_var2atom.erase(it->get_bool_var()); + } + m_atoms.shrink(old_size); + } + + template + void theory_horn_ineq::propagate_core() { + bool consistent = true; + while (consistent && can_propagate()) { + unsigned idx = m_asserted_atoms[m_asserted_qhead]; + m_asserted_qhead++; + consistent = propagate_atom(m_atoms[idx]); + } + } + + template + bool theory_horn_ineq::propagate_atom(atom const& a) { + context& ctx = get_context(); + TRACE("horn_ineq", a.display(*this, tout); tout << "\n";); + if (ctx.inconsistent()) { + return false; + } + int clause_id = a.get_asserted_edge(); + if (!m_graph->enable_clause(clause_id)) { + set_neg_cycle_conflict(); + return false; + } + return true; + } + + template + theory_var theory_horn_ineq::mk_term(app* n) { + context& ctx = get_context(); + + horn_ineq_tester::classify_t cl = m_test.linearize(n); + if (cl == horn_ineq_tester::non_horn) { + found_non_horn_ineq_expr(n); + return null_theory_var; + } + + coeffs coeffs; + rational w; + mk_coeffs(m_test.get_linearization(), coeffs, w); + if (coeffs.empty()) { + return mk_num(n, w); + } + if (coeffs.size() == 1 && coeffs[0].second.is_one()) { + return coeffs[0].first; + } + th_var target = mk_var(ctx.mk_enode(n, false, false, true)); + coeffs.push_back(std::make_pair(target, rational(-1))); + + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); + negate(coeffs, w); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); + return target; + } + + template + theory_var theory_horn_ineq::mk_num(app* n, rational const& r) { + theory_var v = null_theory_var; + context& ctx = get_context(); + if (r.is_zero()) { + v = a.is_int(n)?m_zero_int:m_zero_real; + } + else if (ctx.e_internalized(n)) { + enode* e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + } + else { + v = mk_var(ctx.mk_enode(n, false, false, true)); + // v = k: v <= k k <= v + coeffs coeffs; + coeffs.push_back(std::make_pair(v, rational(1))); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(r)), null_literal))); + coeffs.back().second.neg(); + VERIFY (m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(-r)), null_literal))); + } + return v; + } + + template + theory_var theory_horn_ineq::expand(bool pos, th_var v, rational & k) { + context& ctx = get_context(); + enode* e = get_enode(v); + expr* x, *y; + rational r; + for (;;) { + app* n = e->get_owner(); + if (a.is_add(n, x, y)) { + if (a.is_numeral(x, r)) { + e = ctx.get_enode(y); + } + else if (a.is_numeral(y, r)) { + e = ctx.get_enode(x); + } + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + if (v == null_theory_var) { + break; + } + if (pos) { + k += r; + } + else { + k -= r; + } + } + else { + break; + } + } + return v; + } + + template + bool theory_horn_ineq::is_consistent() const { + return m_graph->is_feasible(); + } + + // models: + template + void theory_horn_ineq::init_model(model_generator & m) { + m_factory = alloc(arith_factory, get_manager()); + m.register_factory(m_factory); + compute_delta(); + } + + template + model_value_proc * theory_horn_ineq::mk_value(enode * n, model_generator & mg) { + theory_var v = n->get_th_var(get_id()); + SASSERT(v != null_theory_var); + numeral val = m_graph->get_assignment(v); + rational num = val.get_infinity()*m_lambda + val.get_rational() + val.get_infinitesimal()*m_delta; + TRACE("horn_ineq", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); + return alloc(expr_wrapper_proc, m_factory->mk_value(num, a.is_int(n->get_owner()))); + } + + /** + \brief Compute numeral values for the infinitesimals to satisfy the inequalities. + */ + + template + void theory_horn_ineq::compute_delta() { + m_delta = rational(1); + m_lambda = rational(0); + unsigned sz = m_graph->get_num_clauses(); + + for (unsigned i = 0; i < sz; ++i) { + if (!m_graph->get_clause(i).is_enabled()) { + continue; + } + numeral b = m_graph->eval_body(i); + numeral h = m_graph->eval_head(i); + + if (b.get_infinity() < h.get_infinity()) { + continue; + } + SASSERT(b.get_infinity() == h.get_infinity()); + + // b <= h + // suppose that h.eps < b.eps + // then we have h.num > b.num + // but also h.num + delta*h.eps >= b.num + delta*b.eps + // <=> + // (h.num - b.num)/(b.eps - h.eps) >= delta + rational num_r = h.get_rational() - b.get_rational(); + rational eps_r = b.get_infinitesimal() - h.get_infinitesimal(); + if (eps_r.is_pos()) { + SASSERT(num_r.is_pos()); + rational new_delta = num_r/eps_r; + if (new_delta < m_delta) { + m_delta = new_delta; + } + } + } + + for (unsigned i = 0; i < sz; ++i) { + if (!m_graph->get_clause(i).is_enabled()) { + continue; + } + numeral b = m_graph->eval_body(i); + numeral h = m_graph->eval_head(i); + + rational ir = h.get_infinity() - b.get_infinity(); + rational hr = b.get_rational() - h.get_rational(); + rational num_r = hr + m_delta*(b.get_infinitesimal() - h.get_infinitesimal()); + + SASSERT(b.get_infinity() <= h.get_infinity()); + + // b <= h + // suppose that h.finite < b.finite + // then we have h.infinite > b.infinite + // but also + // h.infinite*lambda + h.finite >= b.infinite*lambda + b.finite + // <=> + // lambda >= (b.finite - h.finite) / (h.infinite - b.infinite) + if (num_r.is_pos()) { + SASSERT(ir.is_pos()); + rational new_lambda = num_r/ir; + if (new_lambda > m_lambda) { + m_lambda = new_lambda; + } + } + } + } + + + +}; + +#endif diff --git a/src/smt/theory_utvpi.cpp b/src/smt/theory_utvpi.cpp new file mode 100644 index 000000000..e811257a6 --- /dev/null +++ b/src/smt/theory_utvpi.cpp @@ -0,0 +1,159 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_utvpi.h + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ +#include "theory_utvpi.h" +#include "theory_utvpi_def.h" + +namespace smt { + + template class theory_utvpi; + template class theory_utvpi; + + // similar to test_diff_logic: + + utvpi_tester::utvpi_tester(ast_manager& m): m(m), a(m) {} + + bool utvpi_tester::operator()(expr* e) { + m_todo.reset(); + m_mark.reset(); + m_todo.push_back(e); + expr* e1, *e2; + + while (!m_todo.empty()) { + expr* e = m_todo.back(); + m_todo.pop_back(); + if (!m_mark.is_marked(e)) { + m_mark.mark(e, true); + if (is_var(e)) { + continue; + } + if (!is_app(e)) { + return false; + } + app* ap = to_app(e); + if (m.is_eq(ap, e1, e2)) { + if (!linearize(e1, e2)) { + return false; + } + } + else if (ap->get_family_id() == m.get_basic_family_id()) { + continue; + } + else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) || + a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { + if (!linearize(e1, e2)) { + return false; + } + } + else if (is_uninterp_const(e)) { + continue; + } + else { + return false; + } + } + } + return true; + } + + vector > const& utvpi_tester::get_linearization() const { + return m_terms; + } + + bool utvpi_tester::operator()(unsigned num_fmls, expr* const* fmls) { + for (unsigned i = 0; i < num_fmls; ++i) { + if (!(*this)(fmls[i])) { + return false; + } + } + return true; + } + + bool utvpi_tester::linearize(expr* e) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e, rational(1))); + return linearize(); + } + + bool utvpi_tester::linearize(expr* e1, expr* e2) { + m_terms.reset(); + m_terms.push_back(std::make_pair(e1, rational(1))); + m_terms.push_back(std::make_pair(e2, rational(-1))); + return linearize(); + } + + bool utvpi_tester::linearize() { + + m_weight.reset(); + m_coeff_map.reset(); + + while (!m_terms.empty()) { + expr* e1, *e2; + rational num; + rational mul = m_terms.back().second; + expr* e = m_terms.back().first; + m_terms.pop_back(); + if (a.is_add(e)) { + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul)); + } + } + else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) { + m_terms.push_back(std::make_pair(e2, mul*num)); + } + else if (a.is_sub(e, e1, e2)) { + m_terms.push_back(std::make_pair(e1, mul)); + m_terms.push_back(std::make_pair(e2, -mul)); + } + else if (a.is_uminus(e, e1)) { + m_terms.push_back(std::make_pair(e1, -mul)); + } + else if (a.is_numeral(e, num)) { + m_weight += num*mul; + } + else if (a.is_to_real(e, e1)) { + m_terms.push_back(std::make_pair(e1, mul)); + } + else if (!is_uninterp_const(e)) { + return false; + } + else { + m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul; + } + } + obj_map::iterator it = m_coeff_map.begin(); + obj_map::iterator end = m_coeff_map.end(); + for (; it != end; ++it) { + rational r = it->m_value; + if (r.is_zero()) { + continue; + } + m_terms.push_back(std::make_pair(it->m_key, r)); + if (m_terms.size() > 2) { + return false; + } + if (!r.is_one() && !r.is_minus_one()) { + return false; + } + } + return true; + } + + +} diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h new file mode 100644 index 000000000..044fa11a0 --- /dev/null +++ b/src/smt/theory_utvpi.h @@ -0,0 +1,331 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_utvpi.h + +Abstract: + + use Bellman Ford traversal algorithm for UTVPI. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + + The implementaton is derived from theory_diff_logic. + +--*/ + +#ifndef _THEORY_UTVPI_H_ +#define _THEORY_UTVPI_H_ + +#include"theory_diff_logic.h" + +namespace smt { + + class utvpi_tester { + ast_manager& m; + arith_util a; + ptr_vector m_todo; + ast_mark m_mark; + obj_map m_coeff_map; + rational m_weight; + vector > m_terms; + + public: + utvpi_tester(ast_manager& m); + + // test if formula is in the Horn inequality fragment: + bool operator()(expr* fml); + bool operator()(unsigned num_fmls, expr* const* fmls); + + // linearize inequality/equality + bool linearize(expr* e); + bool linearize(expr* e1, expr* e2); + + // retrieve linearization + vector > const& get_linearization() const; + rational const& get_weight() const { return m_weight; } + private: + bool linearize(); + }; + + template + class theory_utvpi : public theory, private Ext { + + typedef typename Ext::numeral numeral; + typedef theory_var th_var; + typedef svector th_var_vector; + typedef vector > coeffs; + + class assignment_trail; + class parent_trail; + + struct GExt : public Ext { + typedef literal explanation; + }; + + class atom { + protected: + bool_var m_bvar; + bool m_true; + int m_pos; + int m_neg; + public: + atom(bool_var bv, int pos, int neg) : + m_bvar(bv), m_true(false), + m_pos(pos), m_neg(neg) {} + virtual ~atom() {} + bool_var get_bool_var() const { return m_bvar; } + void assign_eh(bool is_true) { m_true = is_true; } + int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } + int get_pos() const { return m_pos; } + int get_neg() const { return m_neg; } + std::ostream& display(theory_utvpi const& th, std::ostream& out) const; + }; + typedef svector atoms; + + struct scope { + unsigned m_atoms_lim; + unsigned m_asserted_atoms_lim; + unsigned m_asserted_qhead_old; + }; + + struct stats { + unsigned m_num_conflicts; + unsigned m_num_assertions; + unsigned m_num_core2th_eqs; + unsigned m_num_core2th_diseqs; + + void reset() { + memset(this, 0, sizeof(*this)); + } + + stats() { + reset(); + } + }; + + // Functor used to collect the proofs for a conflict due to + // a negative cycle. + class nc_functor { + literal_vector m_antecedents; + theory_utvpi& m_super; + public: + nc_functor(theory_utvpi& s) : m_super(s) {} + void reset() { m_antecedents.reset(); } + literal_vector const& get_lits() const { return m_antecedents; } + + void operator()(literal const & ex) { + if (ex != null_literal) { + m_antecedents.push_back(ex); + } + } + + void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { + m_super.new_edge(src, dst, num_edges, edges); + } + }; + + + stats m_stats; + smt_params m_params; + arith_util a; + arith_eq_adapter m_arith_eq_adapter; + th_var m_zero_int; // cache the variable representing the zero variable. + th_var m_zero_real; // cache the variable representing the zero variable. + + dl_graph m_graph; + nc_functor m_nc_functor; + atoms m_atoms; + unsigned_vector m_asserted_atoms; // set of asserted atoms + unsigned m_asserted_qhead; + u_map m_bool_var2atom; + svector m_scopes; + + double m_agility; + bool m_lia; + bool m_lra; + bool m_non_utvpi_exprs; + + utvpi_tester m_test; + + + arith_factory * m_factory; + rational m_delta; + + + // Set a conflict due to a negative cycle. + void set_conflict(); + + void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {} + + // Create a new theory variable. + virtual th_var mk_var(enode* n); + + virtual th_var mk_var(expr* n); + + void compute_delta(); + + void found_non_utvpi_expr(expr * n); + + bool is_interpreted(app* n) const { + return n->get_family_id() == get_family_id(); + } + + public: + theory_utvpi(ast_manager& m); + + virtual ~theory_utvpi(); + + virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_utvpi, get_manager()); } + + virtual char const * get_name() const { return "utvpi-logic"; } + + /** + \brief See comment in theory::mk_eq_atom + */ + virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); } + + virtual void init(context * ctx); + + virtual bool internalize_atom(app * atom, bool gate_ctx); + + virtual bool internalize_term(app * term); + + virtual void internalize_eq_eh(app * atom, bool_var v) {} + + virtual void assign_eh(bool_var v, bool is_true); + + virtual void new_eq_eh(th_var v1, th_var v2) { + m_arith_eq_adapter.new_eq_eh(v1, v2); + } + + virtual bool use_diseqs() const { return true; } + + virtual void new_diseq_eh(th_var v1, th_var v2) { + m_arith_eq_adapter.new_diseq_eh(v1, v2); + } + + virtual void push_scope_eh(); + + virtual void pop_scope_eh(unsigned num_scopes); + + virtual void restart_eh() { + m_arith_eq_adapter.restart_eh(); + } + + virtual void relevant_eh(app* e) {} + + virtual void init_search_eh() { + m_arith_eq_adapter.init_search_eh(); + } + + virtual final_check_status final_check_eh(); + + virtual bool is_shared(th_var v) const { + return false; + } + + virtual bool can_propagate() { + SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); + return m_asserted_qhead != m_asserted_atoms.size(); + } + + virtual void propagate(); + + virtual justification * why_is_diseq(th_var v1, th_var v2) { + UNREACHABLE(); + return 0; + } + + virtual void reset_eh(); + + virtual void init_model(model_generator & m); + + virtual model_value_proc * mk_value(enode * n, model_generator & mg); + + virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const { + return true; + } + + virtual void display(std::ostream & out) const; + + virtual void collect_statistics(::statistics & st) const; + + private: + + bool check_z_consistency(); + + virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_eqs++; + new_eq_or_diseq(true, v1, v2, j); + } + + virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { + m_stats.m_num_core2th_diseqs++; + new_eq_or_diseq(false, v1, v2, j); + } + + void negate(coeffs& coeffs, rational& weight); + numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; + void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); + + void del_atoms(unsigned old_size); + + bool propagate_atom(atom const& a); + + th_var mk_term(app* n); + + th_var mk_num(app* n, rational const& r); + + bool is_consistent() const; + + th_var expand(bool pos, th_var v, rational & k); + + void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); + + th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; } + + th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } + + void inc_conflicts(); + + edge_id add_ineq(vector > const& terms, numeral const& weight, literal l); + + bool enable_edge(edge_id id); + + th_var to_var(th_var v) const { + return 2*v; + } + + th_var from_var(th_var v) const { + return v/2; + } + + th_var pos(th_var v) const { + return v & 0xFFFFFFFE; + } + + th_var neg(th_var v) const { + return v | 0x1; + } + + th_var not(th_var v) const { + return v ^ 0x1; + } + + }; + + + typedef theory_utvpi theory_rutvpi; + typedef theory_utvpi theory_iutvpi; +}; + + + + +#endif /* _THEORY_UTVPI_H_ */ diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h new file mode 100644 index 000000000..c369d6826 --- /dev/null +++ b/src/smt/theory_utvpi_def.h @@ -0,0 +1,694 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_utvpi_def.h + +Abstract: + + Implementation of UTVPI solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-26 + +Revision History: + + 1. introduce x^+ and x^-, such that 2*x := x^+ - x^- + 2. rewrite constraints as follows: + + x - y <= k => x^+ - y^+ <= k + y^- - x^- <= k + + x <= k => x^+ - x^- <= 2k + + + x + y <= k => x^+ - y^- <= k + y^+ - x^- <= k + + + - x - y <= k => x^- - y^+ <= k + y^- - x^+ <= k + + 3. Solve for x^+ and x^- + 4. Check parity condition for integers (see Lahiri and Musuvathi 05) + 5. extract model for M(x) := (M(x^+)- M(x^-))/2 + +--*/ + +#ifndef _THEORY_UTVPI_DEF_H_ +#define _THEORY_UTVPI_DEF_H_ +#include "theory_utvpi.h" +#include "heap.h" +#include "ast_pp.h" +#include "smt_context.h" + +namespace smt { + + + template + theory_utvpi::theory_utvpi(ast_manager& m): + theory(m.mk_family_id("arith")), + a(m), + m_arith_eq_adapter(*this, m_params, a), + m_zero_int(null_theory_var), + m_zero_real(null_theory_var), + m_asserted_qhead(0), + m_nc_functor(*this), + m_agility(0.5), + m_lia(false), + m_lra(false), + m_non_utvpi_exprs(false), + m_test(m), + m_factory(0) { + } + + template + theory_utvpi::~theory_utvpi() { + reset_eh(); + } + + template + std::ostream& theory_utvpi::atom::display(theory_utvpi const& th, std::ostream& out) const { + context& ctx = th.get_context(); + lbool asgn = ctx.get_assignment(m_bvar); + bool sign = (l_undef == l_false); + return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; + if (l_undef == asgn) { + out << "unassigned\n"; + } + else { + th.m_graph.display_edge(out, get_asserted_edge()); + } + return out; + } + + template + theory_var theory_utvpi::mk_var(enode* n) { + th_var v = theory::mk_var(n); + TRACE("utvpi", tout << v << " " << mk_pp(n->get_owner(), get_manager()) << "\n";); + m_graph.init_var(to_var(v)); + m_graph.init_var(neg(to_var(v))); + get_context().attach_th_var(n, this, v); + return v; + } + + template + theory_var theory_utvpi::mk_var(expr* n) { + context & ctx = get_context(); + enode* e = 0; + th_var v = null_theory_var; + m_lia |= a.is_int(n); + m_lra |= a.is_real(n); + if (!is_app(n)) { + return v; + } + if (ctx.e_internalized(n)) { + e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + } + else { + ctx.internalize(n, false); + e = ctx.get_enode(n); + } + if (v == null_theory_var) { + v = mk_var(e); + } + if (is_interpreted(to_app(n))) { + found_non_utvpi_expr(n); + } + return v; + } + + template + void theory_utvpi::reset_eh() { + m_graph .reset(); + m_zero_int = null_theory_var; + m_zero_real = null_theory_var; + m_atoms .reset(); + m_asserted_atoms .reset(); + m_stats .reset(); + m_scopes .reset(); + m_asserted_qhead = 0; + m_agility = 0.5; + m_lia = false; + m_lra = false; + m_non_utvpi_exprs = false; + theory::reset_eh(); + } + + template + void theory_utvpi::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) { + rational k; + th_var s = expand(true, v1, k); + th_var t = expand(false, v2, k); + context& ctx = get_context(); + ast_manager& m = get_manager(); + + if (s == t) { + if (is_eq != k.is_zero()) { + // conflict 0 /= k; + inc_conflicts(); + ctx.set_conflict(&eq_just); + } + } + else { + // + // Create equality ast, internalize_atom + // assign the corresponding equality literal. + // + + app_ref eq(m), s2(m), t2(m); + app* s1 = get_enode(s)->get_owner(); + app* t1 = get_enode(t)->get_owner(); + s2 = a.mk_sub(t1, s1); + t2 = a.mk_numeral(k, m.get_sort(s2.get())); + // t1 - s1 = k + eq = m.mk_eq(s2.get(), t2.get()); + + TRACE("utvpi", + tout << v1 << " .. " << v2 << "\n"; + tout << mk_pp(eq.get(), m) <<"\n";); + + if (!internalize_atom(eq.get(), false)) { + UNREACHABLE(); + } + + literal l(ctx.get_literal(eq.get())); + if (!is_eq) { + l = ~l; + } + ctx.assign(l, b_justification(&eq_just), false); + } + } + + template + void theory_utvpi::inc_conflicts() { + m_stats.m_num_conflicts++; + if (m_params.m_arith_adaptive) { + double g = m_params.m_arith_adaptive_propagation_threshold; + m_agility = m_agility*g + 1 - g; + } + } + + template + void theory_utvpi::set_conflict() { + inc_conflicts(); + literal_vector const& lits = m_nc_functor.get_lits(); + context & ctx = get_context(); + TRACE("utvpi", + tout << "conflict: "; + for (unsigned i = 0; i < lits.size(); ++i) { + ctx.display_literal_info(tout, lits[i]); + } + tout << "\n"; + ); + + if (m_params.m_arith_dump_lemmas) { + char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"; + ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); + } + + vector params; + if (get_manager().proofs_enabled()) { + params.push_back(parameter(symbol("farkas"))); + params.resize(lits.size()+1, parameter(rational(1))); + } + + ctx.set_conflict( + ctx.mk_justification( + ext_theory_conflict_justification( + get_id(), ctx.get_region(), + lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); + + m_nc_functor.reset(); + } + + template + void theory_utvpi::found_non_utvpi_expr(expr* n) { + if (!m_non_utvpi_exprs) { + TRACE("utvpi", tout << "found non horn logic expression:\n" << mk_pp(n, get_manager()) << "\n";); + get_context().push_trail(value_trail(m_non_utvpi_exprs)); + m_non_utvpi_exprs = true; + } + } + + template + void theory_utvpi::init(context* ctx) { + theory::init(ctx); + m_zero_int = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); + m_zero_real = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), false), false, false, true)); + } + + /** + \brief Create negated literal. + + The negation of: E <= 0 + + -E + epsilon <= 0 + or + -E + 1 <= 0 + */ + template + void theory_utvpi::negate(coeffs& coeffs, rational& weight) { + for (unsigned i = 0; i < coeffs.size(); ++i) { + coeffs[i].second.neg(); + } + weight.neg(); + } + + template + typename theory_utvpi::numeral theory_utvpi::mk_weight(bool is_real, bool is_strict, rational const& w) const { + if (is_strict) { + return numeral(w) + (is_real?m_epsilon:numeral(1)); + } + else { + return numeral(w); + } + } + + template + void theory_utvpi::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) { + coeffs.reset(); + w = m_test.get_weight(); + for (unsigned i = 0; i < terms.size(); ++i) { + coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second)); + } + } + + template + bool theory_utvpi::internalize_atom(app * n, bool) { + context & ctx = get_context(); + if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) { + found_non_utvpi_expr(n); + return false; + } + SASSERT(!ctx.b_internalized(n)); + expr* e1 = n->get_arg(0), *e2 = n->get_arg(1); + if (a.is_ge(n) || a.is_gt(n)) { + std::swap(e1, e2); + } + bool is_strict = a.is_gt(n) || a.is_lt(n); + + bool cl = m_test.linearize(e1, e2); + if (!cl) { + found_non_utvpi_expr(n); + return false; + } + + rational w; + coeffs coeffs; + mk_coeffs(m_test.get_linearization(), coeffs, w); + bool_var bv = ctx.mk_bool_var(n); + ctx.set_var_theory(bv, get_id()); + literal l(bv); + numeral w1 = mk_weight(a.is_real(e1), is_strict, w); + edge_id pos = add_ineq(coeffs, w1, l); + negate(coeffs, w); + numeral w2 = mk_weight(a.is_real(e1), !is_strict, w); + edge_id neg = add_ineq(coeffs, w2, ~l); + m_bool_var2atom.insert(bv, m_atoms.size()); + m_atoms.push_back(atom(bv, pos, neg)); + + TRACE("utvpi", + tout << mk_pp(n, get_manager()) << "\n"; + m_graph.display_edge(tout << "pos: ", pos); + m_graph.display_edge(tout << "neg: ", neg); + ); + + return true; + } + + template + bool theory_utvpi::internalize_term(app * term) { + bool result = null_theory_var != mk_term(term); + CTRACE("utvpi", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); + TRACE("utvpi", tout << "Terms may not be internalized " << mk_pp(term, get_manager()) << "\n";); + found_non_utvpi_expr(term); + return result; + } + + template + void theory_utvpi::assign_eh(bool_var v, bool is_true) { + m_stats.m_num_assertions++; + unsigned idx = m_bool_var2atom.find(v); + SASSERT(get_context().get_assignment(v) != l_undef); + SASSERT((get_context().get_assignment(v) == l_true) == is_true); + m_atoms[idx].assign_eh(is_true); + m_asserted_atoms.push_back(idx); + } + + template + void theory_utvpi::push_scope_eh() { + theory::push_scope_eh(); + m_graph.push(); + m_scopes.push_back(scope()); + scope & s = m_scopes.back(); + s.m_atoms_lim = m_atoms.size(); + s.m_asserted_atoms_lim = m_asserted_atoms.size(); + s.m_asserted_qhead_old = m_asserted_qhead; + } + + template + void theory_utvpi::pop_scope_eh(unsigned num_scopes) { + unsigned lvl = m_scopes.size(); + SASSERT(num_scopes <= lvl); + unsigned new_lvl = lvl - num_scopes; + scope & s = m_scopes[new_lvl]; + del_atoms(s.m_atoms_lim); + m_asserted_atoms.shrink(s.m_asserted_atoms_lim); + m_asserted_qhead = s.m_asserted_qhead_old; + m_scopes.shrink(new_lvl); + m_graph.pop(num_scopes); + theory::pop_scope_eh(num_scopes); + } + + template + final_check_status theory_utvpi::final_check_eh() { + SASSERT(is_consistent()); + TRACE("utvpi", display(tout);); + if (can_propagate()) { + propagate(); + return FC_CONTINUE; + } + else if (!check_z_consistency()) { + return FC_CONTINUE; + } + else if (m_non_utvpi_exprs) { + return FC_GIVEUP; + } + else { + m_graph.set_to_zero(to_var(m_zero_int), to_var(m_zero_real)); + m_graph.set_to_zero(neg(to_var(m_zero_int)), neg(to_var(m_zero_real))); + m_graph.set_to_zero(to_var(m_zero_int), neg(to_var(m_zero_int))); + return FC_DONE; + } + } + + template + bool theory_utvpi::check_z_consistency() { + int_vector scc_id; + m_graph.compute_zero_edge_scc(scc_id); + + unsigned sz = get_num_vars(); + for (unsigned i = 0; i < sz; ++i) { + enode* e = get_enode(i); + if (a.is_int(e->get_owner())) { + continue; + } + th_var v1 = to_var(i); + th_var v2 = neg(v1); + rational r1 = m_graph.get_assignment(v1).get_rational(); + rational r2 = m_graph.get_assignment(v2).get_rational(); + SASSERT(r1.is_int()); + SASSERT(r2.is_int()); + if (r1.is_even() == r2.is_even()) { + continue; + } + if (scc_id[v1] != scc_id[v2]) { + continue; + } + if (scc_id[v1] == -1) { + continue; + } + // they are in the same SCC and have different parities => contradiction. + m_nc_functor.reset(); + VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, UINT_MAX, m_nc_functor)); + VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, UINT_MAX, m_nc_functor)); + set_conflict(); + + return false; + } + return true; + } + + template + void theory_utvpi::display(std::ostream& out) const { + for (unsigned i = 0; i < m_atoms.size(); ++i) { + m_atoms[i].display(*this, out); out << "\n"; + } + m_graph.display(out); + } + + template + void theory_utvpi::collect_statistics(::statistics& st) const { + st.update("utvpi conflicts", m_stats.m_num_conflicts); + st.update("utvpi assignments", m_stats.m_num_assertions); + m_arith_eq_adapter.collect_statistics(st); + m_graph.collect_statistics(st); + } + + template + void theory_utvpi::del_atoms(unsigned old_size) { + typename atoms::iterator begin = m_atoms.begin() + old_size; + typename atoms::iterator it = m_atoms.end(); + while (it != begin) { + --it; + m_bool_var2atom.erase(it->get_bool_var()); + } + m_atoms.shrink(old_size); + } + + template + void theory_utvpi::propagate() { + bool consistent = true; + while (consistent && can_propagate()) { + unsigned idx = m_asserted_atoms[m_asserted_qhead]; + m_asserted_qhead++; + consistent = propagate_atom(m_atoms[idx]); + } + } + + template + bool theory_utvpi::propagate_atom(atom const& a) { + context& ctx = get_context(); + TRACE("utvpi", a.display(*this, tout); tout << "\n";); + if (ctx.inconsistent()) { + return false; + } + int edge_id = a.get_asserted_edge(); + if (!enable_edge(edge_id)) { + m_graph.traverse_neg_cycle2(m_params.m_arith_stronger_lemmas, m_nc_functor); + set_conflict(); + return false; + } + return true; + } + + template + theory_var theory_utvpi::mk_term(app* n) { + context& ctx = get_context(); + + bool cl = m_test.linearize(n); + if (!cl) { + found_non_utvpi_expr(n); + return null_theory_var; + } + + coeffs coeffs; + rational w; + mk_coeffs(m_test.get_linearization(), coeffs, w); + if (coeffs.empty()) { + return mk_num(n, w); + } + if (coeffs.size() == 1 && coeffs[0].second.is_one()) { + return coeffs[0].first; + } + th_var target = mk_var(ctx.mk_enode(n, false, false, true)); + coeffs.push_back(std::make_pair(target, rational(-1))); + + VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); + negate(coeffs, w); + VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); + return target; + } + + template + theory_var theory_utvpi::mk_num(app* n, rational const& r) { + theory_var v = null_theory_var; + context& ctx = get_context(); + if (r.is_zero()) { + v = a.is_int(n)?m_zero_int:m_zero_real; + } + else if (ctx.e_internalized(n)) { + enode* e = ctx.get_enode(n); + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + } + else { + v = mk_var(ctx.mk_enode(n, false, false, true)); + // v = k: v <= k k <= v + coeffs coeffs; + coeffs.push_back(std::make_pair(v, rational(1))); + VERIFY(enable_edge(add_ineq(coeffs, numeral(r), null_literal))); + coeffs.back().second.neg(); + VERIFY(enable_edge(add_ineq(coeffs, numeral(-r), null_literal))); + } + return v; + } + + template + theory_var theory_utvpi::expand(bool pos, th_var v, rational & k) { + context& ctx = get_context(); + enode* e = get_enode(v); + expr* x, *y; + rational r; + for (;;) { + app* n = e->get_owner(); + if (a.is_add(n, x, y)) { + if (a.is_numeral(x, r)) { + e = ctx.get_enode(y); + } + else if (a.is_numeral(y, r)) { + e = ctx.get_enode(x); + } + v = e->get_th_var(get_id()); + SASSERT(v != null_theory_var); + if (v == null_theory_var) { + break; + } + if (pos) { + k += r; + } + else { + k -= r; + } + } + else { + break; + } + } + return v; + } + + // m_graph(source, target, weight, ex); + // target - source <= weight + + template + edge_id theory_utvpi::add_ineq(vector > const& terms, numeral const& weight, literal l) { + + SASSERT(terms.size() <= 2); + SASSERT(terms.size() < 1 || terms[0].second.is_one() || terms[0].second.is_minus_one()); + SASSERT(terms.size() < 2 || terms[1].second.is_one() || terms[1].second.is_minus_one()); + + th_var v1 = null_theory_var, v2 = null_theory_var; + bool pos1 = true, pos2 = true; + if (terms.size() >= 1) { + v1 = terms[0].first; + pos1 = terms[0].second.is_one(); + SASSERT(v1 != null_theory_var); + SASSERT(pos1 || terms[0].second.is_minus_one()); + } + if (terms.size() >= 2) { + v2 = terms[1].first; + pos2 = terms[1].second.is_one(); + SASSERT(v1 != null_theory_var); + SASSERT(pos2 || terms[1].second.is_minus_one()); + } +// TRACE("utvpi", tout << (pos1?"$":"-$") << v1 << (pos2?" + $":" - $") << v2 << " + " << weight << " <= 0\n";); + edge_id id = m_graph.get_num_edges(); + th_var w1 = to_var(v1), w2 = to_var(v2); + if (terms.size() == 1 && pos1) { + m_graph.add_edge(neg(w1), pos(w1), -weight-weight, l); + m_graph.add_edge(neg(w1), pos(w1), -weight-weight, l); + } + else if (terms.size() == 1 && !pos1) { + m_graph.add_edge(pos(w1), neg(w1), -weight-weight, l); + m_graph.add_edge(pos(w1), neg(w1), -weight-weight, l); + } + else if (pos1 && pos2) { + m_graph.add_edge(neg(w2), pos(w1), -weight, l); + m_graph.add_edge(neg(w1), pos(w2), -weight, l); + } + else if (pos1 && !pos2) { + m_graph.add_edge(pos(w2), pos(w1), -weight, l); + m_graph.add_edge(neg(w1), neg(w2), -weight, l); + } + else if (!pos1 && pos2) { + m_graph.add_edge(neg(w2), neg(w1), -weight, l); + m_graph.add_edge(pos(w1), pos(w2), -weight, l); + } + else { + m_graph.add_edge(pos(w1), neg(w2), -weight, l); + m_graph.add_edge(pos(w2), neg(w1), -weight, l); + } + return id; + } + + template + bool theory_utvpi::enable_edge(edge_id id) { + return (id == null_edge_id) || (m_graph.enable_edge(id) && m_graph.enable_edge(id+1)); + } + + template + bool theory_utvpi::is_consistent() const { + return m_graph.is_feasible(); + } + + // models: + template + void theory_utvpi::init_model(model_generator & m) { + m_factory = alloc(arith_factory, get_manager()); + m.register_factory(m_factory); + // TBD: enforce strong or tight coherence? + compute_delta(); + } + + template + model_value_proc * theory_utvpi::mk_value(enode * n, model_generator & mg) { + theory_var v = n->get_th_var(get_id()); + SASSERT(v != null_theory_var); + numeral val1 = m_graph.get_assignment(to_var(v)); + numeral val2 = m_graph.get_assignment(neg(to_var(v))); + numeral val = val1 - val2; + rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational()); + num = num/rational(2); + TRACE("utvpi", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); + return alloc(expr_wrapper_proc, m_factory->mk_value(num, a.is_int(n->get_owner()))); + } + + /** + \brief Compute numeral values for the infinitesimals to satisfy the inequalities. + */ + + template + void theory_utvpi::compute_delta() { + m_delta = rational(1); + unsigned sz = m_graph.get_num_edges(); + + for (unsigned i = 0; i < sz; ++i) { + if (!m_graph.is_enabled(i)) { + continue; + } + numeral w = m_graph.get_weight(i); + numeral tgt = m_graph.get_assignment(m_graph.get_target(i)); + numeral src = m_graph.get_assignment(m_graph.get_source(i)); + numeral b = tgt - src - w; + SASSERT(b.is_nonpos()); + rational eps_r = b.get_infinitesimal(); + + // Given: b <= 0 + // suppose that 0 < b.eps + // then we have 0 > b.num + // then delta must ensure: + // 0 >= b.num + delta*b.eps + // <=> + // -b.num/b.eps >= delta + if (eps_r.is_pos()) { + rational num_r = -b.get_rational(); + SASSERT(num_r.is_pos()); + rational new_delta = num_r/eps_r; + if (new_delta < m_delta) { + m_delta = new_delta; + } + } + } + } + + + +}; + +#endif + diff --git a/src/util/inf_eps_rational.h b/src/util/inf_eps_rational.h new file mode 100644 index 000000000..659fdc400 --- /dev/null +++ b/src/util/inf_eps_rational.h @@ -0,0 +1,409 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + inf_eps_rational.h + +Abstract: + + Rational numbers with infinity and epsilon. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-4-23. + +Revision History: + +--*/ +#ifndef _INF_EPS_RATIONAL_H_ +#define _INF_EPS_RATIONAL_H_ +#include +#include +#include"debug.h" +#include"vector.h" +#include"rational.h" + +template +class inf_eps_rational { + rational m_infty; + Numeral m_r; + public: + + unsigned hash() const { + return m_infty.hash() ^ m_r.hash(); + } + + struct hash_proc { unsigned operator()(inf_eps_rational const& r) const { return r.hash(); } }; + + struct eq_proc { bool operator()(inf_eps_rational const& r1, inf_eps_rational const& r2) const { return r1 == r2; } }; + + void swap(inf_eps_rational & n) { + m_infty.swap(n.m_infty); + m_r.swap(n.m_r); + } + + std::string to_string() const { + if (m_infty.is_zero()) { + return m_r.to_string(); + } + std::string si; + if (m_infty.is_one()) { + si = "oo"; + } + else if (m_infty.is_minus_one()) { + si = "-oo"; + } + else { + si = m_infty.to_string() += "*oo"; + } + if (m_r.is_zero()) { + return si; + } + std::string s = "("; + s += si; + s += " + "; + s += m_r.to_string(); + s += ")"; + return s; + } + + inf_eps_rational(): + m_infty(), + m_r() + {} + + inf_eps_rational(const inf_eps_rational & r): + m_infty(r.m_infty), + m_r(r.m_r) + {} + + explicit inf_eps_rational(int n): + m_infty(), + m_r(n) + {} + + explicit inf_eps_rational(Numeral const& r): + m_infty(), + m_r(r) + {} + + explicit inf_eps_rational(rational const& i, Numeral const& r): + m_infty(i), + m_r(r) { + } + + ~inf_eps_rational() {} + + /** + \brief Set inf_eps_rational to 0. + */ + void reset() { + m_infty.reset(); + m_r.reset(); + } + + bool is_int() const { + return m_infty.is_zero() && m_r.is_int(); + } + + bool is_int64() const { + return m_infty.is_zero() && m_r.is_int64(); + } + + bool is_uint64() const { + return m_infty.is_zero() && m_r.is_uint64(); + } + + bool is_rational() const { return m_infty.is_zero() && m_r.is_rational(); } + + int64 get_int64() const { + SASSERT(is_int64()); + return m_r.get_int64(); + } + + uint64 get_uint64() const { + SASSERT(is_uint64()); + return m_r.get_uint64(); + } + + rational const& get_rational() const { + return m_r.get_rational(); + } + + rational const& get_infinitesimal() const { + return m_r.get_infinitesimal(); + } + + rational const& get_infinity() const { + return m_infty; + } + + inf_eps_rational & operator=(const inf_eps_rational & r) { + m_infty = r.m_infty; + m_r = r.m_r; + return *this; + } + + inf_eps_rational & operator=(const rational & r) { + m_infty.reset(); + m_r = r; + return *this; + } + + inf_eps_rational & operator+=(const inf_eps_rational & r) { + m_infty += r.m_infty; + m_r += r.m_r; + return *this; + } + + inf_eps_rational & operator-=(const inf_eps_rational & r) { + m_infty -= r.m_infty; + m_r -= r.m_r; + return *this; + } + + inf_eps_rational & operator+=(const rational & r) { + m_r += r; + return *this; + } + + inf_eps_rational & operator-=(const rational & r) { + m_r -= r; + return *this; + } + + inf_eps_rational & operator*=(const rational & r1) { + m_infty *= r1; + m_r *= r1; + return *this; + } + + inf_eps_rational & operator/=(const rational & r) { + m_infty /= r; + m_r /= r; + return *this; + } + + + inf_eps_rational & operator++() { + ++m_r; + return *this; + } + + const inf_eps_rational operator++(int) { inf_eps_rational tmp(*this); ++(*this); return tmp; } + + inf_eps_rational & operator--() { + --m_r; + return *this; + } + + const inf_eps_rational operator--(int) { inf_eps_rational tmp(*this); --(*this); return tmp; } + + friend inline bool operator==(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return r1.m_infty == r2.m_infty && r1.m_r == r2.m_r; + } + + friend inline bool operator==(const rational & r1, const inf_eps_rational & r2) { + return r1 == r2.m_infty && r2.m_r.is_zero(); + } + + friend inline bool operator==(const inf_eps_rational & r1, const rational & r2) { + return r1.m_infty == r2 && r1.m_r.is_zero(); + } + + friend inline bool operator<(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return + (r1.m_infty < r2.m_infty) || + (r1.m_infty == r2.m_infty && r1.m_r < r2.m_r); + } + + friend inline bool operator<(const rational & r1, const inf_eps_rational & r2) { + return + r2.m_infty.is_pos() || + (r2.m_infty.is_zero() && r1 < r2.m_r); + } + + friend inline bool operator<(const inf_eps_rational & r1, const rational & r2) { + return + r1.m_infty.is_neg() || + (r1.m_infty.is_zero() && r1.m_r < r2); + } + + void neg() { + m_infty.neg(); + m_r.neg(); + } + + bool is_zero() const { + return m_infty.is_zero() && m_r.is_zero(); + } + + bool is_one() const { + return m_infty.is_zero() && m_r.is_one(); + } + + bool is_minus_one() const { + return m_infty.is_zero() && m_r.is_minus_one(); + } + + bool is_neg() const { + return + m_infty.is_neg() || + (m_infty.is_zero() && m_r.is_neg()); + } + + bool is_pos() const { + return + m_infty.is_pos() || + (m_infty.is_zero() && m_r.is_pos()); + } + + bool is_nonneg() const { + return + m_infty.is_pos() || + (m_infty.is_zero() && m_r.is_nonneg()); + } + + bool is_nonpos() const { + return + m_infty.is_neg() || + (m_infty.is_zero() && m_r.is_nonpos()); + } + + friend inline rational floor(const inf_eps_rational & r) { + SASSERT(r.m_infty.is_zero()); + return floor(r.m_r); + } + + friend inline rational ceil(const inf_eps_rational & r) { + SASSERT(r.m_infty.is_zero()); + return ceil(r.m_r); + } + + + // Perform: this += c * k + void addmul(const rational & c, const inf_eps_rational & k) { + m_infty.addmul(c, k.m_infty); + m_r.addmul(c, k.m_r); + } + + // Perform: this += c * k + void submul(const rational & c, const inf_eps_rational & k) { + m_infty.submul(c, k.m_infty); + m_r.submul(c, k.m_r); + } +}; + +template +inline bool operator!=(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return !operator==(r1, r2); +} + +template +inline bool operator!=(const rational & r1, const inf_eps_rational & r2) { + return !operator==(r1, r2); +} + +template +inline bool operator!=(const inf_eps_rational & r1, const rational & r2) { + return !operator==(r1, r2); +} + +template +inline bool operator>(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return operator<(r2, r1); +} + +template +inline bool operator>(const inf_eps_rational & r1, const rational & r2) { + return operator<(r2, r1); +} + +template +inline bool operator>(const rational & r1, const inf_eps_rational & r2) { + return operator<(r2, r1); +} + +template +inline bool operator<=(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return !operator>(r1, r2); +} + +template +inline bool operator<=(const rational & r1, const inf_eps_rational & r2) { + return !operator>(r1, r2); +} + +template +inline bool operator<=(const inf_eps_rational & r1, const rational & r2) { + return !operator>(r1, r2); +} + +template +inline bool operator>=(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return !operator<(r1, r2); +} + +template +inline bool operator>=(const rational & r1, const inf_eps_rational & r2) { + return !operator<(r1, r2); +} + +template +inline bool operator>=(const inf_eps_rational & r1, const rational & r2) { + return !operator<(r1, r2); +} + +template +inline inf_eps_rational operator+(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return inf_eps_rational(r1) += r2; +} + +template +inline inf_eps_rational operator-(const inf_eps_rational & r1, const inf_eps_rational & r2) { + return inf_eps_rational(r1) -= r2; +} + +template +inline inf_eps_rational operator-(const inf_eps_rational & r) { + inf_eps_rational result(r); + result.neg(); + return result; +} + +template +inline inf_eps_rational operator*(const rational & r1, const inf_eps_rational & r2) { + inf_eps_rational result(r2); + result *= r1; + return result; +} + +template +inline inf_eps_rational operator*(const inf_eps_rational & r1, const rational & r2) { + return r2 * r1; +} + +template +inline inf_eps_rational operator/(const inf_eps_rational & r1, const rational & r2) { + inf_eps_rational result(r1); + result /= r2; + return result; +} + +template +inline std::ostream & operator<<(std::ostream & target, const inf_eps_rational & r) { + target << r.to_string(); + return target; +} + +template +inline inf_eps_rational abs(const inf_eps_rational & r) { + inf_eps_rational result(r); + if (result.is_neg()) { + result.neg(); + } + return result; +} + +#endif /* _INF_EPS_RATIONAL_H_ */ From 3ac7cbe1c5316dc8eaab62fbc3b338e390c5867a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 28 Apr 2013 12:51:33 -0700 Subject: [PATCH 40/91] fix build breaker Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_rule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index e43445396..1bbb1f342 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -202,7 +202,7 @@ namespace datalog { unsigned index = extract_horn(fml, m_body, m_head); hoist_compound_predicates(index, m_head, m_body); TRACE("dl_rule", - tout << mk_pp(head, m) << " :- "; + tout << mk_pp(m_head, m) << " :- "; for (unsigned i = 0; i < m_body.size(); ++i) { tout << mk_pp(m_body[i].get(), m) << " "; } From 4471d929f7c2b6b9409e37eec638cdb2b8afe98f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 28 Apr 2013 13:20:31 -0700 Subject: [PATCH 41/91] fix linking error in debug mode Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_rule.cpp | 7 ++++++- src/muz_qe/dl_rule.h | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index 1bbb1f342..db8189c64 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -72,6 +72,8 @@ namespace datalog { } } + rule_manager::remove_label_cfg::~remove_label_cfg() {} + br_status rule_manager::remove_label_cfg::reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { @@ -83,6 +85,9 @@ namespace datalog { return BR_FAILED; } + template class rewriter_tpl; + + void rule_manager::remove_labels(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); m_rwr(fml, tmp); @@ -1113,6 +1118,6 @@ namespace datalog { } - + }; diff --git a/src/muz_qe/dl_rule.h b/src/muz_qe/dl_rule.h index 6335c506f..77bf9ac74 100644 --- a/src/muz_qe/dl_rule.h +++ b/src/muz_qe/dl_rule.h @@ -53,8 +53,8 @@ namespace datalog { class remove_label_cfg : public default_rewriter_cfg { family_id m_label_fid; public: - remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {} - virtual ~remove_label_cfg() {} + remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {} + virtual ~remove_label_cfg(); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); From be64e4b238c097f20862502685b01e3f8a0caf88 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 28 Apr 2013 13:37:03 -0700 Subject: [PATCH 42/91] add special procedures for UTVPI and horn arithmetic Signed-off-by: Nikolaj Bjorner --- src/smt/theory_horn_ineq.h | 24 +++++------------------- src/smt/theory_horn_ineq_def.h | 6 ++---- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/smt/theory_horn_ineq.h b/src/smt/theory_horn_ineq.h index f5fb41263..441e46a18 100644 --- a/src/smt/theory_horn_ineq.h +++ b/src/smt/theory_horn_ineq.h @@ -6,30 +6,16 @@ Module Name: theory_horn_ineq.h Abstract: - - A*x <= b + D*x, coefficients to A and D are non-negative, + A*x <= weight + D*x, coefficients to A and D are non-negative, D is a diagonal matrix. - Coefficients to b may have both signs. - + Coefficients to weight may have both signs. - Ford-Fulkerson variant: Label variables by weight. Select inequality that is not satisfied. - Set gamma(LHS) := 0 - Set gamma(RHS(x)) := weight(x) - b - Propagate gamma through inequalities. - Gamma is the increment. - Maintain Heap of variables weighted by gamma. - When processing inequality, - then update gamma of variables by gamma := A(gamma + weight) - b - If some variable in the premise of the original rule gets - relabeled (assignment is increased), then the set of - inequalities is unsatisfiable. - - Propagation updates lower bounds on gamma by taking into - account integer inequalities. The greatest lower bound - is computable by taking integer floor/ceilings. + Set delta(LHS) := 0 + Set delta(RHS(x)) := weight(x) - b + Propagate weight increment through inequalities. Author: diff --git a/src/smt/theory_horn_ineq_def.h b/src/smt/theory_horn_ineq_def.h index b86d45819..1fc51a86c 100644 --- a/src/smt/theory_horn_ineq_def.h +++ b/src/smt/theory_horn_ineq_def.h @@ -937,10 +937,8 @@ namespace smt { template void theory_horn_ineq::collect_statistics(::statistics& st) const { - st.update("hi conflicts", m_stats.m_num_conflicts); -// st.update("hi propagations", m_stats.m_num_th2core_prop); -// st.update("hi asserts", m_stats.m_num_assertions); -// st.update("core->hi eqs", m_stats.m_num_core2th_eqs); + st.update("horn ineq conflicts", m_stats.m_num_conflicts); + st.update("horn ineq assertions", m_stats.m_num_assertions); m_arith_eq_adapter.collect_statistics(st); m_graph->collect_statistics(st); } From fbe4af633665204a49bdda5c79f187c9d2678ffc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 28 Apr 2013 13:39:26 -0700 Subject: [PATCH 43/91] add backward propagation transformation Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_backwards.cpp | 78 ++++++++++++++++++++++++++++++++++ src/muz_qe/dl_mk_backwards.h | 38 +++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/muz_qe/dl_mk_backwards.cpp create mode 100644 src/muz_qe/dl_mk_backwards.h diff --git a/src/muz_qe/dl_mk_backwards.cpp b/src/muz_qe/dl_mk_backwards.cpp new file mode 100644 index 000000000..b1d8b7d36 --- /dev/null +++ b/src/muz_qe/dl_mk_backwards.cpp @@ -0,0 +1,78 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_backwards.cpp + +Abstract: + + Create Horn clauses for backwards flow. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-17 + +Revision History: + +--*/ + +#include"dl_mk_backwards.h" +#include"dl_context.h" + +namespace datalog { + + mk_backwards::mk_backwards(context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx) { + } + + mk_backwards::~mk_backwards() { } + + rule_set * mk_backwards::operator()(rule_set const & source) { + context& ctx = source.get_context(); + rule_manager& rm = source.get_rule_manager(); + rule_set * result = alloc(rule_set, ctx); + unsigned sz = source.get_num_rules(); + rule_ref new_rule(rm); + app_ref_vector tail(m); + app_ref head(m); + svector neg; + app_ref query(m); + query = m.mk_fresh_const("Q", m.mk_bool_sort()); + result->set_output_predicate(query->get_decl()); + m_ctx.register_predicate(query->get_decl(), false); + for (unsigned i = 0; i < sz; ++i) { + tail.reset(); + neg.reset(); + rule & r = *source.get_rule(i); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + if (!source.is_output_predicate(r.get_decl())) { + tail.push_back(r.get_head()); + neg.push_back(false); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + neg.push_back(false); + } + for (unsigned j = 0; j <= utsz; ++j) { + if (j == utsz && j > 0) { + break; + } + if (j == utsz) { + head = query; + } + else { + head = r.get_tail(j); + } + new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + } + } + TRACE("dl", result->display(tout);); + return result; + } + +}; diff --git a/src/muz_qe/dl_mk_backwards.h b/src/muz_qe/dl_mk_backwards.h new file mode 100644 index 000000000..4e546c848 --- /dev/null +++ b/src/muz_qe/dl_mk_backwards.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_backwards.h + +Abstract: + + Create Horn clauses for backwards flow. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-04-17 + +Revision History: + +--*/ +#ifndef _DL_MK_BACKWARDS_H_ +#define _DL_MK_BACKWARDS_H_ + +#include"dl_rule_transformer.h" + +namespace datalog { + + class mk_backwards : public rule_transformer::plugin { + ast_manager& m; + context& m_ctx; + public: + mk_backwards(context & ctx, unsigned priority = 33000); + ~mk_backwards(); + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* _DL_MK_BACKWARDS_H_ */ + From f40df22ccc6d66895bca326ec5056d67809c48a0 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 30 Apr 2013 10:29:41 -0700 Subject: [PATCH 44/91] enable COI transformation in datalog mode Signed-off-by: Nuno Lopes --- src/muz_qe/dl_context.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 0099b16f9..dfcaac791 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -814,6 +814,7 @@ namespace datalog { void context::transform_rules() { m_transf.reset(); + m_transf.register_plugin(alloc(mk_coi_filter, *this)); m_transf.register_plugin(alloc(mk_filter_rules, *this)); m_transf.register_plugin(alloc(mk_simple_joins, *this)); if (unbound_compressor()) { From 21b0a4fcbbe1a77eb6c54203114aee761b710cbe Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Apr 2013 11:53:10 -0700 Subject: [PATCH 45/91] testing utvpi Signed-off-by: Nikolaj Bjorner --- src/muz_qe/pdr_context.cpp | 17 +++- src/muz_qe/pdr_context.h | 2 +- src/muz_qe/pdr_farkas_learner.cpp | 3 + src/muz_qe/pdr_prop_solver.cpp | 44 +++++----- src/smt/theory_utvpi.cpp | 1 + src/smt/theory_utvpi.h | 10 ++- src/smt/theory_utvpi_def.h | 132 +++++++++++++++++++++++++++--- 7 files changed, 176 insertions(+), 33 deletions(-) diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index 62b119538..df95b1f26 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -597,7 +597,7 @@ namespace pdr { expr_ref fml = pm.mk_and(conj); th_rewriter rw(m); rw(fml); - if (ctx.is_dl()) { + if (ctx.is_dl() || ctx.is_utvpi()) { hoist_non_bool_if(fml); } TRACE("pdr", tout << mk_pp(fml, m) << "\n";); @@ -1359,9 +1359,10 @@ namespace pdr { bool m_is_bool_arith; bool m_has_arith; bool m_is_dl; + bool m_is_utvpi; public: classifier_proc(ast_manager& m, datalog::rule_set& rules): - m(m), a(m), m_is_bool(true), m_is_bool_arith(true), m_has_arith(false), m_is_dl(false) { + m(m), a(m), m_is_bool(true), m_is_bool_arith(true), m_has_arith(false), m_is_dl(false), m_is_utvpi(false) { classify(rules); } void operator()(expr* e) { @@ -1407,6 +1408,7 @@ namespace pdr { bool is_dl() const { return m_is_dl; } + bool is_utvpi() const { return m_is_utvpi; } private: @@ -1427,6 +1429,7 @@ namespace pdr { mark.reset(); m_is_dl = false; + m_is_utvpi = false; if (m_has_arith) { ptr_vector forms; for (it = rules.begin(); it != end; ++it) { @@ -1438,6 +1441,11 @@ namespace pdr { } } m_is_dl = is_difference_logic(m, forms.size(), forms.c_ptr()); +#if 0 + if (!m_is_dl) { + m_is_utvpi = is_utvpi_logic(m, forms.size(), forms.c_ptr()); + } +#endif } } @@ -1561,6 +1569,11 @@ namespace pdr { m_fparams.m_arith_mode = AS_DIFF_LOGIC; m_fparams.m_arith_expand_eqs = true; } + else if (classify.is_utvpi()) { + IF_VERBOSE(1, verbose_stream() << "UTVPI\n";); + m_fparams.m_arith_mode = AS_UTVPI; + m_fparams.m_arith_expand_eqs = true; + } } if (!use_mc && m_params.use_inductive_generalizer()) { m_core_generalizers.push_back(alloc(core_bool_inductive_generalizer, *this, 0)); diff --git a/src/muz_qe/pdr_context.h b/src/muz_qe/pdr_context.h index b5652d1b6..1785991c6 100644 --- a/src/muz_qe/pdr_context.h +++ b/src/muz_qe/pdr_context.h @@ -367,7 +367,7 @@ namespace pdr { expr_ref get_answer(); bool is_dl() const { return m_fparams.m_arith_mode == AS_DIFF_LOGIC; } - + bool is_utvpi() const { return m_fparams.m_arith_mode == AS_UTVPI; } void collect_statistics(statistics& st) const; void reset_statistics(); diff --git a/src/muz_qe/pdr_farkas_learner.cpp b/src/muz_qe/pdr_farkas_learner.cpp index 393090299..71404ab12 100644 --- a/src/muz_qe/pdr_farkas_learner.cpp +++ b/src/muz_qe/pdr_farkas_learner.cpp @@ -216,6 +216,9 @@ namespace pdr { } res = m.mk_not(res); th_rewriter rw(m); + params_ref params; + params.set_bool("gcd_rounding", true); + rw.updt_params(params); proof_ref pr(m); expr_ref tmp(m); rw(res, tmp, pr); diff --git a/src/muz_qe/pdr_prop_solver.cpp b/src/muz_qe/pdr_prop_solver.cpp index 863e5c03e..f69af93e9 100644 --- a/src/muz_qe/pdr_prop_solver.cpp +++ b/src/muz_qe/pdr_prop_solver.cpp @@ -383,26 +383,32 @@ namespace pdr { fl.get_lemmas(pr, bs, lemmas); safe.elim_proxies(lemmas); fl.simplify_lemmas(lemmas); // redundant? - if (m_fparams.m_arith_mode == AS_DIFF_LOGIC && - !is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) { - IF_VERBOSE(1, - verbose_stream() << "not diff\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; - }); - extract_subset_core(safe); - return; + + bool outside_of_logic = + (m_fparams.m_arith_mode == AS_DIFF_LOGIC && + !is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) || + (m_fparams.m_arith_mode == AS_UTVPI && + !is_utvpi_logic(m, lemmas.size(), lemmas.c_ptr())); + + if (outside_of_logic) { + IF_VERBOSE(1, + verbose_stream() << "not diff\n"; + for (unsigned i = 0; i < lemmas.size(); ++i) { + verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; + }); + extract_subset_core(safe); + } + else { + + IF_VERBOSE(2, + verbose_stream() << "Lemmas\n"; + for (unsigned i = 0; i < lemmas.size(); ++i) { + verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; + }); + + m_core->reset(); + m_core->append(lemmas); } - - - IF_VERBOSE(2, - verbose_stream() << "Lemmas\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; - }); - - m_core->reset(); - m_core->append(lemmas); } lbool prop_solver::check_assumptions(const expr_ref_vector & atoms) { diff --git a/src/smt/theory_utvpi.cpp b/src/smt/theory_utvpi.cpp index e811257a6..c45cfe74a 100644 --- a/src/smt/theory_utvpi.cpp +++ b/src/smt/theory_utvpi.cpp @@ -70,6 +70,7 @@ namespace smt { } vector > const& utvpi_tester::get_linearization() const { + SASSERT(m_terms.size() <= 2); return m_terms; } diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h index 044fa11a0..40a837136 100644 --- a/src/smt/theory_utvpi.h +++ b/src/smt/theory_utvpi.h @@ -196,7 +196,7 @@ namespace smt { virtual bool internalize_term(app * term); - virtual void internalize_eq_eh(app * atom, bool_var v) {} + virtual void internalize_eq_eh(app * atom, bool_var v); virtual void assign_eh(bool_var v, bool is_true); @@ -258,6 +258,14 @@ namespace smt { private: + rational mk_value(theory_var v); + + void validate_model(); + + bool eval(expr* e); + + rational eval_num(expr* e); + bool check_z_consistency(); virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index c369d6826..97e00c54f 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -228,7 +228,10 @@ namespace smt { template void theory_utvpi::found_non_utvpi_expr(expr* n) { if (!m_non_utvpi_exprs) { - TRACE("utvpi", tout << "found non horn logic expression:\n" << mk_pp(n, get_manager()) << "\n";); + std::stringstream msg; + msg << "found non utvpi logic expression:\n" << mk_pp(n, get_manager()) << "\n"; + TRACE("utvpi", tout << msg.str();); + warning_msg(msg.str().c_str()); get_context().push_trail(value_trail(m_non_utvpi_exprs)); m_non_utvpi_exprs = true; } @@ -277,6 +280,24 @@ namespace smt { } } + template + void theory_utvpi::internalize_eq_eh(app * atom, bool_var v) { + context & ctx = get_context(); + app * lhs = to_app(atom->get_arg(0)); + app * rhs = to_app(atom->get_arg(1)); + if (a.is_numeral(rhs)) { + std::swap(rhs, lhs); + } + if (!a.is_numeral(rhs)) { + return; + } + if (a.is_add(lhs) || a.is_sub(lhs)) { + // force axioms for (= (+ x y) k) + // this is necessary because (+ x y) is not expressible as a utvpi term. + m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs)); + } + } + template bool theory_utvpi::internalize_atom(app * n, bool) { context & ctx = get_context(); @@ -291,7 +312,7 @@ namespace smt { } bool is_strict = a.is_gt(n) || a.is_lt(n); - bool cl = m_test.linearize(e1, e2); + bool cl = m_test.linearize(e1, e2); if (!cl) { found_non_utvpi_expr(n); return false; @@ -324,8 +345,6 @@ namespace smt { bool theory_utvpi::internalize_term(app * term) { bool result = null_theory_var != mk_term(term); CTRACE("utvpi", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); - TRACE("utvpi", tout << "Terms may not be internalized " << mk_pp(term, get_manager()) << "\n";); - found_non_utvpi_expr(term); return result; } @@ -478,6 +497,7 @@ namespace smt { template theory_var theory_utvpi::mk_term(app* n) { + TRACE("utvpi", tout << mk_pp(n, get_manager()) << "\n";); context& ctx = get_context(); bool cl = m_test.linearize(n); @@ -485,7 +505,7 @@ namespace smt { found_non_utvpi_expr(n); return null_theory_var; } - + coeffs coeffs; rational w; mk_coeffs(m_test.get_linearization(), coeffs, w); @@ -495,7 +515,14 @@ namespace smt { if (coeffs.size() == 1 && coeffs[0].second.is_one()) { return coeffs[0].first; } - th_var target = mk_var(ctx.mk_enode(n, false, false, true)); + if (coeffs.size() == 2) { + // do not create an alias. + return null_theory_var; + } + for (unsigned i = 0; i < n->get_num_args(); ++i) { + mk_term(to_app(n->get_arg(i))); + } + th_var target = mk_var(ctx.mk_enode(n, false, false, true)); coeffs.push_back(std::make_pair(target, rational(-1))); VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); @@ -520,7 +547,7 @@ namespace smt { v = mk_var(ctx.mk_enode(n, false, false, true)); // v = k: v <= k k <= v coeffs coeffs; - coeffs.push_back(std::make_pair(v, rational(1))); + coeffs.push_back(std::make_pair(v, rational(-1))); VERIFY(enable_edge(add_ineq(coeffs, numeral(r), null_literal))); coeffs.back().second.neg(); VERIFY(enable_edge(add_ineq(coeffs, numeral(-r), null_literal))); @@ -633,17 +660,102 @@ namespace smt { m.register_factory(m_factory); // TBD: enforce strong or tight coherence? compute_delta(); + DEBUG_CODE(validate_model();); } - + template - model_value_proc * theory_utvpi::mk_value(enode * n, model_generator & mg) { - theory_var v = n->get_th_var(get_id()); + void theory_utvpi::validate_model() { + context& ctx = get_context(); + unsigned sz = m_atoms.size(); + for (unsigned i = 0; i < sz; ++i) { + bool_var b = m_atoms[i].get_bool_var(); + bool ok = true; + expr* e = ctx.bool_var2expr(b); + switch(ctx.get_assignment(b)) { + case l_true: + ok = eval(e); + break; + case l_false: + ok = !eval(e); + break; + default: + break; + } + CTRACE("utvpi", !ok, tout << "validation failed: " << mk_pp(e, get_manager()) << "\n";); + } + } + + template + bool theory_utvpi::eval(expr* e) { + expr* e1, *e2; + if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) { + return eval_num(e1) <= eval_num(e2); + } + if (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { + return eval_num(e1) < eval_num(e2); + } + if (get_manager().is_eq(e, e1, e2)) { + return eval_num(e1) == eval_num(e2); + } + TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); + return false; + } + + template + rational theory_utvpi::eval_num(expr* e) { + rational r; + expr* e1, *e2; + if (a.is_numeral(e, r)) { + return r; + } + if (a.is_sub(e, e1, e2)) { + return eval_num(e1) - eval_num(e2); + } + if (a.is_add(e)) { + r.reset(); + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + r += eval_num(to_app(e)->get_arg(i)); + } + return r; + } + if (a.is_mul(e)) { + r = rational(1); + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + r *= eval_num(to_app(e)->get_arg(i)); + } + return r; + } + if (a.is_uminus(e, e1)) { + return -eval_num(e1); + } + if (a.is_to_real(e, e1)) { + return eval_num(e1); + } + if (is_uninterp_const(e)) { + return mk_value(mk_var(e)); + } + TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); + UNREACHABLE(); + return rational(0); + } + + + template + rational theory_utvpi::mk_value(th_var v) { SASSERT(v != null_theory_var); numeral val1 = m_graph.get_assignment(to_var(v)); numeral val2 = m_graph.get_assignment(neg(to_var(v))); numeral val = val1 - val2; rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational()); num = num/rational(2); + return num; + } + + template + model_value_proc * theory_utvpi::mk_value(enode * n, model_generator & mg) { + theory_var v = n->get_th_var(get_id()); + rational num = mk_value(v); + num = ceil(num); TRACE("utvpi", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); return alloc(expr_wrapper_proc, m_factory->mk_value(num, a.is_int(n->get_owner()))); } From b4d0216728dc3fdaa7f22ccf510e0b8652d4007b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 30 Apr 2013 13:06:59 -0700 Subject: [PATCH 46/91] try to fix gcc build Signed-off-by: Nikolaj Bjorner --- src/smt/theory_horn_ineq_def.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/smt/theory_horn_ineq_def.h b/src/smt/theory_horn_ineq_def.h index 1fc51a86c..0b32c2c8d 100644 --- a/src/smt/theory_horn_ineq_def.h +++ b/src/smt/theory_horn_ineq_def.h @@ -797,10 +797,10 @@ namespace smt { template typename theory_horn_ineq::numeral theory_horn_ineq::mk_weight(bool is_real, bool is_strict, rational const& w) const { if (is_strict) { - return numeral(inf_numeral(w)) + (is_real?m_epsilon:numeral(1)); + return numeral(Ext::inf_numeral(w)) + (is_real?Ext::m_epsilon:numeral(1)); } else { - return numeral(inf_numeral(w)); + return numeral(Ext::inf_numeral(w)); } } @@ -1001,9 +1001,9 @@ namespace smt { th_var target = mk_var(ctx.mk_enode(n, false, false, true)); coeffs.push_back(std::make_pair(target, rational(-1))); - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(Ext::inf_numeral(w)), null_literal))); negate(coeffs, w); - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(Ext::inf_numeral(w)), null_literal))); return target; } @@ -1024,9 +1024,9 @@ namespace smt { // v = k: v <= k k <= v coeffs coeffs; coeffs.push_back(std::make_pair(v, rational(1))); - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(r)), null_literal))); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(Ext::inf_numeral(r)), null_literal))); coeffs.back().second.neg(); - VERIFY (m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(-r)), null_literal))); + VERIFY (m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(Ext::inf_numeral(-r)), null_literal))); } return v; } From 7cb9e7381df1045a7859bf747b62ff62e080ed56 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 May 2013 02:35:57 -0700 Subject: [PATCH 47/91] fix build errors on ubuntu and gcc Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_rule.cpp | 4 ++-- src/muz_qe/fixedpoint_params.pyg | 1 + src/muz_qe/hnf.cpp | 1 + src/muz_qe/pdr_context.cpp | 16 ++++++---------- src/muz_qe/pdr_prop_solver.cpp | 2 +- src/muz_qe/pdr_util.cpp | 6 +++++- src/smt/diff_logic.h | 7 ------- src/smt/theory_diff_logic.h | 3 +-- src/smt/theory_diff_logic_def.h | 7 +------ src/smt/theory_horn_ineq.h | 1 + src/smt/theory_horn_ineq_def.h | 12 ++++++------ src/smt/theory_utvpi.h | 5 +---- src/smt/theory_utvpi_def.h | 16 ++++++++++++---- 13 files changed, 38 insertions(+), 43 deletions(-) diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index db8189c64..ac683eca9 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -85,8 +85,6 @@ namespace datalog { return BR_FAILED; } - template class rewriter_tpl; - void rule_manager::remove_labels(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); @@ -1121,3 +1119,5 @@ namespace datalog { }; +template class rewriter_tpl; + diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index 774559cdb..c516806a6 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -60,6 +60,7 @@ def_module_params('fixedpoint', ('print_answer', BOOL, False, 'print answer instance(s) to query'), ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), ('print_statistics', BOOL, False, 'print statistics'), + ('use_utvpi', BOOL, False, 'experimental use UTVPI strategy'), ('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), )) diff --git a/src/muz_qe/hnf.cpp b/src/muz_qe/hnf.cpp index 764d31bb6..bcc3501de 100644 --- a/src/muz_qe/hnf.cpp +++ b/src/muz_qe/hnf.cpp @@ -186,6 +186,7 @@ private: void mk_horn(expr_ref& fml, proof_ref& premise) { + SASSERT(!premise || fml == m.get_fact(premise)); expr* e1, *e2; expr_ref fml0(m), fml1(m), fml2(m), head(m); proof_ref p(m); diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index df95b1f26..dcfbcca2b 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -1441,11 +1441,7 @@ namespace pdr { } } m_is_dl = is_difference_logic(m, forms.size(), forms.c_ptr()); -#if 0 - if (!m_is_dl) { - m_is_utvpi = is_utvpi_logic(m, forms.size(), forms.c_ptr()); - } -#endif + m_is_utvpi = m_is_dl || is_utvpi_logic(m, forms.size(), forms.c_ptr()); } } @@ -1565,15 +1561,15 @@ namespace pdr { m_fparams.m_arith_auto_config_simplex = true; m_fparams.m_arith_propagate_eqs = false; m_fparams.m_arith_eager_eq_axioms = false; - if (classify.is_dl()) { - m_fparams.m_arith_mode = AS_DIFF_LOGIC; - m_fparams.m_arith_expand_eqs = true; - } - else if (classify.is_utvpi()) { + if (classify.is_utvpi() && m_params.use_utvpi()) { IF_VERBOSE(1, verbose_stream() << "UTVPI\n";); m_fparams.m_arith_mode = AS_UTVPI; m_fparams.m_arith_expand_eqs = true; } + else if (classify.is_dl()) { + m_fparams.m_arith_mode = AS_DIFF_LOGIC; + m_fparams.m_arith_expand_eqs = true; + } } if (!use_mc && m_params.use_inductive_generalizer()) { m_core_generalizers.push_back(alloc(core_bool_inductive_generalizer, *this, 0)); diff --git a/src/muz_qe/pdr_prop_solver.cpp b/src/muz_qe/pdr_prop_solver.cpp index f69af93e9..e3cd0d9c5 100644 --- a/src/muz_qe/pdr_prop_solver.cpp +++ b/src/muz_qe/pdr_prop_solver.cpp @@ -391,7 +391,7 @@ namespace pdr { !is_utvpi_logic(m, lemmas.size(), lemmas.c_ptr())); if (outside_of_logic) { - IF_VERBOSE(1, + IF_VERBOSE(2, verbose_stream() << "not diff\n"; for (unsigned i = 0; i < lemmas.size(); ++i) { verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; diff --git a/src/muz_qe/pdr_util.cpp b/src/muz_qe/pdr_util.cpp index 62185f1d2..9711cffc2 100644 --- a/src/muz_qe/pdr_util.cpp +++ b/src/muz_qe/pdr_util.cpp @@ -1248,7 +1248,11 @@ namespace pdr { } if (!m_is_dl) { - IF_VERBOSE(1, verbose_stream() << "non-diff: " << mk_pp(e, m) << "\n";); + char const* msg = "non-diff: "; + if (m_test_for_utvpi) { + msg = "non-utvpi: "; + } + IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); } } diff --git a/src/smt/diff_logic.h b/src/smt/diff_logic.h index b32a74c2c..f6576a41f 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.h @@ -264,13 +264,6 @@ class dl_graph { m_assignment[e.get_target()] - m_assignment[e.get_source()] <= e.get_weight(); } - bool is_tight(edge_id e) const { - edge const& edge = m_edges[e]; - return edge.is_enabled() && - m_assignment[edge.get_target()] - m_assignment[e.get_source()] == e.get_weight(); - } - - public: // An assignment is feasible if all edges are feasible. bool is_feasible() const { diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 3bfd33b1e..7302ccfd4 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -46,7 +46,6 @@ namespace smt { unsigned m_num_conflicts; unsigned m_num_assertions; unsigned m_num_th2core_eqs; - unsigned m_num_th2core_prop; unsigned m_num_core2th_eqs; unsigned m_num_core2th_diseqs; @@ -260,7 +259,7 @@ namespace smt { m_arith_eq_adapter.restart_eh(); } - virtual void relevant_eh(app* e); + virtual void relevant_eh(app* e) {} virtual void init_search_eh() { m_arith_eq_adapter.init_search_eh(); diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 362962620..20448dc74 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -310,9 +310,9 @@ void theory_diff_logic::assign_eh(bool_var v, bool is_true) { template void theory_diff_logic::collect_statistics(::statistics & st) const { st.update("dl conflicts", m_stats.m_num_conflicts); - st.update("dl propagations", m_stats.m_num_th2core_prop); st.update("dl asserts", m_stats.m_num_assertions); st.update("core->dl eqs", m_stats.m_num_core2th_eqs); + st.update("core->dl diseqs", m_stats.m_num_core2th_diseqs); m_arith_eq_adapter.collect_statistics(st); m_graph.collect_statistics(st); } @@ -598,7 +598,6 @@ void theory_diff_logic::set_neg_cycle_conflict() { literal_vector const& lits = m_nc_functor.get_lits(); context & ctx = get_context(); TRACE("arith_conflict", - //display(tout); tout << "conflict: "; for (unsigned i = 0; i < lits.size(); ++i) { ctx.display_literal_info(tout, lits[i]); @@ -971,10 +970,6 @@ void theory_diff_logic::new_diseq_eh(theory_var v1, theory_var v2) { } -template -void theory_diff_logic::relevant_eh(app* e) { -} - struct imp_functor { conflict_resolution & m_cr; diff --git a/src/smt/theory_horn_ineq.h b/src/smt/theory_horn_ineq.h index 441e46a18..fac6e96df 100644 --- a/src/smt/theory_horn_ineq.h +++ b/src/smt/theory_horn_ineq.h @@ -85,6 +85,7 @@ namespace smt { class theory_horn_ineq : public theory, private Ext { typedef typename Ext::numeral numeral; + typedef typename Ext::inf_numeral inf_numeral; typedef literal explanation; typedef theory_var th_var; typedef svector th_var_vector; diff --git a/src/smt/theory_horn_ineq_def.h b/src/smt/theory_horn_ineq_def.h index 0b32c2c8d..505815b4f 100644 --- a/src/smt/theory_horn_ineq_def.h +++ b/src/smt/theory_horn_ineq_def.h @@ -797,10 +797,10 @@ namespace smt { template typename theory_horn_ineq::numeral theory_horn_ineq::mk_weight(bool is_real, bool is_strict, rational const& w) const { if (is_strict) { - return numeral(Ext::inf_numeral(w)) + (is_real?Ext::m_epsilon:numeral(1)); + return numeral(inf_numeral(w)) + (is_real?Ext::m_epsilon:numeral(1)); } else { - return numeral(Ext::inf_numeral(w)); + return numeral(inf_numeral(w)); } } @@ -1001,9 +1001,9 @@ namespace smt { th_var target = mk_var(ctx.mk_enode(n, false, false, true)); coeffs.push_back(std::make_pair(target, rational(-1))); - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(Ext::inf_numeral(w)), null_literal))); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); negate(coeffs, w); - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(Ext::inf_numeral(w)), null_literal))); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); return target; } @@ -1024,9 +1024,9 @@ namespace smt { // v = k: v <= k k <= v coeffs coeffs; coeffs.push_back(std::make_pair(v, rational(1))); - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(Ext::inf_numeral(r)), null_literal))); + VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(r)), null_literal))); coeffs.back().second.neg(); - VERIFY (m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(Ext::inf_numeral(-r)), null_literal))); + VERIFY (m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(-r)), null_literal))); } return v; } diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h index 40a837136..8b19bdab8 100644 --- a/src/smt/theory_utvpi.h +++ b/src/smt/theory_utvpi.h @@ -201,6 +201,7 @@ namespace smt { virtual void assign_eh(bool_var v, bool is_true); virtual void new_eq_eh(th_var v1, th_var v2) { + m_stats.m_num_core2th_eqs++; m_arith_eq_adapter.new_eq_eh(v1, v2); } @@ -322,10 +323,6 @@ namespace smt { return v | 0x1; } - th_var not(th_var v) const { - return v ^ 0x1; - } - }; diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 97e00c54f..905270a46 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -264,7 +264,7 @@ namespace smt { template typename theory_utvpi::numeral theory_utvpi::mk_weight(bool is_real, bool is_strict, rational const& w) const { if (is_strict) { - return numeral(w) + (is_real?m_epsilon:numeral(1)); + return numeral(w) + (is_real?Ext::m_epsilon:numeral(1)); } else { return numeral(w); @@ -435,6 +435,7 @@ namespace smt { m_nc_functor.reset(); VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, UINT_MAX, m_nc_functor)); VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, UINT_MAX, m_nc_functor)); + IF_VERBOSE(1, verbose_stream() << "parity conflict " << mk_pp(e->get_owner(), get_manager()) << "\n";); set_conflict(); return false; @@ -453,7 +454,9 @@ namespace smt { template void theory_utvpi::collect_statistics(::statistics& st) const { st.update("utvpi conflicts", m_stats.m_num_conflicts); - st.update("utvpi assignments", m_stats.m_num_assertions); + st.update("utvpi asserts", m_stats.m_num_assertions); + st.update("core->utvpi eqs", m_stats.m_num_core2th_eqs); + st.update("core->utvpi diseqs", m_stats.m_num_core2th_diseqs); m_arith_eq_adapter.collect_statistics(st); m_graph.collect_statistics(st); } @@ -669,6 +672,9 @@ namespace smt { unsigned sz = m_atoms.size(); for (unsigned i = 0; i < sz; ++i) { bool_var b = m_atoms[i].get_bool_var(); + if (!ctx.is_relevant(b)) { + continue; + } bool ok = true; expr* e = ctx.bool_var2expr(b); switch(ctx.get_assignment(b)) { @@ -681,7 +687,9 @@ namespace smt { default: break; } - CTRACE("utvpi", !ok, tout << "validation failed: " << mk_pp(e, get_manager()) << "\n";); + CTRACE("utvpi", !ok, tout << "validation failed: " << mk_pp(e, get_manager()) << "\n";); + // CTRACE("utvpi", ok, tout << "validation success: " << mk_pp(e, get_manager()) << "\n";); + SASSERT(ok); } } @@ -748,6 +756,7 @@ namespace smt { numeral val = val1 - val2; rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational()); num = num/rational(2); + num = floor(num); return num; } @@ -755,7 +764,6 @@ namespace smt { model_value_proc * theory_utvpi::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); rational num = mk_value(v); - num = ceil(num); TRACE("utvpi", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); return alloc(expr_wrapper_proc, m_factory->mk_value(num, a.is_int(n->get_owner()))); } From 65af658fd7c3302a876621cec028d27089e7f285 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 1 May 2013 14:08:53 +0100 Subject: [PATCH 48/91] FPA: min/max special cases fixed. Signed-off-by: Christoph M. Wintersteiger --- src/ast/rewriter/float_rewriter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index 70ba09581..b43f07b65 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -242,13 +242,13 @@ br_status float_rewriter::mk_min(expr * arg1, expr * arg2, expr_ref & result) { return BR_DONE; } // expand as using ite's - result = m().mk_ite(mk_eq_nan(arg1), + result = m().mk_ite(m().mk_or(mk_eq_nan(arg1), m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2))), arg2, m().mk_ite(mk_eq_nan(arg2), arg1, m().mk_ite(m_util.mk_lt(arg1, arg2), - arg1, - arg2))); + arg1, + arg2))); return BR_REWRITE_FULL; } @@ -262,7 +262,7 @@ br_status float_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) { return BR_DONE; } // expand as using ite's - result = m().mk_ite(mk_eq_nan(arg1), + result = m().mk_ite(m().mk_or(mk_eq_nan(arg1), m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2))), arg2, m().mk_ite(mk_eq_nan(arg2), arg1, From e50a9e8dbfabdc7678c753d2a21796756d61a12c Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 1 May 2013 14:10:50 +0100 Subject: [PATCH 49/91] MPF: fused-mul-add fixes. Sometimes this is still off by a bit. Signed-off-by: Christoph M. Wintersteiger --- src/util/mpf.cpp | 211 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 184 insertions(+), 27 deletions(-) diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index dfac97626..def9f4fd5 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -453,16 +453,14 @@ bool mpf_manager::lt(mpf const & x, mpf const & y) { else if (sgn(x)) { if (!sgn(y)) return true; - else - // CMW: Problem with denormal numbers? + else return exp(y) < exp(x) || (exp(y) == exp(x) && m_mpz_manager.lt(sig(y), sig(x))); } else { // !sgn(x) if (sgn(y)) return false; - else - // CMW: Problem with denormal numbers? + else return exp(x) < exp(y) || (exp(x)==exp(y) && m_mpz_manager.lt(sig(x), sig(y))); } @@ -545,7 +543,7 @@ void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mp b.get().sign = sgn_y; // Unpack a/b, this inserts the hidden bit and adjusts the exponent. - unpack(a, true); + unpack(a, false); unpack(b, false); if (exp(b) > exp(a)) @@ -556,25 +554,21 @@ void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mp SASSERT(exp(a) >= exp(b)); SASSERT(exp_delta >= 0); - mpf_exp_t u_delta = exp_delta; - if (u_delta > x.sbits+2) - u_delta = x.sbits+2; + if (exp_delta > x.sbits+2) + exp_delta = x.sbits+2; TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); - TRACE("mpf_dbg", tout << "d = " << u_delta << std::endl;); - - TRACE("mpf_dbg", tout << "UP A = " << to_string(a) << std::endl;); - TRACE("mpf_dbg", tout << "UP B = " << to_string(b) << std::endl;); + TRACE("mpf_dbg", tout << "d = " << exp_delta << std::endl;); // Introduce 3 extra bits into both numbers. m_mpz_manager.mul2k(a.significand(), 3, a.significand()); m_mpz_manager.mul2k(b.significand(), 3, b.significand()); // Alignment shift with sticky bit computation. - SASSERT(u_delta <= INT_MAX); + SASSERT(exp_delta <= INT_MAX); scoped_mpz sticky_rem(m_mpz_manager); - m_mpz_manager.machine_div_rem(b.significand(), m_powers2((int)u_delta), b.significand(), sticky_rem); + m_mpz_manager.machine_div_rem(b.significand(), m_powers2((int)exp_delta), b.significand(), sticky_rem); if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(b.significand())) m_mpz_manager.inc(b.significand()); @@ -582,7 +576,7 @@ void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mp TRACE("mpf_dbg", tout << "B' = " << m_mpz_manager.to_string(b.significand()) << std::endl;); // Significand addition - if (sgn(a) ^ sgn(b)) { + if (sgn(a) != sgn(b)) { TRACE("mpf_dbg", tout << "SUBTRACTING" << std::endl;); m_mpz_manager.sub(a.significand(), b.significand(), o.significand); } @@ -765,9 +759,167 @@ void mpf_manager::div(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & } void mpf_manager::fused_mul_add(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf const &z, mpf & o) { - // CMW: Is this precise enough? - mul(rm, x, y, o); - add(rm, o, z, o); + SASSERT(x.sbits == y.sbits && x.ebits == y.ebits && + x.sbits == y.sbits && z.ebits == z.ebits); + + TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); + TRACE("mpf_dbg", tout << "Y = " << to_string(y) << std::endl;); + TRACE("mpf_dbg", tout << "Z = " << to_string(z) << std::endl;); + + if (is_nan(x) || is_nan(y) || is_nan(z)) + mk_nan(x.ebits, x.sbits, o); + else if (is_pinf(x)) { + if (is_zero(y)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, y.sign, o); + } + else if (is_pinf(y)) { + if (is_zero(x)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, x.sign, o); + } + else if (is_ninf(x)) { + if (is_zero(y)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, !y.sign, o); + } + else if (is_ninf(y)) { + if (is_zero(x)) + mk_nan(x.ebits, x.sbits, o); + else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) + mk_nan(x.ebits, x.sbits, o); + else + mk_inf(x.ebits, x.sbits, !x.sign, o); + } + else if (is_inf(z)) { + set(o, z); + } + else if (is_zero(x) || is_zero(y)) { + if (is_zero(z) && rm != MPF_ROUND_TOWARD_NEGATIVE) + mk_pzero(x.ebits, x.sbits, o); + else + set(o, z); + } + else { + o.ebits = x.ebits; + o.sbits = x.sbits; + + scoped_mpf mul_res(*this, x.ebits+2, 2*x.sbits); + scoped_mpf a(*this, x.ebits, x.sbits), b(*this, x.ebits, x.sbits), c(*this, x.ebits, x.sbits); + set(a, x); + set(b, y); + set(c, z); + unpack(a, true); + unpack(b, true); + unpack(c, true); + + TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); + TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); + TRACE("mpf_dbg", tout << "C = " << to_string(c) << std::endl;); + + SASSERT(m_mpz_manager.lt(a.significand(), m_powers2(x.sbits))); + SASSERT(m_mpz_manager.lt(b.significand(), m_powers2(x.sbits))); + SASSERT(m_mpz_manager.lt(c.significand(), m_powers2(x.sbits))); + SASSERT(m_mpz_manager.ge(a.significand(), m_powers2(x.sbits-1))); + SASSERT(m_mpz_manager.ge(b.significand(), m_powers2(x.sbits-1))); + + mul_res.get().sign = (a.sign() != b.sign()); + mul_res.get().exponent = a.exponent() + b.exponent(); + m_mpz_manager.mul(a.significand(), b.significand(), mul_res.get().significand); + + TRACE("mpf_dbg", tout << "PRODUCT = " << to_string(mul_res) << std::endl;); + + // mul_res is [-1][0].[2*sbits - 2], i.e., between 2*sbits-1 and 2*sbits. + SASSERT(m_mpz_manager.lt(mul_res.significand(), m_powers2(2*x.sbits))); + SASSERT(m_mpz_manager.ge(mul_res.significand(), m_powers2(2*x.sbits - 2))); + + // Introduce extra bits into c. + m_mpz_manager.mul2k(c.significand(), x.sbits-1, c.significand()); + + SASSERT(m_mpz_manager.lt(c.significand(), m_powers2(2 * x.sbits - 1))); + SASSERT(m_mpz_manager.is_zero(c.significand()) || + m_mpz_manager.ge(c.significand(), m_powers2(2 * x.sbits - 2))); + + TRACE("mpf_dbg", tout << "C = " << to_string(c) << std::endl;); + + if (exp(c) > exp(mul_res)) + mul_res.swap(c); + + mpf_exp_t exp_delta = exp(mul_res) - exp(c); + + SASSERT(exp(mul_res) >= exp(c) && exp_delta >= 0); + + if (exp_delta > 2 * x.sbits) + exp_delta = 2 * x.sbits; + TRACE("mpf_dbg", tout << "exp_delta = " << exp_delta << std::endl;); + + // Alignment shift with sticky bit computation. + scoped_mpz sticky_rem(m_mpz_manager); + m_mpz_manager.machine_div_rem(c.significand(), m_powers2((int)exp_delta), c.significand(), sticky_rem); + TRACE("mpf_dbg", tout << "alignment shift -> sig = " << m_mpz_manager.to_string(c.significand()) << + " sticky_rem = " << m_mpz_manager.to_string(sticky_rem) << std::endl;); + if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(c.significand())) + m_mpz_manager.inc(c.significand()); + + TRACE("mpf_dbg", tout << "M' = " << m_mpz_manager.to_string(mul_res.significand()) << std::endl;); + TRACE("mpf_dbg", tout << "C' = " << m_mpz_manager.to_string(c.significand()) << std::endl;); + + // Significand addition + if (sgn(mul_res) != sgn(c)) { + TRACE("mpf_dbg", tout << "SUBTRACTING" << std::endl;); + m_mpz_manager.sub(mul_res.significand(), c.significand(), o.significand); + } + else { + TRACE("mpf_dbg", tout << "ADDING" << std::endl;); + m_mpz_manager.add(mul_res.significand(), c.significand(), o.significand); + } + + TRACE("mpf_dbg", tout << "sum[-1:] = " << m_mpz_manager.to_string(o.significand) << std::endl;); + + bool neg = m_mpz_manager.is_neg(o.significand); + TRACE("mpf_dbg", tout << "NEG=" << neg << std::endl;); + if (neg) m_mpz_manager.abs(o.significand); + + o.exponent = mul_res.exponent(); + + unsigned extra = x.sbits-4; + // Result could overflow into 4.xxx ... + SASSERT(m_mpz_manager.lt(o.significand, m_powers2(2 * x.sbits + 2))); + if(m_mpz_manager.ge(o.significand, m_powers2(2 * x.sbits + 1))) + { + extra++; + o.exponent++; + TRACE("mpf_dbg", tout << "Addition overflew!" << std::endl;); + } + + // Get rid of the extra bits. + m_mpz_manager.machine_div_rem(o.significand, m_powers2(extra), o.significand, sticky_rem); + if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(o.significand)) + m_mpz_manager.inc(o.significand); + + TRACE("mpf_dbg", tout << "sum[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); + + if (m_mpz_manager.is_zero(o.significand)) + mk_zero(x.ebits, x.sbits, rm == MPF_ROUND_TOWARD_NEGATIVE, o); + else { + o.sign = ((!mul_res.sign() && c.sign() && neg) || + ( mul_res.sign() && !c.sign() && !neg) || + ( mul_res.sign() && c.sign())); + TRACE("mpf_dbg", tout << "before round = " << to_string(o) << std::endl << + "fs[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); + round(rm, o); + } + } + } void my_mpz_sqrt(unsynch_mpz_manager & m, unsigned sbits, bool odd_exp, mpz & in, mpz & o) { @@ -938,8 +1090,8 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { mk_nan(x.ebits, x.sbits, o); else if (is_inf(y)) set(o, x); - else if (is_zero(x)) - mk_pzero(x.ebits, x.sbits, o); + else if (is_zero(x)) + set(o, x); else if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else { @@ -955,7 +1107,7 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); - + if (a.exponent() < b.exponent()) set(o, x); else { @@ -986,22 +1138,22 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { } void mpf_manager::maximum(mpf const & x, mpf const & y, mpf & o) { - if (is_nan(x)) + if (is_nan(x) || (is_zero(x) && is_zero(y))) set(o, y); else if (is_nan(y)) set(o, x); - else if (gt(x, y) || (is_zero(x) && is_nzero(y))) + else if (gt(x, y)) set(o, x); else set(o, y); } void mpf_manager::minimum(mpf const & x, mpf const & y, mpf & o) { - if (is_nan(x)) + if (is_nan(x) || (is_zero(x) && is_zero(y))) set(o, y); else if (is_nan(y)) set(o, x); - else if (lt(x, y) || (is_nzero(x) && is_zero(y))) + else if (lt(x, y)) set(o, x); else set(o, y); @@ -1340,6 +1492,11 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { TRACE("mpf_dbg", tout << "RND: " << to_string(o) << std::endl;); + DEBUG_CODE({ + const mpz & p_m3 = m_powers2(o.sbits+5); + SASSERT(m_mpz_manager.lt(o.significand, p_m3)); + }); + // Structure of the rounder: // (s, e_out, f_out) == (s, exprd(s, post(e, sigrd(s, f)))). @@ -1354,7 +1511,7 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { "e_max_norm = " << e_max_norm << std::endl;); const mpz & p_m1 = m_powers2(o.sbits+2); - const mpz & p_m2 = m_powers2(o.sbits+3); + const mpz & p_m2 = m_powers2(o.sbits+3); TRACE("mpf_dbg", tout << "p_m1 = " << m_mpz_manager.to_string(p_m1) << std::endl << "p_m2 = " << m_mpz_manager.to_string(p_m2) << std::endl;); @@ -1530,7 +1687,7 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { TRACE("mpf_dbg", tout << "DENORMAL: " << m_mpz_manager.to_string(o.significand) << std::endl;); o.exponent = mk_bot_exp(o.ebits); } - } + } TRACE("mpf_dbg", tout << "ROUNDED = " << to_string(o) << std::endl;); } From 717f131942ed31caee5ef70e13db75a8f6dfe0c6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 1 May 2013 19:54:40 +0100 Subject: [PATCH 50/91] fix warnings and errors from the mint64 build Signed-off-by: Nikolaj Bjorner --- src/muz_qe/hnf.cpp | 1 + src/smt/theory_diff_logic_def.h | 2 +- src/smt/theory_horn_ineq.h | 3 +-- src/smt/theory_horn_ineq_def.h | 2 ++ src/smt/theory_utvpi.h | 2 +- src/smt/theory_utvpi_def.h | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/muz_qe/hnf.cpp b/src/muz_qe/hnf.cpp index bcc3501de..75a85061c 100644 --- a/src/muz_qe/hnf.cpp +++ b/src/muz_qe/hnf.cpp @@ -201,6 +201,7 @@ private: if (!m_sorts.empty()) { proof* p1 = m.mk_pull_quant(fml, to_quantifier(fml1)); premise = mk_modus_ponens(premise, p1); + fml = fml1; } } head = fml0; diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 20448dc74..390803957 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -558,7 +558,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges lits.size(), lits.c_ptr(), params.size(), params.c_ptr()); } - clause* cls = ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); + ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); if (dump_lemmas()) { char const * logic = m_is_lia ? "QF_LIA" : "QF_LRA"; ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); diff --git a/src/smt/theory_horn_ineq.h b/src/smt/theory_horn_ineq.h index fac6e96df..ed96f25f6 100644 --- a/src/smt/theory_horn_ineq.h +++ b/src/smt/theory_horn_ineq.h @@ -91,7 +91,6 @@ namespace smt { typedef svector th_var_vector; typedef unsigned clause_id; typedef vector > coeffs; - static const clause_id null_clause_id = UINT_MAX; class clause; class graph; @@ -108,7 +107,7 @@ namespace smt { atom(bool_var bv, int pos, int neg) : m_bvar(bv), m_true(false), m_pos(pos), m_neg(neg) {} - virtual ~atom() {} + ~atom() {} bool_var get_bool_var() const { return m_bvar; } bool is_true() const { return m_true; } void assign_eh(bool is_true) { m_true = is_true; } diff --git a/src/smt/theory_horn_ineq_def.h b/src/smt/theory_horn_ineq_def.h index 505815b4f..f4fa7e8d7 100644 --- a/src/smt/theory_horn_ineq_def.h +++ b/src/smt/theory_horn_ineq_def.h @@ -27,6 +27,8 @@ Revision History: namespace smt { + static const unsigned null_clause_id = UINT_MAX; + /** A clause represents an inequality of the form diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h index 8b19bdab8..9e8073ec2 100644 --- a/src/smt/theory_utvpi.h +++ b/src/smt/theory_utvpi.h @@ -78,7 +78,7 @@ namespace smt { atom(bool_var bv, int pos, int neg) : m_bvar(bv), m_true(false), m_pos(pos), m_neg(neg) {} - virtual ~atom() {} + ~atom() {} bool_var get_bool_var() const { return m_bvar; } void assign_eh(bool is_true) { m_true = is_true; } int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 905270a46..af372ea50 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -54,8 +54,8 @@ namespace smt { m_arith_eq_adapter(*this, m_params, a), m_zero_int(null_theory_var), m_zero_real(null_theory_var), - m_asserted_qhead(0), m_nc_functor(*this), + m_asserted_qhead(0), m_agility(0.5), m_lia(false), m_lra(false), From 78db1d0f86c35b424522ea600b52f9604c2e1eae Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 1 May 2013 16:13:24 -0700 Subject: [PATCH 51/91] fix build of unit tests Signed-off-by: Nuno Lopes --- src/smt/diff_logic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/smt/diff_logic.h b/src/smt/diff_logic.h index f6576a41f..7216bba7e 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.h @@ -21,6 +21,7 @@ Revision History: #include"vector.h" #include"heap.h" +#include"statistics.h" #include"trace.h" #include"warning.h" From 00d5dea9a5694aae8df2d9a78ae7fce04effd4af Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 2 May 2013 15:24:07 +0100 Subject: [PATCH 52/91] FPA: added support for rewriting quantified floats to quantified bit-vectors. Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 14 +++++ src/tactic/fpa/fpa2bv_converter.h | 4 +- src/tactic/fpa/fpa2bv_rewriter.h | 84 +++++++++++++++++++++++++++-- 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 84a1efaff..2e2a270ee 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -150,6 +150,20 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { } } +void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) { + SASSERT(is_float(srt)); + unsigned ebits = m_util.get_ebits(srt); + unsigned sbits = m_util.get_sbits(srt); + + expr_ref sgn(m), s(m), e(m); + + sgn = m.mk_var(base_inx, m_bv_util.mk_sort(1)); + s = m.mk_var(base_inx + 1, m_bv_util.mk_sort(sbits-1)); + e = m.mk_var(base_inx + 2, m_bv_util.mk_sort(ebits)); + + mk_triple(sgn, s, e, result); +} + void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/tactic/fpa/fpa2bv_converter.h index e5d546763..4ed794a7c 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/tactic/fpa/fpa2bv_converter.h @@ -35,7 +35,7 @@ class fpa2bv_converter { ast_manager & m; basic_simplifier_plugin m_simp; float_util m_util; - mpf_manager & m_mpf_manager; + mpf_manager & m_mpf_manager; unsynch_mpz_manager & m_mpz_manager; bv_util m_bv_util; float_decl_plugin * m_plugin; @@ -48,6 +48,7 @@ public: ~fpa2bv_converter(); float_util & fu() { return m_util; } + bv_util & bu() { return m_bv_util; } bool is_float(sort * s) { return m_util.is_float(s); } bool is_float(expr * e) { return is_app(e) && m_util.is_float(to_app(e)->get_decl()->get_range()); } @@ -68,6 +69,7 @@ public: void mk_value(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_const(func_decl * f, expr_ref & result); void mk_rm_const(func_decl * f, expr_ref & result); + void mk_var(unsigned base_inx, sort * srt, expr_ref & result); void mk_plus_inf(func_decl * f, expr_ref & result); void mk_minus_inf(func_decl * f, expr_ref & result); diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index 4b3525a32..02b40840a 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -29,6 +29,8 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; expr_ref_vector m_out; fpa2bv_converter & m_conv; + sort_ref_vector m_bindings; + expr_ref_vector m_mappings; unsigned long long m_max_memory; unsigned m_max_steps; @@ -38,7 +40,9 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { fpa2bv_rewriter_cfg(ast_manager & m, fpa2bv_converter & c, params_ref const & p): m_manager(m), m_out(m), - m_conv(c) { + m_conv(c), + m_bindings(m), + m_mappings(m) { updt_params(p); // We need to make sure that the mananger has the BV plugin loaded. symbol s_bv("bv"); @@ -53,6 +57,9 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { m_out.finalize(); } + void reset() { + } + void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); @@ -140,17 +147,88 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { return BR_FAILED; } + bool pre_visit(expr * t) + { + if (is_quantifier(t)) { + quantifier * q = to_quantifier(t); + TRACE("fpa2bv", tout << "pre_visit [" << q->get_id() << "]: " << mk_ismt2_pp(q->get_expr(), m()) << std::endl;); + sort_ref_vector new_bindings(m_manager); + for (unsigned i = 0 ; i < q->get_num_decls(); i++) + new_bindings.push_back(q->get_decl_sort(i)); + SASSERT(new_bindings.size() == q->get_num_decls()); + m_bindings.append(new_bindings); + m_mappings.resize(m_bindings.size(), 0); + } + return true; + } + 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 false; + unsigned curr_sz = m_bindings.size(); + SASSERT(old_q->get_num_decls() <= curr_sz); + unsigned num_decls = old_q->get_num_decls(); + unsigned old_sz = curr_sz - num_decls; + string_buffer<> name_buffer; + ptr_buffer new_decl_sorts; + sbuffer new_decl_names; + for (unsigned i = 0; i < num_decls; i++) { + symbol const & n = old_q->get_decl_name(i); + sort * s = old_q->get_decl_sort(i); + if (m_conv.is_float(s)) { + unsigned ebits = m_conv.fu().get_ebits(s); + unsigned sbits = m_conv.fu().get_sbits(s); + name_buffer.reset(); + name_buffer << n << ".exp"; + new_decl_names.push_back(symbol(name_buffer.c_str())); + new_decl_sorts.push_back(m_conv.bu().mk_sort(ebits)); + name_buffer.reset(); + name_buffer << n << ".sig"; + new_decl_names.push_back(symbol(name_buffer.c_str())); + new_decl_sorts.push_back(m_conv.bu().mk_sort(sbits-1)); + name_buffer.reset(); + name_buffer << n << ".sgn"; + new_decl_names.push_back(symbol(name_buffer.c_str())); + new_decl_sorts.push_back(m_conv.bu().mk_sort(1)); + } + else { + new_decl_sorts.push_back(s); + new_decl_names.push_back(n); + } + } + result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), + new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), + old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); + result_pr = 0; + m_bindings.shrink(old_sz); + m_mappings.shrink(old_sz); + TRACE("fpa2bv", tout << "reduce_quantifier[" << old_q->get_depth() << "]: " << + mk_ismt2_pp(old_q->get_expr(), m()) << std::endl << + " new body: " << mk_ismt2_pp(new_body, m()) << std::endl; + tout << "result = " << mk_ismt2_pp(result, m()) << std::endl;); + return true; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { - return false; + if (t->get_idx() >= m_bindings.size()) + return false; + unsigned inx = m_bindings.size() - t->get_idx() - 1; + if (m_mappings[inx] == 0) + { + unsigned shift = 0; + for (unsigned i = m_bindings.size() - 1; i > inx; i--) + if (m_conv.is_float(m_bindings[i].get())) shift += 2; + expr_ref new_var(m()); + m_conv.mk_var(t->get_idx() + shift, t->get_sort(), new_var); + m_mappings[inx] = new_var; + } + result = m_mappings[inx].get(); + result_pr = 0; + TRACE("fpa2bv", tout << "reduce_var: " << mk_ismt2_pp(t, m()) << " -> " << mk_ismt2_pp(result, m()) << std::endl;); + return true; } }; From 8f60a936d2b0f0f0a9f71f3c8339cd364e3d2750 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 3 May 2013 15:57:42 +0100 Subject: [PATCH 53/91] FPA: Added support for float-UF to BV-UF translation. Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 98 ++++++++++++++++++++++++++++- src/tactic/fpa/fpa2bv_converter.h | 20 +++++- src/tactic/fpa/fpa2bv_rewriter.h | 22 ++++++- 3 files changed, 136 insertions(+), 4 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 2e2a270ee..acb41a2b1 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -38,6 +38,17 @@ fpa2bv_converter::fpa2bv_converter(ast_manager & m) : fpa2bv_converter::~fpa2bv_converter() { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); + dec_ref_map_key_values(m, m_uf2bvuf); + + obj_map::iterator it = m_uf23bvuf.begin(); + obj_map::iterator end = m_uf23bvuf.end(); + for (; it != end; ++it) { + m.dec_ref(it->m_key); + m.dec_ref(it->m_value.f_sgn); + m.dec_ref(it->m_value.f_sig); + m.dec_ref(it->m_value.f_exp); + } + m_uf23bvuf.reset(); } void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { @@ -164,6 +175,90 @@ void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) mk_triple(sgn, s, e, result); } +void fpa2bv_converter::mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result) +{ + TRACE("fpa2bv_dbg", tout << "UF: " << mk_ismt2_pp(f, m) << std::endl; ); + SASSERT(f->get_arity() == num); + + expr_ref_buffer new_args(m); + + for (unsigned i = 0; i < num ; i ++) + if (is_float(args[i])) + { + expr * sgn, * sig, * exp; + split(args[i], sgn, sig, exp); + new_args.push_back(sgn); + new_args.push_back(sig); + new_args.push_back(exp); + } + else + new_args.push_back(args[i]); + + func_decl * fd; + func_decl_triple fd3; + if (m_uf2bvuf.find(f, fd)) { + result = m.mk_app(fd, new_args.size(), new_args.c_ptr()); + } + else if (m_uf23bvuf.find(f, fd3)) + { + expr_ref a_sgn(m), a_sig(m), a_exp(m); + a_sgn = m.mk_app(fd3.f_sgn, new_args.size(), new_args.c_ptr()); + a_sig = m.mk_app(fd3.f_sig, new_args.size(), new_args.c_ptr()); + a_exp = m.mk_app(fd3.f_exp, new_args.size(), new_args.c_ptr()); + mk_triple(a_sgn, a_sig, a_exp, result); + } + else { + sort_ref_buffer new_domain(m); + + for (unsigned i = 0; i < f->get_arity() ; i ++) + if (is_float(f->get_domain()[i])) + { + new_domain.push_back(m_bv_util.mk_sort(1)); + new_domain.push_back(m_bv_util.mk_sort(m_util.get_sbits(f->get_domain()[i])-1)); + new_domain.push_back(m_bv_util.mk_sort(m_util.get_ebits(f->get_domain()[i]))); + } + else + new_domain.push_back(f->get_domain()[i]); + + if (!is_float(f->get_range())) + { + func_decl * fbv = m.mk_func_decl(f->get_name(), new_domain.size(), new_domain.c_ptr(), f->get_range(), *f->get_info()); + TRACE("fpa2bv_dbg", tout << "New UF func_decl : " << mk_ismt2_pp(fbv, m) << std::endl; ); + m_uf2bvuf.insert(f, fbv); + m.inc_ref(f); + m.inc_ref(fbv); + result = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); + } + else + { + string_buffer<> name_buffer; + name_buffer.reset(); name_buffer << f->get_name() << ".sgn"; + func_decl * f_sgn = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(1)); + name_buffer.reset(); name_buffer << f->get_name() << ".sig"; + func_decl * f_sig = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(m_util.get_sbits(f->get_range())-1)); + name_buffer.reset(); name_buffer << f->get_name() << ".exp"; + func_decl * f_exp = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(m_util.get_ebits(f->get_range()))); + expr_ref a_sgn(m), a_sig(m), a_exp(m); + a_sgn = m.mk_app(f_sgn, new_args.size(), new_args.c_ptr()); + a_sig = m.mk_app(f_sig, new_args.size(), new_args.c_ptr()); + a_exp = m.mk_app(f_exp, new_args.size(), new_args.c_ptr()); + TRACE("fpa2bv_dbg", tout << "New UF func_decls : " << std::endl; + tout << mk_ismt2_pp(f_sgn, m) << std::endl; + tout << mk_ismt2_pp(f_sig, m) << std::endl; + tout << mk_ismt2_pp(f_exp, m) << std::endl; ); + m_uf23bvuf.insert(f, func_decl_triple(f_sgn, f_sig, f_exp)); + m.inc_ref(f); + m.inc_ref(f_sgn); + m.inc_ref(f_sig); + m.inc_ref(f_exp); + mk_triple(a_sgn, a_sig, a_exp, result); + } + } + + TRACE("fpa2bv_dbg", tout << "UF result: " << mk_ismt2_pp(result, m) << std::endl; ); + + SASSERT(is_well_sorted(m, result)); +} void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); @@ -1953,7 +2048,8 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef _DEBUG - // return; + return; + // CMW: This works only for quantifier-free formulas. expr_ref new_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); extra_assertions.push_back(m.mk_eq(new_e, e)); diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/tactic/fpa/fpa2bv_converter.h index 4ed794a7c..f090e3e8d 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/tactic/fpa/fpa2bv_converter.h @@ -42,6 +42,21 @@ class fpa2bv_converter { obj_map m_const2bv; obj_map m_rm_const2bv; + obj_map m_uf2bvuf; + + struct func_decl_triple { + func_decl_triple () { f_sgn = 0; f_sig = 0; f_exp = 0; } + func_decl_triple (func_decl * sgn, func_decl * sig, func_decl * exp) + { + f_sgn = sgn; + f_sig = sig; + f_exp = exp; + } + func_decl * f_sgn; + func_decl * f_sig; + func_decl * f_exp; + }; + obj_map m_uf23bvuf; public: fpa2bv_converter(ast_manager & m); @@ -67,8 +82,9 @@ public: void mk_rounding_mode(func_decl * f, expr_ref & result); void mk_value(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_const(func_decl * f, expr_ref & result); + void mk_const(func_decl * f, expr_ref & result); void mk_rm_const(func_decl * f, expr_ref & result); + void mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_var(unsigned base_inx, sort * srt, expr_ref & result); void mk_plus_inf(func_decl * f, expr_ref & result); @@ -102,7 +118,7 @@ public: void mk_is_sign_minus(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_float(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); obj_map const & const2bv() const { return m_const2bv; } obj_map const & rm_const2bv() const { return m_rm_const2bv; } diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index 02b40840a..aec00d30c 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -143,6 +143,23 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { throw tactic_exception("NYI"); } } + + if (f->get_family_id() == null_family_id) + { + bool is_float_uf = m_conv.is_float(f->get_range()); + unsigned i = 0; + while (!is_float_uf && i < num) + { + is_float_uf = m_conv.is_float(f->get_domain()[i]); + i++; + } + + if (is_float_uf) + { + m_conv.mk_uninterpreted_function(f, num, args, result); + return BR_DONE; + } + } return BR_FAILED; } @@ -222,7 +239,10 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { for (unsigned i = m_bindings.size() - 1; i > inx; i--) if (m_conv.is_float(m_bindings[i].get())) shift += 2; expr_ref new_var(m()); - m_conv.mk_var(t->get_idx() + shift, t->get_sort(), new_var); + if (m_conv.is_float(t->get_sort())) + m_conv.mk_var(t->get_idx() + shift, t->get_sort(), new_var); + else + new_var = m().mk_var(t->get_idx() + shift, t->get_sort()); m_mappings[inx] = new_var; } result = m_mappings[inx].get(); From 121e83b6b7960263b5fec710202a070ad2477ed4 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 3 May 2013 17:54:30 +0100 Subject: [PATCH 54/91] FPA: bugfixes for UF in model converter for fpa2bv. Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 44 ++++++++++++++++++++--- src/tactic/fpa/fpa2bv_converter.h | 55 ++++++++++++++++++++--------- src/tactic/fpa/fpa2bv_tactic.cpp | 2 +- 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index acb41a2b1..363cf1df8 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -2403,6 +2403,25 @@ void fpa2bv_model_converter::display(std::ostream & out) { unsigned indent = n.size() + 4; out << mk_ismt2_pp(it->m_value, m, indent) << ")"; } + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_uf23bvuf.begin(); + it != m_uf23bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value.f_sgn, m, indent) << " ; " << + mk_ismt2_pp(it->m_value.f_sig, m, indent) << " ; " << + mk_ismt2_pp(it->m_value.f_exp, m, indent) << " ; " << + ")"; + } out << ")" << std::endl; } @@ -2523,6 +2542,20 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { } } + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) + seen.insert(it->m_value); + + for (obj_map::iterator it = m_uf23bvuf.begin(); + it != m_uf23bvuf.end(); + it++) + { + seen.insert(it->m_value.f_sgn); + seen.insert(it->m_value.f_sig); + seen.insert(it->m_value.f_exp); + } + fu.fm().del(fp_val); // Keep all the non-float constants. @@ -2530,7 +2563,7 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { for (unsigned i = 0; i < sz; i++) { func_decl * c = bv_mdl->get_constant(i); - if (!seen.contains(c)) + if (!seen.contains(c)) float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); } @@ -2539,7 +2572,8 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { for (unsigned i = 0; i < sz; i++) { func_decl * c = bv_mdl->get_function(i); - float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); + if (!seen.contains(c)) + float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); } sz = bv_mdl->get_num_uninterpreted_sorts(); @@ -2553,6 +2587,8 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { model_converter * mk_fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv) { - return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv); + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf) { + return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv, uf2bvuf, uf23bvuf); } diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/tactic/fpa/fpa2bv_converter.h index f090e3e8d..2a68342f8 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/tactic/fpa/fpa2bv_converter.h @@ -31,20 +31,7 @@ typedef enum { BV_RM_TIES_TO_AWAY=0, BV_RM_TIES_TO_EVEN=1, BV_RM_TO_NEGATIVE=2, class fpa2bv_model_converter; -class fpa2bv_converter { - ast_manager & m; - basic_simplifier_plugin m_simp; - float_util m_util; - mpf_manager & m_mpf_manager; - unsynch_mpz_manager & m_mpz_manager; - bv_util m_bv_util; - float_decl_plugin * m_plugin; - - obj_map m_const2bv; - obj_map m_rm_const2bv; - obj_map m_uf2bvuf; - - struct func_decl_triple { +struct func_decl_triple { func_decl_triple () { f_sgn = 0; f_sig = 0; f_exp = 0; } func_decl_triple (func_decl * sgn, func_decl * sig, func_decl * exp) { @@ -56,6 +43,19 @@ class fpa2bv_converter { func_decl * f_sig; func_decl * f_exp; }; + +class fpa2bv_converter { + ast_manager & m; + basic_simplifier_plugin m_simp; + float_util m_util; + mpf_manager & m_mpf_manager; + unsynch_mpz_manager & m_mpz_manager; + bv_util m_bv_util; + float_decl_plugin * m_plugin; + + obj_map m_const2bv; + obj_map m_rm_const2bv; + obj_map m_uf2bvuf; obj_map m_uf23bvuf; public: @@ -122,6 +122,8 @@ public: obj_map const & const2bv() const { return m_const2bv; } obj_map const & rm_const2bv() const { return m_rm_const2bv; } + obj_map const & uf2bvuf() const { return m_uf2bvuf; } + obj_map const & uf23bvuf() const { return m_uf23bvuf; } void dbg_decouple(const char * prefix, expr_ref & e); expr_ref_vector extra_assertions; @@ -166,10 +168,14 @@ class fpa2bv_model_converter : public model_converter { ast_manager & m; obj_map m_const2bv; obj_map m_rm_const2bv; + obj_map m_uf2bvuf; + obj_map m_uf23bvuf; public: fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv) : + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf) : m(m) { // Just create a copy? for (obj_map::iterator it = const2bv.begin(); @@ -188,6 +194,21 @@ public: m.inc_ref(it->m_key); m.inc_ref(it->m_value); } + for (obj_map::iterator it = uf2bvuf.begin(); + it != uf2bvuf.end(); + it++) + { + m_uf2bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = uf23bvuf.begin(); + it != uf23bvuf.end(); + it++) + { + m_uf23bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + } } virtual ~fpa2bv_model_converter() { @@ -220,6 +241,8 @@ protected: model_converter * mk_fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, - obj_map const & rm_const2bv); + obj_map const & rm_const2bv, + obj_map const & uf2bvuf, + obj_map const & uf23bvuf); #endif diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index a90ff9317..9bb35eea6 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -90,7 +90,7 @@ class fpa2bv_tactic : public tactic { } if (g->models_enabled()) - mc = mk_fpa2bv_model_converter(m, m_conv.const2bv(), m_conv.rm_const2bv()); + mc = mk_fpa2bv_model_converter(m, m_conv.const2bv(), m_conv.rm_const2bv(), m_conv.uf2bvuf(), m_conv.uf23bvuf()); g->inc_depth(); result.push_back(g.get()); From 622484929f6b4665362f968311de2e8a9bd5188d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 6 May 2013 01:33:40 +0200 Subject: [PATCH 55/91] postpone rule flushing dependent on engine Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 3 ++ src/muz_qe/dl_context.cpp | 79 +++++++++++++++++++-------------- src/muz_qe/dl_context.h | 3 +- src/muz_qe/dl_mk_slice.cpp | 24 +++++----- src/muz_qe/pdr_dl_interface.cpp | 4 +- 5 files changed, 66 insertions(+), 47 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index c138bcd62..bbd267292 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -585,6 +585,9 @@ class FuncDeclRef(AstRef): def as_ast(self): return Z3_func_decl_to_ast(self.ctx_ref(), self.ast) + def as_func_decl(self): + return self.ast + def name(self): """Return the name of the function declaration `self`. diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index dfcaac791..f8e572ab1 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -234,6 +234,7 @@ namespace datalog { m_vars(m), m_rule_set(*this), m_transformed_rule_set(*this), + m_rule_fmls_head(0), m_rule_fmls(m), m_background(m), m_mc(0), @@ -243,8 +244,6 @@ namespace datalog { m_last_answer(m), m_engine(LAST_ENGINE), m_cancel(false) { - - //register plugins for builtin tables } context::~context() { @@ -254,6 +253,9 @@ namespace datalog { void context::reset() { m_trail.reset(); m_rule_set.reset(); + m_rule_fmls_head = 0; + m_rule_fmls.reset(); + m_rule_names.reset(); m_argument_var_names.reset(); m_preds.reset(); m_preds_by_name.reset(); @@ -457,13 +459,18 @@ namespace datalog { void context::flush_add_rules() { datalog::rule_manager& rm = get_rule_manager(); scoped_proof_mode _scp(m, generate_proof_trace()?PGM_FINE:PGM_DISABLED); - for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { - expr* fml = m_rule_fmls[i].get(); + while (m_rule_fmls_head < m_rule_fmls.size()) { + expr* fml = m_rule_fmls[m_rule_fmls_head].get(); proof* p = generate_proof_trace()?m.mk_asserted(fml):0; - rm.mk_rule(fml, p, m_rule_set, m_rule_names[i]); + rm.mk_rule(fml, p, m_rule_set, m_rule_names[m_rule_fmls_head]); + ++m_rule_fmls_head; + } + rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); + rule_ref r(m_rule_manager); + for (; it != end; ++it) { + r = *it; + check_rule(r); } - m_rule_fmls.reset(); - m_rule_names.reset(); } // @@ -958,18 +965,21 @@ namespace datalog { engine_type_proc(ast_manager& m): m(m), a(m), dt(m), m_engine(DATALOG_ENGINE) {} DL_ENGINE get_engine() const { return m_engine; } + void operator()(expr* e) { if (is_quantifier(e)) { m_engine = QPDR_ENGINE; } - else if (a.is_int_real(e) && m_engine != QPDR_ENGINE) { - m_engine = PDR_ENGINE; - } - else if (is_var(e) && m.is_bool(e)) { - m_engine = PDR_ENGINE; - } - else if (dt.is_datatype(m.get_sort(e))) { - m_engine = PDR_ENGINE; + else if (m_engine != QPDR_ENGINE) { + if (a.is_int_real(e)) { + m_engine = PDR_ENGINE; + } + else if (is_var(e) && m.is_bool(e)) { + m_engine = PDR_ENGINE; + } + else if (dt.is_datatype(m.get_sort(e))) { + m_engine = PDR_ENGINE; + } } } }; @@ -1002,7 +1012,7 @@ namespace datalog { if (m_engine == LAST_ENGINE) { expr_fast_mark1 mark; engine_type_proc proc(m); - m_engine = DATALOG_ENGINE; + m_engine = DATALOG_ENGINE; for (unsigned i = 0; m_engine == DATALOG_ENGINE && i < m_rule_set.get_num_rules(); ++i) { rule * r = m_rule_set.get_rule(i); quick_for_each_expr(proc, mark, r->get_head()); @@ -1011,29 +1021,38 @@ namespace datalog { } m_engine = proc.get_engine(); } + for (unsigned i = m_rule_fmls_head; m_engine == DATALOG_ENGINE && i < m_rule_fmls.size(); ++i) { + expr* fml = m_rule_fmls[i].get(); + while (is_quantifier(fml)) { + fml = to_quantifier(fml)->get_expr(); + } + quick_for_each_expr(proc, mark, fml); + m_engine = proc.get_engine(); + } } } lbool context::query(expr* query) { - new_query(); - rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); - rule_ref r(m_rule_manager); - for (; it != end; ++it) { - r = *it; - check_rule(r); - } - switch(get_engine()) { + m_mc = mk_skip_model_converter(); + m_last_status = OK; + m_last_answer = 0; + switch (get_engine()) { case DATALOG_ENGINE: + flush_add_rules(); return rel_query(query); case PDR_ENGINE: case QPDR_ENGINE: + flush_add_rules(); return pdr_query(query); case BMC_ENGINE: case QBMC_ENGINE: + flush_add_rules(); return bmc_query(query); case TAB_ENGINE: + flush_add_rules(); return tab_query(query); case CLP_ENGINE: + flush_add_rules(); return clp_query(query); default: UNREACHABLE(); @@ -1041,14 +1060,6 @@ namespace datalog { } } - void context::new_query() { - m_mc = mk_skip_model_converter(); - - flush_add_rules(); - m_last_status = OK; - m_last_answer = 0; - } - model_ref context::get_model() { switch(get_engine()) { case PDR_ENGINE: @@ -1277,7 +1288,7 @@ namespace datalog { datalog::rule_manager& rm = get_rule_manager(); // ensure that rules are all using bound variables. - for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { + for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { ptr_vector sorts; get_free_vars(m_rule_fmls[i].get(), sorts); if (!sorts.empty()) { @@ -1295,7 +1306,7 @@ namespace datalog { rules.push_back(fml); names.push_back((*it)->name()); } - for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { + for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { rules.push_back(m_rule_fmls[i].get()); names.push_back(m_rule_names[i]); } diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index 0a01b3e01..e9abf7f23 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -114,6 +114,7 @@ namespace datalog { pred2syms m_argument_var_names; rule_set m_rule_set; rule_set m_transformed_rule_set; + unsigned m_rule_fmls_head; expr_ref_vector m_rule_fmls; svector m_rule_names; expr_ref_vector m_background; @@ -482,8 +483,6 @@ namespace datalog { void ensure_rel(); - void new_query(); - lbool rel_query(expr* query); lbool pdr_query(expr* query); diff --git a/src/muz_qe/dl_mk_slice.cpp b/src/muz_qe/dl_mk_slice.cpp index 89804447b..1a97c1b81 100644 --- a/src/muz_qe/dl_mk_slice.cpp +++ b/src/muz_qe/dl_mk_slice.cpp @@ -120,12 +120,11 @@ namespace datalog { obj_map::iterator end = m_rule2slice.end(); expr_ref fml(m); for (; it != end; ++it) { - TRACE("dl", - it->m_key->display(m_ctx, tout << "orig:\n"); - it->m_value->display(m_ctx, tout << "new:\n");); - it->m_value->to_formula(fml); m_pinned_exprs.push_back(fml); + TRACE("dl", + tout << "orig: " << mk_pp(fml, m) << "\n"; + it->m_value->display(m_ctx, tout << "new:\n");); m_sliceform2rule.insert(fml, it->m_key); } } @@ -202,9 +201,10 @@ namespace datalog { proof* p0_new = m_new_proof.find(p0); expr* fact0 = m.get_fact(p0); TRACE("dl", tout << "fact0: " << mk_pp(fact0, m) << "\n";); - rule* orig0 = m_sliceform2rule.find(fact0); - /* rule* slice0 = */ m_rule2slice.find(orig0); - /* unsigned_vector const& renaming0 = m_renaming.find(orig0); */ + rule* orig0; + if (!m_sliceform2rule.find(fact0, orig0)) { + return false; + } premises.push_back(p0_new); rule_ref r1(rm), r2(rm), r3(rm); r1 = orig0; @@ -214,9 +214,10 @@ namespace datalog { proof* p1_new = m_new_proof.find(p1); expr* fact1 = m.get_fact(p1); TRACE("dl", tout << "fact1: " << mk_pp(fact1, m) << "\n";); - rule* orig1 = m_sliceform2rule.find(fact1); - /* rule* slice1 = */ m_rule2slice.find(orig1); - /* unsigned_vector const& renaming1 = m_renaming.find(orig1); TBD */ + rule* orig1 = 0; + if (!m_sliceform2rule.find(fact1, orig1)) { + return false; + } premises.push_back(p1_new); // TODO: work with substitutions. @@ -241,6 +242,9 @@ namespace datalog { proof* new_p = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), concl, positions, substs); m_pinned_exprs.push_back(new_p); m_pinned_rules.push_back(r1.get()); + TRACE("dl", + tout << "orig: " << mk_pp(slice_concl, m) << "\n"; + r1->display(m_ctx, tout << "new:");); m_sliceform2rule.insert(slice_concl, r1.get()); m_rule2slice.insert(r1.get(), 0); m_renaming.insert(r1.get(), unsigned_vector()); diff --git a/src/muz_qe/pdr_dl_interface.cpp b/src/muz_qe/pdr_dl_interface.cpp index 63ac6cf4b..3ff54e68e 100644 --- a/src/muz_qe/pdr_dl_interface.cpp +++ b/src/muz_qe/pdr_dl_interface.cpp @@ -32,6 +32,7 @@ Revision History: #include "dl_mk_slice.h" #include "dl_mk_unfold.h" #include "dl_mk_coalesce.h" +#include "model_smt2_pp.h" using namespace pdr; @@ -134,10 +135,10 @@ lbool dl_interface::query(expr * query) { } query_pred = m_ctx.get_rules().get_output_predicate(); - IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); m_pdr_rules.replace_rules(m_ctx.get_rules()); m_pdr_rules.close(); + m_ctx.record_transformed_rules(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); @@ -151,6 +152,7 @@ lbool dl_interface::query(expr * query) { if (m_pdr_rules.get_rules().empty()) { m_context->set_unsat(); + IF_VERBOSE(1, model_smt2_pp(verbose_stream(), m, *m_context->get_model(),0);); return l_false; } From 157b5f0d9ce1a7618ce11f7708a72342bf5db55d Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Tue, 7 May 2013 08:10:43 -0700 Subject: [PATCH 56/91] Add expr_vector example Signed-off-by: Leonardo de Moura --- examples/c++/example.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index ab5d0132c..bec8c59a1 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -906,6 +906,28 @@ void enum_sort_example() { std::cout << "2: " << result_goal.as_expr() << std::endl; } +void expr_vector_example() { + context c; + const unsigned N = 10; + + expr_vector x(c); + + for (unsigned i = 0; i < N; i++) { + std::stringstream x_name; + x_name << "x_" << i; + x.push_back(c.int_const(x_name.str().c_str())); + } + + solver s(c); + for (unsigned i = 0; i < N; i++) { + s.add(x[i] >= 1); + } + + std::cout << s << "\n" << "solving...\n" << s.check() << "\n"; + model m = s.get_model(); + std::cout << "solution\n" << m; +} + int main() { try { demorgan(); std::cout << "\n"; @@ -941,6 +963,7 @@ int main() { incremental_example2(); std::cout << "\n"; incremental_example3(); std::cout << "\n"; enum_sort_example(); std::cout << "\n"; + expr_vector_example(); std::cout << "\n"; std::cout << "done\n"; } catch (exception & ex) { From b65adc10da41a90f883592424c52e1f9bd683d12 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 7 May 2013 17:58:43 +0100 Subject: [PATCH 57/91] FPA: bugfix for quantified FP -> quantified BV conversion. Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_rewriter.h | 41 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index aec00d30c..a891337ec 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -166,9 +166,11 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { bool pre_visit(expr * t) { + TRACE("fpa2bv", tout << "pre_visit: " << mk_ismt2_pp(t, m()) << std::endl;); + if (is_quantifier(t)) { quantifier * q = to_quantifier(t); - TRACE("fpa2bv", tout << "pre_visit [" << q->get_id() << "]: " << mk_ismt2_pp(q->get_expr(), m()) << std::endl;); + TRACE("fpa2bv", tout << "pre_visit quantifier [" << q->get_id() << "]: " << mk_ismt2_pp(q->get_expr(), m()) << std::endl;); sort_ref_vector new_bindings(m_manager); for (unsigned i = 0 ; i < q->get_num_decls(); i++) new_bindings.push_back(q->get_decl_sort(i)); @@ -199,17 +201,9 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { unsigned ebits = m_conv.fu().get_ebits(s); unsigned sbits = m_conv.fu().get_sbits(s); name_buffer.reset(); - name_buffer << n << ".exp"; + name_buffer << n << ".bv"; new_decl_names.push_back(symbol(name_buffer.c_str())); - new_decl_sorts.push_back(m_conv.bu().mk_sort(ebits)); - name_buffer.reset(); - name_buffer << n << ".sig"; - new_decl_names.push_back(symbol(name_buffer.c_str())); - new_decl_sorts.push_back(m_conv.bu().mk_sort(sbits-1)); - name_buffer.reset(); - name_buffer << n << ".sgn"; - new_decl_names.push_back(symbol(name_buffer.c_str())); - new_decl_sorts.push_back(m_conv.bu().mk_sort(1)); + new_decl_sorts.push_back(m_conv.bu().mk_sort(sbits+ebits)); } else { new_decl_sorts.push_back(s); @@ -231,19 +225,26 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { if (t->get_idx() >= m_bindings.size()) - return false; + return false; unsigned inx = m_bindings.size() - t->get_idx() - 1; if (m_mappings[inx] == 0) { - unsigned shift = 0; - for (unsigned i = m_bindings.size() - 1; i > inx; i--) - if (m_conv.is_float(m_bindings[i].get())) shift += 2; - expr_ref new_var(m()); - if (m_conv.is_float(t->get_sort())) - m_conv.mk_var(t->get_idx() + shift, t->get_sort(), new_var); + expr_ref new_exp(m()); + sort * s = t->get_sort(); + if (m_conv.is_float(s)) + { + expr_ref new_var(m()); + unsigned ebits = m_conv.fu().get_ebits(s); + unsigned sbits = m_conv.fu().get_sbits(s); + new_var = m().mk_var(t->get_idx(), m_conv.bu().mk_sort(sbits+ebits)); + m_conv.mk_triple(m_conv.bu().mk_extract(sbits+ebits-1, sbits+ebits-1, new_var), + m_conv.bu().mk_extract(sbits+ebits-2, ebits, new_var), + m_conv.bu().mk_extract(ebits-1, 0, new_var), + new_exp); + } else - new_var = m().mk_var(t->get_idx() + shift, t->get_sort()); - m_mappings[inx] = new_var; + new_exp = m().mk_var(t->get_idx(), s); + m_mappings[inx] = new_exp; } result = m_mappings[inx].get(); result_pr = 0; From 787a65be299e88eb7dd4628e5858b5ecb53026ba Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 7 May 2013 18:27:47 +0100 Subject: [PATCH 58/91] FPA: bugfix for QFPA -> QBV conversion. Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_rewriter.h | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index a891337ec..3398874f5 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -30,7 +30,8 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { expr_ref_vector m_out; fpa2bv_converter & m_conv; sort_ref_vector m_bindings; - expr_ref_vector m_mappings; + expr_ref_vector m_mappings; + unsigned long long m_max_memory; unsigned m_max_steps; @@ -227,25 +228,24 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { if (t->get_idx() >= m_bindings.size()) return false; unsigned inx = m_bindings.size() - t->get_idx() - 1; - if (m_mappings[inx] == 0) + + expr_ref new_exp(m()); + sort * s = t->get_sort(); + if (m_conv.is_float(s)) { - expr_ref new_exp(m()); - sort * s = t->get_sort(); - if (m_conv.is_float(s)) - { - expr_ref new_var(m()); - unsigned ebits = m_conv.fu().get_ebits(s); - unsigned sbits = m_conv.fu().get_sbits(s); - new_var = m().mk_var(t->get_idx(), m_conv.bu().mk_sort(sbits+ebits)); - m_conv.mk_triple(m_conv.bu().mk_extract(sbits+ebits-1, sbits+ebits-1, new_var), - m_conv.bu().mk_extract(sbits+ebits-2, ebits, new_var), - m_conv.bu().mk_extract(ebits-1, 0, new_var), - new_exp); - } - else - new_exp = m().mk_var(t->get_idx(), s); - m_mappings[inx] = new_exp; + expr_ref new_var(m()); + unsigned ebits = m_conv.fu().get_ebits(s); + unsigned sbits = m_conv.fu().get_sbits(s); + new_var = m().mk_var(t->get_idx(), m_conv.bu().mk_sort(sbits+ebits)); + m_conv.mk_triple(m_conv.bu().mk_extract(sbits+ebits-1, sbits+ebits-1, new_var), + m_conv.bu().mk_extract(sbits+ebits-2, ebits, new_var), + m_conv.bu().mk_extract(ebits-1, 0, new_var), + new_exp); } + else + new_exp = m().mk_var(t->get_idx(), s); + m_mappings[inx] = new_exp; + result = m_mappings[inx].get(); result_pr = 0; TRACE("fpa2bv", tout << "reduce_var: " << mk_ismt2_pp(t, m()) << " -> " << mk_ismt2_pp(result, m()) << std::endl;); From c8c5f30b49405fe2ae9bcd74021f84258f91ed5d Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 9 May 2013 21:30:31 -0700 Subject: [PATCH 59/91] Add new C++ APIs for creating forall/exists expressions. Signed-off-by: Leonardo de Moura --- examples/c++/example.cpp | 25 +++++++++ src/api/c++/z3++.h | 106 +++++++++++++++++++++++---------------- 2 files changed, 88 insertions(+), 43 deletions(-) diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index bec8c59a1..55dbebe27 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -907,6 +907,7 @@ void enum_sort_example() { } void expr_vector_example() { + std::cout << "expr_vector example\n"; context c; const unsigned N = 10; @@ -928,6 +929,29 @@ void expr_vector_example() { std::cout << "solution\n" << m; } +void exists_expr_vector_example() { + std::cout << "exists expr_vector example\n"; + context c; + const unsigned N = 10; + + expr_vector xs(c); + expr x(c); + expr b(c); + b = c.bool_val(true); + + for (unsigned i = 0; i < N; i++) { + std::stringstream x_name; + x_name << "x_" << i; + x = c.int_const(x_name.str().c_str()); + xs.push_back(x); + b = b && x >= 0; + } + + expr ex(c); + ex = exists(xs, b); + std::cout << ex << std::endl; +} + int main() { try { demorgan(); std::cout << "\n"; @@ -964,6 +988,7 @@ int main() { incremental_example3(); std::cout << "\n"; enum_sort_example(); std::cout << "\n"; expr_vector_example(); std::cout << "\n"; + exists_expr_vector_example(); std::cout << "\n"; std::cout << "done\n"; } catch (exception & ex) { diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index a044149e3..a255c2d97 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -249,6 +249,8 @@ namespace z3 { array & operator=(array const & s); public: array(unsigned sz):m_size(sz) { m_array = new T[sz]; } + template + array(ast_vector_tpl const & v); ~array() { delete[] m_array; } unsigned size() const { return m_size; } T & operator[](int i) { assert(0 <= i); assert(static_cast(i) < m_size); return m_array[i]; } @@ -939,49 +941,6 @@ namespace z3 { inline expr udiv(expr const & a, int b) { return udiv(a, a.ctx().num_val(b, a.get_sort())); } inline expr udiv(int a, expr const & b) { return udiv(b.ctx().num_val(a, b.get_sort()), b); } - // Basic functions for creating quantified formulas. - // The C API should be used for creating quantifiers with patterns, weights, many variables, etc. - inline expr forall(expr const & x, expr const & b) { - check_context(x, b); - Z3_app vars[] = {(Z3_app) x}; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr forall(expr const & x1, expr const & x2, expr const & b) { - check_context(x1, b); check_context(x2, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; - Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x, expr const & b) { - check_context(x, b); - Z3_app vars[] = {(Z3_app) x}; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x1, expr const & x2, expr const & b) { - check_context(x1, b); check_context(x2, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { - check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); - Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; - Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); - } - template class cast_ast; template<> class cast_ast { @@ -1043,6 +1002,67 @@ namespace z3 { friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } }; + template + template + array::array(ast_vector_tpl const & v) { + m_array = new T[v.size()]; + m_size = v.size(); + for (unsigned i = 0; i < m_size; i++) { + m_array[i] = v[i]; + } + } + + // Basic functions for creating quantified formulas. + // The C API should be used for creating quantifiers with patterns, weights, many variables, etc. + inline expr forall(expr const & x, expr const & b) { + check_context(x, b); + Z3_app vars[] = {(Z3_app) x}; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr const & x1, expr const & x2, expr const & b) { + check_context(x1, b); check_context(x2, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr forall(expr_vector const & xs, expr const & b) { + array vars(xs); + Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x, expr const & b) { + check_context(x, b); + Z3_app vars[] = {(Z3_app) x}; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x1, expr const & x2, expr const & b) { + check_context(x1, b); check_context(x2, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr exists(expr_vector const & xs, expr const & b) { + array vars(xs); + Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); + } + class func_entry : public object { Z3_func_entry m_entry; void init(Z3_func_entry e) { From 5eed106ffe81565dcfb345d25c7f9f69dcbfbc0b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 12 May 2013 17:02:25 -0700 Subject: [PATCH 60/91] fix parameters in utvpi and make Karr invariants use backward propagation Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_compiler.cpp | 3 +- src/muz_qe/dl_mk_karr_invariants.cpp | 83 +++++++++++++++++----------- src/muz_qe/dl_mk_karr_invariants.h | 16 +++--- src/muz_qe/dl_product_relation.cpp | 10 ++-- src/muz_qe/dl_sieve_relation.cpp | 5 +- src/smt/theory_utvpi.h | 13 +++-- src/smt/theory_utvpi_def.h | 37 ++++++++----- 7 files changed, 101 insertions(+), 66 deletions(-) diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index 57d9880c0..05a0d24b2 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -499,8 +499,7 @@ namespace datalog { expr * arg = a->get_arg(i); if(is_app(arg)) { app * c = to_app(arg); //argument is a constant - SASSERT(c->get_num_args()==0); - SASSERT(m_context.get_decl_util().is_numeral_ext(arg)); + SASSERT(m.is_value(c)); reg_idx new_reg; make_select_equal_and_project(single_res, c, single_res_expr.size(), new_reg, acc); if(single_res!=t_reg) { diff --git a/src/muz_qe/dl_mk_karr_invariants.cpp b/src/muz_qe/dl_mk_karr_invariants.cpp index d676c3dd4..143a38636 100644 --- a/src/muz_qe/dl_mk_karr_invariants.cpp +++ b/src/muz_qe/dl_mk_karr_invariants.cpp @@ -37,6 +37,7 @@ Revision History: #include"bool_rewriter.h" #include"dl_mk_backwards.h" #include"dl_mk_loop_counter.h" +#include "for_each_expr.h" namespace datalog { @@ -47,7 +48,8 @@ namespace datalog { m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_inner_ctx(m, ctx.get_fparams()), - a(m) { + a(m), + m_pinned(m) { params_ref params; params.set_sym("default_relation", symbol("karr_relation")); params.set_sym("engine", symbol("datalog")); @@ -201,29 +203,27 @@ namespace datalog { return 0; } } - mk_loop_counter lc(m_ctx); mk_backwards bwd(m_ctx); scoped_ptr src_loop = lc(source); TRACE("dl", src_loop->display(tout << "source loop\n");); - // run propagation forwards, then backwards - scoped_ptr src_annot = update_using_propagation(*src_loop, *src_loop); - TRACE("dl", src_annot->display(tout << "updated using propagation\n");); + get_invariants(*src_loop); -#if 0 // figure out whether to update same rules as used for saturation. - scoped_ptr rev_source = bwd(*src_annot); - src_annot = update_using_propagation(*src_annot, *rev_source); -#endif + scoped_ptr rev_source = bwd(*src_loop); + get_invariants(*rev_source); + scoped_ptr src_annot = update_rules(*src_loop); rule_set* rules = lc.revert(*src_annot); rules->inherit_predicates(source); TRACE("dl", rules->display(tout);); + m_pinned.reset(); + m_fun2inv.reset(); return rules; } - rule_set* mk_karr_invariants::update_using_propagation(rule_set const& src, rule_set const& srcref) { + void mk_karr_invariants::get_invariants(rule_set const& src) { m_inner_ctx.reset(); rel_context& rctx = m_inner_ctx.get_rel_context(); ptr_vector heads; @@ -232,19 +232,41 @@ namespace datalog { m_inner_ctx.register_predicate(*fit, false); } m_inner_ctx.ensure_opened(); - m_inner_ctx.replace_rules(srcref); + m_inner_ctx.replace_rules(src); m_inner_ctx.close(); - rule_set::decl2rules::iterator dit = srcref.begin_grouped_rules(); - rule_set::decl2rules::iterator dend = srcref.end_grouped_rules(); + rule_set::decl2rules::iterator dit = src.begin_grouped_rules(); + rule_set::decl2rules::iterator dend = src.end_grouped_rules(); for (; dit != dend; ++dit) { heads.push_back(dit->m_key); } m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); - - rule_set* dst = alloc(rule_set, m_ctx); + + // retrieve invariants. + dit = src.begin_grouped_rules(); + for (; dit != dend; ++dit) { + func_decl* p = dit->m_key; + relation_base* rb = rctx.try_get_relation(p); + if (rb) { + expr_ref fml(m); + rb->to_formula(fml); + if (m.is_true(fml)) { + continue; + } + expr* inv = 0; + if (m_fun2inv.find(p, inv)) { + fml = m.mk_and(inv, fml); + } + m_pinned.push_back(fml); + m_fun2inv.insert(p, fml); + } + } + } + + rule_set* mk_karr_invariants::update_rules(rule_set const& src) { + scoped_ptr dst = alloc(rule_set, m_ctx); rule_set::iterator it = src.begin(), end = src.end(); for (; it != end; ++it) { - update_body(rctx, *dst, **it); + update_body(*dst, **it); } if (m_ctx.get_model_converter()) { add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m); @@ -252,10 +274,8 @@ namespace datalog { rule_set::decl2rules::iterator gend = src.end_grouped_rules(); for (; git != gend; ++git) { func_decl* p = git->m_key; - expr_ref fml(m); - relation_base* rb = rctx.try_get_relation(p); - if (rb) { - rb->to_formula(fml); + expr* fml = 0; + if (m_fun2inv.find(p, fml)) { kmc->add(p, fml); } } @@ -263,10 +283,10 @@ namespace datalog { } dst->inherit_predicates(src); - return dst; + return dst.detach(); } - void mk_karr_invariants::update_body(rel_context& rctx, rule_set& rules, rule& r) { + void mk_karr_invariants::update_body(rule_set& rules, rule& r) { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); app_ref_vector tail(m); @@ -275,17 +295,17 @@ namespace datalog { tail.push_back(r.get_tail(i)); } for (unsigned i = 0; i < utsz; ++i) { - func_decl* q = r.get_decl(i); - relation_base* rb = rctx.try_get_relation(r.get_decl(i)); - if (rb) { - rb->to_formula(fml); + func_decl* q = r.get_decl(i); + expr* fml = 0; + if (m_fun2inv.find(q, fml)) { expr_safe_replace rep(m); for (unsigned j = 0; j < q->get_arity(); ++j) { rep.insert(m.mk_var(j, q->get_domain(j)), r.get_tail(i)->get_arg(j)); } - rep(fml); - tail.push_back(to_app(fml)); + expr_ref tmp(fml, m); + rep(tmp); + tail.push_back(to_app(tmp)); } } rule* new_rule = &r; @@ -1029,16 +1049,17 @@ namespace datalog { class karr_relation_plugin::filter_equal_fn : public relation_mutator_fn { unsigned m_col; rational m_value; + bool m_valid; public: filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) : m_col(col) { arith_util arith(m.get_context().get_manager()); - VERIFY(arith.is_numeral(value, m_value)); + m_valid = arith.is_numeral(value, m_value) && m_value.is_int(); } virtual void operator()(relation_base & _r) { karr_relation & r = get(_r); - if (m_value.is_int()) { + if (m_valid) { r.get_ineqs(); vector row; row.resize(r.get_signature().size()); @@ -1054,7 +1075,7 @@ namespace datalog { relation_mutator_fn * karr_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { - if(check_kind(r)) { + if (check_kind(r)) { return alloc(filter_equal_fn, get_manager(), value, col); } return 0; diff --git a/src/muz_qe/dl_mk_karr_invariants.h b/src/muz_qe/dl_mk_karr_invariants.h index 330260671..ec554e284 100644 --- a/src/muz_qe/dl_mk_karr_invariants.h +++ b/src/muz_qe/dl_mk_karr_invariants.h @@ -55,8 +55,13 @@ namespace datalog { rule_manager& rm; context m_inner_ctx; arith_util a; - void update_body(rel_context& rctx, rule_set& result, rule& r); - rule_set* update_using_propagation(rule_set const& src, rule_set const& srcref); + obj_map m_fun2inv; + ast_ref_vector m_pinned; + + void get_invariants(rule_set const& src); + + void update_body(rule_set& result, rule& r); + rule_set* update_rules(rule_set const& src); public: mk_karr_invariants(context & ctx, unsigned priority); @@ -89,12 +94,7 @@ namespace datalog { {} virtual bool can_handle_signature(const relation_signature & sig) { - for (unsigned i = 0; i < sig.size(); ++i) { - if (a.is_int(sig[i])) { - return true; - } - } - return false; + return true; } static symbol get_name() { return symbol("karr_relation"); } diff --git a/src/muz_qe/dl_product_relation.cpp b/src/muz_qe/dl_product_relation.cpp index 66074d463..c10b5799a 100644 --- a/src/muz_qe/dl_product_relation.cpp +++ b/src/muz_qe/dl_product_relation.cpp @@ -269,7 +269,7 @@ namespace datalog { unsigned_vector r1_tables_indexes; unsigned_vector r2_tables_indexes; for (unsigned i = 0; i < num_rels1; ++i) { - if(is_tableish_relation(*r1[i])) { + if (is_tableish_relation(*r1[i])) { r1_tables_indexes.push_back(i); continue; } @@ -291,7 +291,7 @@ namespace datalog { if (!found) { relation_plugin & r1_plugin = get_nonsieve_plugin(*r1[i]); relation_base* rel2; - if(r1_plugin.can_handle_signature(r2_sig)) { + if (r1_plugin.can_handle_signature(r2_sig)) { rel2 = r1_plugin.mk_full(p, r2_sig, r1_kind); } else { @@ -307,7 +307,7 @@ namespace datalog { } } for (unsigned i = 0; i < num_rels2; ++i) { - if(is_tableish_relation(*r2[i])) { + if (is_tableish_relation(*r2[i])) { r2_tables_indexes.push_back(i); continue; } @@ -315,7 +315,7 @@ namespace datalog { relation_plugin & r2_plugin = get_nonsieve_plugin(*r2[i]); family_id r2_kind = get_nonsieve_kind(*r2[i]); relation_base* rel1; - if(r2_plugin.can_handle_signature(r1_sig)) { + if (r2_plugin.can_handle_signature(r1_sig)) { rel1 = r2_plugin.mk_full(p, r1_sig, r2_kind); } else { @@ -331,7 +331,7 @@ namespace datalog { } } - if(!r1_tables_indexes.empty() && !r2_tables_indexes.empty()) { + if (!r1_tables_indexes.empty() && !r2_tables_indexes.empty()) { //We may perhaps want to group the table relations by kinds so that tables of the same kind //get joined... diff --git a/src/muz_qe/dl_sieve_relation.cpp b/src/muz_qe/dl_sieve_relation.cpp index e80462900..9f9419089 100644 --- a/src/muz_qe/dl_sieve_relation.cpp +++ b/src/muz_qe/dl_sieve_relation.cpp @@ -158,7 +158,7 @@ namespace datalog { inner_sig_singleton.push_back(s[i]); inner_columns[i] = inner.can_handle_signature(inner_sig_singleton); } -#if Z3DEBUG +#if Z3DEBUG //we assume that if a relation plugin can handle two sets of columns separetely, //it can also handle them in one relation relation_signature inner_sig; @@ -246,7 +246,8 @@ namespace datalog { relation_base * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { relation_signature empty_sig; - relation_base * inner = get_manager().mk_full_relation(empty_sig, p, null_family_id); + relation_plugin& plugin = get_manager().get_appropriate_plugin(s); + relation_base * inner = plugin.mk_full(p, empty_sig, null_family_id); svector inner_cols; inner_cols.resize(s.size(), false); return mk_from_inner(s, inner_cols, inner); diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h index 9e8073ec2..a9dd869a9 100644 --- a/src/smt/theory_utvpi.h +++ b/src/smt/theory_utvpi.h @@ -65,7 +65,7 @@ namespace smt { class parent_trail; struct GExt : public Ext { - typedef literal explanation; + typedef std::pair explanation; }; class atom { @@ -113,15 +113,18 @@ namespace smt { // a negative cycle. class nc_functor { literal_vector m_antecedents; + unsigned_vector m_coeffs; theory_utvpi& m_super; public: nc_functor(theory_utvpi& s) : m_super(s) {} - void reset() { m_antecedents.reset(); } + void reset() { m_antecedents.reset(); m_coeffs.reset(); } literal_vector const& get_lits() const { return m_antecedents; } + unsigned_vector const& get_coeffs() const { return m_coeffs; } - void operator()(literal const & ex) { - if (ex != null_literal) { - m_antecedents.push_back(ex); + void operator()(std::pair const & ex) { + if (ex.first != null_literal) { + m_antecedents.push_back(ex.first); + m_coeffs.push_back(ex.second); } } diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index af372ea50..457ac83ad 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -197,6 +197,15 @@ namespace smt { inc_conflicts(); literal_vector const& lits = m_nc_functor.get_lits(); context & ctx = get_context(); + IF_VERBOSE(2, + verbose_stream() << "conflict:\n"; + for (unsigned i = 0; i < lits.size(); ++i) { + ast_manager& m = get_manager(); + expr_ref e(m); + ctx.literal2expr(lits[i], e); + verbose_stream() << mk_pp(e, m) << "\n"; + } + verbose_stream() << "\n";); TRACE("utvpi", tout << "conflict: "; for (unsigned i = 0; i < lits.size(); ++i) { @@ -213,7 +222,9 @@ namespace smt { vector params; if (get_manager().proofs_enabled()) { params.push_back(parameter(symbol("farkas"))); - params.resize(lits.size()+1, parameter(rational(1))); + for (unsigned i = 0; i < m_nc_functor.get_coeffs().size(); ++i) { + params.push_back(parameter(rational(m_nc_functor.get_coeffs()[i]))); + } } ctx.set_conflict( @@ -620,28 +631,28 @@ namespace smt { edge_id id = m_graph.get_num_edges(); th_var w1 = to_var(v1), w2 = to_var(v2); if (terms.size() == 1 && pos1) { - m_graph.add_edge(neg(w1), pos(w1), -weight-weight, l); - m_graph.add_edge(neg(w1), pos(w1), -weight-weight, l); + m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2)); + m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2)); } else if (terms.size() == 1 && !pos1) { - m_graph.add_edge(pos(w1), neg(w1), -weight-weight, l); - m_graph.add_edge(pos(w1), neg(w1), -weight-weight, l); + m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2)); + m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2)); } else if (pos1 && pos2) { - m_graph.add_edge(neg(w2), pos(w1), -weight, l); - m_graph.add_edge(neg(w1), pos(w2), -weight, l); + m_graph.add_edge(neg(w2), pos(w1), -weight, std::make_pair(l,1)); + m_graph.add_edge(neg(w1), pos(w2), -weight, std::make_pair(l,1)); } else if (pos1 && !pos2) { - m_graph.add_edge(pos(w2), pos(w1), -weight, l); - m_graph.add_edge(neg(w1), neg(w2), -weight, l); + m_graph.add_edge(pos(w2), pos(w1), -weight, std::make_pair(l,1)); + m_graph.add_edge(neg(w1), neg(w2), -weight, std::make_pair(l,1)); } else if (!pos1 && pos2) { - m_graph.add_edge(neg(w2), neg(w1), -weight, l); - m_graph.add_edge(pos(w1), pos(w2), -weight, l); + m_graph.add_edge(neg(w2), neg(w1), -weight, std::make_pair(l,1)); + m_graph.add_edge(pos(w1), pos(w2), -weight, std::make_pair(l,1)); } else { - m_graph.add_edge(pos(w1), neg(w2), -weight, l); - m_graph.add_edge(pos(w2), neg(w1), -weight, l); + m_graph.add_edge(pos(w1), neg(w2), -weight, std::make_pair(l,1)); + m_graph.add_edge(pos(w2), neg(w1), -weight, std::make_pair(l,1)); } return id; } From e35fd58968ec08dd4184e20b639e81a6aa4a9aa6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 May 2013 11:43:30 -0700 Subject: [PATCH 61/91] add rewriting option to simplify store equalities Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/array_rewriter.cpp | 38 ++++++++++++++++++++++ src/ast/rewriter/array_rewriter.h | 3 ++ src/ast/rewriter/array_rewriter_params.pyg | 1 + src/ast/rewriter/mk_simplified_app.cpp | 2 ++ src/ast/rewriter/th_rewriter.cpp | 4 ++- 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index 0bb7378f4..1f4418fd5 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -25,6 +25,7 @@ void array_rewriter::updt_params(params_ref const & _p) { array_rewriter_params p(_p); m_sort_store = p.sort_store(); m_expand_select_store = p.expand_select_store(); + m_expand_store_eq = p.expand_store_eq(); } void array_rewriter::get_param_descrs(param_descrs & r) { @@ -365,3 +366,40 @@ br_status array_rewriter::mk_set_subset(expr * arg1, expr * arg2, expr_ref & res return BR_REWRITE3; } +br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { + if (!m_expand_store_eq) { + return BR_FAILED; + } + expr* lhs1 = lhs; + while (m_util.is_store(lhs1)) { + lhs1 = to_app(lhs1)->get_arg(0); + } + expr* rhs1 = rhs; + while (m_util.is_store(rhs1)) { + rhs1 = to_app(rhs1)->get_arg(0); + } + if (lhs1 != rhs1) { + return BR_FAILED; + } + ptr_buffer fmls, args; + expr* e; + expr_ref tmp1(m()), tmp2(m()); +#define MK_EQ() \ + while (m_util.is_store(e)) { \ + args.push_back(lhs); \ + args.append(to_app(e)->get_num_args()-2,to_app(e)->get_args()+1); \ + mk_select(args.size(), args.c_ptr(), tmp1); \ + args[0] = rhs; \ + mk_select(args.size(), args.c_ptr(), tmp2); \ + fmls.push_back(m().mk_eq(tmp1, tmp2)); \ + e = to_app(e)->get_arg(0); \ + args.reset(); \ + } \ + + e = lhs; + MK_EQ(); + e = rhs; + MK_EQ(); + result = m().mk_and(fmls.size(), fmls.c_ptr()); + return BR_REWRITE_FULL; +} diff --git a/src/ast/rewriter/array_rewriter.h b/src/ast/rewriter/array_rewriter.h index f1362802d..5f20f61ba 100644 --- a/src/ast/rewriter/array_rewriter.h +++ b/src/ast/rewriter/array_rewriter.h @@ -31,12 +31,14 @@ class array_rewriter { array_util m_util; bool m_sort_store; bool m_expand_select_store; + bool m_expand_store_eq; template lbool compare_args(unsigned num_args, expr * const * args1, expr * const * args2); public: array_rewriter(ast_manager & m, params_ref const & p = params_ref()): m_util(m) { updt_params(p); + } ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } @@ -60,6 +62,7 @@ public: br_status mk_set_complement(expr * arg, expr_ref & result); br_status mk_set_difference(expr * arg1, expr * arg2, expr_ref & result); br_status mk_set_subset(expr * arg1, expr * arg2, expr_ref & result); + br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); }; #endif diff --git a/src/ast/rewriter/array_rewriter_params.pyg b/src/ast/rewriter/array_rewriter_params.pyg index 2e3ae9f6a..a43fadecf 100644 --- a/src/ast/rewriter/array_rewriter_params.pyg +++ b/src/ast/rewriter/array_rewriter_params.pyg @@ -2,4 +2,5 @@ def_module_params(module_name='rewriter', class_name='array_rewriter_params', export=True, params=(("expand_select_store", BOOL, False, "replace a (select (store ...) ...) term by an if-then-else term"), + ("expand_store_eq", BOOL, False, "reduce (store ...) = (store ...) with a common base into selects"), ("sort_store", BOOL, False, "sort nested stores when the indices are known to be different"))) diff --git a/src/ast/rewriter/mk_simplified_app.cpp b/src/ast/rewriter/mk_simplified_app.cpp index da615e195..a46e71582 100644 --- a/src/ast/rewriter/mk_simplified_app.cpp +++ b/src/ast/rewriter/mk_simplified_app.cpp @@ -62,6 +62,8 @@ struct mk_simplified_app::imp { st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); + else if (s_fid == m_ar_rw.get_fid()) + st = m_ar_rw.mk_eq_core(args[0], args[1], result); if (st != BR_FAILED) return st; diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 966544b78..5d19c53e3 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -169,7 +169,9 @@ struct th_rewriter_cfg : public default_rewriter_cfg { st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); - + else if (s_fid == m_ar_rw.get_fid()) + st = m_ar_rw.mk_eq_core(args[0], args[1], result); + if (st != BR_FAILED) return st; } From ac6488a19533166798b9cb733748cd844d3ed343 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 13 May 2013 13:21:45 -0700 Subject: [PATCH 62/91] relax pre-processing to untangle non-horn formulas, based on Eldarica/linear benchmarks Signed-off-by: Nikolaj Bjorner --- src/muz_qe/horn_tactic.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/muz_qe/horn_tactic.cpp b/src/muz_qe/horn_tactic.cpp index 5db4f1a00..9d331cbfa 100644 --- a/src/muz_qe/horn_tactic.cpp +++ b/src/muz_qe/horn_tactic.cpp @@ -124,6 +124,15 @@ class horn_tactic : public tactic { enum formula_kind { IS_RULE, IS_QUERY, IS_NONE }; + bool is_implication(expr* f) { + expr* e1; + while (is_forall(f)) { + f = to_quantifier(f)->get_expr(); + } + while (m.is_implies(f, e1, f)) ; + return is_predicate(f); + } + formula_kind get_formula_kind(expr_ref& f) { expr_ref tmp(f); normalize(tmp); @@ -149,7 +158,10 @@ class horn_tactic : public tactic { } } if (head) { - // f = m.mk_implies(f, head); + if (!is_implication(f)) { + f = m.mk_and(body.size(), body.c_ptr()); + f = m.mk_implies(f, head); + } return IS_RULE; } else { @@ -200,6 +212,7 @@ class horn_tactic : public tactic { break; default: msg << "formula is not in Horn fragment: " << mk_pp(g->form(i), m) << "\n"; + TRACE("horn", tout << msg.str();); throw tactic_exception(msg.str().c_str()); } } @@ -230,7 +243,15 @@ class horn_tactic : public tactic { model_converter_ref & mc, proof_converter_ref & pc) { - lbool is_reachable = m_ctx.query(q); + lbool is_reachable = l_undef; + + try { + is_reachable = m_ctx.query(q); + } + catch (default_exception& ex) { + IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";); + throw ex; + } g->inc_depth(); bool produce_models = g->models_enabled(); From 7fc93b94f54439fec2dc993415fdb0c6c0c86342 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 14 May 2013 08:54:04 -0700 Subject: [PATCH 63/91] remove unimplemented method Signed-off-by: Nuno Lopes --- src/tactic/aig/aig.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tactic/aig/aig.h b/src/tactic/aig/aig.h index 96aa59ee6..c8befd9b2 100644 --- a/src/tactic/aig/aig.h +++ b/src/tactic/aig/aig.h @@ -70,7 +70,6 @@ public: void max_sharing(aig_ref & r); void to_formula(aig_ref const & r, expr_ref & result); void to_formula(aig_ref const & r, goal & result); - void to_cnf(aig_ref const & r, goal & result); void display(std::ostream & out, aig_ref const & r) const; void display_smt2(std::ostream & out, aig_ref const & r) const; unsigned get_num_aigs() const; From 878d57d139b4be7344dfb2c0678b06efa8bf06ad Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 15 May 2013 09:23:57 -0700 Subject: [PATCH 64/91] minor code simplification Signed-off-by: Nuno Lopes --- src/tactic/aig/aig.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/tactic/aig/aig.cpp b/src/tactic/aig/aig.cpp index 997ec642e..9fdce96e9 100644 --- a/src/tactic/aig/aig.cpp +++ b/src/tactic/aig/aig.cpp @@ -869,11 +869,7 @@ struct aig_manager::imp { void mk_ite(aig * n) { aig_lit c, t, e; -#ifdef Z3DEBUG - bool ok = -#endif - m.is_ite(n, c, t, e); - SASSERT(ok); + VERIFY(m.is_ite(n, c, t, e)); if (c.is_inverted()) { c.invert(); std::swap(t, e); From e6c814987328f1bc7951f78bf66724f956e19a84 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 15 May 2013 10:50:46 -0700 Subject: [PATCH 65/91] horn rule bit blaster: fix propagation of output predicates when arity == 0 Signed-off-by: Nuno Lopes --- src/muz_qe/dl_mk_bit_blast.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/muz_qe/dl_mk_bit_blast.cpp b/src/muz_qe/dl_mk_bit_blast.cpp index 51a9d5927..a1eeac6f5 100644 --- a/src/muz_qe/dl_mk_bit_blast.cpp +++ b/src/muz_qe/dl_mk_bit_blast.cpp @@ -141,13 +141,17 @@ namespace datalog { func_decl_ref_vector const& new_funcs() const { return m_new_funcs; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - bool found = false; - for (unsigned j = 0; !found && j < num; ++j) { - found = m_util.is_mkbv(args[j]); - } - if (!found) { + if (num == 0) { + if (m_src->is_output_predicate(f)) + m_dst->set_output_predicate(f); return BR_FAILED; } + + for (unsigned i = 0; i < num; ++i) { + if (!m_util.is_mkbv(args[i])) + return BR_FAILED; + } + // // f(mk_bv(args),...) // @@ -260,7 +264,7 @@ namespace datalog { m_rewriter.m_cfg.set_dst(result); for (unsigned i = 0; !m_context.canceled() && i < sz; ++i) { rule * r = source.get_rule(i); - r->to_formula(fml); + r->to_formula(fml); if (blast(r, fml)) { proof_ref pr(m); if (m_context.generate_proof_trace()) { From 5efdc58194ccc00490772c49ff4ba783fb80d520 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 15 May 2013 13:17:00 -0700 Subject: [PATCH 66/91] horn clause bit blasting: propagate output predicates for predicates without rules (most likely an UNSAT prog) Signed-off-by: Nuno Lopes --- src/muz_qe/dl_mk_bit_blast.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/muz_qe/dl_mk_bit_blast.cpp b/src/muz_qe/dl_mk_bit_blast.cpp index a1eeac6f5..136b8ffae 100644 --- a/src/muz_qe/dl_mk_bit_blast.cpp +++ b/src/muz_qe/dl_mk_bit_blast.cpp @@ -279,6 +279,13 @@ namespace datalog { result->add_rule(r); } } + + // copy output predicates without any rule (bit-blasting not really needed) + const func_decl_set& decls = source.get_output_predicates(); + for (func_decl_set::iterator I = decls.begin(), E = decls.end(); I != E; ++I) { + if (!result->contains(*I)) + result->set_output_predicate(*I); + } if (m_context.get_model_converter()) { filter_model_converter* fmc = alloc(filter_model_converter, m); From 100e396618cef0f836304b24a15572d21146c46c Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 15 May 2013 13:33:42 -0700 Subject: [PATCH 67/91] fix typo in my previous commit Signed-off-by: Nuno Lopes --- src/muz_qe/dl_mk_bit_blast.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz_qe/dl_mk_bit_blast.cpp b/src/muz_qe/dl_mk_bit_blast.cpp index 136b8ffae..271003fff 100644 --- a/src/muz_qe/dl_mk_bit_blast.cpp +++ b/src/muz_qe/dl_mk_bit_blast.cpp @@ -283,7 +283,7 @@ namespace datalog { // copy output predicates without any rule (bit-blasting not really needed) const func_decl_set& decls = source.get_output_predicates(); for (func_decl_set::iterator I = decls.begin(), E = decls.end(); I != E; ++I) { - if (!result->contains(*I)) + if (!source.contains(*I)) result->set_output_predicate(*I); } From 6560fc0a2c85d8acb4289fc6d760b71877b36500 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 16 May 2013 09:58:31 -0700 Subject: [PATCH 68/91] add experimental Horn clause to AIG (AAG format) converter. Clauses should be over booleans only (or bit-blasted with fixedpoint.bit_blast=true). We will crash if that's not the case. Only linear clauses supported for now Signed-off-by: Nuno Lopes --- scripts/mk_project.py | 2 +- src/muz_qe/aig_exporter.cpp | 321 +++++++++++++++++++++++++++++++ src/muz_qe/aig_exporter.h | 66 +++++++ src/muz_qe/fixedpoint_params.pyg | 1 + src/muz_qe/rel_context.cpp | 8 + src/muz_qe/rel_context.h | 3 +- 6 files changed, 398 insertions(+), 3 deletions(-) create mode 100755 src/muz_qe/aig_exporter.cpp create mode 100755 src/muz_qe/aig_exporter.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 6ea794040..e6e7d5dc8 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -54,7 +54,7 @@ def init_project_def(): add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') # TODO: split muz_qe into muz, qe. Perhaps, we should also consider breaking muz into muz and pdr. - add_lib('muz_qe', ['smt', 'sat', 'smt2parser']) + add_lib('muz_qe', ['smt', 'sat', 'smt2parser', 'aig_tactic']) add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'muz_qe'], 'tactic/smtlogics') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') diff --git a/src/muz_qe/aig_exporter.cpp b/src/muz_qe/aig_exporter.cpp new file mode 100755 index 000000000..1b0dae1ba --- /dev/null +++ b/src/muz_qe/aig_exporter.cpp @@ -0,0 +1,321 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + aig_exporter.cpp + +Abstract: + + Export AIG files from horn clauses + +--*/ + +#include "aig_exporter.h" +#include "dl_context.h" +#include + +namespace datalog { + + aig_exporter::aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts) : + m_rules(rules), m_facts(facts), m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), + m_aigm(m), m_next_decl_id(1), m_next_aig_expr_id(2), m_num_and_gates(0), + m_latch_vars(m), m_latch_varsp(m), m_ruleid_var_set(m), m_ruleid_varp_set(m) + { + std::set predicates; + unsigned num_variables = 0; + for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), + E = m_rules.end_grouped_rules(); I != E; ++I) { + predicates.insert(I->m_key); + num_variables = std::max(num_variables, I->m_key->get_arity()); + } + + for (fact_vector::const_iterator I = facts->begin(), E = facts->end(); I != E; ++I) { + predicates.insert(I->first); + num_variables = std::max(num_variables, I->first->get_arity()); + } + + // reserve pred id = 0 for initalization purposes + unsigned num_preds = (unsigned)predicates.size() + 1; + + // poor's man round-up log2 + unsigned preds_bitsize = log2(num_preds); + if ((1U << preds_bitsize) < num_preds) + ++preds_bitsize; + SASSERT((1U << preds_bitsize) >= num_preds); + + for (unsigned i = 0; i < preds_bitsize; ++i) { + m_ruleid_var_set.push_back(m.mk_fresh_const("rule_id", m.mk_bool_sort())); + m_ruleid_varp_set.push_back(m.mk_fresh_const("rule_id_p", m.mk_bool_sort())); + } + + for (unsigned i = 0; i < num_variables; ++i) { + m_latch_vars.push_back(m.mk_fresh_const("latch_var", m.mk_bool_sort())); + m_latch_varsp.push_back(m.mk_fresh_const("latch_varp", m.mk_bool_sort())); + } + } + + void aig_exporter::assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs) { + unsigned id = 0; + if (decl && !m_decl_id_map.find(decl, id)) { + id = m_next_decl_id++; + SASSERT(id < (1U << vars.size())); + m_decl_id_map.insert(decl, id); + } + + for (unsigned i = 0; i < vars.size(); ++i) { + exprs.push_back((id & (1U << i)) ? vars[i] : m.mk_not(vars[i])); + } + } + + void aig_exporter::collect_var_substs(substitution& subst, const app *h, + const expr_ref_vector& vars, expr_ref_vector& eqs) { + for (unsigned i = 0; i < h->get_num_args(); ++i) { + expr *arg = h->get_arg(i); + expr *latchvar = vars.get(i); + + if (is_var(arg)) { + var *v = to_var(arg); + expr_offset othervar; + if (subst.find(v, 0, othervar)) { + eqs.push_back(m.mk_eq(latchvar, othervar.get_expr())); + } else { + subst.insert(v, 0, expr_offset(latchvar, 0)); + } + } else { + eqs.push_back(m.mk_eq(latchvar, arg)); + } + } + } + + void aig_exporter::operator()(std::ostream& out) { + expr_ref_vector transition_function(m), output_preds(m); + var_ref_vector input_vars(m); + + rule_counter& vc = m_rm.get_counter(); + expr_ref_vector exprs(m); + substitution subst(m); + + for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), + E = m_rules.end_grouped_rules(); I != E; ++I) { + for (rule_vector::iterator II = I->get_value()->begin(), + EE = I->get_value()->end(); II != EE; ++II) { + rule *r = *II; + unsigned numqs = r->get_positive_tail_size(); + if (numqs > 1) { + std::cerr << "non-linear clauses not supported\n"; + exit(-1); + } + + if (numqs != r->get_uninterpreted_tail_size()) { + std::cerr << "negation of queries not supported\n"; + exit(-1); + } + + exprs.reset(); + assert_pred_id(numqs ? r->get_tail(0)->get_decl() : 0, m_ruleid_var_set, exprs); + assert_pred_id(r->get_head()->get_decl(), m_ruleid_varp_set, exprs); + + subst.reset(); + subst.reserve(1, vc.get_max_rule_var(*r)+1); + if (numqs) + collect_var_substs(subst, r->get_tail(0), m_latch_vars, exprs); + collect_var_substs(subst, r->get_head(), m_latch_varsp, exprs); + + for (unsigned i = numqs; i < r->get_tail_size(); ++i) { + expr_ref e(m); + subst.apply(r->get_tail(i), e); + exprs.push_back(e); + } + + transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); + } + } + + // collect table facts + if (m_facts) { + for (fact_vector::const_iterator I = m_facts->begin(), E = m_facts->end(); I != E; ++I) { + exprs.reset(); + assert_pred_id(0, m_ruleid_var_set, exprs); + assert_pred_id(I->first, m_ruleid_varp_set, exprs); + + for (unsigned i = 0; i < I->second.size(); ++i) { + exprs.push_back(m.mk_eq(m_latch_varsp.get(i), I->second[i])); + } + + transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); + } + } + + expr *tr = m.mk_or(transition_function.size(), transition_function.c_ptr()); + aig_ref aig = m_aigm.mk_aig(tr); + expr_ref aig_expr(m); + m_aigm.to_formula(aig, aig_expr); + +#if 0 + std::cout << mk_pp(tr, m) << "\n\n"; + std::cout << mk_pp(aig_expr, m) << "\n\n"; +#endif + + // make rule_id vars latches + for (unsigned i = 0; i < m_ruleid_var_set.size(); ++i) { + m_latch_vars.push_back(m_ruleid_var_set.get(i)); + m_latch_varsp.push_back(m_ruleid_varp_set.get(i)); + } + + // create vars for latches + for (unsigned i = 0; i < m_latch_vars.size(); ++i) { + mk_var(m_latch_vars.get(i)); + mk_input_var(m_latch_varsp.get(i)); + } + + unsigned tr_id = expr_to_aig(aig_expr); + + // create latch next state variables: (ite tr varp var) + unsigned_vector latch_varp_ids; + for (unsigned i = 0; i < m_latch_vars.size(); ++i) { + unsigned in_val = mk_and(tr_id, get_var(m_latch_varsp.get(i))); + unsigned latch_val = mk_and(neg(tr_id), get_var(m_latch_vars.get(i))); + latch_varp_ids.push_back(mk_or(in_val, latch_val)); + } + m_latch_varsp.reset(); + + // create output variable (true iff an output predicate is derivable) + unsigned output_id = 0; + { + expr_ref_vector output(m); + const func_decl_set& preds = m_rules.get_output_predicates(); + + for (func_decl_set::iterator I = preds.begin(), E = preds.end(); I != E; ++I) { + exprs.reset(); + assert_pred_id(*I, m_ruleid_var_set, exprs); + output.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); + } + + expr *out = m.mk_or(output.size(), output.c_ptr()); + aig = m_aigm.mk_aig(out); + m_aigm.to_formula(aig, aig_expr); + output_id = expr_to_aig(aig_expr); + +#if 0 + std::cout << "output formula\n"; + std::cout << mk_pp(out, m) << "\n\n"; + std::cout << mk_pp(aig_expr, m) << "\n\n"; +#endif + } + + // 1) print header + // aag var_index inputs latches outputs andgates + out << "aag " << (m_next_aig_expr_id-1)/2 << ' ' << m_input_vars.size() + << ' ' << m_latch_vars.size() << " 1 " << m_num_and_gates << '\n'; + + // 2) print inputs + for (unsigned i = 0; i < m_input_vars.size(); ++i) { + out << m_input_vars[i] << '\n'; + } + + // 3) print latches + for (unsigned i = 0; i < m_latch_vars.size(); ++i) { + out << get_var(m_latch_vars.get(i)) << ' ' << latch_varp_ids[i] << '\n'; + } + + // 4) print outputs (just one for now) + out << output_id << '\n'; + + // 5) print formula + out << m_buffer.str(); + } + + unsigned aig_exporter::expr_to_aig(const expr *e) { + unsigned id; + if (m_aig_expr_id_map.find(e, id)) + return id; + + switch (e->get_kind()) { + case AST_APP: { + const app *a = to_app(e); + switch (a->get_decl_kind()) { + case OP_OR: + SASSERT(a->get_num_args() > 0); + id = expr_to_aig(a->get_arg(0)); + for (unsigned i = 1; i < a->get_num_args(); ++i) { + id = mk_or(id, expr_to_aig(a->get_arg(i))); + } + m_aig_expr_id_map.insert(e, id); + return id; + + case null_decl_kind: + return get_var(a); + + case OP_NOT: + return neg(expr_to_aig(a->get_arg(0))); + + case OP_FALSE: + return 0; + + case OP_TRUE: + return 1; + } + break;} + + case AST_VAR: + return get_var(e); + } + + UNREACHABLE(); + return 0; + } + + unsigned aig_exporter::neg(unsigned id) const { + return (id % 2) ? (id-1) : (id+1); + } + + unsigned aig_exporter::mk_and(unsigned id1, unsigned id2) { + if (id1 > id2) + std::swap(id1, id2); + + std::pair key(id1, id2); + and_gates_map::const_iterator I = m_and_gates_map.find(key); + if (I != m_and_gates_map.end()) + return I->second; + + unsigned id = mk_expr_id(); + m_buffer << id << ' ' << id1 << ' ' << id2 << '\n'; + m_and_gates_map[key] = id; + ++m_num_and_gates; + return id; + } + + unsigned aig_exporter::mk_or(unsigned id1, unsigned id2) { + return neg(mk_and(neg(id1), neg(id2))); + } + + unsigned aig_exporter::get_var(const expr *e) { + unsigned id; + if (m_aig_expr_id_map.find(e, id)) + return id; + return mk_input_var(e); + } + + unsigned aig_exporter::mk_var(const expr *e) { + SASSERT(!m_aig_expr_id_map.contains(e)); + unsigned id = mk_expr_id(); + m_aig_expr_id_map.insert(e, id); + return id; + } + + unsigned aig_exporter::mk_input_var(const expr *e) { + SASSERT(!m_aig_expr_id_map.contains(e)); + unsigned id = mk_expr_id(); + m_input_vars.push_back(id); + if (e) + m_aig_expr_id_map.insert(e, id); + return id; + } + + unsigned aig_exporter::mk_expr_id() { + unsigned id = m_next_aig_expr_id; + m_next_aig_expr_id += 2; + return id; + } +} diff --git a/src/muz_qe/aig_exporter.h b/src/muz_qe/aig_exporter.h new file mode 100755 index 000000000..f70945e7f --- /dev/null +++ b/src/muz_qe/aig_exporter.h @@ -0,0 +1,66 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + aig_exporter.h + +Abstract: + + Export AIG files from horn clauses + +--*/ + +#ifndef _AIG_EXPORTER_H_ +#define _AIG_EXPORTER_H_ + +#include "aig.h" +#include "dl_rule_set.h" +#include "rel_context.h" +#include +#include + +namespace datalog { + class aig_exporter { + public: + aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts = 0); + void operator()(std::ostream& out); + + private: + typedef obj_map decl_id_map; + typedef obj_map aig_expr_id_map; + typedef std::map, unsigned> and_gates_map; + + const rule_set& m_rules; + const fact_vector *m_facts; + ast_manager& m; + rule_manager& m_rm; + aig_manager m_aigm; + decl_id_map m_decl_id_map; + unsigned m_next_decl_id; + aig_expr_id_map m_aig_expr_id_map; + unsigned m_next_aig_expr_id; + and_gates_map m_and_gates_map; + unsigned m_num_and_gates; + + expr_ref_vector m_latch_vars, m_latch_varsp; + expr_ref_vector m_ruleid_var_set, m_ruleid_varp_set; + unsigned_vector m_input_vars; + + std::stringstream m_buffer; + + void assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs); + void collect_var_substs(substitution& subst, const app *h, + const expr_ref_vector& vars, expr_ref_vector& eqs); + unsigned expr_to_aig(const expr *e); + unsigned neg(unsigned id) const; + unsigned mk_and(unsigned id1, unsigned id2); + unsigned mk_or(unsigned id1, unsigned id2); + unsigned get_var(const expr *e); + unsigned mk_var(const expr *e); + unsigned mk_input_var(const expr *e = 0); + unsigned mk_expr_id(); + }; +} + +#endif diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index c516806a6..c2d33cb05 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -62,6 +62,7 @@ def_module_params('fixedpoint', ('print_statistics', BOOL, False, 'print statistics'), ('use_utvpi', BOOL, False, 'experimental use UTVPI strategy'), ('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), + ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), )) diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index b42adca79..7ad91ecb6 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -32,6 +32,7 @@ Revision History: #include"dl_sparse_table.h" #include"dl_table.h" #include"dl_table_relation.h" +#include"aig_exporter.h" namespace datalog { @@ -127,6 +128,13 @@ namespace datalog { } TRACE("dl", m_context.display(tout);); + if (m_context.get_params().dump_aig().size()) { + const char *filename = static_cast(m_context.get_params().dump_aig().c_ptr()); + aig_exporter aig(m_context.get_rules(), get_context(), &m_table_facts); + aig(std::ofstream(filename, std::ios_base::binary)); + exit(0); + } + compiler::compile(m_context, m_context.get_rules(), m_code, termination_code); TRACE("dl", m_code.display(*this, tout); ); diff --git a/src/muz_qe/rel_context.h b/src/muz_qe/rel_context.h index 8e4c6f2de..7b4ee551c 100644 --- a/src/muz_qe/rel_context.h +++ b/src/muz_qe/rel_context.h @@ -28,10 +28,9 @@ Revision History: namespace datalog { class context; + typedef vector > fact_vector; class rel_context { - typedef vector > fact_vector; - context& m_context; ast_manager& m; relation_manager m_rmanager; From 69b7c3ede7ef06e90a8529aac2c29975c3cb9d41 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 May 2013 15:36:27 -0700 Subject: [PATCH 69/91] fixing parity bug in model generation for UTVPI Signed-off-by: Nikolaj Bjorner --- src/smt/diff_logic.h | 80 +++++++++++++++++++++++--- src/smt/theory_utvpi.h | 6 +- src/smt/theory_utvpi_def.h | 115 ++++++++++++++++++++++++++++++++----- 3 files changed, 179 insertions(+), 22 deletions(-) diff --git a/src/smt/diff_logic.h b/src/smt/diff_logic.h index 7216bba7e..020268e57 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.h @@ -306,14 +306,6 @@ private: return true; } - // Update the assignment of variable v, that is, - // m_assignment[v] += inc - // This method also stores the old value of v in the assignment stack. - void acc_assignment(dl_var v, const numeral & inc) { - TRACE("diff_logic_bug", tout << "update v: " << v << " += " << inc << " m_assignment[v] " << m_assignment[v] << "\n";); - m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); - m_assignment[v] += inc; - } // Restore the assignment using the information in m_assignment_stack. // This method is called when make_feasible fails. @@ -827,6 +819,16 @@ public: } } + // Update the assignment of variable v, that is, + // m_assignment[v] += inc + // This method also stores the old value of v in the assignment stack. + void acc_assignment(dl_var v, const numeral & inc) { + TRACE("diff_logic_bug", tout << "update v: " << v << " += " << inc << " m_assignment[v] " << m_assignment[v] << "\n";); + m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); + m_assignment[v] += inc; + } + + struct every_var_proc { bool operator()(dl_var v) const { return true; @@ -837,6 +839,36 @@ public: display_core(out, every_var_proc()); } + void display_agl(std::ostream & out) const { + uint_set vars; + typename edges::const_iterator it = m_edges.begin(); + typename edges::const_iterator end = m_edges.end(); + for (; it != end; ++it) { + edge const& e = *it; + if (e.is_enabled()) { + vars.insert(e.get_source()); + vars.insert(e.get_target()); + } + } + out << "digraph "" {\n"; + + unsigned n = m_assignment.size(); + for (unsigned v = 0; v < n; v++) { + if (vars.contains(v)) { + out << "\"" << v << "\" [label=\"" << v << ":" << m_assignment[v] << "\"]\n"; + } + } + it = m_edges.begin(); + for (; it != end; ++it) { + edge const& e = *it; + if (e.is_enabled()) { + out << "\"" << e.get_source() << "\"->\"" << e.get_target() << "\"[label=\"" << e.get_weight() << "\"]\n"; + } + } + + out << "}\n"; + } + template void display_core(std::ostream & out, FilterAssignmentProc p) const { display_edges(out); @@ -1000,6 +1032,38 @@ public: } } + void compute_zero_succ(dl_var v, int_vector& succ) { + unsigned n = m_assignment.size(); + m_dfs_time.reset(); + m_dfs_time.resize(n, -1); + m_dfs_time[v] = 0; + succ.push_back(v); + numeral gamma; + for (unsigned i = 0; i < succ.size(); ++i) { + v = succ[i]; + edge_id_vector & edges = m_out_edges[v]; + typename edge_id_vector::iterator it = edges.begin(); + typename edge_id_vector::iterator end = edges.end(); + for (; it != end; ++it) { + edge_id e_id = *it; + edge & e = m_edges[e_id]; + if (!e.is_enabled()) { + continue; + } + SASSERT(e.get_source() == v); + set_gamma(e, gamma); + if (gamma.is_zero()) { + dl_var target = e.get_target(); + if (m_dfs_time[target] == -1) { + succ.push_back(target); + m_dfs_time[target] = 0; + } + } + } + + } + } + numeral get_assignment(dl_var v) const { return m_assignment[v]; } diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h index a9dd869a9..4a5d97d52 100644 --- a/src/smt/theory_utvpi.h +++ b/src/smt/theory_utvpi.h @@ -262,7 +262,11 @@ namespace smt { private: - rational mk_value(theory_var v); + rational mk_value(theory_var v, bool is_int); + + bool is_parity_ok(unsigned v) const; + + void enforce_parity(); void validate_model(); diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 457ac83ad..6c59b568e 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -397,7 +397,6 @@ namespace smt { template final_check_status theory_utvpi::final_check_eh() { SASSERT(is_consistent()); - TRACE("utvpi", display(tout);); if (can_propagate()) { propagate(); return FC_CONTINUE; @@ -424,7 +423,7 @@ namespace smt { unsigned sz = get_num_vars(); for (unsigned i = 0; i < sz; ++i) { enode* e = get_enode(i); - if (a.is_int(e->get_owner())) { + if (!a.is_int(e->get_owner())) { continue; } th_var v1 = to_var(i); @@ -511,7 +510,7 @@ namespace smt { template theory_var theory_utvpi::mk_term(app* n) { - TRACE("utvpi", tout << mk_pp(n, get_manager()) << "\n";); + TRACE("utvpi", tout << mk_pp(n, get_manager()) << "\n";); context& ctx = get_context(); bool cl = m_test.linearize(n); @@ -519,7 +518,7 @@ namespace smt { found_non_utvpi_expr(n); return null_theory_var; } - + coeffs coeffs; rational w; mk_coeffs(m_test.get_linearization(), coeffs, w); @@ -667,14 +666,80 @@ namespace smt { return m_graph.is_feasible(); } + + template + bool theory_utvpi::is_parity_ok(unsigned i) const { + th_var v1 = to_var(i); + th_var v2 = neg(v1); + rational r1 = m_graph.get_assignment(v1).get_rational(); + rational r2 = m_graph.get_assignment(v2).get_rational(); + return r1.is_even() == r2.is_even(); + } + + + template + void theory_utvpi::enforce_parity() { + unsigned_vector todo; + + unsigned sz = get_num_vars(); + for (unsigned i = 0; i < sz; ++i) { + enode* e = get_enode(i); + if (a.is_int(e->get_owner()) && !is_parity_ok(i)) { + todo.push_back(i); + } + } + if (todo.empty()) { + return; + } + while (!todo.empty()) { + unsigned i = todo.back(); + if (is_parity_ok(i)) { + continue; + } + todo.pop_back(); + th_var v1 = to_var(i); + th_var v2 = neg(v1); + TRACE("utvpi", tout << "disparity: " << v1 << "\n";); + int_vector zero_v; + m_graph.compute_zero_succ(v1, zero_v); + bool found0 = false; + for (unsigned j = 0; !found0 && j < zero_v.size(); ++j) { + found0 = + (to_var(m_zero_int) == zero_v[j]) || + (neg(to_var(m_zero_int)) == zero_v[j]); + } + if (found0) { + zero_v.reset(); + m_graph.compute_zero_succ(v2, zero_v); + } + TRACE("utvpi", + for (unsigned j = 0; j < zero_v.size(); ++j) { + tout << "increment: " << zero_v[j] << "\n"; + }); + + for (unsigned j = 0; j < zero_v.size(); ++j) { + int v = zero_v[j]; + m_graph.acc_assignment(v, numeral(1)); + th_var k = from_var(v); + if (!is_parity_ok(k)) { + TRACE("utvpi", tout << "new disparity: " << k << "\n";); + todo.push_back(k); + } + } + } + } + + // models: template void theory_utvpi::init_model(model_generator & m) { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); - // TBD: enforce strong or tight coherence? + // TBD: enforce strong or tight coherence + enforce_parity(); compute_delta(); - DEBUG_CODE(validate_model();); + // DEBUG_CODE(validate_model();); + validate_model(); } template @@ -688,7 +753,8 @@ namespace smt { } bool ok = true; expr* e = ctx.bool_var2expr(b); - switch(ctx.get_assignment(b)) { + lbool assign = ctx.get_assignment(b); + switch(assign) { case l_true: ok = eval(e); break; @@ -698,7 +764,23 @@ namespace smt { default: break; } - CTRACE("utvpi", !ok, tout << "validation failed: " << mk_pp(e, get_manager()) << "\n";); + CTRACE("utvpi", !ok, + tout << "validation failed:\n"; + tout << "Assignment: " << assign << "\n"; + m_atoms[i].display(*this, tout); + tout << "\n"; + display(tout); + m_graph.display_agl(tout); + ); + if (!ok) { + std::cout << "validation failed:\n"; + std::cout << "Assignment: " << assign << "\n"; + m_atoms[i].display(*this, std::cout); + std::cout << "\n"; + display(std::cout); + m_graph.display_agl(std::cout); + + } // CTRACE("utvpi", ok, tout << "validation success: " << mk_pp(e, get_manager()) << "\n";); SASSERT(ok); } @@ -751,7 +833,7 @@ namespace smt { return eval_num(e1); } if (is_uninterp_const(e)) { - return mk_value(mk_var(e)); + return mk_value(mk_var(e), a.is_int(e)); } TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); UNREACHABLE(); @@ -760,23 +842,30 @@ namespace smt { template - rational theory_utvpi::mk_value(th_var v) { + rational theory_utvpi::mk_value(th_var v, bool is_int) { SASSERT(v != null_theory_var); numeral val1 = m_graph.get_assignment(to_var(v)); numeral val2 = m_graph.get_assignment(neg(to_var(v))); numeral val = val1 - val2; rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational()); num = num/rational(2); - num = floor(num); + if (is_int && !num.is_int()) { + num = floor(num); + } + TRACE("utvpi", + expr* n = get_enode(v)->get_owner(); + tout << mk_pp(n, get_manager()) << " |-> (" << val1 << " - " << val2 << ")/2 = " << num << "\n";); + return num; } template model_value_proc * theory_utvpi::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); - rational num = mk_value(v); + bool is_int = a.is_int(n->get_owner()); + rational num = mk_value(v, is_int); TRACE("utvpi", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); - return alloc(expr_wrapper_proc, m_factory->mk_value(num, a.is_int(n->get_owner()))); + return alloc(expr_wrapper_proc, m_factory->mk_value(num, is_int)); } /** From ef2a9994a92ca23f683862371677fbe7ff7eea76 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 16 May 2013 19:58:14 -0700 Subject: [PATCH 70/91] fix UTVPI model generation Signed-off-by: Nikolaj Bjorner --- src/smt/theory_utvpi_def.h | 48 ++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 6c59b568e..6c85e40b9 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -33,7 +33,17 @@ Revision History: 3. Solve for x^+ and x^- 4. Check parity condition for integers (see Lahiri and Musuvathi 05) - 5. extract model for M(x) := (M(x^+)- M(x^-))/2 + This checks if x^+ and x^- are in the same component but of different + parities. + 5. Enforce parity on variables. This checks if x^+ and x^- have different + parities. If they have different parities, the assignment to one + of the variables is decremented (choose the variable that is not tightly + constrained with 0). + The process that adjusts parities converges: Suppose we break a parity + of a different variable y while fixing x's parity. A cyclic breaking/fixing + of parities implies there is a strongly connected component between x, y + and the two polarities of the variables. This contradicts the test in 4. + 6. extract model for M(x) := (M(x^+)- M(x^-))/2 --*/ @@ -677,6 +687,17 @@ namespace smt { } + /** + \brief adjust values for variables in the difference graph + such that for variables of integer sort it is + the case that x^+ - x^- is even. + The informal justification for the procedure enforce_parity is that + the graph does not contain a strongly connected component where + x^+ and x+- are connected. They can be independently changed. + Since we would like variables representing 0 (zero) map to 0, + we selectively update the subgraph that can be updated without + changing the value of zero (which should be 0). + */ template void theory_utvpi::enforce_parity() { unsigned_vector todo; @@ -693,10 +714,10 @@ namespace smt { } while (!todo.empty()) { unsigned i = todo.back(); + todo.pop_back(); if (is_parity_ok(i)) { continue; } - todo.pop_back(); th_var v1 = to_var(i); th_var v2 = neg(v1); TRACE("utvpi", tout << "disparity: " << v1 << "\n";); @@ -708,18 +729,20 @@ namespace smt { (to_var(m_zero_int) == zero_v[j]) || (neg(to_var(m_zero_int)) == zero_v[j]); } + // variables that are tightly connected + // to 0 should not have their values changed. if (found0) { zero_v.reset(); m_graph.compute_zero_succ(v2, zero_v); } TRACE("utvpi", for (unsigned j = 0; j < zero_v.size(); ++j) { - tout << "increment: " << zero_v[j] << "\n"; + tout << "decrement: " << zero_v[j] << "\n"; }); for (unsigned j = 0; j < zero_v.size(); ++j) { int v = zero_v[j]; - m_graph.acc_assignment(v, numeral(1)); + m_graph.acc_assignment(v, numeral(-1)); th_var k = from_var(v); if (!is_parity_ok(k)) { TRACE("utvpi", tout << "new disparity: " << k << "\n";); @@ -727,6 +750,15 @@ namespace smt { } } } + SASSERT(m_graph.is_feasible()); + DEBUG_CODE( + for (unsigned i = 0; i < sz; ++i) { + enode* e = get_enode(i); + if (a.is_int(e->get_owner()) && !is_parity_ok(i)) { + IF_VERBOSE(0, verbose_stream() << "disparities not fixed\n";); + UNREACHABLE(); + } + }); } @@ -735,11 +767,9 @@ namespace smt { void theory_utvpi::init_model(model_generator & m) { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); - // TBD: enforce strong or tight coherence enforce_parity(); compute_delta(); - // DEBUG_CODE(validate_model();); - validate_model(); + DEBUG_CODE(validate_model();); } template @@ -849,9 +879,7 @@ namespace smt { numeral val = val1 - val2; rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational()); num = num/rational(2); - if (is_int && !num.is_int()) { - num = floor(num); - } + SASSERT(!is_int || num.is_int()); TRACE("utvpi", expr* n = get_enode(v)->get_owner(); tout << mk_pp(n, get_manager()) << " |-> (" << val1 << " - " << val2 << ")/2 = " << num << "\n";); From 5d1339beec902151e801dba46cac633d201c2d8d Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 17 May 2013 13:43:32 +0100 Subject: [PATCH 71/91] .NET/Java: API doc update for Context constructor. Signed-off-by: Christoph M. Wintersteiger --- src/api/dotnet/Context.cs | 15 +++++++++++++++ src/api/java/Context.java | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 35fe6611d..68cca046e 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -44,6 +44,21 @@ namespace Microsoft.Z3 /// /// Constructor. /// + /// + /// The following parameters can be set: + /// - proof (Boolean) Enable proof generation + /// - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting + /// - trace (Boolean) Tracing support for VCC + /// - trace_file_name (String) Trace out file for VCC traces + /// - timeout (unsigned) default timeout (in milliseconds) used for solvers + /// - well_sorted_check type checker + /// - auto_config use heuristics to automatically select solver and configure it + /// - model model generation for solvers, this parameter can be overwritten when creating a solver + /// - model_validate validate models produced by solvers + /// - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver + /// Note that in previous versions of Z3, this constructor was also used to set global and module parameters. + /// For this purpose we should now use + /// public Context(Dictionary settings) : base() { diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 5277dab38..6b6c63ac3 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -27,6 +27,21 @@ public class Context extends IDisposable /** * Constructor. + * + * The following parameters can be set: + * - proof (Boolean) Enable proof generation + * - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting + * - trace (Boolean) Tracing support for VCC + * - trace_file_name (String) Trace out file for VCC traces + * - timeout (unsigned) default timeout (in milliseconds) used for solvers + * - well_sorted_check type checker + * - auto_config use heuristics to automatically select solver and configure it + * - model model generation for solvers, this parameter can be overwritten when creating a solver + * - model_validate validate models produced by solvers + * - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver + * Note that in previous versions of Z3, this constructor was also used to set global and + * module parameters. For this purpose we should now use + * **/ public Context(Map settings) throws Z3Exception { From d1999b3424e6ad16a00867549db4ad1eb16b6000 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 17 May 2013 09:46:30 -0700 Subject: [PATCH 72/91] AIG exporter: create latches lazily properly check for constants Signed-off-by: Nuno Lopes --- src/muz_qe/aig_exporter.cpp | 23 ++++++++++++++--------- src/muz_qe/aig_exporter.h | 2 ++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/muz_qe/aig_exporter.cpp b/src/muz_qe/aig_exporter.cpp index 1b0dae1ba..c82ff7245 100755 --- a/src/muz_qe/aig_exporter.cpp +++ b/src/muz_qe/aig_exporter.cpp @@ -23,16 +23,13 @@ namespace datalog { m_latch_vars(m), m_latch_varsp(m), m_ruleid_var_set(m), m_ruleid_varp_set(m) { std::set predicates; - unsigned num_variables = 0; for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), E = m_rules.end_grouped_rules(); I != E; ++I) { predicates.insert(I->m_key); - num_variables = std::max(num_variables, I->m_key->get_arity()); } for (fact_vector::const_iterator I = facts->begin(), E = facts->end(); I != E; ++I) { predicates.insert(I->first); - num_variables = std::max(num_variables, I->first->get_arity()); } // reserve pred id = 0 for initalization purposes @@ -48,11 +45,19 @@ namespace datalog { m_ruleid_var_set.push_back(m.mk_fresh_const("rule_id", m.mk_bool_sort())); m_ruleid_varp_set.push_back(m.mk_fresh_const("rule_id_p", m.mk_bool_sort())); } + } - for (unsigned i = 0; i < num_variables; ++i) { + void aig_exporter::mk_latch_vars(unsigned n) { + for (int i = m_latch_vars.size() - 1; i <= (int)n; ++i) { m_latch_vars.push_back(m.mk_fresh_const("latch_var", m.mk_bool_sort())); m_latch_varsp.push_back(m.mk_fresh_const("latch_varp", m.mk_bool_sort())); } + SASSERT(m_latch_vars.size() > n); + } + + expr* aig_exporter::get_latch_var(unsigned i, const expr_ref_vector& vars) { + mk_latch_vars(i); + return vars.get(i); } void aig_exporter::assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs) { @@ -72,7 +77,7 @@ namespace datalog { const expr_ref_vector& vars, expr_ref_vector& eqs) { for (unsigned i = 0; i < h->get_num_args(); ++i) { expr *arg = h->get_arg(i); - expr *latchvar = vars.get(i); + expr *latchvar = get_latch_var(i, vars); if (is_var(arg)) { var *v = to_var(arg); @@ -140,7 +145,7 @@ namespace datalog { assert_pred_id(I->first, m_ruleid_varp_set, exprs); for (unsigned i = 0; i < I->second.size(); ++i) { - exprs.push_back(m.mk_eq(m_latch_varsp.get(i), I->second[i])); + exprs.push_back(m.mk_eq(get_latch_var(i, m_latch_varsp), I->second[i])); } transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); @@ -231,6 +236,9 @@ namespace datalog { if (m_aig_expr_id_map.find(e, id)) return id; + if (is_uninterp_const(e)) + return get_var(e); + switch (e->get_kind()) { case AST_APP: { const app *a = to_app(e); @@ -244,9 +252,6 @@ namespace datalog { m_aig_expr_id_map.insert(e, id); return id; - case null_decl_kind: - return get_var(a); - case OP_NOT: return neg(expr_to_aig(a->get_arg(0))); diff --git a/src/muz_qe/aig_exporter.h b/src/muz_qe/aig_exporter.h index f70945e7f..20b31f01b 100755 --- a/src/muz_qe/aig_exporter.h +++ b/src/muz_qe/aig_exporter.h @@ -49,6 +49,8 @@ namespace datalog { std::stringstream m_buffer; + void mk_latch_vars(unsigned n); + expr* get_latch_var(unsigned i, const expr_ref_vector& vars); void assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs); void collect_var_substs(substitution& subst, const app *h, const expr_ref_vector& vars, expr_ref_vector& eqs); From aea667d09bdac18dc244bc03a4f1b2301f5a34a8 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 17 May 2013 12:17:35 -0700 Subject: [PATCH 73/91] fix a one-too-many in my previous commit Signed-off-by: Nuno Lopes --- src/muz_qe/aig_exporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz_qe/aig_exporter.cpp b/src/muz_qe/aig_exporter.cpp index c82ff7245..1d6ef3633 100755 --- a/src/muz_qe/aig_exporter.cpp +++ b/src/muz_qe/aig_exporter.cpp @@ -48,7 +48,7 @@ namespace datalog { } void aig_exporter::mk_latch_vars(unsigned n) { - for (int i = m_latch_vars.size() - 1; i <= (int)n; ++i) { + for (unsigned i = m_latch_vars.size(); i <= n; ++i) { m_latch_vars.push_back(m.mk_fresh_const("latch_var", m.mk_bool_sort())); m_latch_varsp.push_back(m.mk_fresh_const("latch_varp", m.mk_bool_sort())); } From 56dedec740b3f7bf4c4ab35fa5758afc109a8e87 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 18 May 2013 10:02:53 -0700 Subject: [PATCH 74/91] fix build break include uint_set.h Signed-off-by: Nikolaj Bjorner --- src/smt/diff_logic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/smt/diff_logic.h b/src/smt/diff_logic.h index 020268e57..6fd156e41 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.h @@ -24,6 +24,7 @@ Revision History: #include"statistics.h" #include"trace.h" #include"warning.h" +#include"uint_set.h" typedef int dl_var; From dc91a754dd405b3348dd13f61a72207852869e9a Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 21 May 2013 10:48:55 -0700 Subject: [PATCH 75/91] improve clp solver - run default rule transformations - sort a predicate's rules by number of queries in the body to bias search Signed-off-by: Nuno Lopes --- src/muz_qe/clp_context.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/muz_qe/clp_context.cpp b/src/muz_qe/clp_context.cpp index 25ea1455b..5dd6fb9d8 100644 --- a/src/muz_qe/clp_context.cpp +++ b/src/muz_qe/clp_context.cpp @@ -66,9 +66,10 @@ namespace datalog { m_ctx.ensure_opened(); m_solver.reset(); m_goals.reset(); - rm.mk_query(query, m_ctx.get_rules()); - expr_ref head(m); - head = m_ctx.get_rules().last()->get_head(); + func_decl *head_decl = rm.mk_query(query, m_ctx.get_rules()); + m_ctx.apply_default_transformation(); + + expr_ref head(m_ctx.get_rules().get_predicate_rules(head_decl)[0]->get_head(), m); ground(head); m_goals.push_back(to_app(head)); return search(20, 0); @@ -125,6 +126,10 @@ namespace datalog { m_var_subst(e, m_ground.size(), m_ground.c_ptr(), e); } + static bool rule_sort_fn(const rule *r1, const rule *r2) { + return r1->get_uninterpreted_tail_size() < r2->get_uninterpreted_tail_size(); + } + lbool search(unsigned depth, unsigned index) { if (index == m_goals.size()) { return l_true; @@ -135,7 +140,10 @@ namespace datalog { IF_VERBOSE(1, verbose_stream() << "search " << depth << " " << index << "\n";); unsigned num_goals = m_goals.size(); app* head = m_goals[index].get(); - rule_vector const& rules = m_ctx.get_rules().get_predicate_rules(head->get_decl()); + + rule_vector rules(m_ctx.get_rules().get_predicate_rules(head->get_decl())); + std::stable_sort(rules.begin(), rules.end(), rule_sort_fn); + lbool status = l_false; for (unsigned i = 0; i < rules.size(); ++i) { rule* r = rules[i]; From 09945dc2cb753ea80951c2f084ec65f9e3528c3e Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 23 May 2013 08:07:19 -0700 Subject: [PATCH 76/91] Fix compilation error with gcc Signed-off-by: Leonardo de Moura --- src/muz_qe/rel_context.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index 7ad91ecb6..63e8ba700 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -131,7 +131,8 @@ namespace datalog { if (m_context.get_params().dump_aig().size()) { const char *filename = static_cast(m_context.get_params().dump_aig().c_ptr()); aig_exporter aig(m_context.get_rules(), get_context(), &m_table_facts); - aig(std::ofstream(filename, std::ios_base::binary)); + std::ofstream strm(filename, std::ios_base::binary); + aig(strm); exit(0); } From ccf10d0abe86c86c66bc5e7908d3f5deda042d10 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 25 May 2013 14:38:02 -0700 Subject: [PATCH 77/91] fix crash in PDR engine when transformations don't produce output predicates Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_mk_slice.cpp | 6 ++++++ src/muz_qe/pdr_dl_interface.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/muz_qe/dl_mk_slice.cpp b/src/muz_qe/dl_mk_slice.cpp index 1a97c1b81..5b9d43acc 100644 --- a/src/muz_qe/dl_mk_slice.cpp +++ b/src/muz_qe/dl_mk_slice.cpp @@ -710,6 +710,7 @@ namespace datalog { void mk_slice::declare_predicates(rule_set const& src, rule_set& dst) { obj_map::iterator it = m_sliceable.begin(), end = m_sliceable.end(); ptr_vector domain; + bool has_output = false; func_decl* f; for (; it != end; ++it) { domain.reset(); @@ -731,8 +732,13 @@ namespace datalog { } else if (src.is_output_predicate(p)) { dst.set_output_predicate(p); + has_output = true; } } + // disable slicing if the output predicates don't occur in rules. + if (!has_output) { + m_predicates.reset(); + } } bool mk_slice::rule_updated(rule const& r) { diff --git a/src/muz_qe/pdr_dl_interface.cpp b/src/muz_qe/pdr_dl_interface.cpp index 3ff54e68e..437c08f6a 100644 --- a/src/muz_qe/pdr_dl_interface.cpp +++ b/src/muz_qe/pdr_dl_interface.cpp @@ -133,6 +133,12 @@ lbool dl_interface::query(expr * query) { --num_unfolds; } } + + if (m_ctx.get_rules().get_output_predicates().empty()) { + m_context->set_unsat(); + return l_false; + } + query_pred = m_ctx.get_rules().get_output_predicate(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); From 7c12ab47165a88ab514c8cddcf65b1be6e784ec8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 25 May 2013 14:40:57 -0700 Subject: [PATCH 78/91] fix some compiler warnings Signed-off-by: Nikolaj Bjorner --- src/muz_qe/aig_exporter.cpp | 2 ++ src/muz_qe/clp_context.cpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/muz_qe/aig_exporter.cpp b/src/muz_qe/aig_exporter.cpp index 1d6ef3633..ca0030fc3 100755 --- a/src/muz_qe/aig_exporter.cpp +++ b/src/muz_qe/aig_exporter.cpp @@ -265,6 +265,8 @@ namespace datalog { case AST_VAR: return get_var(e); + default: + UNREACHABLE(); } UNREACHABLE(); diff --git a/src/muz_qe/clp_context.cpp b/src/muz_qe/clp_context.cpp index 5dd6fb9d8..94a956eb9 100644 --- a/src/muz_qe/clp_context.cpp +++ b/src/muz_qe/clp_context.cpp @@ -66,8 +66,9 @@ namespace datalog { m_ctx.ensure_opened(); m_solver.reset(); m_goals.reset(); - func_decl *head_decl = rm.mk_query(query, m_ctx.get_rules()); + rm.mk_query(query, m_ctx.get_rules()); m_ctx.apply_default_transformation(); + func_decl *head_decl = m_ctx.get_rules().get_output_predicate(); expr_ref head(m_ctx.get_rules().get_predicate_rules(head_decl)[0]->get_head(), m); ground(head); From edb2f8554d7418b8a202db374d810a66a18774eb Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 27 May 2013 17:45:56 -0700 Subject: [PATCH 79/91] Add new example Signed-off-by: Leonardo de Moura --- examples/c++/example.cpp | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index 55dbebe27..77f7702f2 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -952,6 +952,26 @@ void exists_expr_vector_example() { std::cout << ex << std::endl; } +void substitute_example() { + std::cout << "substitute example\n"; + context c; + expr x(c); + x = c.int_const("x"); + expr f(c); + f = (x == 2) || (x == 1); + std::cout << f << std::endl; + + expr two(c), three(c); + two = c.int_val(2); + three = c.int_val(3); + Z3_ast from[] = { two }; + Z3_ast to[] = { three }; + expr new_f(c); + new_f = to_expr(c, Z3_substitute(c, f, 1, from, to)); + + std::cout << new_f << std::endl; +} + int main() { try { demorgan(); std::cout << "\n"; @@ -983,12 +1003,13 @@ int main() { tactic_example9(); std::cout << "\n"; tactic_qe(); std::cout << "\n"; tst_visit(); std::cout << "\n"; - incremental_example1(); std::cout << "\n"; - incremental_example2(); std::cout << "\n"; - incremental_example3(); std::cout << "\n"; + incremental_example1(); std::cout << "\n"; + incremental_example2(); std::cout << "\n"; + incremental_example3(); std::cout << "\n"; enum_sort_example(); std::cout << "\n"; expr_vector_example(); std::cout << "\n"; exists_expr_vector_example(); std::cout << "\n"; + substitute_example(); std::cout << "\n"; std::cout << "done\n"; } catch (exception & ex) { From c6f4cdab0f0b47759c5e686462fd09449de90a0d Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 27 May 2013 17:49:03 -0700 Subject: [PATCH 80/91] Fix bug reported at https://z3.codeplex.com/workitem/41 Signed-off-by: Leonardo de Moura --- src/ast/pattern/database.smt2 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast/pattern/database.smt2 b/src/ast/pattern/database.smt2 index 9021e44f0..c42c9c25f 100644 --- a/src/ast/pattern/database.smt2 +++ b/src/ast/pattern/database.smt2 @@ -119,13 +119,13 @@ :pattern (?select (?select (?asElems e) a) i)))) (assert (forall ((x Int) (f Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?fClosedTime f))) 0) + (or (<= (+ a0 (* (- 1) (?fClosedTime f))) 0) (not (= (?isAllocated x a0) 1)) (= (?isAllocated (?select f x) a0) 1)) :pattern (?isAllocated (?select f x) a0)))) (assert (forall ((a Int) (e Int) (i Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?eClosedTime e))) 0) + (or (<= (+ a0 (* (- 1) (?eClosedTime e))) 0) (not (= (?isAllocated a a0) 1)) (= (?isAllocated (?select (?select e a) i) a0) 1)) :pattern (?isAllocated (?select (?select e a) i) a0)))) @@ -281,13 +281,13 @@ :pattern (IntsAllocated h (?StructGet_ s f))))) (assert (forall ((x Int) (f Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?fClosedTime f))) 0) + (or (<= (+ a0 (* (- 1) (?fClosedTime f))) 0) (not (?isAllocated_ x a0)) (?isAllocated_ (?select f x) a0)) :pattern (?isAllocated_ (?select f x) a0)))) (assert (forall ((a Int) (e Int) (i Int) (a0 Int)) (! - (or (<= (+ a0 (* -1 (?eClosedTime e))) 0) + (or (<= (+ a0 (* (- 1) (?eClosedTime e))) 0) (not (?isAllocated_ a a0)) (?isAllocated_ (?select (?select e a) i) a0)) :pattern (?isAllocated_ (?select (?select e a) i) a0)))) From 9a666966398c997bfa0d0b17e5fa0d5e24a84e4d Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 29 May 2013 14:35:32 -0700 Subject: [PATCH 81/91] merge hassel table code from branch Signed-off-by: Nuno Lopes --- src/muz_qe/dl_base.h | 14 +- src/muz_qe/dl_check_table.cpp | 81 +- src/muz_qe/dl_check_table.h | 10 + src/muz_qe/dl_compiler.cpp | 125 +++- src/muz_qe/dl_compiler.h | 2 + src/muz_qe/dl_hassel_common.cpp | 434 +++++++++++ src/muz_qe/dl_hassel_common.h | 1079 +++++++++++++++++++++++++++ src/muz_qe/dl_hassel_diff_table.cpp | 219 ++++++ src/muz_qe/dl_hassel_diff_table.h | 87 +++ src/muz_qe/dl_hassel_table.cpp | 27 + src/muz_qe/dl_hassel_table.h | 39 + src/muz_qe/dl_instruction.cpp | 59 ++ src/muz_qe/dl_instruction.h | 2 + src/muz_qe/dl_relation_manager.cpp | 46 ++ src/muz_qe/dl_relation_manager.h | 7 + src/muz_qe/dl_table_relation.cpp | 15 + src/muz_qe/dl_table_relation.h | 2 + src/muz_qe/rel_context.cpp | 11 + src/util/bit_vector.h | 7 + 19 files changed, 2259 insertions(+), 7 deletions(-) create mode 100755 src/muz_qe/dl_hassel_common.cpp create mode 100755 src/muz_qe/dl_hassel_common.h create mode 100755 src/muz_qe/dl_hassel_diff_table.cpp create mode 100755 src/muz_qe/dl_hassel_diff_table.h create mode 100644 src/muz_qe/dl_hassel_table.cpp create mode 100644 src/muz_qe/dl_hassel_table.h diff --git a/src/muz_qe/dl_base.h b/src/muz_qe/dl_base.h index 491bb261d..200ce2d83 100644 --- a/src/muz_qe/dl_base.h +++ b/src/muz_qe/dl_base.h @@ -331,6 +331,10 @@ namespace datalog { virtual mutator_fn * mk_filter_interpreted_fn(const base_object & t, app * condition) { return 0; } + virtual transformer_fn * mk_filter_interpreted_and_project_fn(const base_object & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) + { return 0; } + virtual transformer_fn * mk_select_equal_and_project_fn(const base_object & t, const element & value, unsigned col) { return 0; } @@ -454,8 +458,8 @@ namespace datalog { class convenient_join_fn : public join_fn { signature m_result_sig; protected: - const unsigned_vector m_cols1; - const unsigned_vector m_cols2; + unsigned_vector m_cols1; + unsigned_vector m_cols2; convenient_join_fn(const signature & o1_sig, const signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) @@ -470,8 +474,8 @@ namespace datalog { class convenient_join_project_fn : public join_fn { signature m_result_sig; protected: - const unsigned_vector m_cols1; - const unsigned_vector m_cols2; + unsigned_vector m_cols1; + unsigned_vector m_cols2; //it is non-const because it needs to be modified in sparse_table version of the join_project operator unsigned_vector m_removed_cols; @@ -498,7 +502,7 @@ namespace datalog { class convenient_project_fn : public convenient_transformer_fn { protected: - const unsigned_vector m_removed_cols; + unsigned_vector m_removed_cols; convenient_project_fn(const signature & orig_sig, unsigned col_cnt, const unsigned * removed_cols) : m_removed_cols(col_cnt, removed_cols) { diff --git a/src/muz_qe/dl_check_table.cpp b/src/muz_qe/dl_check_table.cpp index 5081654b5..ea4003e5f 100644 --- a/src/muz_qe/dl_check_table.cpp +++ b/src/muz_qe/dl_check_table.cpp @@ -82,6 +82,34 @@ namespace datalog { return alloc(join_fn, *this, t1, t2, col_cnt, cols1, cols2); } + class check_table_plugin::join_project_fn : public table_join_fn { + scoped_ptr m_tocheck; + scoped_ptr m_checker; + public: + join_project_fn(check_table_plugin& p, const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { + m_tocheck = p.get_manager().mk_join_project_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + m_checker = p.get_manager().mk_join_project_fn(checker(t1), checker(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + } + + virtual table_base* operator()(const table_base & t1, const table_base & t2) { + table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); + table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); + check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_join_fn * check_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) { + if (!check_kind(t1) || !check_kind(t2)) { + return 0; + } + return alloc(join_project_fn, *this, t1, t2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols); + } + class check_table_plugin::union_fn : public table_union_fn { scoped_ptr m_tocheck; scoped_ptr m_checker; @@ -120,7 +148,6 @@ namespace datalog { } table_base* operator()(table_base const& src) { - IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); @@ -135,6 +162,31 @@ namespace datalog { return alloc(project_fn, *this, t, col_cnt, removed_cols); } + class check_table_plugin::select_equal_and_project_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + select_equal_and_project_fn(check_table_plugin& p, const table_base & t, const table_element & value, unsigned col) { + m_checker = p.get_manager().mk_select_equal_and_project_fn(checker(t), value, col); + m_tocheck = p.get_manager().mk_select_equal_and_project_fn(tocheck(t), value, col); + } + + table_base* operator()(table_base const& src) { + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col) { + if (!check_kind(t)) { + return 0; + } + return alloc(select_equal_and_project_fn, *this, t, value, col); + } + class check_table_plugin::rename_fn : public table_transformer_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; @@ -233,6 +285,33 @@ namespace datalog { return 0; } + class check_table_plugin::filter_interpreted_and_project_fn : public table_transformer_fn { + scoped_ptr m_checker; + scoped_ptr m_tocheck; + public: + filter_interpreted_and_project_fn(check_table_plugin& p, const table_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols) + { + m_checker = p.get_manager().mk_filter_interpreted_and_project_fn(checker(t), condition, removed_col_cnt, removed_cols); + m_tocheck = p.get_manager().mk_filter_interpreted_and_project_fn(tocheck(t), condition, removed_col_cnt, removed_cols); + } + + table_base* operator()(table_base const& src) { + table_base* tchecker = (*m_checker)(checker(src)); + table_base* ttocheck = (*m_tocheck)(tocheck(src)); + check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); + return result; + } + }; + + table_transformer_fn * check_table_plugin::mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + if (check_kind(t)) { + return alloc(filter_interpreted_and_project_fn, *this, t, condition, removed_col_cnt, removed_cols); + } + return 0; + } + class check_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; diff --git a/src/muz_qe/dl_check_table.h b/src/muz_qe/dl_check_table.h index 40a3d5207..e4f439590 100644 --- a/src/muz_qe/dl_check_table.h +++ b/src/muz_qe/dl_check_table.h @@ -35,13 +35,16 @@ namespace datalog { unsigned m_count; protected: class join_fn; + class join_project_fn; class union_fn; class transformer_fn; class rename_fn; class project_fn; + class select_equal_and_project_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; + class filter_interpreted_and_project_fn; class filter_by_negation_fn; public: @@ -54,10 +57,15 @@ namespace datalog { virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols); virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta); virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols); + virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col); virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, @@ -65,6 +73,8 @@ namespace datalog { virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col); virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); + virtual table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); virtual table_intersection_filter_fn * mk_filter_by_negation_fn( const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index 05a0d24b2..3f16d0dab 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -73,6 +73,18 @@ namespace datalog { vars.get_cols2(), removed_cols.size(), removed_cols.c_ptr(), result)); } + void compiler::make_filter_interpreted_and_project(reg_idx src, app_ref & cond, + const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc) { + SASSERT(!removed_cols.empty()); + relation_signature res_sig; + relation_signature::from_project(m_reg_signatures[src], removed_cols.size(), + removed_cols.c_ptr(), res_sig); + result = get_fresh_register(res_sig); + + acc.push_back(instruction::mk_filter_interpreted_and_project(src, cond, + removed_cols.size(), removed_cols.c_ptr(), result)); + } + void compiler::make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col, reg_idx & result, instruction_block & acc) { relation_signature res_sig; @@ -619,6 +631,116 @@ namespace datalog { } // enforce interpreted tail predicates + unsigned ft_len = r->get_tail_size(); // full tail + ptr_vector tail; + for (unsigned tail_index = ut_len; tail_index < ft_len; ++tail_index) { + tail.push_back(r->get_tail(tail_index)); + } + + if (!tail.empty()) { + app_ref filter_cond(tail.size() == 1 ? to_app(tail.back()) : m.mk_and(tail.size(), tail.c_ptr()), m); + ptr_vector filter_vars; + get_free_vars(filter_cond, filter_vars); + + // create binding + expr_ref_vector binding(m); + binding.resize(filter_vars.size()+1); + + for (unsigned v = 0; v < filter_vars.size(); ++v) { + if (!filter_vars[v]) + continue; + + int2ints::entry * entry = var_indexes.find_core(v); + unsigned src_col; + if (entry) { + src_col = entry->get_data().m_value.back(); + } else { + // we have an unbound variable, so we add an unbound column for it + relation_sort unbound_sort = filter_vars[v]; + + reg_idx new_reg; + bool new_dealloc; + make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + dealloc = new_dealloc; + filtered_res = new_reg; + + src_col = single_res_expr.size(); + single_res_expr.push_back(m.mk_var(v, unbound_sort)); + + entry = var_indexes.insert_if_not_there2(v, unsigned_vector()); + entry->get_data().m_value.push_back(src_col); + } + relation_sort var_sort = m_reg_signatures[filtered_res][src_col]; + binding[filter_vars.size()-v] = m.mk_var(src_col, var_sort); + } + + // check if there are any columns to remove + unsigned_vector remove_columns; + { + unsigned_vector var_idx_to_remove; + ptr_vector vars; + get_free_vars(r->get_head(), vars); + for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); + I != E; ++I) { + unsigned var_idx = I->m_key; + if (!vars.get(var_idx, 0)) { + unsigned_vector & cols = I->m_value; + for (unsigned i = 0; i < cols.size(); ++i) { + remove_columns.push_back(cols[i]); + } + var_idx_to_remove.push_back(var_idx); + } + } + + for (unsigned i = 0; i < var_idx_to_remove.size(); ++i) { + var_indexes.remove(var_idx_to_remove[i]); + } + + // update column idx for after projection state + if (!remove_columns.empty()) { + unsigned_vector offsets; + offsets.resize(single_res_expr.size(), 0); + + for (unsigned i = 0; i < remove_columns.size(); ++i) { + for (unsigned col = remove_columns[i]; col < offsets.size(); ++col) { + ++offsets[col]; + } + } + + for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); + I != E; ++I) { + unsigned_vector & cols = I->m_value; + for (unsigned i = 0; i < cols.size(); ++i) { + cols[i] -= offsets[cols[i]]; + } + } + } + } + + expr_ref renamed(m); + m_context.get_var_subst()(filter_cond, binding.size(), binding.c_ptr(), renamed); + app_ref app_renamed(to_app(renamed), m); + if (remove_columns.empty()) { + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); + acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); + } else { + reg_idx new_reg; + std::sort(remove_columns.begin(), remove_columns.end()); + make_filter_interpreted_and_project(filtered_res, app_renamed, remove_columns, new_reg, acc); + if (dealloc) + make_dealloc_non_void(filtered_res, acc); + filtered_res = new_reg; + } + dealloc = true; + } + +#if 0 + // this version is potentially better for non-symbolic tables, + // since it constraints each unbound column at a time (reducing the + // size of intermediate results). unsigned ft_len=r->get_tail_size(); //full tail for(unsigned tail_index=ut_len; tail_indexget_tail(tail_index); @@ -686,6 +808,7 @@ namespace datalog { acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); dealloc = true; } +#endif { //put together the columns of head relation @@ -737,7 +860,7 @@ namespace datalog { make_dealloc_non_void(new_head_reg, acc); } - finish: +// finish: m_instruction_observer.finish_rule(); } diff --git a/src/muz_qe/dl_compiler.h b/src/muz_qe/dl_compiler.h index 78b4623de..e0f9af424 100644 --- a/src/muz_qe/dl_compiler.h +++ b/src/muz_qe/dl_compiler.h @@ -145,6 +145,8 @@ namespace datalog { instruction_block & acc); void make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc); + void make_filter_interpreted_and_project(reg_idx src, app_ref & cond, + const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc); void make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col, reg_idx & result, instruction_block & acc); /** diff --git a/src/muz_qe/dl_hassel_common.cpp b/src/muz_qe/dl_hassel_common.cpp new file mode 100755 index 000000000..6201868ca --- /dev/null +++ b/src/muz_qe/dl_hassel_common.cpp @@ -0,0 +1,434 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_hassel_common.cpp + +Abstract: + + + +Revision History: + +--*/ + +#include "dl_hassel_common.h" +#include "dl_context.h" + +#include + +namespace datalog { + + static void formula_to_dnf_aux(app *and, unsigned idx, std::set& conjexpr, std::set& toplevel, ast_manager& m) { + if (idx == and->get_num_args()) { + std::vector v(conjexpr.begin(), conjexpr.end()); + toplevel.insert(m.mk_and((unsigned)v.size(), &v[0])); + return; + } + + expr *e = and->get_arg(idx); + if (is_app(e) && to_app(e)->get_decl_kind() == OP_OR) { + app *or = to_app(e); + // quick subsumption test: if any of the elements of the OR is already ANDed, then we skip this OR + for (unsigned i = 0; i < or->get_num_args(); ++i) { + if (conjexpr.count(or->get_arg(i))) { + formula_to_dnf_aux(and, idx+1, conjexpr, toplevel, m); + return; + } + } + + for (unsigned i = 0; i < or->get_num_args(); ++i) { + std::set conjexpr2(conjexpr); + conjexpr2.insert(or->get_arg(i)); + formula_to_dnf_aux(and, idx+1, conjexpr2, toplevel, m); + } + } else { + conjexpr.insert(e); + formula_to_dnf_aux(and, idx+1, conjexpr, toplevel, m); + } + } + + expr_ref formula_to_dnf(expr_ref f) { + app *a = to_app(f); + SASSERT(a->get_decl_kind() == OP_AND); + std::set toplevel, conjexpr; + formula_to_dnf_aux(a, 0, conjexpr, toplevel, f.m()); + + if (toplevel.size() > 1) { + std::vector v(toplevel.begin(), toplevel.end()); + return expr_ref(f.m().mk_or((unsigned)v.size(), &v[0]), f.m()); + } else { + return expr_ref(*toplevel.begin(), f.m()); + } + } + + bool bit_vector::contains(const bit_vector & other) const { + unsigned n = num_words(); + if (n == 0) + return true; + + for (unsigned i = 0; i < n - 1; ++i) { + if ((m_data[i] & other.m_data[i]) != other.m_data[i]) + return false; + } + unsigned bit_rest = m_num_bits % 32; + unsigned mask = (1 << bit_rest) - 1; + if (mask == 0) mask = UINT_MAX; + unsigned other_data = other.m_data[n-1] & mask; + return (m_data[n-1] & other_data) == other_data; + } + + bool bit_vector::contains(const bit_vector & other, unsigned idx) const { + // TODO: optimize this to avoid copy + return slice(idx, other.size()).contains(other); + } + + bool bit_vector::contains_consecutive_zeros() const { + unsigned n = num_words(); + if (n == 0) + return false; + + for (unsigned i = 0; i < n - 1; ++i) { + if ((((m_data[i] << 1) | m_data[i]) & 0xAAAAAAAA) != 0xAAAAAAAA) + return true; + } + unsigned bit_rest = m_num_bits % 32; + unsigned mask = (1 << bit_rest) - 1; + if (mask == 0) mask = UINT_MAX; + mask &= 0xAAAAAAAA; + return ((((m_data[n-1] << 1) | m_data[n-1]) & mask) != mask); + } + + bit_vector bit_vector::slice(unsigned idx, unsigned length) const { + bit_vector Res(length); + // TODO: optimize w/ memcpy when possible + for (unsigned i = idx; i < idx + length; ++i) { + Res.push_back(get(i)); + } + SASSERT(Res.size() == length); + return Res; + } + + void bit_vector::append(const bit_vector & other) { + if (other.empty()) + return; + + if ((m_num_bits % 32) == 0) { + unsigned prev_num_bits = m_num_bits; + resize(m_num_bits + other.m_num_bits); + memcpy(&get_bit_word(prev_num_bits), other.m_data, other.num_words() * sizeof(unsigned)); + return; + } + + // TODO: optimize the other cases. + for (unsigned i = 0; i < other.m_num_bits; ++i) { + push_back(other.get(i)); + } + } + + uint64 bit_vector::to_number(unsigned idx, unsigned length) const { + SASSERT(length <= 64); + uint64 r = 0; + for (unsigned i = 0; i < length; ++i) { + r = (r << 1) | (uint64)get(idx+i); + } + return r; + } + + bool bit_vector::operator<(bit_vector const & other) const { + SASSERT(m_num_bits == other.m_num_bits); + unsigned n = num_words(); + if (n == 0) + return false; + + for (unsigned i = 0; i < n - 1; ++i) { + if (m_data[i] > other.m_data[i]) + return false; + if (m_data[i] < other.m_data[i]) + return true; + } + + unsigned bit_rest = m_num_bits % 32; + unsigned mask = (1 << bit_rest) - 1; + if (mask == 0) mask = UINT_MAX; + return (m_data[n-1] & mask) < (other.m_data[n-1] & mask); + } + + table_information::table_information(table_plugin & p, const table_signature& sig) : + m_column_info(sig.size()+1), + m_bv_util(p.get_context().get_manager()), + m_decl_util(p.get_context().get_manager()) { + + unsigned column = 0; + for (unsigned i = 0; i < sig.size(); ++i) { + unsigned num_bits = uint64_log2(sig[i]); + SASSERT(num_bits == 64 || (1ULL << num_bits) == sig[i]); + m_column_info[i] = column; + column += num_bits; + } + m_column_info[sig.size()] = column; + } + + void table_information::expand_column_vector(unsigned_vector& v, const table_information *other) const { + unsigned_vector orig; + orig.swap(v); + + for (unsigned i = 0; i < orig.size(); ++i) { + unsigned col, limit; + if (orig[i] < get_num_cols()) { + col = column_idx(orig[i]); + limit = col + column_num_bits(orig[i]); + } else { + unsigned idx = orig[i] - get_num_cols(); + col = get_num_bits() + other->column_idx(idx); + limit = col + other->column_num_bits(idx); + } + + for (; col < limit; ++col) { + v.push_back(col); + } + } + } + + void table_information::display(std::ostream & out) const { + out << '<'; + for (unsigned i = 0; i < get_num_cols(); ++i) { + if (i > 0) + out << ", "; + out << column_num_bits(i); + } + out << ">\n"; + } + + ternary_bitvector::ternary_bitvector(unsigned size, bool full) : + bit_vector() { + resize(size, full); + } + + ternary_bitvector::ternary_bitvector(uint64 n, unsigned num_bits) : + bit_vector(2 * num_bits) { + append_number(n, num_bits); + } + + ternary_bitvector::ternary_bitvector(const table_fact& f, const table_information& t) : + bit_vector(2 * t.get_num_bits()) { + for (unsigned i = 0; i < f.size(); ++i) { + SASSERT(t.column_idx(i) == size()); + append_number(f[i], t.column_num_bits(i)); + } + SASSERT(size() == t.get_num_bits()); + } + + void ternary_bitvector::fill1() { + memset(m_data, 0xFF, m_capacity * sizeof(unsigned)); + } + + unsigned ternary_bitvector::get(unsigned idx) const { + idx *= 2; + return (bit_vector::get(idx) << 1) | (unsigned)bit_vector::get(idx+1); + } + + void ternary_bitvector::set(unsigned idx, unsigned val) { + SASSERT(val == BIT_0 || val == BIT_1 || val == BIT_x); + idx *= 2; + bit_vector::set(idx, (val >> 1) != 0); + bit_vector::set(idx+1, (val & 1) != 0); + } + + void ternary_bitvector::push_back(unsigned val) { + SASSERT(val == BIT_0 || val == BIT_1 || val == BIT_x); + bit_vector::push_back((val >> 1) != 0); + bit_vector::push_back((val & 1) != 0); + } + + void ternary_bitvector::append_number(uint64 n, unsigned num_bits) { + SASSERT(num_bits <= 64); + for (int bit = num_bits-1; bit >= 0; --bit) { + if (n & (1ULL << bit)) { + push_back(BIT_1); + } else { + push_back(BIT_0); + } + } + } + + void ternary_bitvector::mk_idx_eq(unsigned idx, ternary_bitvector& val) { + for (unsigned i = 0; i < val.size(); ++i) { + set(idx+i, val.get(i)); + } + } + + ternary_bitvector ternary_bitvector::and(const ternary_bitvector& other) const{ + ternary_bitvector result(*this); + result &= other; + return result; + } + + void ternary_bitvector::neg(union_ternary_bitvector& result) const { + ternary_bitvector negated; + negated.resize(size()); + + for (unsigned i = 0; i < size(); ++i) { + switch (get(i)) { + case BIT_0: + negated.fill1(); + negated.set(i, BIT_1); + break; + case BIT_1: + negated.fill1(); + negated.set(i, BIT_0); + break; + default: + continue; + } + result.add_fact(negated); + } + } + + static void join_fix_eqs(ternary_bitvector& TBV, unsigned idx, unsigned col2_offset, + const unsigned_vector& cols1, const unsigned_vector& cols2, + union_ternary_bitvector& result) { + if (idx == cols1.size()) { + result.add_fact(TBV); + return; + } + + unsigned idx1 = cols1[idx]; + unsigned idx2 = cols2[idx] + col2_offset; + unsigned v1 = TBV.get(idx1); + unsigned v2 = TBV.get(idx2); + + if (v1 == BIT_x) { + if (v2 == BIT_x) { + // both x: duplicate row + ternary_bitvector TBV2(TBV); + TBV2.set(idx1, BIT_0); + TBV2.set(idx2, BIT_0); + join_fix_eqs(TBV2, idx+1, col2_offset, cols1, cols2, result); + + TBV.set(idx1, BIT_1); + TBV.set(idx2, BIT_1); + } else { + TBV.set(idx1, v2); + } + } else if (v2 == BIT_x) { + TBV.set(idx2, v1); + } else if (v1 != v2) { + // columns don't match + return; + } + join_fix_eqs(TBV, idx+1, col2_offset, cols1, cols2, result); + } + + void ternary_bitvector::join(const ternary_bitvector& other, + const unsigned_vector& cols1, + const unsigned_vector& cols2, + union_ternary_bitvector& result) const { + ternary_bitvector TBV(*this); + TBV.append(other); + join_fix_eqs(TBV, 0, size(), cols1, cols2, result); + } + + bool ternary_bitvector::project(const unsigned_vector& delcols, ternary_bitvector& result) const { + unsigned *rm_cols = delcols.c_ptr(); + + for (unsigned i = 0; i < size(); ++i) { + if (*rm_cols == i) { + ++rm_cols; + continue; + } + result.push_back(get(i)); + } + return true; + } + + static void copy_column(ternary_bitvector& CopyTo, const ternary_bitvector& CopyFrom, + unsigned col_dst, unsigned col_src, const table_information& src_table, + const table_information& dst_table) { + unsigned idx_dst = dst_table.column_idx(col_dst); + unsigned idx_src = src_table.column_idx(col_src); + unsigned num_bits = dst_table.column_num_bits(col_dst); + SASSERT(num_bits == src_table.column_num_bits(col_src)); + + for (unsigned i = 0; i < num_bits; ++i) { + CopyTo.set(idx_dst+i, CopyFrom.get(idx_src+i)); + } + } + + void ternary_bitvector::rename(const unsigned_vector& cyclecols, + const unsigned_vector& out_of_cycle_cols, + const table_information& src_table, + const table_information& dst_table, + ternary_bitvector& result) const { + result.resize(dst_table.get_num_bits()); + + for (unsigned i = 1; i < cyclecols.size(); ++i) { + copy_column(result, *this, cyclecols[i-1], cyclecols[i], src_table, dst_table); + } + copy_column(result, *this, cyclecols[cyclecols.size()-1], cyclecols[0], src_table, dst_table); + + for (unsigned i = 0; i < out_of_cycle_cols.size(); ++i) { + unsigned col = out_of_cycle_cols[i]; + copy_column(result, *this, col, col, src_table, dst_table); + } + } + + unsigned ternary_bitvector::size_in_bytes() const { + return sizeof(*this) + m_capacity; + } + + void ternary_bitvector::display(std::ostream & out) const { + for (unsigned i = 0; i < size(); ++i) { + switch (get(i)) { + case BIT_0: + out << '0'; + break; + case BIT_1: + out << '1'; + break; + case BIT_x: + out << 'x'; + break; + default: + UNREACHABLE(); + } + } + } + +#if Z3DEBUG + void ternary_bitvector::expand(std::set & BVs) const { + bit_vector BV(m_num_bits); + expand(BVs, BV, 0); + } + + void ternary_bitvector::expand(std::set & BVs, bit_vector &BV, unsigned idx) const { + if (idx == size()) { + BVs.insert(BV); + return; + } + + switch (get(idx)) { + case BIT_0: + BV.push_back(false); + expand(BVs, BV, idx+1); + break; + case BIT_1: + BV.push_back(true); + expand(BVs, BV, idx+1); + break; + case BIT_x: { // x: duplicate + bit_vector BV2(BV); + BV.push_back(false); + BV2.push_back(true); + expand(BVs, BV, idx+1); + expand(BVs, BV2, idx+1); + } + break; + default: + UNREACHABLE(); + } + } +#endif + +} diff --git a/src/muz_qe/dl_hassel_common.h b/src/muz_qe/dl_hassel_common.h new file mode 100755 index 000000000..7c1d1e614 --- /dev/null +++ b/src/muz_qe/dl_hassel_common.h @@ -0,0 +1,1079 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_hassel_common.h + +Abstract: + + + +Revision History: + +--*/ + +#ifndef _DL_HASSEL_COMMON_H_ +#define _DL_HASSEL_COMMON_H_ + +#include "bit_vector.h" +#include "dl_base.h" +#include "bv_decl_plugin.h" +#include "union_find.h" +#include +#include + +#define BIT_0 ((0<<1)|1) +#define BIT_1 ((1<<1)|0) +#define BIT_x ((1<<1)|1) + +namespace datalog { + + expr_ref formula_to_dnf(expr_ref f); + + class bit_vector : public ::bit_vector { + public: + bit_vector() : ::bit_vector() {} + bit_vector(unsigned bits) : ::bit_vector(bits) {} + + bool contains(const bit_vector & other) const; + bool contains(const bit_vector & other, unsigned idx) const; + bool contains_consecutive_zeros() const; + + bit_vector slice(unsigned idx, unsigned length) const; + void append(const bit_vector & other); + + uint64 to_number(unsigned idx, unsigned length) const; + + // for std::less operations + bool operator<(bit_vector const & other) const; + }; + + + class table_information { + unsigned_vector m_column_info; + bv_util m_bv_util; + dl_decl_util m_decl_util; + + public: + table_information(table_plugin & p, const table_signature& sig); + + unsigned get_num_bits() const { + return m_column_info.back(); + } + + unsigned get_num_cols() const { + return m_column_info.size()-1; + } + + unsigned column_idx(unsigned col) const { + return m_column_info[col]; + } + + unsigned column_num_bits(unsigned col) const { + return m_column_info[col+1] - m_column_info[col]; + } + + void expand_column_vector(unsigned_vector& v, const table_information *other = 0) const; + + void display(std::ostream & out) const; + + const bv_util& get_bv_util() const { return m_bv_util; } + const dl_decl_util& get_decl_util() const { return m_decl_util; } + }; + + + template class union_ternary_bitvector; + + + class ternary_bitvector : private bit_vector { + public: + ternary_bitvector() : bit_vector() {} + ternary_bitvector(unsigned size) : bit_vector(2 * size) {} + + ternary_bitvector(unsigned size, bool full); + ternary_bitvector(uint64 n, unsigned num_bits); + ternary_bitvector(const table_fact& f, const table_information& t); + + void swap(ternary_bitvector& other) { + SASSERT(size() == other.size()); + bit_vector::swap(other); + } + + void resize(unsigned new_size, bool val = false) { + bit_vector::resize(2 * new_size, val); + } + + void reset() { + m_num_bits = 0; + } + + unsigned size() const { + SASSERT((m_num_bits % 2) == 0); + return m_num_bits/2; + } + + void fill1(); + + void append(const ternary_bitvector & other) { bit_vector::append(other); } + bool contains(const ternary_bitvector & other) const { return bit_vector::contains(other); } + + bool is_empty() const { return contains_consecutive_zeros(); } + + unsigned get(unsigned idx) const; + void set(unsigned idx, unsigned val); + void push_back(unsigned val); + void append_number(uint64 n, unsigned num_bits); + void mk_idx_eq(unsigned idx, ternary_bitvector& val); + + ternary_bitvector and(const ternary_bitvector& other) const; + void neg(union_ternary_bitvector& result) const; + + void join(const ternary_bitvector& other, const unsigned_vector& cols1, + const unsigned_vector& cols2, union_ternary_bitvector& result) const; + + bool project(const unsigned_vector& delcols, ternary_bitvector& result) const; + + void rename(const unsigned_vector& cyclecols, const unsigned_vector& out_of_cycle_cols, + const table_information& src_table, const table_information& dst_table, + ternary_bitvector& result) const; + + static bool has_subtract() { return false; } + void subtract(const union_ternary_bitvector& other, + union_ternary_bitvector& result) const { UNREACHABLE(); } + + void display(std::ostream & out) const; + unsigned size_in_bytes() const; + +#if Z3DEBUG + void expand(std::set & BVs) const; +#endif + + private: +#if Z3DEBUG + void expand(std::set & BVs, bit_vector &BV, unsigned idx) const; +#endif + }; + + + template + class union_ternary_bitvector { + typedef std::list union_t; + + union_t m_bitvectors; + unsigned m_bv_size; //!< number of ternary bits + + public: + union_ternary_bitvector(unsigned bv_size) : m_bv_size(bv_size) {} + + union_ternary_bitvector(unsigned bv_size, bool full) : m_bv_size(bv_size) { + if (full) + mk_full(); + } + + union_ternary_bitvector and(const union_ternary_bitvector & Other) const { + if (empty()) + return *this; + if (Other.empty()) + return Other; + + union_ternary_bitvector Ret(m_bv_size); + + for (const_iterator I = begin(), E = end(); I != E; ++I) { + for (const_iterator II = Other.begin(), EE = Other.end(); II != EE; ++II) { + T row(I->and(*II)); + if (!row.is_empty()) + Ret.add_fact(row); + } + } + return Ret; + } + + union_ternary_bitvector or(const union_ternary_bitvector & Other) const { + if (empty()) + return Other; + if (Other.empty()) + return *this; + + union_ternary_bitvector Ret(*this); + Ret.add_facts(Other); + return Ret; + } + + union_ternary_bitvector neg() const { + union_ternary_bitvector Ret(m_bv_size); + Ret.mk_full(); + + union_ternary_bitvector negated(m_bv_size); + for (const_iterator I = begin(), E = end(); I != E; ++I) { + negated.reset(); + I->neg(negated); + Ret.swap(Ret.and(negated)); + } + return Ret; + } + + void subtract(const union_ternary_bitvector& other) { + if (!T::has_subtract()) { + swap(this->and(other.neg())); + return; + } + + union_ternary_bitvector subtracted(m_bv_size); + for (const_iterator I = begin(), E = end(); I != E; ++I) { + I->subtract(other, subtracted); + } + swap(subtracted); + } + +#if 0 + union_ternary_bitvector gc() const { + // Simple subsumption-based cleaning. + union_ternary_bitvector Ret(m_bv_size); + for (union_t::const_reverse_iterator I = m_bitvectors.rbegin(), E = m_bitvectors.rend(); I != E; ++I) { + Ret.add_fact(*I); + } + return Ret; + } +#endif + + void join(const union_ternary_bitvector& other, const unsigned_vector& cols1, + const unsigned_vector& cols2, union_ternary_bitvector& result) const { + for (const_iterator I = begin(), E = end(); I != E; ++I) { + for (const_iterator II = other.begin(), EE = other.end(); II != EE; ++II) { + I->join(*II, cols1, cols2, result); + } + } + } + + void rename(const unsigned_vector& cyclecols, const unsigned_vector& out_of_cycle_cols, + const table_information& src_table, const table_information& dst_table, + union_ternary_bitvector& result) const { + T row(m_bv_size); + for (const_iterator I = begin(), E = end(); I != E; ++I) { + row.reset(); + I->rename(cyclecols, out_of_cycle_cols, src_table, dst_table, row); + result.add_new_fact(row); + } + } + + void project(const unsigned_vector& delcols, union_ternary_bitvector& result) const { + unsigned new_size = m_bv_size - (delcols.size()-1); + T row(new_size); + + for (const_iterator I = begin(), E = end(); I != E; ++I) { + row.reset(); + if (I->project(delcols, row)) { + SASSERT(!row.is_empty()); + result.add_fact(row); + } + } + } + + private: + typedef union_find<> subset_ints; + + // returns 1 if row should be removed, 0 otherwise + static int fix_single_bit(T & BV, unsigned idx, unsigned value, const subset_ints& equalities) { + unsigned root = equalities.find(idx); + idx = root; + do { + unsigned bitval = BV.get(idx); + if (bitval == BIT_x) { + BV.set(idx, value); + } else if (bitval != value) { + return 1; + } + idx = equalities.next(idx); + } while (idx != root); + return 0; + } + + static int fix_single_bit(T & BV1, unsigned idx1, T & BV2, unsigned idx2, + subset_ints& equalities, bool discard_col) { + unsigned A = BV1.get(idx1); + unsigned B = BV2.get(idx2); + + if (A == BIT_x) { + if (B == BIT_x) { + // Both are don't cares. + /////// FIXME::: don't duplicate rows with diff table + if (!discard_col) + return 2; // duplicate row + equalities.merge(idx1, idx2); + return 0; + } else { + // only A is don't care. + return fix_single_bit(BV1, idx1, B, equalities); + } + } else if (B == BIT_x) { + // Only B is don't care. + return fix_single_bit(BV2, idx2, A, equalities); + } else if (A == B) { + return 0; + } else { + return 1; // remove row + } + } + + void fix_eq_bits(unsigned idx1, const T *BV, unsigned idx2, unsigned length, + subset_ints& equalities, const bit_vector& discard_cols) { + for (unsigned i = 0; i < length; ++i) { + for (union_t::iterator I = m_bitvectors.begin(), E = m_bitvectors.end(); I != E; ) { + T *eqBV = BV ? const_cast(BV) : &*I; + bool discard_col = discard_cols.get(idx1+i) || (!BV && discard_cols.get(idx2+i)); + + switch (fix_single_bit(*I, idx1+i, *eqBV, idx2+i, equalities, discard_col)) { + case 1: + // remove row + I = m_bitvectors.erase(I); + break; + + case 2: { + // duplicate row + T BV2(*I); + I->set(idx1+i, BIT_0); + I->set(idx2+i, BIT_0); + + BV2.set(idx1+i, BIT_1); + BV2.set(idx2+i, BIT_1); + m_bitvectors.insert(I, BV2); + ++I; + break;} + + default: + // bits fixed + ++I; + } + } + } + } + + /// make bits of table [idx,idx+max_length] equal to e sliced starting at idx2 + unsigned fix_eq_bits(unsigned idx, const expr *e, unsigned idx2, unsigned max_length, + const table_information& t, subset_ints& equalities, + const bit_vector & discard_cols) { + const bv_util& bvu = t.get_bv_util(); + const dl_decl_util& dutil = t.get_decl_util(); + + rational n; + unsigned bv_size; + if (bvu.is_numeral(e, n, bv_size)) { + T num(n.get_int64(), bv_size); + SASSERT(idx2 < bv_size); + max_length = std::min(max_length, bv_size - idx2); + fix_eq_bits(idx, &num, idx2, max_length, equalities, discard_cols); + return idx + max_length; + } + + uint64 num; + if (dutil.is_numeral(e, num)) { + T num_bv(num, max_length); + fix_eq_bits(idx, &num_bv, idx2, max_length, equalities, discard_cols); + return idx + max_length; + } + + if (bvu.is_concat(e)) { + const app *a = to_app(e); + + // skip the first elements of the concat if e.g. we have a top level extract + unsigned i = 0; + for (; i < a->get_num_args(); ++i) { + unsigned arg_size = bvu.get_bv_size(a->get_arg(i)); + if (idx2 < arg_size) + break; + idx2 -= arg_size; + } + + SASSERT(i < a->get_num_args()); + for (; max_length > 0 && i < a->get_num_args(); ++i) { + unsigned idx0 = idx; + idx = fix_eq_bits(idx, a->get_arg(i), idx2, max_length, t, equalities, discard_cols); + idx2 = 0; + SASSERT((idx - idx0) <= max_length); + max_length = max_length - (idx - idx0); + } + return idx; + } + + unsigned low, high; + expr *e2; + if (bvu.is_extract(e, low, high, e2)) { + SASSERT(low <= high); + unsigned size = bvu.get_bv_size(e2); + unsigned offset = size - (high+1) + idx2; + SASSERT(idx2 < (high-low+1)); + max_length = std::min(max_length, high - low + 1 - idx2); + return fix_eq_bits(idx, e2, offset, max_length, t, equalities, discard_cols); + } + + if (e->get_kind() == AST_VAR) { + unsigned idx_var = idx2 + t.column_idx(to_var(e)->get_idx()); + SASSERT(idx2 < t.column_num_bits(to_var(e)->get_idx())); + max_length = std::min(max_length, t.column_num_bits(to_var(e)->get_idx()) - idx2); + fix_eq_bits(idx, 0, idx_var, max_length, equalities, discard_cols); + return idx + max_length; + } + + NOT_IMPLEMENTED_YET(); + return 0; + } + + void filter(const expr *e, subset_ints& equalities, const bit_vector& discard_cols, + const table_information& t) { + switch (e->get_kind()) { + case AST_APP: { + const app *app = to_app(e); + switch (app->get_decl_kind()) { + case OP_AND: + for (unsigned i = 0; i < app->get_num_args(); ++i) { + filter(app->get_arg(i), equalities, discard_cols, t); + } + return; + + case OP_EQ: { + const expr *a = app->get_arg(0); + const var *v; + unsigned vidx = 0; + unsigned length; + + unsigned low, high; + expr *e2; + if (is_var(a)) { + v = to_var(a); + length = t.column_num_bits(v->get_idx()); + } else if (t.get_bv_util().is_extract(a, low, high, e2)) { + vidx = t.get_bv_util().get_bv_size(e2) - high - 1; + length = high - low + 1; + SASSERT(is_var(e2)); + v = to_var(e2); + } else { + NOT_IMPLEMENTED_YET(); + } + vidx += t.column_idx(v->get_idx()); + + unsigned final_idx = fix_eq_bits(vidx, app->get_arg(1), 0, length, t, equalities, discard_cols); + SASSERT(final_idx == vidx + length); + (void)final_idx; + return;} + + case OP_FALSE: + reset(); + return; + + case OP_NOT: { + union_ternary_bitvector sub(m_bv_size, true); + sub.filter(app->get_arg(0), equalities, discard_cols, t); + this->subtract(sub); + return;} + + case OP_OR: { + union_ternary_bitvector orig(m_bv_size); + swap(orig); + for (unsigned i = 0; i < app->get_num_args(); ++i) { + union_ternary_bitvector tmp(orig); + subset_ints eqs(equalities); + tmp.filter(app->get_arg(i), eqs, discard_cols, t); + add_facts(tmp); + } + return;} + + case OP_TRUE: + return; + + default: + std::cerr << "app decl: " << app->get_decl()->get_name() << " (" << app->get_decl_kind() << ")\n"; + NOT_IMPLEMENTED_YET(); + } + break;} + + case AST_VAR: { + // boolean var must be true (10) + SASSERT(t.column_num_bits(to_var(e)->get_idx()) == 1); + unsigned idx = t.column_idx(to_var(e)->get_idx()); + ternary_bitvector BV(1); + BV.push_back(BIT_1); + T BV2(BV); + fix_eq_bits(idx, &BV2, 0, 2, equalities, discard_cols); + return;} + + default: + break; + } + std::cerr << "expr kind: " << get_ast_kind_name(e->get_kind()) << '\n'; + NOT_IMPLEMENTED_YET(); + } + + public: + void filter(const expr *cond, const bit_vector& discard_cols, const table_information& table) { + // datastructure to store equalities with columns that will be projected out + union_find_default_ctx union_ctx; + subset_ints equalities(union_ctx); + for (unsigned i = 0, e = discard_cols.size(); i < e; ++i) { + equalities.mk_var(); + } + + filter(cond, equalities, discard_cols, table); + } + + bool contains(const T & fact) const { + for (const_iterator I = begin(), E = end(); I != E; ++I) { + if (I->contains(fact)) + return true; + } + return false; + } + + bool contains(const union_ternary_bitvector & other) const { + for (const_iterator I = other.begin(), E = other.end(); I != E; ++I) { + for (const_iterator II = begin(), EE = end(); II != EE; ++II) { + if (II->contains(*I)) + goto next_iter; + } + return false; +next_iter: ; + } + return true; + } + + unsigned num_disjs() const { + return (unsigned)m_bitvectors.size(); + } + + unsigned num_bytes() const { + unsigned size = sizeof(*this); + for (const_iterator I = begin(), E = end(); I != E; ++I) { + size += I->size_in_bytes(); + } + return size; + } + +#if Z3DEBUG + void expand(std::set & BVs) const { + for (const_iterator I = begin(), E = end(); I != E; ++I) { + I->expand(BVs); + } + } +#endif + + void add_facts(const union_ternary_bitvector & Other, union_ternary_bitvector *Delta = 0) { + for (const_iterator I = Other.begin(), E = Other.end(); I != E; ++I) { + if (add_fact(*I) && Delta) + Delta->add_fact(*I); + } + } + + bool add_fact(const T & fact) { + if (contains(fact)) + return false; + add_new_fact(fact); + return true; + } + + void add_new_fact(const T & fact) { + SASSERT(m_bv_size == fact.size()); + + // TODO: optimize sequence (karnaugh maps??) + // At least join 1-bit different BVs + m_bitvectors.push_back(fact); + } + + void mk_full() { + reset(); + add_new_fact(T(m_bv_size, true)); + } + + typedef typename union_t::const_iterator const_iterator; + + const_iterator begin() const { return m_bitvectors.begin(); } + const_iterator end() const { return m_bitvectors.end(); } + + bool empty() const { return m_bitvectors.empty(); } + void reset() { m_bitvectors.clear(); } + + void swap(union_ternary_bitvector& other) { + SASSERT(m_bv_size == other.m_bv_size); + m_bitvectors.swap(other.m_bitvectors); + } + + void display(std::ostream & out) const { + out << '#' << num_disjs() << " (bv" << m_bv_size << ") "; + + bool first = true; + for (const_iterator I = begin(), E = end(); I != E; ++I) { + if (!first) + out << " \\/ "; + first = false; + I->display(out); + } + out << '\n'; + } + }; + + + template + class common_hassel_table_plugin : public table_plugin { + public: + common_hassel_table_plugin(symbol &s, relation_manager & manager) : + table_plugin(s, manager) { } + + virtual table_base * mk_empty(const table_signature & s) { + return alloc(T, *this, s); + } + + virtual table_base * mk_full(func_decl* p, const table_signature & s) { + T *t = static_cast(mk_empty(s)); + t->mk_full(); + return t; + } + + virtual bool can_handle_signature(const table_signature & s) { + return s.functional_columns() == 0; + } + + private: + ast_manager& get_ast_manager() { return get_context().get_manager(); } + + class join_fn : public convenient_table_join_fn { + public: + join_fn(const T & t1, const T & t2, unsigned col_cnt, const unsigned *cols1, const unsigned *cols2) + : convenient_table_join_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2) { + t1.expand_column_vector(m_cols1); + t2.expand_column_vector(m_cols2); + } + + virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { + const T & T1 = static_cast(tb1); + const T & T2 = static_cast(tb2); + T * Res = static_cast(T1.get_plugin().mk_empty(get_result_signature())); + T1.m_bitsets.join(T2.m_bitsets, m_cols1, m_cols2, Res->m_bitsets); + TRACE("dl_hassel", tout << "final size: " << Res->get_size_estimate_rows() << '\n';); + return Res; + } + }; + + public: + virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(t1) || !check_kind(t2)) + return 0; + return alloc(join_fn, static_cast(t1), static_cast(t2), col_cnt, cols1, cols2); + } + + private: + class union_fn : public table_union_fn { + public: + virtual void operator()(table_base & tgt0, const table_base & src0, table_base * delta0) { + T & tgt = static_cast(tgt0); + const T & src = static_cast(src0); + T * delta = static_cast(delta0); + tgt.m_bitsets.add_facts(src.m_bitsets, delta ? &delta->m_bitsets : 0); + TRACE("dl_hassel", tout << "final size: " << tgt.get_size_estimate_rows() << '\n';); + } + }; + + public: + virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta) { + if (!check_kind(tgt) || !check_kind(src)) + return 0; + return alloc(union_fn); + } + + private: + class project_fn : public convenient_table_project_fn { + public: + project_fn(const T & t, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_table_project_fn(t.get_signature(), removed_col_cnt, removed_cols) { + t.expand_column_vector(m_removed_cols); + m_removed_cols.push_back(UINT_MAX); + } + + virtual table_base * operator()(const table_base & tb) { + const T & t = static_cast(tb); + T * res = static_cast(t.get_plugin().mk_empty(get_result_signature())); + t.m_bitsets.project(m_removed_cols, res->m_bitsets); + TRACE("dl_hassel", tout << "final size: " << res->get_size_estimate_rows() << '\n';); + return res; + } + }; + + public: + virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + if (!check_kind(t)) + return 0; + return alloc(project_fn, static_cast(t), col_cnt, removed_cols); + } + + private: + class rename_fn : public convenient_table_rename_fn { + unsigned_vector m_out_of_cycle; + public: + rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) + : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { + SASSERT(permutation_cycle_len >= 2); + idx_set cycle_cols; + for (unsigned i = 0; i < permutation_cycle_len; ++i) { + cycle_cols.insert(permutation_cycle[i]); + } + for (unsigned i = 0; i < orig_sig.size(); ++i) { + if (!cycle_cols.contains(i)) + m_out_of_cycle.push_back(i); + } + } + + virtual table_base * operator()(const table_base & tb) { + const T & t = static_cast(tb); + T * res = static_cast(t.get_plugin().mk_empty(get_result_signature())); + t.m_bitsets.rename(m_cycle, m_out_of_cycle, t, *res, res->m_bitsets); + TRACE("dl_hassel", tout << "final size: " << res->get_size_estimate_rows() << '\n';); + return res; + } + }; + + public: + virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) { + if (!check_kind(t)) + return 0; + return alloc(rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); + } + + private: + class filter_equal_fn : public table_mutator_fn { + typename T::bitset_t m_filter; + public: + filter_equal_fn(const T & t, const table_element val, unsigned col) : + m_filter(t.get_num_bits()) { + ternary_bitvector filter_row(t.get_num_bits(), true); + ternary_bitvector column(val, t.column_num_bits(col)); + filter_row.mk_idx_eq(t.column_idx(col), column); + m_filter.add_new_fact(filter_row); + } + + virtual void operator()(table_base & tb) { + T & t = static_cast(tb); + t.m_bitsets.swap(m_filter.and(t.m_bitsets)); + TRACE("dl_hassel", tout << "final size: " << t.get_size_estimate_rows() << '\n';); + } + }; + + public: + virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, + unsigned col) { + if (!check_kind(t)) + return 0; + return alloc(filter_equal_fn, static_cast(t), value, col); + } + + private: + static bool cond_is_guard(const expr *e, const table_information& t) { + switch (e->get_kind()) { + case AST_APP: { + const app *app = to_app(e); + switch (app->get_decl_kind()) { + case OP_AND: + case OP_OR: + case OP_NOT: + for (unsigned i = 0; i < app->get_num_args(); ++i) { + if (!cond_is_guard(app->get_arg(i), t)) + return false; + } + return true; + + case OP_EQ: { + const expr *a = app->get_arg(0), *b = app->get_arg(1); + + // column equality is not succinctly representable with TBVs + if (is_var(a) && is_var(b)) + return false; + + // (= var (concat var foo)) + if (t.get_bv_util().is_concat(b)) + return false; + + return true;} + + case OP_FALSE: + case OP_TRUE: + return true; + + default: + return false; + } + break;} + + case AST_VAR: + return true; + + default: + break; + } + return false; + } + + static void split_cond_guard(app *cond, expr_ref& guard, expr_ref& leftover, const table_information& t) { + expr_ref_vector guards(guard.m()); + expr_ref_vector leftovers(leftover.m()); + + if (is_app(cond) && to_app(cond)->get_decl_kind() == OP_AND) { + app *a = to_app(cond); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + expr *arg = a->get_arg(i); + if (cond_is_guard(arg, t)) { + guards.push_back(arg); + } else { + leftovers.push_back(arg); + } + } + } else if (cond_is_guard(cond, t)) { + guard = cond; + return; + } else { + leftover = cond; + return; + } + + if (guards.size() > 1) { + guard = formula_to_dnf(expr_ref(guard.m().mk_and(guards.size(), guards.c_ptr()), guard.m())); + } else if (guards.size() == 1) { + guard = guards.get(0); + } + + if (leftovers.size() > 1) { + leftover = formula_to_dnf(expr_ref(leftover.m().mk_and(leftovers.size(), leftovers.c_ptr()), leftover.m())); + } else if (leftovers.size() == 1) { + leftover = leftovers.get(0); + } + } + + class filter_fn : public table_mutator_fn { + expr_ref m_condition; + typename T::bitset_t m_filter; + bit_vector m_empty_bv; + public: + filter_fn(const T & t, ast_manager& m, app *condition) : + m_condition(m), m_filter(t.get_num_bits(), true) { + m_empty_bv.resize(t.get_num_bits(), false); + + expr_ref guard(m); + split_cond_guard(condition, guard, m_condition, t); + if (guard) + m_filter.filter(guard, m_empty_bv, t); + } + + virtual void operator()(table_base & tb) { + T & t = static_cast(tb); + // first apply guard and then run the interpreter on the leftover + t.m_bitsets.swap(m_filter.and(t.m_bitsets)); + if (m_condition) + t.m_bitsets.filter(m_condition, m_empty_bv, t); + TRACE("dl_hassel", tout << "final size: " << t.get_size_estimate_rows() << '\n';); + } + }; + + public: + virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition) { + if (!check_kind(t)) + return 0; + TRACE("dl_hassel", tout << mk_pp(condition, get_ast_manager()) << '\n';); + return alloc(filter_fn, static_cast(t), get_ast_manager(), condition); + } + + private: + class filter_proj_fn : public convenient_table_project_fn { + expr_ref m_condition; + typename T::bitset_t m_filter; + bit_vector m_col_list; // map: col idx -> bool (whether the column is to be removed) + public: + filter_proj_fn(const T & t, ast_manager& m, app *condition, + unsigned col_cnt, const unsigned * removed_cols) : + convenient_table_project_fn(t.get_signature(), col_cnt, removed_cols), + m_condition(m), m_filter(t.get_num_bits(), true) { + t.expand_column_vector(m_removed_cols); + + m_col_list.resize(t.get_num_bits(), false); + for (unsigned i = 0; i < m_removed_cols.size(); ++i) { + m_col_list.set(m_removed_cols[i]); + } + m_removed_cols.push_back(UINT_MAX); + + expr_ref guard(m); + split_cond_guard(condition, guard, m_condition, t); + if (guard) + m_filter.filter(guard, m_col_list, t); + } + + virtual table_base* operator()(const table_base & tb) { + const T & t = static_cast(tb); + // first apply guard and then run the interpreter on the leftover + typename T::bitset_t filtered(t.get_num_bits()); + filtered.swap(m_filter.and(t.m_bitsets)); + if (m_condition) + filtered.filter(m_condition, m_col_list, t); + + T * res = static_cast(t.get_plugin().mk_empty(get_result_signature())); + filtered.project(m_removed_cols, res->m_bitsets); + TRACE("dl_hassel", tout << "final size: " << res->get_size_estimate_rows() << '\n';); + return res; + } + }; + + public: + virtual table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols) { + if (!check_kind(t)) + return 0; + TRACE("dl_hassel", tout << mk_pp(condition, get_ast_manager()) << '\n';); + return alloc(filter_proj_fn, static_cast(t), get_ast_manager(), + condition, removed_col_cnt, removed_cols); + } + + + virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, + const unsigned * negated_cols) { + NOT_IMPLEMENTED_YET(); + } + }; + + template + class common_hassel_table : public table_base, public table_information { + public: + typedef T bitset_t; + + common_hassel_table(table_plugin & p, const table_signature & sig) : + table_base(p, sig), table_information(p, sig), m_bitsets(get_num_bits()) { } + + virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const { + SASSERT(!func_columns); + + if (empty()) + return get_plugin().mk_full(p, get_signature()); + + common_hassel_table *res = static_cast(get_plugin().mk_empty(get_signature())); + res->m_bitsets.swap(m_bitsets.neg()); + return res; + } + + virtual void add_fact(const table_fact & f) { + m_bitsets.add_fact(ternary_bitvector(f, *this)); + } + + virtual void add_new_fact(const table_fact & f) { + m_bitsets.add_new_fact(ternary_bitvector(f, *this)); + } + + virtual void remove_fact(table_element const* fact) { + NOT_IMPLEMENTED_YET(); + } + + virtual void reset() { + m_bitsets.reset(); + } + + void mk_full() { + m_bitsets.mk_full(); + } + + virtual table_base * clone() const { + common_hassel_table *res = static_cast(get_plugin().mk_empty(get_signature())); + res->m_bitsets = m_bitsets; + return res; + } + + virtual bool contains_fact(const table_fact & f) { + return m_bitsets.contains(ternary_bitvector(f, *this)); + } + + virtual bool empty() const { + return m_bitsets.empty(); + } + +#if Z3DEBUG + class our_iterator_core : public iterator_core { + class our_row : public row_interface { + const our_iterator_core & m_parent; + const table_information& m_table; + public: + our_row(const common_hassel_table & t, const our_iterator_core & parent) : + row_interface(t), m_parent(parent), m_table(t) {} + + virtual table_element operator[](unsigned col) const { + return m_parent.it->to_number(m_table.column_idx(col), m_table.column_num_bits(col)); + } + }; + + our_row m_row_obj; + std::set BVs; + std::set::iterator it; + + public: + our_iterator_core(const common_hassel_table & t, bool finished) : + m_row_obj(t, *this) { + if (finished) { + it = BVs.end(); + return; + } + t.m_bitsets.expand(BVs); + it = BVs.begin(); + } + + virtual bool is_finished() const { + return it == BVs.end(); + } + + virtual row_interface & operator*() { + SASSERT(!is_finished()); + return m_row_obj; + } + + virtual void operator++() { + SASSERT(!is_finished()); + ++it; + } + }; +#endif + + virtual iterator begin() const { +#if Z3DEBUG + return mk_iterator(alloc(our_iterator_core, *this, false)); +#else + SASSERT(0 && "begin() disabled"); + return mk_iterator(0); +#endif + } + + virtual iterator end() const { +#if Z3DEBUG + return mk_iterator(alloc(our_iterator_core, *this, true)); +#else + SASSERT(0 && "end() disabled"); + return mk_iterator(0); +#endif + } + + virtual void display(std::ostream & out) const { + table_information::display(out); + m_bitsets.display(out); + } + + virtual void to_formula(relation_signature const& sig, expr_ref& fml) const { + // TODO + } + + virtual unsigned get_size_estimate_rows() const { + return m_bitsets.num_disjs(); + } + + virtual unsigned get_size_estimate_bytes() const { + return m_bitsets.num_bytes(); + } + + T m_bitsets; + }; + +} + +#endif diff --git a/src/muz_qe/dl_hassel_diff_table.cpp b/src/muz_qe/dl_hassel_diff_table.cpp new file mode 100755 index 000000000..3ddcb3bbe --- /dev/null +++ b/src/muz_qe/dl_hassel_diff_table.cpp @@ -0,0 +1,219 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_hassel_diff_table.cpp + +Abstract: + + + +Revision History: + +--*/ + +#include "ast_printer.h" +#include "dl_context.h" +#include "dl_util.h" +#include "dl_hassel_diff_table.h" + + +namespace datalog { + + ternary_diff_bitvector::ternary_diff_bitvector(unsigned size, bool full) : + m_pos(size, full), m_neg(size) { } + + ternary_diff_bitvector::ternary_diff_bitvector(uint64 n, unsigned num_bits) : + m_pos(n, num_bits), m_neg(num_bits) { } + + ternary_diff_bitvector::ternary_diff_bitvector(const ternary_bitvector & tbv) : + m_pos(tbv), m_neg(tbv.size()) { } + + bool ternary_diff_bitvector::contains(const ternary_diff_bitvector & other) const { + return m_pos.contains(other.m_pos) && other.m_neg.contains(m_neg); + } + + bool ternary_diff_bitvector::is_empty() const { + if (m_pos.is_empty()) + return true; + + return m_neg.contains(m_pos); + } + + ternary_diff_bitvector ternary_diff_bitvector::and(const ternary_diff_bitvector& other) const { + ternary_diff_bitvector result(m_pos.and(other.m_pos)); + result.m_neg.swap(m_neg.or(other.m_neg)); + return result; + } + + void ternary_diff_bitvector::neg(union_ternary_bitvector& result) const { + // not(A\B) <-> (T\A) U B + ternary_diff_bitvector negated(size(), true); + negated.m_neg.add_new_fact(m_pos); + result.add_fact(negated); + + for (union_ternary_bitvector::const_iterator I = m_neg.begin(), + E = m_neg.end(); I != E; ++I) { + result.add_fact(*I); + } + } + + void ternary_diff_bitvector::subtract(const union_ternary_bitvector& other, + union_ternary_bitvector& result) const { + ternary_diff_bitvector newfact(*this); + for (union_ternary_bitvector::const_iterator I = other.begin(), + E = other.end(); I != E; ++I) { + if (!I->m_neg.empty()) { + NOT_IMPLEMENTED_YET(); + } + newfact.m_neg.add_fact(I->m_pos); + } + + if (!newfact.is_empty()) + result.add_fact(newfact); + } + + void ternary_diff_bitvector::join(const ternary_diff_bitvector& other, + const unsigned_vector& cols1, + const unsigned_vector& cols2, + union_ternary_bitvector& result) const { + unsigned new_size = size() + other.size(); + ternary_diff_bitvector res(new_size); + + res.m_pos = m_pos; + res.m_pos.append(other.m_pos); + + for (unsigned i = 0; i < cols1.size(); ++i) { + unsigned idx1 = cols1[i]; + unsigned idx2 = size() + cols2[i]; + unsigned v1 = res.m_pos.get(idx1); + unsigned v2 = res.m_pos.get(idx2); + + if (v1 == BIT_x) { + if (v2 == BIT_x) { + // add to subtracted TBVs: 1xx0 and 0xx1 + { + ternary_bitvector r(new_size, true); + r.set(idx1, BIT_0); + r.set(idx2, BIT_1); + res.m_neg.add_new_fact(r); + } + { + ternary_bitvector r(new_size, true); + r.set(idx1, BIT_1); + r.set(idx2, BIT_0); + res.m_neg.add_new_fact(r); + } + } else { + res.m_pos.set(idx1, v2); + } + } else if (v2 == BIT_x) { + res.m_pos.set(idx2, v1); + } else if (v1 != v2) { + // columns don't match + return; + } + } + + // handle subtracted TBVs: 1010 -> 1010xxx + if (!m_neg.empty()) { + ternary_bitvector padding(other.size(), true); + for (union_ternary_bitvector::const_iterator I = m_neg.begin(), + E = m_neg.end(); I != E; ++I) { + ternary_bitvector BV(*I); + BV.append(padding); + res.m_neg.add_new_fact(BV); + } + } + + if (!other.m_neg.empty()) { + ternary_bitvector padding(size(), true); + for (union_ternary_bitvector::const_iterator I = other.m_neg.begin(), + E = other.m_neg.end(); I != E; ++I) { + ternary_bitvector BV(padding); + BV.append(*I); + res.m_neg.add_new_fact(BV); + } + } + + result.add_fact(res); + } + + bool ternary_diff_bitvector::project(const unsigned_vector& delcols, ternary_diff_bitvector& result) const { + m_pos.project(delcols, result.m_pos); + if (m_neg.empty()) + return true; + + ternary_bitvector newneg; + for (union_ternary_bitvector::const_iterator I = m_neg.begin(), + E = m_neg.end(); I != E; ++I) { + for (unsigned i = 0; i < delcols.size()-1; ++i) { + unsigned idx = delcols[i]; + if (I->get(idx) != BIT_x && m_pos.get(idx) == BIT_x) + goto skip_row; + } + newneg.reset(); + I->project(delcols, newneg); + result.m_neg.add_fact(newneg); +skip_row: ; + } + return !result.is_empty(); + } + + void ternary_diff_bitvector::rename(const unsigned_vector& cyclecols, + const unsigned_vector& out_of_cycle_cols, + const table_information& src_table, + const table_information& dst_table, + ternary_diff_bitvector& result) const { + m_pos.rename(cyclecols, out_of_cycle_cols, src_table, dst_table, result.m_pos); + m_neg.rename(cyclecols, out_of_cycle_cols, src_table, dst_table, result.m_neg); + } + + unsigned ternary_diff_bitvector::get(unsigned idx) { + return m_pos.get(idx); + } + + void ternary_diff_bitvector::set(unsigned idx, unsigned val) { + m_pos.set(idx, val); + } + + void ternary_diff_bitvector::swap(ternary_diff_bitvector & other) { + m_pos.swap(other.m_pos); + m_neg.swap(other.m_neg); + } + + void ternary_diff_bitvector::reset() { + m_pos.reset(); + m_neg.reset(); + } + + void ternary_diff_bitvector::display(std::ostream & out) const { + m_pos.display(out); + if (!m_neg.empty()) { + out << " \\ "; + if (m_neg.num_disjs() > 1) out << '('; + m_neg.display(out); + if (m_neg.num_disjs() > 1) out << ')'; + } + } + + unsigned ternary_diff_bitvector::size_in_bytes() const { + return m_pos.size_in_bytes() + m_neg.num_bytes(); + } + +#if Z3DEBUG + void ternary_diff_bitvector::expand(std::set & BVs) const { + m_pos.expand(BVs); + SASSERT(!BVs.empty()); + + std::set NegBVs; + m_neg.expand(NegBVs); + BVs.erase(NegBVs.begin(), NegBVs.end()); + } +#endif + + hassel_diff_table_plugin::hassel_diff_table_plugin(relation_manager & manager) + : common_hassel_table_plugin(symbol("hassel_diff"), manager) {} + +} diff --git a/src/muz_qe/dl_hassel_diff_table.h b/src/muz_qe/dl_hassel_diff_table.h new file mode 100755 index 000000000..07340c7e8 --- /dev/null +++ b/src/muz_qe/dl_hassel_diff_table.h @@ -0,0 +1,87 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_hassel_diff_table.h + +Abstract: + + + +Revision History: + +--*/ + +#ifndef _DL_HASSEL_DIFF_TABLE_H_ +#define _DL_HASSEL_DIFF_TABLE_H_ + +#include "dl_hassel_common.h" + +namespace datalog { + + class hassel_diff_table; + + class ternary_diff_bitvector { + // pos \ (neg0 \/ ... \/ negn) + ternary_bitvector m_pos; + union_ternary_bitvector m_neg; + + public: + ternary_diff_bitvector() : m_pos(), m_neg(0) {} + ternary_diff_bitvector(unsigned size) : m_pos(size), m_neg(size) {} + ternary_diff_bitvector(unsigned size, bool full); + ternary_diff_bitvector(uint64 n, unsigned num_bits); + ternary_diff_bitvector(const ternary_bitvector & tbv); + + bool contains(const ternary_diff_bitvector & other) const; + bool is_empty() const; + + ternary_diff_bitvector and(const ternary_diff_bitvector& other) const; + void neg(union_ternary_bitvector& result) const; + + static bool has_subtract() { return true; } + void subtract(const union_ternary_bitvector& other, + union_ternary_bitvector& result) const; + + void join(const ternary_diff_bitvector& other, const unsigned_vector& cols1, + const unsigned_vector& cols2, union_ternary_bitvector& result) const; + + bool project(const unsigned_vector& delcols, ternary_diff_bitvector& result) const; + + void rename(const unsigned_vector& cyclecols, const unsigned_vector& out_of_cycle_cols, + const table_information& src_table, const table_information& dst_table, + ternary_diff_bitvector& result) const; + + unsigned get(unsigned idx); + void set(unsigned idx, unsigned val); + + void swap(ternary_diff_bitvector & other); + void reset(); + + unsigned size() const { return m_pos.size(); } + + void display(std::ostream & out) const; + unsigned size_in_bytes() const; + +#if Z3DEBUG + void expand(std::set & BVs) const; +#endif + }; + + typedef union_ternary_bitvector union_ternary_diff_bitvector; + + class hassel_diff_table : public common_hassel_table { + public: + hassel_diff_table(table_plugin & p, const table_signature & sig) : + common_hassel_table(p, sig) {} + }; + + class hassel_diff_table_plugin : public common_hassel_table_plugin { + public: + hassel_diff_table_plugin(relation_manager & manager); + }; + +} + +#endif diff --git a/src/muz_qe/dl_hassel_table.cpp b/src/muz_qe/dl_hassel_table.cpp new file mode 100644 index 000000000..6ec28df87 --- /dev/null +++ b/src/muz_qe/dl_hassel_table.cpp @@ -0,0 +1,27 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_hassel_table.cpp + +Abstract: + + + +Revision History: + +--*/ + +#include "ast_printer.h" +#include "dl_context.h" +#include "dl_util.h" +#include "dl_hassel_table.h" + + +namespace datalog { + + hassel_table_plugin::hassel_table_plugin(relation_manager & manager) + : common_hassel_table_plugin(symbol("hassel"), manager) {} + +} diff --git a/src/muz_qe/dl_hassel_table.h b/src/muz_qe/dl_hassel_table.h new file mode 100644 index 000000000..6c4e9c1fe --- /dev/null +++ b/src/muz_qe/dl_hassel_table.h @@ -0,0 +1,39 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_hassel_table.h + +Abstract: + + + +Revision History: + +--*/ + +#ifndef _DL_HASSEL_TABLE_H_ +#define _DL_HASSEL_TABLE_H_ + +#include "dl_hassel_common.h" + +namespace datalog { + + class hassel_table; + typedef union_ternary_bitvector union_ternary_bitvectors; + + class hassel_table : public common_hassel_table { + public: + hassel_table(table_plugin & p, const table_signature & sig) : + common_hassel_table(p, sig) {} + }; + + class hassel_table_plugin : public common_hassel_table_plugin { + public: + hassel_table_plugin(relation_manager & manager); + }; + +} + +#endif diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz_qe/dl_instruction.cpp index b103d743e..6b16968c2 100644 --- a/src/muz_qe/dl_instruction.cpp +++ b/src/muz_qe/dl_instruction.cpp @@ -526,6 +526,64 @@ namespace datalog { return alloc(instr_filter_interpreted, reg, condition); } + class instr_filter_interpreted_and_project : public instruction { + reg_idx m_src; + reg_idx m_res; + app_ref m_cond; + unsigned_vector m_cols; + public: + instr_filter_interpreted_and_project(reg_idx src, app_ref & condition, + unsigned col_cnt, const unsigned * removed_cols, reg_idx result) + : m_src(src), m_cond(condition), m_cols(col_cnt, removed_cols), + m_res(result) {} + + virtual bool perform(execution_context & ctx) { + if (!ctx.reg(m_src)) { + ctx.make_empty(m_res); + return true; + } + + relation_transformer_fn * fn; + relation_base & reg = *ctx.reg(m_src); + if (!find_fn(reg, fn)) { + fn = reg.get_manager().mk_filter_interpreted_and_project_fn(reg, m_cond, m_cols.size(), m_cols.c_ptr()); + if (!fn) { + throw default_exception( + "trying to perform unsupported filter_interpreted_and_project operation on a relation of kind %s", + reg.get_plugin().get_name().bare_str()); + } + store_fn(reg, fn); + } + + ctx.set_reg(m_res, (*fn)(reg)); + + if (ctx.eager_emptiness_checking() && ctx.reg(m_res)->empty()) { + ctx.make_empty(m_res); + } + return true; + } + + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + out << "filter_interpreted_and_project " << m_src << " into " << m_res; + out << " using " << mk_pp(m_cond, m_cond.get_manager()); + out << " deleting columns "; + print_container(m_cols, out); + } + + virtual void make_annotations(execution_context & ctx) { + std::stringstream s; + std::string a = "rel_src"; + ctx.get_register_annotation(m_src, a); + s << "filter_interpreted_and_project " << mk_pp(m_cond, m_cond.get_manager()); + ctx.set_register_annotation(m_res, s.str()); + } + }; + + instruction * instruction::mk_filter_interpreted_and_project(reg_idx reg, app_ref & condition, + unsigned col_cnt, const unsigned * removed_cols, reg_idx result) { + return alloc(instr_filter_interpreted_and_project, reg, condition, col_cnt, removed_cols, result); + } + class instr_union : public instruction { reg_idx m_src; @@ -592,6 +650,7 @@ namespace datalog { } } + SASSERT(r_src.get_signature().size() == r_tgt.get_signature().size()); TRACE("dl_verbose", r_tgt.display(tout <<"pre-union:");); (*fn)(r_tgt, r_src, r_delta); diff --git a/src/muz_qe/dl_instruction.h b/src/muz_qe/dl_instruction.h index ae6310ba6..97622c6f3 100644 --- a/src/muz_qe/dl_instruction.h +++ b/src/muz_qe/dl_instruction.h @@ -260,6 +260,8 @@ namespace datalog { static instruction * mk_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col); static instruction * mk_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols); static instruction * mk_filter_interpreted(reg_idx reg, app_ref & condition); + static instruction * mk_filter_interpreted_and_project(reg_idx src, app_ref & condition, + unsigned col_cnt, const unsigned * removed_cols, reg_idx result); static instruction * mk_union(reg_idx src, reg_idx tgt, reg_idx delta); static instruction * mk_widen(reg_idx src, reg_idx tgt, reg_idx delta); static instruction * mk_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, diff --git a/src/muz_qe/dl_relation_manager.cpp b/src/muz_qe/dl_relation_manager.cpp index 986a1f2c4..ce20961f1 100644 --- a/src/muz_qe/dl_relation_manager.cpp +++ b/src/muz_qe/dl_relation_manager.cpp @@ -720,6 +720,13 @@ namespace datalog { return t.get_plugin().mk_filter_interpreted_fn(t, condition); } + relation_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, + unsigned removed_col_cnt, + const unsigned * removed_cols) { + return t.get_plugin().mk_filter_interpreted_and_project_fn(t, condition, removed_col_cnt, removed_cols); + } + class relation_manager::default_relation_select_equal_and_project_fn : public relation_transformer_fn { scoped_ptr m_filter; @@ -1387,6 +1394,45 @@ namespace datalog { } + class relation_manager::default_table_filter_interpreted_and_project_fn + : public table_transformer_fn { + scoped_ptr m_filter; + scoped_ptr m_project; + app_ref m_condition; + unsigned_vector m_removed_cols; + public: + default_table_filter_interpreted_and_project_fn(context & ctx, table_mutator_fn * filter, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) + : m_filter(filter), m_condition(condition, ctx.get_manager()), + m_removed_cols(removed_col_cnt, removed_cols) {} + + virtual table_base* operator()(const table_base & tb) { + table_base *t2 = tb.clone(); + (*m_filter)(*t2); + if (!m_project) { + relation_manager & rmgr = t2->get_plugin().get_manager(); + m_project = rmgr.mk_project_fn(*t2, m_removed_cols.size(), m_removed_cols.c_ptr()); + if (!m_project) { + throw default_exception("projection does not exist"); + } + } + return (*m_project)(*t2); + } + }; + + table_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + table_transformer_fn * res = t.get_plugin().mk_filter_interpreted_and_project_fn(t, condition, removed_col_cnt, removed_cols); + if (res) + return res; + + table_mutator_fn * filter = mk_filter_interpreted_fn(t, condition); + SASSERT(filter); + res = alloc(default_table_filter_interpreted_and_project_fn, get_context(), filter, condition, removed_col_cnt, removed_cols); + return res; + } + + table_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const table_base & t, const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, diff --git a/src/muz_qe/dl_relation_manager.h b/src/muz_qe/dl_relation_manager.h index f22ae7923..ccdba2783 100644 --- a/src/muz_qe/dl_relation_manager.h +++ b/src/muz_qe/dl_relation_manager.h @@ -55,6 +55,7 @@ namespace datalog { class default_table_filter_equal_fn; class default_table_filter_identical_fn; class default_table_filter_interpreted_fn; + class default_table_filter_interpreted_and_project_fn; class default_table_negation_filter_fn; class default_table_filter_not_equal_fn; class default_table_select_equal_and_project_fn; @@ -350,6 +351,9 @@ namespace datalog { relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols); + /** \brief Operations that returns all rows of \c t for which is column \c col equal to \c value with the column \c col removed. @@ -522,6 +526,9 @@ namespace datalog { table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); + table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols); + /** \brief Operations that returns all rows of \c t for which is column \c col equal to \c value with the column \c col removed. diff --git a/src/muz_qe/dl_table_relation.cpp b/src/muz_qe/dl_table_relation.cpp index fc661366d..3c30c58bb 100644 --- a/src/muz_qe/dl_table_relation.cpp +++ b/src/muz_qe/dl_table_relation.cpp @@ -354,6 +354,21 @@ namespace datalog { return alloc(tr_mutator_fn, tfun); } + relation_transformer_fn * table_relation_plugin::mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { + if (!t.from_table()) + return 0; + + const table_relation & tr = static_cast(t); + table_transformer_fn * tfun = get_manager().mk_filter_interpreted_and_project_fn(tr.get_table(), + condition, removed_col_cnt, removed_cols); + SASSERT(tfun); + + relation_signature sig; + relation_signature::from_project(t.get_signature(), removed_col_cnt, removed_cols, sig); + return alloc(tr_transformer_fn, sig, tfun); + } + class table_relation_plugin::tr_intersection_filter_fn : public relation_intersection_filter_fn { scoped_ptr m_tfun; public: diff --git a/src/muz_qe/dl_table_relation.h b/src/muz_qe/dl_table_relation.h index 12f0d4eaa..f86143c0f 100644 --- a/src/muz_qe/dl_table_relation.h +++ b/src/muz_qe/dl_table_relation.h @@ -71,6 +71,8 @@ namespace datalog { virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); virtual relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t, const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index 63e8ba700..3d61c04f0 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -18,6 +18,9 @@ Revision History: Extracted from dl_context --*/ + +#define Z3_HASSEL_TABLE + #include"rel_context.h" #include"dl_context.h" #include"dl_compiler.h" @@ -30,6 +33,10 @@ Revision History: #include"dl_mk_karr_invariants.h" #include"dl_finite_product_relation.h" #include"dl_sparse_table.h" +#ifdef Z3_HASSEL_TABLE +# include"dl_hassel_table.h" +# include"dl_hassel_diff_table.h" +#endif #include"dl_table.h" #include"dl_table_relation.h" #include"aig_exporter.h" @@ -87,6 +94,10 @@ namespace datalog { get_rmanager().register_plugin(alloc(bitvector_table_plugin, get_rmanager())); get_rmanager().register_plugin(alloc(equivalence_table_plugin, get_rmanager())); +#ifdef Z3_HASSEL_TABLE + get_rmanager().register_plugin(alloc(hassel_table_plugin, get_rmanager())); + get_rmanager().register_plugin(alloc(hassel_diff_table_plugin, get_rmanager())); +#endif // register plugins for builtin relations diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index 2c641c5a6..cbe7a0618 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -28,6 +28,7 @@ COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); #define BV_DEFAULT_CAPACITY 2 class bit_vector { +protected: unsigned m_num_bits; unsigned m_capacity; //!< in words unsigned * m_data; @@ -64,6 +65,12 @@ public: m_data(0) { } + bit_vector(unsigned reserve_num_bits) : + m_num_bits(0), + m_capacity(num_words(reserve_num_bits)), + m_data(alloc_svect(unsigned, m_capacity)) { + } + bit_vector(bit_vector const & source): m_num_bits(source.m_num_bits), m_capacity(source.m_capacity), From 60c4973c1d133993dbfd15f2052dd95dbc892f21 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 29 May 2013 17:56:23 -0700 Subject: [PATCH 82/91] fix crash in proof generation in BMC Signed-off-by: Nikolaj Bjorner --- src/muz_qe/dl_bmc_engine.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/muz_qe/dl_bmc_engine.cpp b/src/muz_qe/dl_bmc_engine.cpp index c313f7d7b..8e9fb510e 100644 --- a/src/muz_qe/dl_bmc_engine.cpp +++ b/src/muz_qe/dl_bmc_engine.cpp @@ -297,6 +297,7 @@ namespace datalog { r->to_formula(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); @@ -306,7 +307,10 @@ namespace datalog { r1->to_formula(concl); scoped_proof _sp(m); - proof* p = r->get_proof(); + p = r->get_proof(); + if (!p) { + p = m.mk_asserted(fml); + } proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); @@ -319,13 +323,17 @@ namespace datalog { else { r2->to_formula(concl); scoped_proof _sp(m); - proof* p = r->get_proof(); + p = r->get_proof(); + if (!p) { + p = m.mk_asserted(fml); + } if (sub.empty()) { pr = p; } else { substs.push_back(sub); - pr = m.mk_hyper_resolve(1, &p, concl, positions, substs); + proof* ps[1] = { p }; + pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } @@ -1211,6 +1219,15 @@ namespace datalog { r->to_formula(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); @@ -1218,9 +1235,8 @@ namespace datalog { apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); r1->to_formula(concl); - scoped_proof _sp(m); - proof* p = r->get_proof(); + scoped_proof _sp(m); proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); @@ -1233,13 +1249,13 @@ namespace datalog { else { r2->to_formula(concl); scoped_proof _sp(m); - proof* p = r->get_proof(); if (sub.empty()) { pr = p; } else { substs.push_back(sub); - pr = m.mk_hyper_resolve(1, &p, concl, positions, substs); + proof * ps[1] = { p }; + pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } From 37215b03bc9e604387f4d32990b0ba5d30e62a1f Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 29 May 2013 18:18:24 -0700 Subject: [PATCH 83/91] Remove redundant register_on_timeout_proc Signed-off-by: Leonardo de Moura --- src/shell/smtlib_frontend.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index ef0b4ad6b..a005462e2 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -103,7 +103,6 @@ unsigned read_smtlib2_commands(char const * file_name) { install_subpaving_cmds(ctx); g_cmd_context = &ctx; - register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); bool result = true; From c0895e5548adca97e50624d527f9a7a68a2dcb86 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 31 May 2013 17:48:19 -0700 Subject: [PATCH 84/91] remove hassel table from unstable: does not compile under other plantforms Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/Microsoft.Z3.csproj | 2 +- src/api/ml/README-test-win | 46 +- src/api/ml/README-win | 46 +- src/api/ml/build-lib.cmd | 6 +- src/api/ml/build-test.cmd | 38 +- src/api/ml/exec.cmd | 10 +- src/ast/rewriter/poly_rewriter_def.h | 1864 +++++++++++++------------- src/cmd_context/README | 2 +- src/math/euclid/README | 2 +- src/math/interval/README | 2 +- src/math/polynomial/README | 2 +- src/muz_qe/dl_hassel_common.cpp | 434 ------ src/muz_qe/dl_hassel_common.h | 1079 --------------- src/muz_qe/dl_hassel_diff_table.cpp | 219 --- src/muz_qe/dl_hassel_diff_table.h | 87 -- src/muz_qe/dl_hassel_table.cpp | 27 - src/muz_qe/dl_hassel_table.h | 39 - src/muz_qe/pdr_context.cpp | 11 +- src/muz_qe/rel_context.cpp | 9 - src/smt/database.smt | 2 +- src/smt/theory_utvpi_def.h | 78 +- 21 files changed, 1070 insertions(+), 2935 deletions(-) delete mode 100755 src/muz_qe/dl_hassel_common.cpp delete mode 100755 src/muz_qe/dl_hassel_common.h delete mode 100755 src/muz_qe/dl_hassel_diff_table.cpp delete mode 100755 src/muz_qe/dl_hassel_diff_table.h delete mode 100644 src/muz_qe/dl_hassel_table.cpp delete mode 100644 src/muz_qe/dl_hassel_table.h diff --git a/src/api/dotnet/Microsoft.Z3.csproj b/src/api/dotnet/Microsoft.Z3.csproj index 8f6e34517..9eb9eb660 100644 --- a/src/api/dotnet/Microsoft.Z3.csproj +++ b/src/api/dotnet/Microsoft.Z3.csproj @@ -399,4 +399,4 @@ --> - \ No newline at end of file + diff --git a/src/api/ml/README-test-win b/src/api/ml/README-test-win index d9be0174b..0e8b73ccd 100644 --- a/src/api/ml/README-test-win +++ b/src/api/ml/README-test-win @@ -1,23 +1,23 @@ -This directory contains scripts to build the test application using -OCaml. You also need CamlIDL to be able to generate the OCaml API. - -- To download OCaml: - http://caml.inria.fr/ocaml/ - -- To download CamlIDL: - http://forge.ocamlcore.org/projects/camlidl/ - -- One must build the OCaml library before compiling the example. - Go to directory ../ocaml - -- Use 'build-test.cmd' to build the test application using the OCaml compiler. - -Remark: The OCaml and C compiler tool chains must be configured in your environment. -Running from the Visual Studio Command Prompt configures the Microsoft C compiler. - -- The script 'exec.cmd' adds the bin directory to the path. So, - test_mlapi.exe can find z3.dll. - - - - +This directory contains scripts to build the test application using +OCaml. You also need CamlIDL to be able to generate the OCaml API. + +- To download OCaml: + http://caml.inria.fr/ocaml/ + +- To download CamlIDL: + http://forge.ocamlcore.org/projects/camlidl/ + +- One must build the OCaml library before compiling the example. + Go to directory ../ocaml + +- Use 'build-test.cmd' to build the test application using the OCaml compiler. + +Remark: The OCaml and C compiler tool chains must be configured in your environment. +Running from the Visual Studio Command Prompt configures the Microsoft C compiler. + +- The script 'exec.cmd' adds the bin directory to the path. So, + test_mlapi.exe can find z3.dll. + + + + diff --git a/src/api/ml/README-win b/src/api/ml/README-win index 91d2faa4f..82ddc2652 100644 --- a/src/api/ml/README-win +++ b/src/api/ml/README-win @@ -1,23 +1,23 @@ -The OCaml API for Z3 was tested using OCaml 3.12.1. -You also need CamlIDL to be able to generate the OCaml API. - -- To download OCaml: - http://caml.inria.fr/ocaml/ - -- To download CamlIDL: - http://forge.ocamlcore.org/projects/camlidl/ - -- To build the OCaml API for Z3: - .\build-lib.cmd - -Remark: The OCaml and C compiler tool chains must be configured in your environment. -Running from the Visual Studio Command Prompt configures the Microsoft C compiler. - -Remark: Building the OCaml API copies some pathnames into files, -so the OCaml API must be recompiled if the Z3 library files are moved. - -See ..\examples\ocaml\build-test.cmd for an example of how to compile and link with Z3. - -Acknowledgements: -The OCaml interface for Z3 was written by Josh Berdine and Jakob Lichtenberg. -Many thanks to them! +The OCaml API for Z3 was tested using OCaml 3.12.1. +You also need CamlIDL to be able to generate the OCaml API. + +- To download OCaml: + http://caml.inria.fr/ocaml/ + +- To download CamlIDL: + http://forge.ocamlcore.org/projects/camlidl/ + +- To build the OCaml API for Z3: + .\build-lib.cmd + +Remark: The OCaml and C compiler tool chains must be configured in your environment. +Running from the Visual Studio Command Prompt configures the Microsoft C compiler. + +Remark: Building the OCaml API copies some pathnames into files, +so the OCaml API must be recompiled if the Z3 library files are moved. + +See ..\examples\ocaml\build-test.cmd for an example of how to compile and link with Z3. + +Acknowledgements: +The OCaml interface for Z3 was written by Josh Berdine and Jakob Lichtenberg. +Many thanks to them! diff --git a/src/api/ml/build-lib.cmd b/src/api/ml/build-lib.cmd index 93d667a34..7cf1bbcbd 100755 --- a/src/api/ml/build-lib.cmd +++ b/src/api/ml/build-lib.cmd @@ -1,3 +1,3 @@ -@echo off - -call .\compile_mlapi.cmd ..\include ..\bin ..\bin +@echo off + +call .\compile_mlapi.cmd ..\include ..\bin ..\bin diff --git a/src/api/ml/build-test.cmd b/src/api/ml/build-test.cmd index 7b80dc795..13a752dbb 100755 --- a/src/api/ml/build-test.cmd +++ b/src/api/ml/build-test.cmd @@ -1,19 +1,19 @@ -@echo off - -if not exist ..\..\ocaml\z3.cmxa ( - echo "YOU MUST BUILD OCAML API! Go to directory ..\ocaml" - goto :EOF -) - -REM ocaml (>= 3.11) calls the linker through flexlink -ocamlc -version >> ocaml_version -set /p OCAML_VERSION= = 3.11) calls the linker through flexlink +ocamlc -version >> ocaml_version +set /p OCAML_VERSION= -char const * poly_rewriter::g_ste_blowup_msg = "sum of monomials blowup"; - - -template -void poly_rewriter::updt_params(params_ref const & _p) { - poly_rewriter_params p(_p); - m_flat = p.flat(); - m_som = p.som(); - m_hoist_mul = p.hoist_mul(); - m_hoist_cmul = p.hoist_cmul(); - m_som_blowup = p.som_blowup(); -} - -template -void poly_rewriter::get_param_descrs(param_descrs & r) { - poly_rewriter_params::collect_param_descrs(r); -} - -template -expr * poly_rewriter::mk_add_app(unsigned num_args, expr * const * args) { - switch (num_args) { - case 0: return mk_numeral(numeral(0)); - case 1: return args[0]; - default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args); - } -} - -// t = (^ x y) --> return x, and set k = y if k is an integer >= 1 -// Otherwise return t and set k = 1 -template -expr * poly_rewriter::get_power_body(expr * t, rational & k) { - if (!is_power(t)) { - k = rational(1); - return t; - } - if (is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k > rational(1)) { - return to_app(t)->get_arg(0); - } - k = rational(1); - return t; -} - -template -expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) { - switch (num_args) { - case 0: - return mk_numeral(numeral(1)); - case 1: - return args[0]; - default: - if (use_power()) { - rational k_prev; - expr * prev = get_power_body(args[0], k_prev); - rational k; - ptr_buffer new_args; -#define PUSH_POWER() { \ - if (k_prev.is_one()) { \ - new_args.push_back(prev); \ - } \ - else { \ - expr * pargs[2] = { prev, mk_numeral(k_prev) }; \ - new_args.push_back(m().mk_app(get_fid(), power_decl_kind(), 2, pargs)); \ - } \ - } - - for (unsigned i = 1; i < num_args; i++) { - expr * arg = get_power_body(args[i], k); - if (arg == prev) { - k_prev += k; - } - else { - PUSH_POWER(); - prev = arg; - k_prev = k; - } - } - PUSH_POWER(); - SASSERT(new_args.size() > 0); - if (new_args.size() == 1) { - return new_args[0]; - } - else { - return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.c_ptr()); - } - } - else { - return m().mk_app(get_fid(), mul_decl_kind(), num_args, args); - } - } -} - -template -expr * poly_rewriter::mk_mul_app(numeral const & c, expr * arg) { - if (c.is_one()) { - return arg; - } - else { - expr * new_args[2] = { mk_numeral(c), arg }; - return mk_mul_app(2, new_args); - } -} - -template -br_status poly_rewriter::mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args >= 2); - // only try to apply flattening if it is not already in one of the flat monomial forms - // - (* c x) - // - (* c (* x_1 ... x_n)) - if (num_args != 2 || !is_numeral(args[0]) || (is_mul(args[1]) && is_numeral(to_app(args[1])->get_arg(0)))) { - unsigned i; - for (i = 0; i < num_args; i++) { - if (is_mul(args[i])) - break; - } - if (i < num_args) { - // input has nested monomials. - ptr_buffer flat_args; - // we need the todo buffer to handle: (* (* c (* x_1 ... x_n)) (* d (* y_1 ... y_n))) - ptr_buffer todo; - flat_args.append(i, args); - for (unsigned j = i; j < num_args; j++) { - if (is_mul(args[j])) { - todo.push_back(args[j]); - while (!todo.empty()) { - expr * curr = todo.back(); - todo.pop_back(); - if (is_mul(curr)) { - unsigned k = to_app(curr)->get_num_args(); - while (k > 0) { - --k; - todo.push_back(to_app(curr)->get_arg(k)); - } - } - else { - flat_args.push_back(curr); - } - } - } - else { - flat_args.push_back(args[j]); - } - } - TRACE("poly_rewriter", - tout << "flat mul:\n"; - for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n"; - tout << "---->\n"; - for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n";); - br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.c_ptr(), result); - if (st == BR_FAILED) { - result = mk_mul_app(flat_args.size(), flat_args.c_ptr()); - return BR_DONE; - } - return st; - } - } - return mk_nflat_mul_core(num_args, args, result); -} - - -template -struct poly_rewriter::mon_pw_lt { - poly_rewriter & m_owner; - mon_pw_lt(poly_rewriter & o):m_owner(o) {} - - bool operator()(expr * n1, expr * n2) const { - rational k; - return lt(m_owner.get_power_body(n1, k), - m_owner.get_power_body(n2, k)); - } -}; - - -template -br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args >= 2); - // cheap case - numeral a; - if (num_args == 2 && is_numeral(args[0], a) && !a.is_one() && !a.is_zero() && - (is_var(args[1]) || to_app(args[1])->get_decl()->get_family_id() != get_fid())) - return BR_FAILED; - numeral c(1); - unsigned num_coeffs = 0; - unsigned num_add = 0; - expr * var = 0; - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg, a)) { - num_coeffs++; - c *= a; - } - else { - var = arg; - if (is_add(arg)) - num_add++; - } - } - - normalize(c); - // (* c_1 ... c_n) --> c_1*...*c_n - if (num_coeffs == num_args) { - result = mk_numeral(c); - return BR_DONE; - } - - // (* s ... 0 ... r) --> 0 - if (c.is_zero()) { - result = mk_numeral(c); - return BR_DONE; - } - - if (num_coeffs == num_args - 1) { - SASSERT(var != 0); - // (* c_1 ... c_n x) --> x if c_1*...*c_n == 1 - if (c.is_one()) { - result = var; - return BR_DONE; - } - - numeral c_prime; - if (is_mul(var)) { - // apply basic simplification even when flattening is not enabled. - // (* c1 (* c2 x)) --> (* c1*c2 x) - if (to_app(var)->get_num_args() == 2 && is_numeral(to_app(var)->get_arg(0), c_prime)) { - c *= c_prime; - normalize(c); - result = mk_mul_app(c, to_app(var)->get_arg(1)); - return BR_REWRITE1; - } - else { - // var is a power-product - return BR_FAILED; - } - } - - if (num_add == 0 || m_hoist_cmul) { - SASSERT(!is_add(var) || m_hoist_cmul); - if (num_args == 2 && args[1] == var) { - DEBUG_CODE({ - numeral c_prime; - SASSERT(is_numeral(args[0], c_prime) && c == c_prime); - }); - // it is already simplified - return BR_FAILED; - } - - // (* c_1 ... c_n x) --> (* c_1*...*c_n x) - result = mk_mul_app(c, var); - return BR_DONE; - } - else { - SASSERT(is_add(var)); - // (* c_1 ... c_n (+ t_1 ... t_m)) --> (+ (* c_1*...*c_n t_1) ... (* c_1*...*c_n t_m)) - ptr_buffer new_add_args; - unsigned num = to_app(var)->get_num_args(); - for (unsigned i = 0; i < num; i++) { - new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i))); - } - result = mk_add_app(new_add_args.size(), new_add_args.c_ptr()); - TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";); - return BR_REWRITE2; - } - } - - SASSERT(num_coeffs <= num_args - 2); - - if (!m_som || num_add == 0) { - ptr_buffer new_args; - expr * prev = 0; - bool ordered = true; - for (unsigned i = 0; i < num_args; i++) { - expr * curr = args[i]; - if (is_numeral(curr)) - continue; - if (prev != 0 && lt(curr, prev)) - ordered = false; - new_args.push_back(curr); - prev = curr; - } - TRACE("poly_rewriter", - for (unsigned i = 0; i < new_args.size(); i++) { - if (i > 0) - tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); - tout << mk_ismt2_pp(new_args[i], m()); - } - tout << "\nordered: " << ordered << "\n";); - if (ordered && num_coeffs == 0 && !use_power()) - return BR_FAILED; - if (!ordered) { - if (use_power()) - std::sort(new_args.begin(), new_args.end(), mon_pw_lt(*this)); - else - std::sort(new_args.begin(), new_args.end(), ast_to_lt()); - TRACE("poly_rewriter", - tout << "after sorting:\n"; - for (unsigned i = 0; i < new_args.size(); i++) { - if (i > 0) - tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); - tout << mk_ismt2_pp(new_args[i], m()); - } - tout << "\n";); - } - SASSERT(new_args.size() >= 2); - result = mk_mul_app(new_args.size(), new_args.c_ptr()); - result = mk_mul_app(c, result); - TRACE("poly_rewriter", tout << "mk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";); - return BR_DONE; - } - - SASSERT(m_som && num_add > 0); - - sbuffer szs; - sbuffer it; - sbuffer sums; - for (unsigned i = 0; i < num_args; i ++) { - it.push_back(0); - expr * arg = args[i]; - if (is_add(arg)) { - sums.push_back(const_cast(to_app(arg)->get_args())); - szs.push_back(to_app(arg)->get_num_args()); - } - else { - sums.push_back(const_cast(args + i)); - szs.push_back(1); - SASSERT(sums.back()[0] == arg); - } - } - expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception - ptr_buffer m_args; - TRACE("som", tout << "starting som...\n";); - do { - TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " "; - tout << "\n";); - if (sum.size() > m_som_blowup) - throw rewriter_exception(g_ste_blowup_msg); - m_args.reset(); - for (unsigned i = 0; i < num_args; i++) { - expr * const * v = sums[i]; - expr * arg = v[it[i]]; - m_args.push_back(arg); - } - sum.push_back(mk_mul_app(m_args.size(), m_args.c_ptr())); - } - while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); - result = mk_add_app(sum.size(), sum.c_ptr()); - return BR_REWRITE2; -} - -template -br_status poly_rewriter::mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { - unsigned i; - for (i = 0; i < num_args; i++) { - if (is_add(args[i])) - break; - } - if (i < num_args) { - // has nested ADDs - ptr_buffer flat_args; - flat_args.append(i, args); - for (; i < num_args; i++) { - expr * arg = args[i]; - // Remark: all rewrites are depth 1. - if (is_add(arg)) { - unsigned num = to_app(arg)->get_num_args(); - for (unsigned j = 0; j < num; j++) - flat_args.push_back(to_app(arg)->get_arg(j)); - } - else { - flat_args.push_back(arg); - } - } - br_status st = mk_nflat_add_core(flat_args.size(), flat_args.c_ptr(), result); - if (st == BR_FAILED) { - result = mk_add_app(flat_args.size(), flat_args.c_ptr()); - return BR_DONE; - } - return st; - } - return mk_nflat_add_core(num_args, args, result); -} - -template -inline expr * poly_rewriter::get_power_product(expr * t) { - if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0))) - return to_app(t)->get_arg(1); - return t; -} - -template -inline expr * poly_rewriter::get_power_product(expr * t, numeral & a) { - if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0), a)) - return to_app(t)->get_arg(1); - a = numeral(1); - return t; -} - -template -bool poly_rewriter::is_mul(expr * t, numeral & c, expr * & pp) { - if (!is_mul(t) || to_app(t)->get_num_args() != 2) - return false; - if (!is_numeral(to_app(t)->get_arg(0), c)) - return false; - pp = to_app(t)->get_arg(1); - return true; -} - -template -struct poly_rewriter::hoist_cmul_lt { - poly_rewriter & m_r; - hoist_cmul_lt(poly_rewriter & r):m_r(r) {} - - bool operator()(expr * t1, expr * t2) const { - expr * pp1, * pp2; - numeral c1, c2; - bool is_mul1 = m_r.is_mul(t1, c1, pp1); - bool is_mul2 = m_r.is_mul(t2, c2, pp2); - if (!is_mul1 && is_mul2) - return true; - if (is_mul1 && !is_mul2) - return false; - if (!is_mul1 && !is_mul2) - return t1->get_id() < t2->get_id(); - if (c1 < c2) - return true; - if (c1 > c2) - return false; - return pp1->get_id() < pp2->get_id(); - } -}; - -template -void poly_rewriter::hoist_cmul(expr_ref_buffer & args) { - unsigned sz = args.size(); - std::sort(args.c_ptr(), args.c_ptr() + sz, hoist_cmul_lt(*this)); - numeral c, c_prime; - ptr_buffer pps; - expr * pp, * pp_prime; - unsigned j = 0; - unsigned i = 0; - while (i < sz) { - expr * mon = args[i]; - if (is_mul(mon, c, pp) && i < sz - 1) { - expr * mon_prime = args[i+1]; - if (is_mul(mon_prime, c_prime, pp_prime) && c == c_prime) { - // found target - pps.reset(); - pps.push_back(pp); - pps.push_back(pp_prime); - i += 2; - while (i < sz && is_mul(args[i], c_prime, pp_prime) && c == c_prime) { - pps.push_back(pp_prime); - i++; - } - SASSERT(is_numeral(to_app(mon)->get_arg(0), c_prime) && c == c_prime); - expr * mul_args[2] = { to_app(mon)->get_arg(0), mk_add_app(pps.size(), pps.c_ptr()) }; - args.set(j, mk_mul_app(2, mul_args)); - j++; - continue; - } - } - args.set(j, mon); - j++; - i++; - } - args.resize(j); -} - -template -br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args >= 2); - numeral c; - unsigned num_coeffs = 0; - numeral a; - expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args - expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once - bool has_multiple = false; - expr * prev = 0; - bool ordered = true; - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg, a)) { - num_coeffs++; - c += a; - } - else { - // arg is not a numeral - if (m_sort_sums && ordered) { - if (prev != 0 && lt(arg, prev)) - ordered = false; - prev = arg; - } - } - - arg = get_power_product(arg); - if (visited.is_marked(arg)) { - multiple.mark(arg); - has_multiple = true; - } - else { - visited.mark(arg); - } - } - normalize(c); - SASSERT(m_sort_sums || ordered); - TRACE("sort_sums", - tout << "ordered: " << ordered << "\n"; - for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";); - - if (has_multiple) { - // expensive case - buffer coeffs; - m_expr2pos.reset(); - // compute the coefficient of power products that occur multiple times. - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) - continue; - unsigned pos; - if (m_expr2pos.find(pp, pos)) { - coeffs[pos] += a; - } - else { - m_expr2pos.insert(pp, coeffs.size()); - coeffs.push_back(a); - } - } - expr_ref_buffer new_args(m()); - if (!c.is_zero()) { - new_args.push_back(mk_numeral(c)); - } - // copy power products with non zero coefficients to new_args - visited.reset(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg); - if (!multiple.is_marked(pp)) { - new_args.push_back(arg); - } - else if (!visited.is_marked(pp)) { - visited.mark(pp); - unsigned pos = UINT_MAX; - m_expr2pos.find(pp, pos); - SASSERT(pos != UINT_MAX); - a = coeffs[pos]; - normalize(a); - if (!a.is_zero()) - new_args.push_back(mk_mul_app(a, pp)); - } - } - if (m_hoist_cmul) { - hoist_cmul(new_args); - } - else if (m_sort_sums) { - TRACE("sort_sums_bug", tout << "new_args.size(): " << new_args.size() << "\n";); - if (c.is_zero()) - std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); - else - std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); - } - result = mk_add_app(new_args.size(), new_args.c_ptr()); - if (hoist_multiplication(result)) { - return BR_REWRITE_FULL; - } - return BR_DONE; - } - else { - SASSERT(!has_multiple); - if (ordered && !m_hoist_mul && !m_hoist_cmul) { - if (num_coeffs == 0) - return BR_FAILED; - if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero()) - return BR_FAILED; - } - expr_ref_buffer new_args(m()); - if (!c.is_zero()) - new_args.push_back(mk_numeral(c)); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = args[i]; - if (is_numeral(arg)) - continue; - new_args.push_back(arg); - } - if (m_hoist_cmul) { - hoist_cmul(new_args); - } - else if (!ordered) { - if (c.is_zero()) - std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); - else - std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); - } - result = mk_add_app(new_args.size(), new_args.c_ptr()); - if (hoist_multiplication(result)) { - return BR_REWRITE_FULL; - } - return BR_DONE; - } -} - - -template -br_status poly_rewriter::mk_uminus(expr * arg, expr_ref & result) { - numeral a; - set_curr_sort(m().get_sort(arg)); - if (is_numeral(arg, a)) { - a.neg(); - normalize(a); - result = mk_numeral(a); - return BR_DONE; - } - else { - result = mk_mul_app(numeral(-1), arg); - return BR_REWRITE1; - } -} - -template -br_status poly_rewriter::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) { - SASSERT(num_args > 0); - if (num_args == 1) { - result = args[0]; - return BR_DONE; - } - set_curr_sort(m().get_sort(args[0])); - expr * minus_one = mk_numeral(numeral(-1)); - ptr_buffer new_args; - new_args.push_back(args[0]); - for (unsigned i = 1; i < num_args; i++) { - expr * aux_args[2] = { minus_one, args[i] }; - new_args.push_back(mk_mul_app(2, aux_args)); - } - result = mk_add_app(new_args.size(), new_args.c_ptr()); - return BR_REWRITE2; -} - -/** - \brief Cancel/Combine monomials that occur is the left and right hand sides. - - \remark If move = true, then all non-constant monomials are moved to the left-hand-side. -*/ -template -br_status poly_rewriter::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) { - set_curr_sort(m().get_sort(lhs)); - unsigned lhs_sz; - expr * const * lhs_monomials = get_monomials(lhs, lhs_sz); - unsigned rhs_sz; - expr * const * rhs_monomials = get_monomials(rhs, rhs_sz); - - expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in lhs or rhs - expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once - bool has_multiple = false; - - numeral c(0); - numeral a; - unsigned num_coeffs = 0; - - for (unsigned i = 0; i < lhs_sz; i++) { - expr * arg = lhs_monomials[i]; - if (is_numeral(arg, a)) { - c += a; - num_coeffs++; - } - else { - visited.mark(get_power_product(arg)); - } - } - - if (move && num_coeffs == 0 && is_numeral(rhs)) - return BR_FAILED; - - for (unsigned i = 0; i < rhs_sz; i++) { - expr * arg = rhs_monomials[i]; - if (is_numeral(arg, a)) { - c -= a; - num_coeffs++; - } - else { - expr * pp = get_power_product(arg); - if (visited.is_marked(pp)) { - multiple.mark(pp); - has_multiple = true; - } - } - } - - normalize(c); - - if (!has_multiple && num_coeffs <= 1) { - if (move) { - if (is_numeral(rhs)) - return BR_FAILED; - } - else { - if (num_coeffs == 0 || is_numeral(rhs)) - return BR_FAILED; - } - } - - buffer coeffs; - m_expr2pos.reset(); - for (unsigned i = 0; i < lhs_sz; i++) { - expr * arg = lhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) - continue; - unsigned pos; - if (m_expr2pos.find(pp, pos)) { - coeffs[pos] += a; - } - else { - m_expr2pos.insert(pp, coeffs.size()); - coeffs.push_back(a); - } - } - - for (unsigned i = 0; i < rhs_sz; i++) { - expr * arg = rhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) - continue; - unsigned pos = UINT_MAX; - m_expr2pos.find(pp, pos); - SASSERT(pos != UINT_MAX); - coeffs[pos] -= a; - } - - - ptr_buffer new_lhs_monomials; - new_lhs_monomials.push_back(0); // save space for coefficient if needed - // copy power products with non zero coefficients to new_lhs_monomials - visited.reset(); - for (unsigned i = 0; i < lhs_sz; i++) { - expr * arg = lhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg); - if (!multiple.is_marked(pp)) { - new_lhs_monomials.push_back(arg); - } - else if (!visited.is_marked(pp)) { - visited.mark(pp); - unsigned pos = UINT_MAX; - m_expr2pos.find(pp, pos); - SASSERT(pos != UINT_MAX); - a = coeffs[pos]; - if (!a.is_zero()) - new_lhs_monomials.push_back(mk_mul_app(a, pp)); - } - } - - ptr_buffer new_rhs_monomials; - new_rhs_monomials.push_back(0); // save space for coefficient if needed - for (unsigned i = 0; i < rhs_sz; i++) { - expr * arg = rhs_monomials[i]; - if (is_numeral(arg)) - continue; - expr * pp = get_power_product(arg, a); - if (!multiple.is_marked(pp)) { - if (move) { - if (!a.is_zero()) { - if (a.is_minus_one()) { - new_lhs_monomials.push_back(pp); - } - else { - a.neg(); - SASSERT(!a.is_one()); - expr * args[2] = { mk_numeral(a), pp }; - new_lhs_monomials.push_back(mk_mul_app(2, args)); - } - } - } - else { - new_rhs_monomials.push_back(arg); - } - } - } - - bool c_at_rhs = false; - if (move) { - if (m_sort_sums) { - // + 1 to skip coefficient - std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), ast_to_lt()); - } - c_at_rhs = true; - } - else if (new_rhs_monomials.size() == 1) { // rhs is empty - c_at_rhs = true; - } - else if (new_lhs_monomials.size() > 1) { - c_at_rhs = true; - } - - if (c_at_rhs) { - c.neg(); - normalize(c); - new_rhs_monomials[0] = mk_numeral(c); - lhs_result = mk_add_app(new_lhs_monomials.size() - 1, new_lhs_monomials.c_ptr() + 1); - rhs_result = mk_add_app(new_rhs_monomials.size(), new_rhs_monomials.c_ptr()); - } - else { - new_lhs_monomials[0] = mk_numeral(c); - lhs_result = mk_add_app(new_lhs_monomials.size(), new_lhs_monomials.c_ptr()); - rhs_result = mk_add_app(new_rhs_monomials.size() - 1, new_rhs_monomials.c_ptr() + 1); - } - return BR_DONE; -} - -#define TO_BUFFER(_tester_, _buffer_, _e_) \ - _buffer_.push_back(_e_); \ - for (unsigned _i = 0; _i < _buffer_.size(); ) { \ - expr* _e = _buffer_[_i]; \ - if (_tester_(_e)) { \ - app* a = to_app(_e); \ - _buffer_[_i] = a->get_arg(0); \ - for (unsigned _j = 1; _j < a->get_num_args(); ++_j) { \ - _buffer_.push_back(a->get_arg(_j)); \ - } \ - } \ - else { \ - ++_i; \ - } \ - } \ - -template -bool poly_rewriter::hoist_multiplication(expr_ref& som) { - if (!m_hoist_mul) { - return false; - } - ptr_buffer adds, muls; - TO_BUFFER(is_add, adds, som); - buffer valid(adds.size(), true); - obj_map mul_map; - unsigned j; - bool change = false; - for (unsigned k = 0; k < adds.size(); ++k) { - expr* e = adds[k]; - muls.reset(); - TO_BUFFER(is_mul, muls, e); - for (unsigned i = 0; i < muls.size(); ++i) { - e = muls[i]; - if (is_numeral(e)) { - continue; - } - if (mul_map.find(e, j) && valid[j] && j != k) { - m_curr_sort = m().get_sort(adds[k]); - adds[j] = merge_muls(adds[j], adds[k]); - adds[k] = mk_numeral(rational(0)); - valid[j] = false; - valid[k] = false; - change = true; - break; - } - else { - mul_map.insert(e, k); - } - } - } - if (!change) { - return false; - } - - som = mk_add_app(adds.size(), adds.c_ptr()); - - - return true; -} - -template -expr* poly_rewriter::merge_muls(expr* x, expr* y) { - ptr_buffer m1, m2; - TO_BUFFER(is_mul, m1, x); - TO_BUFFER(is_mul, m2, y); - unsigned k = 0; - for (unsigned i = 0; i < m1.size(); ++i) { - x = m1[i]; - bool found = false; - unsigned j; - for (j = k; j < m2.size(); ++j) { - found = m2[j] == x; - if (found) break; - } - if (found) { - std::swap(m1[i],m1[k]); - std::swap(m2[j],m2[k]); - ++k; - } - } - m_curr_sort = m().get_sort(x); - SASSERT(k > 0); - SASSERT(m1.size() >= k); - SASSERT(m2.size() >= k); - expr* args[2] = { mk_mul_app(m1.size()-k, m1.c_ptr()+k), - mk_mul_app(m2.size()-k, m2.c_ptr()+k) }; - if (k == m1.size()) { - m1.push_back(0); - } - m1[k] = mk_add_app(2, args); - return mk_mul_app(k+1, m1.c_ptr()); -} +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + poly_rewriter_def.h + +Abstract: + + Basic rewriting rules for Polynomials. + +Author: + + Leonardo (leonardo) 2011-04-08 + +Notes: + +--*/ +#include"poly_rewriter.h" +#include"poly_rewriter_params.hpp" +#include"ast_lt.h" +#include"ast_ll_pp.h" +#include"ast_smt2_pp.h" + +template +char const * poly_rewriter::g_ste_blowup_msg = "sum of monomials blowup"; + + +template +void poly_rewriter::updt_params(params_ref const & _p) { + poly_rewriter_params p(_p); + m_flat = p.flat(); + m_som = p.som(); + m_hoist_mul = p.hoist_mul(); + m_hoist_cmul = p.hoist_cmul(); + m_som_blowup = p.som_blowup(); +} + +template +void poly_rewriter::get_param_descrs(param_descrs & r) { + poly_rewriter_params::collect_param_descrs(r); +} + +template +expr * poly_rewriter::mk_add_app(unsigned num_args, expr * const * args) { + switch (num_args) { + case 0: return mk_numeral(numeral(0)); + case 1: return args[0]; + default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args); + } +} + +// t = (^ x y) --> return x, and set k = y if k is an integer >= 1 +// Otherwise return t and set k = 1 +template +expr * poly_rewriter::get_power_body(expr * t, rational & k) { + if (!is_power(t)) { + k = rational(1); + return t; + } + if (is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k > rational(1)) { + return to_app(t)->get_arg(0); + } + k = rational(1); + return t; +} + +template +expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) { + switch (num_args) { + case 0: + return mk_numeral(numeral(1)); + case 1: + return args[0]; + default: + if (use_power()) { + rational k_prev; + expr * prev = get_power_body(args[0], k_prev); + rational k; + ptr_buffer new_args; +#define PUSH_POWER() { \ + if (k_prev.is_one()) { \ + new_args.push_back(prev); \ + } \ + else { \ + expr * pargs[2] = { prev, mk_numeral(k_prev) }; \ + new_args.push_back(m().mk_app(get_fid(), power_decl_kind(), 2, pargs)); \ + } \ + } + + for (unsigned i = 1; i < num_args; i++) { + expr * arg = get_power_body(args[i], k); + if (arg == prev) { + k_prev += k; + } + else { + PUSH_POWER(); + prev = arg; + k_prev = k; + } + } + PUSH_POWER(); + SASSERT(new_args.size() > 0); + if (new_args.size() == 1) { + return new_args[0]; + } + else { + return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.c_ptr()); + } + } + else { + return m().mk_app(get_fid(), mul_decl_kind(), num_args, args); + } + } +} + +template +expr * poly_rewriter::mk_mul_app(numeral const & c, expr * arg) { + if (c.is_one()) { + return arg; + } + else { + expr * new_args[2] = { mk_numeral(c), arg }; + return mk_mul_app(2, new_args); + } +} + +template +br_status poly_rewriter::mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args >= 2); + // only try to apply flattening if it is not already in one of the flat monomial forms + // - (* c x) + // - (* c (* x_1 ... x_n)) + if (num_args != 2 || !is_numeral(args[0]) || (is_mul(args[1]) && is_numeral(to_app(args[1])->get_arg(0)))) { + unsigned i; + for (i = 0; i < num_args; i++) { + if (is_mul(args[i])) + break; + } + if (i < num_args) { + // input has nested monomials. + ptr_buffer flat_args; + // we need the todo buffer to handle: (* (* c (* x_1 ... x_n)) (* d (* y_1 ... y_n))) + ptr_buffer todo; + flat_args.append(i, args); + for (unsigned j = i; j < num_args; j++) { + if (is_mul(args[j])) { + todo.push_back(args[j]); + while (!todo.empty()) { + expr * curr = todo.back(); + todo.pop_back(); + if (is_mul(curr)) { + unsigned k = to_app(curr)->get_num_args(); + while (k > 0) { + --k; + todo.push_back(to_app(curr)->get_arg(k)); + } + } + else { + flat_args.push_back(curr); + } + } + } + else { + flat_args.push_back(args[j]); + } + } + TRACE("poly_rewriter", + tout << "flat mul:\n"; + for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n"; + tout << "---->\n"; + for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n";); + br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.c_ptr(), result); + if (st == BR_FAILED) { + result = mk_mul_app(flat_args.size(), flat_args.c_ptr()); + return BR_DONE; + } + return st; + } + } + return mk_nflat_mul_core(num_args, args, result); +} + + +template +struct poly_rewriter::mon_pw_lt { + poly_rewriter & m_owner; + mon_pw_lt(poly_rewriter & o):m_owner(o) {} + + bool operator()(expr * n1, expr * n2) const { + rational k; + return lt(m_owner.get_power_body(n1, k), + m_owner.get_power_body(n2, k)); + } +}; + + +template +br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args >= 2); + // cheap case + numeral a; + if (num_args == 2 && is_numeral(args[0], a) && !a.is_one() && !a.is_zero() && + (is_var(args[1]) || to_app(args[1])->get_decl()->get_family_id() != get_fid())) + return BR_FAILED; + numeral c(1); + unsigned num_coeffs = 0; + unsigned num_add = 0; + expr * var = 0; + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg, a)) { + num_coeffs++; + c *= a; + } + else { + var = arg; + if (is_add(arg)) + num_add++; + } + } + + normalize(c); + // (* c_1 ... c_n) --> c_1*...*c_n + if (num_coeffs == num_args) { + result = mk_numeral(c); + return BR_DONE; + } + + // (* s ... 0 ... r) --> 0 + if (c.is_zero()) { + result = mk_numeral(c); + return BR_DONE; + } + + if (num_coeffs == num_args - 1) { + SASSERT(var != 0); + // (* c_1 ... c_n x) --> x if c_1*...*c_n == 1 + if (c.is_one()) { + result = var; + return BR_DONE; + } + + numeral c_prime; + if (is_mul(var)) { + // apply basic simplification even when flattening is not enabled. + // (* c1 (* c2 x)) --> (* c1*c2 x) + if (to_app(var)->get_num_args() == 2 && is_numeral(to_app(var)->get_arg(0), c_prime)) { + c *= c_prime; + normalize(c); + result = mk_mul_app(c, to_app(var)->get_arg(1)); + return BR_REWRITE1; + } + else { + // var is a power-product + return BR_FAILED; + } + } + + if (num_add == 0 || m_hoist_cmul) { + SASSERT(!is_add(var) || m_hoist_cmul); + if (num_args == 2 && args[1] == var) { + DEBUG_CODE({ + numeral c_prime; + SASSERT(is_numeral(args[0], c_prime) && c == c_prime); + }); + // it is already simplified + return BR_FAILED; + } + + // (* c_1 ... c_n x) --> (* c_1*...*c_n x) + result = mk_mul_app(c, var); + return BR_DONE; + } + else { + SASSERT(is_add(var)); + // (* c_1 ... c_n (+ t_1 ... t_m)) --> (+ (* c_1*...*c_n t_1) ... (* c_1*...*c_n t_m)) + ptr_buffer new_add_args; + unsigned num = to_app(var)->get_num_args(); + for (unsigned i = 0; i < num; i++) { + new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i))); + } + result = mk_add_app(new_add_args.size(), new_add_args.c_ptr()); + TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";); + return BR_REWRITE2; + } + } + + SASSERT(num_coeffs <= num_args - 2); + + if (!m_som || num_add == 0) { + ptr_buffer new_args; + expr * prev = 0; + bool ordered = true; + for (unsigned i = 0; i < num_args; i++) { + expr * curr = args[i]; + if (is_numeral(curr)) + continue; + if (prev != 0 && lt(curr, prev)) + ordered = false; + new_args.push_back(curr); + prev = curr; + } + TRACE("poly_rewriter", + for (unsigned i = 0; i < new_args.size(); i++) { + if (i > 0) + tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); + tout << mk_ismt2_pp(new_args[i], m()); + } + tout << "\nordered: " << ordered << "\n";); + if (ordered && num_coeffs == 0 && !use_power()) + return BR_FAILED; + if (!ordered) { + if (use_power()) + std::sort(new_args.begin(), new_args.end(), mon_pw_lt(*this)); + else + std::sort(new_args.begin(), new_args.end(), ast_to_lt()); + TRACE("poly_rewriter", + tout << "after sorting:\n"; + for (unsigned i = 0; i < new_args.size(); i++) { + if (i > 0) + tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); + tout << mk_ismt2_pp(new_args[i], m()); + } + tout << "\n";); + } + SASSERT(new_args.size() >= 2); + result = mk_mul_app(new_args.size(), new_args.c_ptr()); + result = mk_mul_app(c, result); + TRACE("poly_rewriter", tout << "mk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";); + return BR_DONE; + } + + SASSERT(m_som && num_add > 0); + + sbuffer szs; + sbuffer it; + sbuffer sums; + for (unsigned i = 0; i < num_args; i ++) { + it.push_back(0); + expr * arg = args[i]; + if (is_add(arg)) { + sums.push_back(const_cast(to_app(arg)->get_args())); + szs.push_back(to_app(arg)->get_num_args()); + } + else { + sums.push_back(const_cast(args + i)); + szs.push_back(1); + SASSERT(sums.back()[0] == arg); + } + } + expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception + ptr_buffer m_args; + TRACE("som", tout << "starting som...\n";); + do { + TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " "; + tout << "\n";); + if (sum.size() > m_som_blowup) + throw rewriter_exception(g_ste_blowup_msg); + m_args.reset(); + for (unsigned i = 0; i < num_args; i++) { + expr * const * v = sums[i]; + expr * arg = v[it[i]]; + m_args.push_back(arg); + } + sum.push_back(mk_mul_app(m_args.size(), m_args.c_ptr())); + } + while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); + result = mk_add_app(sum.size(), sum.c_ptr()); + return BR_REWRITE2; +} + +template +br_status poly_rewriter::mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { + unsigned i; + for (i = 0; i < num_args; i++) { + if (is_add(args[i])) + break; + } + if (i < num_args) { + // has nested ADDs + ptr_buffer flat_args; + flat_args.append(i, args); + for (; i < num_args; i++) { + expr * arg = args[i]; + // Remark: all rewrites are depth 1. + if (is_add(arg)) { + unsigned num = to_app(arg)->get_num_args(); + for (unsigned j = 0; j < num; j++) + flat_args.push_back(to_app(arg)->get_arg(j)); + } + else { + flat_args.push_back(arg); + } + } + br_status st = mk_nflat_add_core(flat_args.size(), flat_args.c_ptr(), result); + if (st == BR_FAILED) { + result = mk_add_app(flat_args.size(), flat_args.c_ptr()); + return BR_DONE; + } + return st; + } + return mk_nflat_add_core(num_args, args, result); +} + +template +inline expr * poly_rewriter::get_power_product(expr * t) { + if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0))) + return to_app(t)->get_arg(1); + return t; +} + +template +inline expr * poly_rewriter::get_power_product(expr * t, numeral & a) { + if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0), a)) + return to_app(t)->get_arg(1); + a = numeral(1); + return t; +} + +template +bool poly_rewriter::is_mul(expr * t, numeral & c, expr * & pp) { + if (!is_mul(t) || to_app(t)->get_num_args() != 2) + return false; + if (!is_numeral(to_app(t)->get_arg(0), c)) + return false; + pp = to_app(t)->get_arg(1); + return true; +} + +template +struct poly_rewriter::hoist_cmul_lt { + poly_rewriter & m_r; + hoist_cmul_lt(poly_rewriter & r):m_r(r) {} + + bool operator()(expr * t1, expr * t2) const { + expr * pp1, * pp2; + numeral c1, c2; + bool is_mul1 = m_r.is_mul(t1, c1, pp1); + bool is_mul2 = m_r.is_mul(t2, c2, pp2); + if (!is_mul1 && is_mul2) + return true; + if (is_mul1 && !is_mul2) + return false; + if (!is_mul1 && !is_mul2) + return t1->get_id() < t2->get_id(); + if (c1 < c2) + return true; + if (c1 > c2) + return false; + return pp1->get_id() < pp2->get_id(); + } +}; + +template +void poly_rewriter::hoist_cmul(expr_ref_buffer & args) { + unsigned sz = args.size(); + std::sort(args.c_ptr(), args.c_ptr() + sz, hoist_cmul_lt(*this)); + numeral c, c_prime; + ptr_buffer pps; + expr * pp, * pp_prime; + unsigned j = 0; + unsigned i = 0; + while (i < sz) { + expr * mon = args[i]; + if (is_mul(mon, c, pp) && i < sz - 1) { + expr * mon_prime = args[i+1]; + if (is_mul(mon_prime, c_prime, pp_prime) && c == c_prime) { + // found target + pps.reset(); + pps.push_back(pp); + pps.push_back(pp_prime); + i += 2; + while (i < sz && is_mul(args[i], c_prime, pp_prime) && c == c_prime) { + pps.push_back(pp_prime); + i++; + } + SASSERT(is_numeral(to_app(mon)->get_arg(0), c_prime) && c == c_prime); + expr * mul_args[2] = { to_app(mon)->get_arg(0), mk_add_app(pps.size(), pps.c_ptr()) }; + args.set(j, mk_mul_app(2, mul_args)); + j++; + continue; + } + } + args.set(j, mon); + j++; + i++; + } + args.resize(j); +} + +template +br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args >= 2); + numeral c; + unsigned num_coeffs = 0; + numeral a; + expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args + expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once + bool has_multiple = false; + expr * prev = 0; + bool ordered = true; + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg, a)) { + num_coeffs++; + c += a; + } + else { + // arg is not a numeral + if (m_sort_sums && ordered) { + if (prev != 0 && lt(arg, prev)) + ordered = false; + prev = arg; + } + } + + arg = get_power_product(arg); + if (visited.is_marked(arg)) { + multiple.mark(arg); + has_multiple = true; + } + else { + visited.mark(arg); + } + } + normalize(c); + SASSERT(m_sort_sums || ordered); + TRACE("sort_sums", + tout << "ordered: " << ordered << "\n"; + for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";); + + if (has_multiple) { + // expensive case + buffer coeffs; + m_expr2pos.reset(); + // compute the coefficient of power products that occur multiple times. + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) + continue; + unsigned pos; + if (m_expr2pos.find(pp, pos)) { + coeffs[pos] += a; + } + else { + m_expr2pos.insert(pp, coeffs.size()); + coeffs.push_back(a); + } + } + expr_ref_buffer new_args(m()); + if (!c.is_zero()) { + new_args.push_back(mk_numeral(c)); + } + // copy power products with non zero coefficients to new_args + visited.reset(); + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg); + if (!multiple.is_marked(pp)) { + new_args.push_back(arg); + } + else if (!visited.is_marked(pp)) { + visited.mark(pp); + unsigned pos = UINT_MAX; + m_expr2pos.find(pp, pos); + SASSERT(pos != UINT_MAX); + a = coeffs[pos]; + normalize(a); + if (!a.is_zero()) + new_args.push_back(mk_mul_app(a, pp)); + } + } + if (m_hoist_cmul) { + hoist_cmul(new_args); + } + else if (m_sort_sums) { + TRACE("sort_sums_bug", tout << "new_args.size(): " << new_args.size() << "\n";); + if (c.is_zero()) + std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); + else + std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); + } + result = mk_add_app(new_args.size(), new_args.c_ptr()); + if (hoist_multiplication(result)) { + return BR_REWRITE_FULL; + } + return BR_DONE; + } + else { + SASSERT(!has_multiple); + if (ordered && !m_hoist_mul && !m_hoist_cmul) { + if (num_coeffs == 0) + return BR_FAILED; + if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero()) + return BR_FAILED; + } + expr_ref_buffer new_args(m()); + if (!c.is_zero()) + new_args.push_back(mk_numeral(c)); + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; + if (is_numeral(arg)) + continue; + new_args.push_back(arg); + } + if (m_hoist_cmul) { + hoist_cmul(new_args); + } + else if (!ordered) { + if (c.is_zero()) + std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); + else + std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); + } + result = mk_add_app(new_args.size(), new_args.c_ptr()); + if (hoist_multiplication(result)) { + return BR_REWRITE_FULL; + } + return BR_DONE; + } +} + + +template +br_status poly_rewriter::mk_uminus(expr * arg, expr_ref & result) { + numeral a; + set_curr_sort(m().get_sort(arg)); + if (is_numeral(arg, a)) { + a.neg(); + normalize(a); + result = mk_numeral(a); + return BR_DONE; + } + else { + result = mk_mul_app(numeral(-1), arg); + return BR_REWRITE1; + } +} + +template +br_status poly_rewriter::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) { + SASSERT(num_args > 0); + if (num_args == 1) { + result = args[0]; + return BR_DONE; + } + set_curr_sort(m().get_sort(args[0])); + expr * minus_one = mk_numeral(numeral(-1)); + ptr_buffer new_args; + new_args.push_back(args[0]); + for (unsigned i = 1; i < num_args; i++) { + expr * aux_args[2] = { minus_one, args[i] }; + new_args.push_back(mk_mul_app(2, aux_args)); + } + result = mk_add_app(new_args.size(), new_args.c_ptr()); + return BR_REWRITE2; +} + +/** + \brief Cancel/Combine monomials that occur is the left and right hand sides. + + \remark If move = true, then all non-constant monomials are moved to the left-hand-side. +*/ +template +br_status poly_rewriter::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) { + set_curr_sort(m().get_sort(lhs)); + unsigned lhs_sz; + expr * const * lhs_monomials = get_monomials(lhs, lhs_sz); + unsigned rhs_sz; + expr * const * rhs_monomials = get_monomials(rhs, rhs_sz); + + expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in lhs or rhs + expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once + bool has_multiple = false; + + numeral c(0); + numeral a; + unsigned num_coeffs = 0; + + for (unsigned i = 0; i < lhs_sz; i++) { + expr * arg = lhs_monomials[i]; + if (is_numeral(arg, a)) { + c += a; + num_coeffs++; + } + else { + visited.mark(get_power_product(arg)); + } + } + + if (move && num_coeffs == 0 && is_numeral(rhs)) + return BR_FAILED; + + for (unsigned i = 0; i < rhs_sz; i++) { + expr * arg = rhs_monomials[i]; + if (is_numeral(arg, a)) { + c -= a; + num_coeffs++; + } + else { + expr * pp = get_power_product(arg); + if (visited.is_marked(pp)) { + multiple.mark(pp); + has_multiple = true; + } + } + } + + normalize(c); + + if (!has_multiple && num_coeffs <= 1) { + if (move) { + if (is_numeral(rhs)) + return BR_FAILED; + } + else { + if (num_coeffs == 0 || is_numeral(rhs)) + return BR_FAILED; + } + } + + buffer coeffs; + m_expr2pos.reset(); + for (unsigned i = 0; i < lhs_sz; i++) { + expr * arg = lhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) + continue; + unsigned pos; + if (m_expr2pos.find(pp, pos)) { + coeffs[pos] += a; + } + else { + m_expr2pos.insert(pp, coeffs.size()); + coeffs.push_back(a); + } + } + + for (unsigned i = 0; i < rhs_sz; i++) { + expr * arg = rhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) + continue; + unsigned pos = UINT_MAX; + m_expr2pos.find(pp, pos); + SASSERT(pos != UINT_MAX); + coeffs[pos] -= a; + } + + + ptr_buffer new_lhs_monomials; + new_lhs_monomials.push_back(0); // save space for coefficient if needed + // copy power products with non zero coefficients to new_lhs_monomials + visited.reset(); + for (unsigned i = 0; i < lhs_sz; i++) { + expr * arg = lhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg); + if (!multiple.is_marked(pp)) { + new_lhs_monomials.push_back(arg); + } + else if (!visited.is_marked(pp)) { + visited.mark(pp); + unsigned pos = UINT_MAX; + m_expr2pos.find(pp, pos); + SASSERT(pos != UINT_MAX); + a = coeffs[pos]; + if (!a.is_zero()) + new_lhs_monomials.push_back(mk_mul_app(a, pp)); + } + } + + ptr_buffer new_rhs_monomials; + new_rhs_monomials.push_back(0); // save space for coefficient if needed + for (unsigned i = 0; i < rhs_sz; i++) { + expr * arg = rhs_monomials[i]; + if (is_numeral(arg)) + continue; + expr * pp = get_power_product(arg, a); + if (!multiple.is_marked(pp)) { + if (move) { + if (!a.is_zero()) { + if (a.is_minus_one()) { + new_lhs_monomials.push_back(pp); + } + else { + a.neg(); + SASSERT(!a.is_one()); + expr * args[2] = { mk_numeral(a), pp }; + new_lhs_monomials.push_back(mk_mul_app(2, args)); + } + } + } + else { + new_rhs_monomials.push_back(arg); + } + } + } + + bool c_at_rhs = false; + if (move) { + if (m_sort_sums) { + // + 1 to skip coefficient + std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), ast_to_lt()); + } + c_at_rhs = true; + } + else if (new_rhs_monomials.size() == 1) { // rhs is empty + c_at_rhs = true; + } + else if (new_lhs_monomials.size() > 1) { + c_at_rhs = true; + } + + if (c_at_rhs) { + c.neg(); + normalize(c); + new_rhs_monomials[0] = mk_numeral(c); + lhs_result = mk_add_app(new_lhs_monomials.size() - 1, new_lhs_monomials.c_ptr() + 1); + rhs_result = mk_add_app(new_rhs_monomials.size(), new_rhs_monomials.c_ptr()); + } + else { + new_lhs_monomials[0] = mk_numeral(c); + lhs_result = mk_add_app(new_lhs_monomials.size(), new_lhs_monomials.c_ptr()); + rhs_result = mk_add_app(new_rhs_monomials.size() - 1, new_rhs_monomials.c_ptr() + 1); + } + return BR_DONE; +} + +#define TO_BUFFER(_tester_, _buffer_, _e_) \ + _buffer_.push_back(_e_); \ + for (unsigned _i = 0; _i < _buffer_.size(); ) { \ + expr* _e = _buffer_[_i]; \ + if (_tester_(_e)) { \ + app* a = to_app(_e); \ + _buffer_[_i] = a->get_arg(0); \ + for (unsigned _j = 1; _j < a->get_num_args(); ++_j) { \ + _buffer_.push_back(a->get_arg(_j)); \ + } \ + } \ + else { \ + ++_i; \ + } \ + } \ + +template +bool poly_rewriter::hoist_multiplication(expr_ref& som) { + if (!m_hoist_mul) { + return false; + } + ptr_buffer adds, muls; + TO_BUFFER(is_add, adds, som); + buffer valid(adds.size(), true); + obj_map mul_map; + unsigned j; + bool change = false; + for (unsigned k = 0; k < adds.size(); ++k) { + expr* e = adds[k]; + muls.reset(); + TO_BUFFER(is_mul, muls, e); + for (unsigned i = 0; i < muls.size(); ++i) { + e = muls[i]; + if (is_numeral(e)) { + continue; + } + if (mul_map.find(e, j) && valid[j] && j != k) { + m_curr_sort = m().get_sort(adds[k]); + adds[j] = merge_muls(adds[j], adds[k]); + adds[k] = mk_numeral(rational(0)); + valid[j] = false; + valid[k] = false; + change = true; + break; + } + else { + mul_map.insert(e, k); + } + } + } + if (!change) { + return false; + } + + som = mk_add_app(adds.size(), adds.c_ptr()); + + + return true; +} + +template +expr* poly_rewriter::merge_muls(expr* x, expr* y) { + ptr_buffer m1, m2; + TO_BUFFER(is_mul, m1, x); + TO_BUFFER(is_mul, m2, y); + unsigned k = 0; + for (unsigned i = 0; i < m1.size(); ++i) { + x = m1[i]; + bool found = false; + unsigned j; + for (j = k; j < m2.size(); ++j) { + found = m2[j] == x; + if (found) break; + } + if (found) { + std::swap(m1[i],m1[k]); + std::swap(m2[j],m2[k]); + ++k; + } + } + m_curr_sort = m().get_sort(x); + SASSERT(k > 0); + SASSERT(m1.size() >= k); + SASSERT(m2.size() >= k); + expr* args[2] = { mk_mul_app(m1.size()-k, m1.c_ptr()+k), + mk_mul_app(m2.size()-k, m2.c_ptr()+k) }; + if (k == m1.size()) { + m1.push_back(0); + } + m1[k] = mk_add_app(2, args); + return mk_mul_app(k+1, m1.c_ptr()); +} diff --git a/src/cmd_context/README b/src/cmd_context/README index c44a00690..feb3abd20 100644 --- a/src/cmd_context/README +++ b/src/cmd_context/README @@ -1,2 +1,2 @@ Command context provides the infrastructure for executing commands in front-ends such as SMT-LIB 2.0. -It is also provides the solver abstraction to plugin solvers in this kind of front-end. \ No newline at end of file +It is also provides the solver abstraction to plugin solvers in this kind of front-end. diff --git a/src/math/euclid/README b/src/math/euclid/README index 7235cd76f..17d408fc9 100644 --- a/src/math/euclid/README +++ b/src/math/euclid/README @@ -1,2 +1,2 @@ Basic Euclidean solver for linear integer equations. -This solver generates "explanations". \ No newline at end of file +This solver generates "explanations". diff --git a/src/math/interval/README b/src/math/interval/README index 75aa2e9c6..06ca1ea7a 100644 --- a/src/math/interval/README +++ b/src/math/interval/README @@ -1,2 +1,2 @@ Template for interval arithmetic. The template can be instantiated using different numeral (integers/mpz, rationals/mpq, floating-point/mpf, etc) packages. -The class im_default_config defines a default configuration for the template that uses rationals. It also shows what is the expected signature used by the template. \ No newline at end of file +The class im_default_config defines a default configuration for the template that uses rationals. It also shows what is the expected signature used by the template. diff --git a/src/math/polynomial/README b/src/math/polynomial/README index 5d079eea0..2d2f9f0a0 100644 --- a/src/math/polynomial/README +++ b/src/math/polynomial/README @@ -1,3 +1,3 @@ Polynomial manipulation package. It contains support for univariate (upolynomial.*) and multivariate polynomials (polynomial.*). -Multivariate polynomial factorization does not work yet (polynomial_factorization.*), and it is disabled. \ No newline at end of file +Multivariate polynomial factorization does not work yet (polynomial_factorization.*), and it is disabled. diff --git a/src/muz_qe/dl_hassel_common.cpp b/src/muz_qe/dl_hassel_common.cpp deleted file mode 100755 index 6201868ca..000000000 --- a/src/muz_qe/dl_hassel_common.cpp +++ /dev/null @@ -1,434 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - dl_hassel_common.cpp - -Abstract: - - - -Revision History: - ---*/ - -#include "dl_hassel_common.h" -#include "dl_context.h" - -#include - -namespace datalog { - - static void formula_to_dnf_aux(app *and, unsigned idx, std::set& conjexpr, std::set& toplevel, ast_manager& m) { - if (idx == and->get_num_args()) { - std::vector v(conjexpr.begin(), conjexpr.end()); - toplevel.insert(m.mk_and((unsigned)v.size(), &v[0])); - return; - } - - expr *e = and->get_arg(idx); - if (is_app(e) && to_app(e)->get_decl_kind() == OP_OR) { - app *or = to_app(e); - // quick subsumption test: if any of the elements of the OR is already ANDed, then we skip this OR - for (unsigned i = 0; i < or->get_num_args(); ++i) { - if (conjexpr.count(or->get_arg(i))) { - formula_to_dnf_aux(and, idx+1, conjexpr, toplevel, m); - return; - } - } - - for (unsigned i = 0; i < or->get_num_args(); ++i) { - std::set conjexpr2(conjexpr); - conjexpr2.insert(or->get_arg(i)); - formula_to_dnf_aux(and, idx+1, conjexpr2, toplevel, m); - } - } else { - conjexpr.insert(e); - formula_to_dnf_aux(and, idx+1, conjexpr, toplevel, m); - } - } - - expr_ref formula_to_dnf(expr_ref f) { - app *a = to_app(f); - SASSERT(a->get_decl_kind() == OP_AND); - std::set toplevel, conjexpr; - formula_to_dnf_aux(a, 0, conjexpr, toplevel, f.m()); - - if (toplevel.size() > 1) { - std::vector v(toplevel.begin(), toplevel.end()); - return expr_ref(f.m().mk_or((unsigned)v.size(), &v[0]), f.m()); - } else { - return expr_ref(*toplevel.begin(), f.m()); - } - } - - bool bit_vector::contains(const bit_vector & other) const { - unsigned n = num_words(); - if (n == 0) - return true; - - for (unsigned i = 0; i < n - 1; ++i) { - if ((m_data[i] & other.m_data[i]) != other.m_data[i]) - return false; - } - unsigned bit_rest = m_num_bits % 32; - unsigned mask = (1 << bit_rest) - 1; - if (mask == 0) mask = UINT_MAX; - unsigned other_data = other.m_data[n-1] & mask; - return (m_data[n-1] & other_data) == other_data; - } - - bool bit_vector::contains(const bit_vector & other, unsigned idx) const { - // TODO: optimize this to avoid copy - return slice(idx, other.size()).contains(other); - } - - bool bit_vector::contains_consecutive_zeros() const { - unsigned n = num_words(); - if (n == 0) - return false; - - for (unsigned i = 0; i < n - 1; ++i) { - if ((((m_data[i] << 1) | m_data[i]) & 0xAAAAAAAA) != 0xAAAAAAAA) - return true; - } - unsigned bit_rest = m_num_bits % 32; - unsigned mask = (1 << bit_rest) - 1; - if (mask == 0) mask = UINT_MAX; - mask &= 0xAAAAAAAA; - return ((((m_data[n-1] << 1) | m_data[n-1]) & mask) != mask); - } - - bit_vector bit_vector::slice(unsigned idx, unsigned length) const { - bit_vector Res(length); - // TODO: optimize w/ memcpy when possible - for (unsigned i = idx; i < idx + length; ++i) { - Res.push_back(get(i)); - } - SASSERT(Res.size() == length); - return Res; - } - - void bit_vector::append(const bit_vector & other) { - if (other.empty()) - return; - - if ((m_num_bits % 32) == 0) { - unsigned prev_num_bits = m_num_bits; - resize(m_num_bits + other.m_num_bits); - memcpy(&get_bit_word(prev_num_bits), other.m_data, other.num_words() * sizeof(unsigned)); - return; - } - - // TODO: optimize the other cases. - for (unsigned i = 0; i < other.m_num_bits; ++i) { - push_back(other.get(i)); - } - } - - uint64 bit_vector::to_number(unsigned idx, unsigned length) const { - SASSERT(length <= 64); - uint64 r = 0; - for (unsigned i = 0; i < length; ++i) { - r = (r << 1) | (uint64)get(idx+i); - } - return r; - } - - bool bit_vector::operator<(bit_vector const & other) const { - SASSERT(m_num_bits == other.m_num_bits); - unsigned n = num_words(); - if (n == 0) - return false; - - for (unsigned i = 0; i < n - 1; ++i) { - if (m_data[i] > other.m_data[i]) - return false; - if (m_data[i] < other.m_data[i]) - return true; - } - - unsigned bit_rest = m_num_bits % 32; - unsigned mask = (1 << bit_rest) - 1; - if (mask == 0) mask = UINT_MAX; - return (m_data[n-1] & mask) < (other.m_data[n-1] & mask); - } - - table_information::table_information(table_plugin & p, const table_signature& sig) : - m_column_info(sig.size()+1), - m_bv_util(p.get_context().get_manager()), - m_decl_util(p.get_context().get_manager()) { - - unsigned column = 0; - for (unsigned i = 0; i < sig.size(); ++i) { - unsigned num_bits = uint64_log2(sig[i]); - SASSERT(num_bits == 64 || (1ULL << num_bits) == sig[i]); - m_column_info[i] = column; - column += num_bits; - } - m_column_info[sig.size()] = column; - } - - void table_information::expand_column_vector(unsigned_vector& v, const table_information *other) const { - unsigned_vector orig; - orig.swap(v); - - for (unsigned i = 0; i < orig.size(); ++i) { - unsigned col, limit; - if (orig[i] < get_num_cols()) { - col = column_idx(orig[i]); - limit = col + column_num_bits(orig[i]); - } else { - unsigned idx = orig[i] - get_num_cols(); - col = get_num_bits() + other->column_idx(idx); - limit = col + other->column_num_bits(idx); - } - - for (; col < limit; ++col) { - v.push_back(col); - } - } - } - - void table_information::display(std::ostream & out) const { - out << '<'; - for (unsigned i = 0; i < get_num_cols(); ++i) { - if (i > 0) - out << ", "; - out << column_num_bits(i); - } - out << ">\n"; - } - - ternary_bitvector::ternary_bitvector(unsigned size, bool full) : - bit_vector() { - resize(size, full); - } - - ternary_bitvector::ternary_bitvector(uint64 n, unsigned num_bits) : - bit_vector(2 * num_bits) { - append_number(n, num_bits); - } - - ternary_bitvector::ternary_bitvector(const table_fact& f, const table_information& t) : - bit_vector(2 * t.get_num_bits()) { - for (unsigned i = 0; i < f.size(); ++i) { - SASSERT(t.column_idx(i) == size()); - append_number(f[i], t.column_num_bits(i)); - } - SASSERT(size() == t.get_num_bits()); - } - - void ternary_bitvector::fill1() { - memset(m_data, 0xFF, m_capacity * sizeof(unsigned)); - } - - unsigned ternary_bitvector::get(unsigned idx) const { - idx *= 2; - return (bit_vector::get(idx) << 1) | (unsigned)bit_vector::get(idx+1); - } - - void ternary_bitvector::set(unsigned idx, unsigned val) { - SASSERT(val == BIT_0 || val == BIT_1 || val == BIT_x); - idx *= 2; - bit_vector::set(idx, (val >> 1) != 0); - bit_vector::set(idx+1, (val & 1) != 0); - } - - void ternary_bitvector::push_back(unsigned val) { - SASSERT(val == BIT_0 || val == BIT_1 || val == BIT_x); - bit_vector::push_back((val >> 1) != 0); - bit_vector::push_back((val & 1) != 0); - } - - void ternary_bitvector::append_number(uint64 n, unsigned num_bits) { - SASSERT(num_bits <= 64); - for (int bit = num_bits-1; bit >= 0; --bit) { - if (n & (1ULL << bit)) { - push_back(BIT_1); - } else { - push_back(BIT_0); - } - } - } - - void ternary_bitvector::mk_idx_eq(unsigned idx, ternary_bitvector& val) { - for (unsigned i = 0; i < val.size(); ++i) { - set(idx+i, val.get(i)); - } - } - - ternary_bitvector ternary_bitvector::and(const ternary_bitvector& other) const{ - ternary_bitvector result(*this); - result &= other; - return result; - } - - void ternary_bitvector::neg(union_ternary_bitvector& result) const { - ternary_bitvector negated; - negated.resize(size()); - - for (unsigned i = 0; i < size(); ++i) { - switch (get(i)) { - case BIT_0: - negated.fill1(); - negated.set(i, BIT_1); - break; - case BIT_1: - negated.fill1(); - negated.set(i, BIT_0); - break; - default: - continue; - } - result.add_fact(negated); - } - } - - static void join_fix_eqs(ternary_bitvector& TBV, unsigned idx, unsigned col2_offset, - const unsigned_vector& cols1, const unsigned_vector& cols2, - union_ternary_bitvector& result) { - if (idx == cols1.size()) { - result.add_fact(TBV); - return; - } - - unsigned idx1 = cols1[idx]; - unsigned idx2 = cols2[idx] + col2_offset; - unsigned v1 = TBV.get(idx1); - unsigned v2 = TBV.get(idx2); - - if (v1 == BIT_x) { - if (v2 == BIT_x) { - // both x: duplicate row - ternary_bitvector TBV2(TBV); - TBV2.set(idx1, BIT_0); - TBV2.set(idx2, BIT_0); - join_fix_eqs(TBV2, idx+1, col2_offset, cols1, cols2, result); - - TBV.set(idx1, BIT_1); - TBV.set(idx2, BIT_1); - } else { - TBV.set(idx1, v2); - } - } else if (v2 == BIT_x) { - TBV.set(idx2, v1); - } else if (v1 != v2) { - // columns don't match - return; - } - join_fix_eqs(TBV, idx+1, col2_offset, cols1, cols2, result); - } - - void ternary_bitvector::join(const ternary_bitvector& other, - const unsigned_vector& cols1, - const unsigned_vector& cols2, - union_ternary_bitvector& result) const { - ternary_bitvector TBV(*this); - TBV.append(other); - join_fix_eqs(TBV, 0, size(), cols1, cols2, result); - } - - bool ternary_bitvector::project(const unsigned_vector& delcols, ternary_bitvector& result) const { - unsigned *rm_cols = delcols.c_ptr(); - - for (unsigned i = 0; i < size(); ++i) { - if (*rm_cols == i) { - ++rm_cols; - continue; - } - result.push_back(get(i)); - } - return true; - } - - static void copy_column(ternary_bitvector& CopyTo, const ternary_bitvector& CopyFrom, - unsigned col_dst, unsigned col_src, const table_information& src_table, - const table_information& dst_table) { - unsigned idx_dst = dst_table.column_idx(col_dst); - unsigned idx_src = src_table.column_idx(col_src); - unsigned num_bits = dst_table.column_num_bits(col_dst); - SASSERT(num_bits == src_table.column_num_bits(col_src)); - - for (unsigned i = 0; i < num_bits; ++i) { - CopyTo.set(idx_dst+i, CopyFrom.get(idx_src+i)); - } - } - - void ternary_bitvector::rename(const unsigned_vector& cyclecols, - const unsigned_vector& out_of_cycle_cols, - const table_information& src_table, - const table_information& dst_table, - ternary_bitvector& result) const { - result.resize(dst_table.get_num_bits()); - - for (unsigned i = 1; i < cyclecols.size(); ++i) { - copy_column(result, *this, cyclecols[i-1], cyclecols[i], src_table, dst_table); - } - copy_column(result, *this, cyclecols[cyclecols.size()-1], cyclecols[0], src_table, dst_table); - - for (unsigned i = 0; i < out_of_cycle_cols.size(); ++i) { - unsigned col = out_of_cycle_cols[i]; - copy_column(result, *this, col, col, src_table, dst_table); - } - } - - unsigned ternary_bitvector::size_in_bytes() const { - return sizeof(*this) + m_capacity; - } - - void ternary_bitvector::display(std::ostream & out) const { - for (unsigned i = 0; i < size(); ++i) { - switch (get(i)) { - case BIT_0: - out << '0'; - break; - case BIT_1: - out << '1'; - break; - case BIT_x: - out << 'x'; - break; - default: - UNREACHABLE(); - } - } - } - -#if Z3DEBUG - void ternary_bitvector::expand(std::set & BVs) const { - bit_vector BV(m_num_bits); - expand(BVs, BV, 0); - } - - void ternary_bitvector::expand(std::set & BVs, bit_vector &BV, unsigned idx) const { - if (idx == size()) { - BVs.insert(BV); - return; - } - - switch (get(idx)) { - case BIT_0: - BV.push_back(false); - expand(BVs, BV, idx+1); - break; - case BIT_1: - BV.push_back(true); - expand(BVs, BV, idx+1); - break; - case BIT_x: { // x: duplicate - bit_vector BV2(BV); - BV.push_back(false); - BV2.push_back(true); - expand(BVs, BV, idx+1); - expand(BVs, BV2, idx+1); - } - break; - default: - UNREACHABLE(); - } - } -#endif - -} diff --git a/src/muz_qe/dl_hassel_common.h b/src/muz_qe/dl_hassel_common.h deleted file mode 100755 index 7c1d1e614..000000000 --- a/src/muz_qe/dl_hassel_common.h +++ /dev/null @@ -1,1079 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - dl_hassel_common.h - -Abstract: - - - -Revision History: - ---*/ - -#ifndef _DL_HASSEL_COMMON_H_ -#define _DL_HASSEL_COMMON_H_ - -#include "bit_vector.h" -#include "dl_base.h" -#include "bv_decl_plugin.h" -#include "union_find.h" -#include -#include - -#define BIT_0 ((0<<1)|1) -#define BIT_1 ((1<<1)|0) -#define BIT_x ((1<<1)|1) - -namespace datalog { - - expr_ref formula_to_dnf(expr_ref f); - - class bit_vector : public ::bit_vector { - public: - bit_vector() : ::bit_vector() {} - bit_vector(unsigned bits) : ::bit_vector(bits) {} - - bool contains(const bit_vector & other) const; - bool contains(const bit_vector & other, unsigned idx) const; - bool contains_consecutive_zeros() const; - - bit_vector slice(unsigned idx, unsigned length) const; - void append(const bit_vector & other); - - uint64 to_number(unsigned idx, unsigned length) const; - - // for std::less operations - bool operator<(bit_vector const & other) const; - }; - - - class table_information { - unsigned_vector m_column_info; - bv_util m_bv_util; - dl_decl_util m_decl_util; - - public: - table_information(table_plugin & p, const table_signature& sig); - - unsigned get_num_bits() const { - return m_column_info.back(); - } - - unsigned get_num_cols() const { - return m_column_info.size()-1; - } - - unsigned column_idx(unsigned col) const { - return m_column_info[col]; - } - - unsigned column_num_bits(unsigned col) const { - return m_column_info[col+1] - m_column_info[col]; - } - - void expand_column_vector(unsigned_vector& v, const table_information *other = 0) const; - - void display(std::ostream & out) const; - - const bv_util& get_bv_util() const { return m_bv_util; } - const dl_decl_util& get_decl_util() const { return m_decl_util; } - }; - - - template class union_ternary_bitvector; - - - class ternary_bitvector : private bit_vector { - public: - ternary_bitvector() : bit_vector() {} - ternary_bitvector(unsigned size) : bit_vector(2 * size) {} - - ternary_bitvector(unsigned size, bool full); - ternary_bitvector(uint64 n, unsigned num_bits); - ternary_bitvector(const table_fact& f, const table_information& t); - - void swap(ternary_bitvector& other) { - SASSERT(size() == other.size()); - bit_vector::swap(other); - } - - void resize(unsigned new_size, bool val = false) { - bit_vector::resize(2 * new_size, val); - } - - void reset() { - m_num_bits = 0; - } - - unsigned size() const { - SASSERT((m_num_bits % 2) == 0); - return m_num_bits/2; - } - - void fill1(); - - void append(const ternary_bitvector & other) { bit_vector::append(other); } - bool contains(const ternary_bitvector & other) const { return bit_vector::contains(other); } - - bool is_empty() const { return contains_consecutive_zeros(); } - - unsigned get(unsigned idx) const; - void set(unsigned idx, unsigned val); - void push_back(unsigned val); - void append_number(uint64 n, unsigned num_bits); - void mk_idx_eq(unsigned idx, ternary_bitvector& val); - - ternary_bitvector and(const ternary_bitvector& other) const; - void neg(union_ternary_bitvector& result) const; - - void join(const ternary_bitvector& other, const unsigned_vector& cols1, - const unsigned_vector& cols2, union_ternary_bitvector& result) const; - - bool project(const unsigned_vector& delcols, ternary_bitvector& result) const; - - void rename(const unsigned_vector& cyclecols, const unsigned_vector& out_of_cycle_cols, - const table_information& src_table, const table_information& dst_table, - ternary_bitvector& result) const; - - static bool has_subtract() { return false; } - void subtract(const union_ternary_bitvector& other, - union_ternary_bitvector& result) const { UNREACHABLE(); } - - void display(std::ostream & out) const; - unsigned size_in_bytes() const; - -#if Z3DEBUG - void expand(std::set & BVs) const; -#endif - - private: -#if Z3DEBUG - void expand(std::set & BVs, bit_vector &BV, unsigned idx) const; -#endif - }; - - - template - class union_ternary_bitvector { - typedef std::list union_t; - - union_t m_bitvectors; - unsigned m_bv_size; //!< number of ternary bits - - public: - union_ternary_bitvector(unsigned bv_size) : m_bv_size(bv_size) {} - - union_ternary_bitvector(unsigned bv_size, bool full) : m_bv_size(bv_size) { - if (full) - mk_full(); - } - - union_ternary_bitvector and(const union_ternary_bitvector & Other) const { - if (empty()) - return *this; - if (Other.empty()) - return Other; - - union_ternary_bitvector Ret(m_bv_size); - - for (const_iterator I = begin(), E = end(); I != E; ++I) { - for (const_iterator II = Other.begin(), EE = Other.end(); II != EE; ++II) { - T row(I->and(*II)); - if (!row.is_empty()) - Ret.add_fact(row); - } - } - return Ret; - } - - union_ternary_bitvector or(const union_ternary_bitvector & Other) const { - if (empty()) - return Other; - if (Other.empty()) - return *this; - - union_ternary_bitvector Ret(*this); - Ret.add_facts(Other); - return Ret; - } - - union_ternary_bitvector neg() const { - union_ternary_bitvector Ret(m_bv_size); - Ret.mk_full(); - - union_ternary_bitvector negated(m_bv_size); - for (const_iterator I = begin(), E = end(); I != E; ++I) { - negated.reset(); - I->neg(negated); - Ret.swap(Ret.and(negated)); - } - return Ret; - } - - void subtract(const union_ternary_bitvector& other) { - if (!T::has_subtract()) { - swap(this->and(other.neg())); - return; - } - - union_ternary_bitvector subtracted(m_bv_size); - for (const_iterator I = begin(), E = end(); I != E; ++I) { - I->subtract(other, subtracted); - } - swap(subtracted); - } - -#if 0 - union_ternary_bitvector gc() const { - // Simple subsumption-based cleaning. - union_ternary_bitvector Ret(m_bv_size); - for (union_t::const_reverse_iterator I = m_bitvectors.rbegin(), E = m_bitvectors.rend(); I != E; ++I) { - Ret.add_fact(*I); - } - return Ret; - } -#endif - - void join(const union_ternary_bitvector& other, const unsigned_vector& cols1, - const unsigned_vector& cols2, union_ternary_bitvector& result) const { - for (const_iterator I = begin(), E = end(); I != E; ++I) { - for (const_iterator II = other.begin(), EE = other.end(); II != EE; ++II) { - I->join(*II, cols1, cols2, result); - } - } - } - - void rename(const unsigned_vector& cyclecols, const unsigned_vector& out_of_cycle_cols, - const table_information& src_table, const table_information& dst_table, - union_ternary_bitvector& result) const { - T row(m_bv_size); - for (const_iterator I = begin(), E = end(); I != E; ++I) { - row.reset(); - I->rename(cyclecols, out_of_cycle_cols, src_table, dst_table, row); - result.add_new_fact(row); - } - } - - void project(const unsigned_vector& delcols, union_ternary_bitvector& result) const { - unsigned new_size = m_bv_size - (delcols.size()-1); - T row(new_size); - - for (const_iterator I = begin(), E = end(); I != E; ++I) { - row.reset(); - if (I->project(delcols, row)) { - SASSERT(!row.is_empty()); - result.add_fact(row); - } - } - } - - private: - typedef union_find<> subset_ints; - - // returns 1 if row should be removed, 0 otherwise - static int fix_single_bit(T & BV, unsigned idx, unsigned value, const subset_ints& equalities) { - unsigned root = equalities.find(idx); - idx = root; - do { - unsigned bitval = BV.get(idx); - if (bitval == BIT_x) { - BV.set(idx, value); - } else if (bitval != value) { - return 1; - } - idx = equalities.next(idx); - } while (idx != root); - return 0; - } - - static int fix_single_bit(T & BV1, unsigned idx1, T & BV2, unsigned idx2, - subset_ints& equalities, bool discard_col) { - unsigned A = BV1.get(idx1); - unsigned B = BV2.get(idx2); - - if (A == BIT_x) { - if (B == BIT_x) { - // Both are don't cares. - /////// FIXME::: don't duplicate rows with diff table - if (!discard_col) - return 2; // duplicate row - equalities.merge(idx1, idx2); - return 0; - } else { - // only A is don't care. - return fix_single_bit(BV1, idx1, B, equalities); - } - } else if (B == BIT_x) { - // Only B is don't care. - return fix_single_bit(BV2, idx2, A, equalities); - } else if (A == B) { - return 0; - } else { - return 1; // remove row - } - } - - void fix_eq_bits(unsigned idx1, const T *BV, unsigned idx2, unsigned length, - subset_ints& equalities, const bit_vector& discard_cols) { - for (unsigned i = 0; i < length; ++i) { - for (union_t::iterator I = m_bitvectors.begin(), E = m_bitvectors.end(); I != E; ) { - T *eqBV = BV ? const_cast(BV) : &*I; - bool discard_col = discard_cols.get(idx1+i) || (!BV && discard_cols.get(idx2+i)); - - switch (fix_single_bit(*I, idx1+i, *eqBV, idx2+i, equalities, discard_col)) { - case 1: - // remove row - I = m_bitvectors.erase(I); - break; - - case 2: { - // duplicate row - T BV2(*I); - I->set(idx1+i, BIT_0); - I->set(idx2+i, BIT_0); - - BV2.set(idx1+i, BIT_1); - BV2.set(idx2+i, BIT_1); - m_bitvectors.insert(I, BV2); - ++I; - break;} - - default: - // bits fixed - ++I; - } - } - } - } - - /// make bits of table [idx,idx+max_length] equal to e sliced starting at idx2 - unsigned fix_eq_bits(unsigned idx, const expr *e, unsigned idx2, unsigned max_length, - const table_information& t, subset_ints& equalities, - const bit_vector & discard_cols) { - const bv_util& bvu = t.get_bv_util(); - const dl_decl_util& dutil = t.get_decl_util(); - - rational n; - unsigned bv_size; - if (bvu.is_numeral(e, n, bv_size)) { - T num(n.get_int64(), bv_size); - SASSERT(idx2 < bv_size); - max_length = std::min(max_length, bv_size - idx2); - fix_eq_bits(idx, &num, idx2, max_length, equalities, discard_cols); - return idx + max_length; - } - - uint64 num; - if (dutil.is_numeral(e, num)) { - T num_bv(num, max_length); - fix_eq_bits(idx, &num_bv, idx2, max_length, equalities, discard_cols); - return idx + max_length; - } - - if (bvu.is_concat(e)) { - const app *a = to_app(e); - - // skip the first elements of the concat if e.g. we have a top level extract - unsigned i = 0; - for (; i < a->get_num_args(); ++i) { - unsigned arg_size = bvu.get_bv_size(a->get_arg(i)); - if (idx2 < arg_size) - break; - idx2 -= arg_size; - } - - SASSERT(i < a->get_num_args()); - for (; max_length > 0 && i < a->get_num_args(); ++i) { - unsigned idx0 = idx; - idx = fix_eq_bits(idx, a->get_arg(i), idx2, max_length, t, equalities, discard_cols); - idx2 = 0; - SASSERT((idx - idx0) <= max_length); - max_length = max_length - (idx - idx0); - } - return idx; - } - - unsigned low, high; - expr *e2; - if (bvu.is_extract(e, low, high, e2)) { - SASSERT(low <= high); - unsigned size = bvu.get_bv_size(e2); - unsigned offset = size - (high+1) + idx2; - SASSERT(idx2 < (high-low+1)); - max_length = std::min(max_length, high - low + 1 - idx2); - return fix_eq_bits(idx, e2, offset, max_length, t, equalities, discard_cols); - } - - if (e->get_kind() == AST_VAR) { - unsigned idx_var = idx2 + t.column_idx(to_var(e)->get_idx()); - SASSERT(idx2 < t.column_num_bits(to_var(e)->get_idx())); - max_length = std::min(max_length, t.column_num_bits(to_var(e)->get_idx()) - idx2); - fix_eq_bits(idx, 0, idx_var, max_length, equalities, discard_cols); - return idx + max_length; - } - - NOT_IMPLEMENTED_YET(); - return 0; - } - - void filter(const expr *e, subset_ints& equalities, const bit_vector& discard_cols, - const table_information& t) { - switch (e->get_kind()) { - case AST_APP: { - const app *app = to_app(e); - switch (app->get_decl_kind()) { - case OP_AND: - for (unsigned i = 0; i < app->get_num_args(); ++i) { - filter(app->get_arg(i), equalities, discard_cols, t); - } - return; - - case OP_EQ: { - const expr *a = app->get_arg(0); - const var *v; - unsigned vidx = 0; - unsigned length; - - unsigned low, high; - expr *e2; - if (is_var(a)) { - v = to_var(a); - length = t.column_num_bits(v->get_idx()); - } else if (t.get_bv_util().is_extract(a, low, high, e2)) { - vidx = t.get_bv_util().get_bv_size(e2) - high - 1; - length = high - low + 1; - SASSERT(is_var(e2)); - v = to_var(e2); - } else { - NOT_IMPLEMENTED_YET(); - } - vidx += t.column_idx(v->get_idx()); - - unsigned final_idx = fix_eq_bits(vidx, app->get_arg(1), 0, length, t, equalities, discard_cols); - SASSERT(final_idx == vidx + length); - (void)final_idx; - return;} - - case OP_FALSE: - reset(); - return; - - case OP_NOT: { - union_ternary_bitvector sub(m_bv_size, true); - sub.filter(app->get_arg(0), equalities, discard_cols, t); - this->subtract(sub); - return;} - - case OP_OR: { - union_ternary_bitvector orig(m_bv_size); - swap(orig); - for (unsigned i = 0; i < app->get_num_args(); ++i) { - union_ternary_bitvector tmp(orig); - subset_ints eqs(equalities); - tmp.filter(app->get_arg(i), eqs, discard_cols, t); - add_facts(tmp); - } - return;} - - case OP_TRUE: - return; - - default: - std::cerr << "app decl: " << app->get_decl()->get_name() << " (" << app->get_decl_kind() << ")\n"; - NOT_IMPLEMENTED_YET(); - } - break;} - - case AST_VAR: { - // boolean var must be true (10) - SASSERT(t.column_num_bits(to_var(e)->get_idx()) == 1); - unsigned idx = t.column_idx(to_var(e)->get_idx()); - ternary_bitvector BV(1); - BV.push_back(BIT_1); - T BV2(BV); - fix_eq_bits(idx, &BV2, 0, 2, equalities, discard_cols); - return;} - - default: - break; - } - std::cerr << "expr kind: " << get_ast_kind_name(e->get_kind()) << '\n'; - NOT_IMPLEMENTED_YET(); - } - - public: - void filter(const expr *cond, const bit_vector& discard_cols, const table_information& table) { - // datastructure to store equalities with columns that will be projected out - union_find_default_ctx union_ctx; - subset_ints equalities(union_ctx); - for (unsigned i = 0, e = discard_cols.size(); i < e; ++i) { - equalities.mk_var(); - } - - filter(cond, equalities, discard_cols, table); - } - - bool contains(const T & fact) const { - for (const_iterator I = begin(), E = end(); I != E; ++I) { - if (I->contains(fact)) - return true; - } - return false; - } - - bool contains(const union_ternary_bitvector & other) const { - for (const_iterator I = other.begin(), E = other.end(); I != E; ++I) { - for (const_iterator II = begin(), EE = end(); II != EE; ++II) { - if (II->contains(*I)) - goto next_iter; - } - return false; -next_iter: ; - } - return true; - } - - unsigned num_disjs() const { - return (unsigned)m_bitvectors.size(); - } - - unsigned num_bytes() const { - unsigned size = sizeof(*this); - for (const_iterator I = begin(), E = end(); I != E; ++I) { - size += I->size_in_bytes(); - } - return size; - } - -#if Z3DEBUG - void expand(std::set & BVs) const { - for (const_iterator I = begin(), E = end(); I != E; ++I) { - I->expand(BVs); - } - } -#endif - - void add_facts(const union_ternary_bitvector & Other, union_ternary_bitvector *Delta = 0) { - for (const_iterator I = Other.begin(), E = Other.end(); I != E; ++I) { - if (add_fact(*I) && Delta) - Delta->add_fact(*I); - } - } - - bool add_fact(const T & fact) { - if (contains(fact)) - return false; - add_new_fact(fact); - return true; - } - - void add_new_fact(const T & fact) { - SASSERT(m_bv_size == fact.size()); - - // TODO: optimize sequence (karnaugh maps??) - // At least join 1-bit different BVs - m_bitvectors.push_back(fact); - } - - void mk_full() { - reset(); - add_new_fact(T(m_bv_size, true)); - } - - typedef typename union_t::const_iterator const_iterator; - - const_iterator begin() const { return m_bitvectors.begin(); } - const_iterator end() const { return m_bitvectors.end(); } - - bool empty() const { return m_bitvectors.empty(); } - void reset() { m_bitvectors.clear(); } - - void swap(union_ternary_bitvector& other) { - SASSERT(m_bv_size == other.m_bv_size); - m_bitvectors.swap(other.m_bitvectors); - } - - void display(std::ostream & out) const { - out << '#' << num_disjs() << " (bv" << m_bv_size << ") "; - - bool first = true; - for (const_iterator I = begin(), E = end(); I != E; ++I) { - if (!first) - out << " \\/ "; - first = false; - I->display(out); - } - out << '\n'; - } - }; - - - template - class common_hassel_table_plugin : public table_plugin { - public: - common_hassel_table_plugin(symbol &s, relation_manager & manager) : - table_plugin(s, manager) { } - - virtual table_base * mk_empty(const table_signature & s) { - return alloc(T, *this, s); - } - - virtual table_base * mk_full(func_decl* p, const table_signature & s) { - T *t = static_cast(mk_empty(s)); - t->mk_full(); - return t; - } - - virtual bool can_handle_signature(const table_signature & s) { - return s.functional_columns() == 0; - } - - private: - ast_manager& get_ast_manager() { return get_context().get_manager(); } - - class join_fn : public convenient_table_join_fn { - public: - join_fn(const T & t1, const T & t2, unsigned col_cnt, const unsigned *cols1, const unsigned *cols2) - : convenient_table_join_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2) { - t1.expand_column_vector(m_cols1); - t2.expand_column_vector(m_cols2); - } - - virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { - const T & T1 = static_cast(tb1); - const T & T2 = static_cast(tb2); - T * Res = static_cast(T1.get_plugin().mk_empty(get_result_signature())); - T1.m_bitsets.join(T2.m_bitsets, m_cols1, m_cols2, Res->m_bitsets); - TRACE("dl_hassel", tout << "final size: " << Res->get_size_estimate_rows() << '\n';); - return Res; - } - }; - - public: - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { - if (!check_kind(t1) || !check_kind(t2)) - return 0; - return alloc(join_fn, static_cast(t1), static_cast(t2), col_cnt, cols1, cols2); - } - - private: - class union_fn : public table_union_fn { - public: - virtual void operator()(table_base & tgt0, const table_base & src0, table_base * delta0) { - T & tgt = static_cast(tgt0); - const T & src = static_cast(src0); - T * delta = static_cast(delta0); - tgt.m_bitsets.add_facts(src.m_bitsets, delta ? &delta->m_bitsets : 0); - TRACE("dl_hassel", tout << "final size: " << tgt.get_size_estimate_rows() << '\n';); - } - }; - - public: - virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta) { - if (!check_kind(tgt) || !check_kind(src)) - return 0; - return alloc(union_fn); - } - - private: - class project_fn : public convenient_table_project_fn { - public: - project_fn(const T & t, unsigned removed_col_cnt, const unsigned * removed_cols) - : convenient_table_project_fn(t.get_signature(), removed_col_cnt, removed_cols) { - t.expand_column_vector(m_removed_cols); - m_removed_cols.push_back(UINT_MAX); - } - - virtual table_base * operator()(const table_base & tb) { - const T & t = static_cast(tb); - T * res = static_cast(t.get_plugin().mk_empty(get_result_signature())); - t.m_bitsets.project(m_removed_cols, res->m_bitsets); - TRACE("dl_hassel", tout << "final size: " << res->get_size_estimate_rows() << '\n';); - return res; - } - }; - - public: - virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols) { - if (!check_kind(t)) - return 0; - return alloc(project_fn, static_cast(t), col_cnt, removed_cols); - } - - private: - class rename_fn : public convenient_table_rename_fn { - unsigned_vector m_out_of_cycle; - public: - rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) - : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { - SASSERT(permutation_cycle_len >= 2); - idx_set cycle_cols; - for (unsigned i = 0; i < permutation_cycle_len; ++i) { - cycle_cols.insert(permutation_cycle[i]); - } - for (unsigned i = 0; i < orig_sig.size(); ++i) { - if (!cycle_cols.contains(i)) - m_out_of_cycle.push_back(i); - } - } - - virtual table_base * operator()(const table_base & tb) { - const T & t = static_cast(tb); - T * res = static_cast(t.get_plugin().mk_empty(get_result_signature())); - t.m_bitsets.rename(m_cycle, m_out_of_cycle, t, *res, res->m_bitsets); - TRACE("dl_hassel", tout << "final size: " << res->get_size_estimate_rows() << '\n';); - return res; - } - }; - - public: - virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle) { - if (!check_kind(t)) - return 0; - return alloc(rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); - } - - private: - class filter_equal_fn : public table_mutator_fn { - typename T::bitset_t m_filter; - public: - filter_equal_fn(const T & t, const table_element val, unsigned col) : - m_filter(t.get_num_bits()) { - ternary_bitvector filter_row(t.get_num_bits(), true); - ternary_bitvector column(val, t.column_num_bits(col)); - filter_row.mk_idx_eq(t.column_idx(col), column); - m_filter.add_new_fact(filter_row); - } - - virtual void operator()(table_base & tb) { - T & t = static_cast(tb); - t.m_bitsets.swap(m_filter.and(t.m_bitsets)); - TRACE("dl_hassel", tout << "final size: " << t.get_size_estimate_rows() << '\n';); - } - }; - - public: - virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, - unsigned col) { - if (!check_kind(t)) - return 0; - return alloc(filter_equal_fn, static_cast(t), value, col); - } - - private: - static bool cond_is_guard(const expr *e, const table_information& t) { - switch (e->get_kind()) { - case AST_APP: { - const app *app = to_app(e); - switch (app->get_decl_kind()) { - case OP_AND: - case OP_OR: - case OP_NOT: - for (unsigned i = 0; i < app->get_num_args(); ++i) { - if (!cond_is_guard(app->get_arg(i), t)) - return false; - } - return true; - - case OP_EQ: { - const expr *a = app->get_arg(0), *b = app->get_arg(1); - - // column equality is not succinctly representable with TBVs - if (is_var(a) && is_var(b)) - return false; - - // (= var (concat var foo)) - if (t.get_bv_util().is_concat(b)) - return false; - - return true;} - - case OP_FALSE: - case OP_TRUE: - return true; - - default: - return false; - } - break;} - - case AST_VAR: - return true; - - default: - break; - } - return false; - } - - static void split_cond_guard(app *cond, expr_ref& guard, expr_ref& leftover, const table_information& t) { - expr_ref_vector guards(guard.m()); - expr_ref_vector leftovers(leftover.m()); - - if (is_app(cond) && to_app(cond)->get_decl_kind() == OP_AND) { - app *a = to_app(cond); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - expr *arg = a->get_arg(i); - if (cond_is_guard(arg, t)) { - guards.push_back(arg); - } else { - leftovers.push_back(arg); - } - } - } else if (cond_is_guard(cond, t)) { - guard = cond; - return; - } else { - leftover = cond; - return; - } - - if (guards.size() > 1) { - guard = formula_to_dnf(expr_ref(guard.m().mk_and(guards.size(), guards.c_ptr()), guard.m())); - } else if (guards.size() == 1) { - guard = guards.get(0); - } - - if (leftovers.size() > 1) { - leftover = formula_to_dnf(expr_ref(leftover.m().mk_and(leftovers.size(), leftovers.c_ptr()), leftover.m())); - } else if (leftovers.size() == 1) { - leftover = leftovers.get(0); - } - } - - class filter_fn : public table_mutator_fn { - expr_ref m_condition; - typename T::bitset_t m_filter; - bit_vector m_empty_bv; - public: - filter_fn(const T & t, ast_manager& m, app *condition) : - m_condition(m), m_filter(t.get_num_bits(), true) { - m_empty_bv.resize(t.get_num_bits(), false); - - expr_ref guard(m); - split_cond_guard(condition, guard, m_condition, t); - if (guard) - m_filter.filter(guard, m_empty_bv, t); - } - - virtual void operator()(table_base & tb) { - T & t = static_cast(tb); - // first apply guard and then run the interpreter on the leftover - t.m_bitsets.swap(m_filter.and(t.m_bitsets)); - if (m_condition) - t.m_bitsets.filter(m_condition, m_empty_bv, t); - TRACE("dl_hassel", tout << "final size: " << t.get_size_estimate_rows() << '\n';); - } - }; - - public: - virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition) { - if (!check_kind(t)) - return 0; - TRACE("dl_hassel", tout << mk_pp(condition, get_ast_manager()) << '\n';); - return alloc(filter_fn, static_cast(t), get_ast_manager(), condition); - } - - private: - class filter_proj_fn : public convenient_table_project_fn { - expr_ref m_condition; - typename T::bitset_t m_filter; - bit_vector m_col_list; // map: col idx -> bool (whether the column is to be removed) - public: - filter_proj_fn(const T & t, ast_manager& m, app *condition, - unsigned col_cnt, const unsigned * removed_cols) : - convenient_table_project_fn(t.get_signature(), col_cnt, removed_cols), - m_condition(m), m_filter(t.get_num_bits(), true) { - t.expand_column_vector(m_removed_cols); - - m_col_list.resize(t.get_num_bits(), false); - for (unsigned i = 0; i < m_removed_cols.size(); ++i) { - m_col_list.set(m_removed_cols[i]); - } - m_removed_cols.push_back(UINT_MAX); - - expr_ref guard(m); - split_cond_guard(condition, guard, m_condition, t); - if (guard) - m_filter.filter(guard, m_col_list, t); - } - - virtual table_base* operator()(const table_base & tb) { - const T & t = static_cast(tb); - // first apply guard and then run the interpreter on the leftover - typename T::bitset_t filtered(t.get_num_bits()); - filtered.swap(m_filter.and(t.m_bitsets)); - if (m_condition) - filtered.filter(m_condition, m_col_list, t); - - T * res = static_cast(t.get_plugin().mk_empty(get_result_signature())); - filtered.project(m_removed_cols, res->m_bitsets); - TRACE("dl_hassel", tout << "final size: " << res->get_size_estimate_rows() << '\n';); - return res; - } - }; - - public: - virtual table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, - unsigned removed_col_cnt, const unsigned * removed_cols) { - if (!check_kind(t)) - return 0; - TRACE("dl_hassel", tout << mk_pp(condition, get_ast_manager()) << '\n';); - return alloc(filter_proj_fn, static_cast(t), get_ast_manager(), - condition, removed_col_cnt, removed_cols); - } - - - virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, - const unsigned * negated_cols) { - NOT_IMPLEMENTED_YET(); - } - }; - - template - class common_hassel_table : public table_base, public table_information { - public: - typedef T bitset_t; - - common_hassel_table(table_plugin & p, const table_signature & sig) : - table_base(p, sig), table_information(p, sig), m_bitsets(get_num_bits()) { } - - virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const { - SASSERT(!func_columns); - - if (empty()) - return get_plugin().mk_full(p, get_signature()); - - common_hassel_table *res = static_cast(get_plugin().mk_empty(get_signature())); - res->m_bitsets.swap(m_bitsets.neg()); - return res; - } - - virtual void add_fact(const table_fact & f) { - m_bitsets.add_fact(ternary_bitvector(f, *this)); - } - - virtual void add_new_fact(const table_fact & f) { - m_bitsets.add_new_fact(ternary_bitvector(f, *this)); - } - - virtual void remove_fact(table_element const* fact) { - NOT_IMPLEMENTED_YET(); - } - - virtual void reset() { - m_bitsets.reset(); - } - - void mk_full() { - m_bitsets.mk_full(); - } - - virtual table_base * clone() const { - common_hassel_table *res = static_cast(get_plugin().mk_empty(get_signature())); - res->m_bitsets = m_bitsets; - return res; - } - - virtual bool contains_fact(const table_fact & f) { - return m_bitsets.contains(ternary_bitvector(f, *this)); - } - - virtual bool empty() const { - return m_bitsets.empty(); - } - -#if Z3DEBUG - class our_iterator_core : public iterator_core { - class our_row : public row_interface { - const our_iterator_core & m_parent; - const table_information& m_table; - public: - our_row(const common_hassel_table & t, const our_iterator_core & parent) : - row_interface(t), m_parent(parent), m_table(t) {} - - virtual table_element operator[](unsigned col) const { - return m_parent.it->to_number(m_table.column_idx(col), m_table.column_num_bits(col)); - } - }; - - our_row m_row_obj; - std::set BVs; - std::set::iterator it; - - public: - our_iterator_core(const common_hassel_table & t, bool finished) : - m_row_obj(t, *this) { - if (finished) { - it = BVs.end(); - return; - } - t.m_bitsets.expand(BVs); - it = BVs.begin(); - } - - virtual bool is_finished() const { - return it == BVs.end(); - } - - virtual row_interface & operator*() { - SASSERT(!is_finished()); - return m_row_obj; - } - - virtual void operator++() { - SASSERT(!is_finished()); - ++it; - } - }; -#endif - - virtual iterator begin() const { -#if Z3DEBUG - return mk_iterator(alloc(our_iterator_core, *this, false)); -#else - SASSERT(0 && "begin() disabled"); - return mk_iterator(0); -#endif - } - - virtual iterator end() const { -#if Z3DEBUG - return mk_iterator(alloc(our_iterator_core, *this, true)); -#else - SASSERT(0 && "end() disabled"); - return mk_iterator(0); -#endif - } - - virtual void display(std::ostream & out) const { - table_information::display(out); - m_bitsets.display(out); - } - - virtual void to_formula(relation_signature const& sig, expr_ref& fml) const { - // TODO - } - - virtual unsigned get_size_estimate_rows() const { - return m_bitsets.num_disjs(); - } - - virtual unsigned get_size_estimate_bytes() const { - return m_bitsets.num_bytes(); - } - - T m_bitsets; - }; - -} - -#endif diff --git a/src/muz_qe/dl_hassel_diff_table.cpp b/src/muz_qe/dl_hassel_diff_table.cpp deleted file mode 100755 index 3ddcb3bbe..000000000 --- a/src/muz_qe/dl_hassel_diff_table.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - dl_hassel_diff_table.cpp - -Abstract: - - - -Revision History: - ---*/ - -#include "ast_printer.h" -#include "dl_context.h" -#include "dl_util.h" -#include "dl_hassel_diff_table.h" - - -namespace datalog { - - ternary_diff_bitvector::ternary_diff_bitvector(unsigned size, bool full) : - m_pos(size, full), m_neg(size) { } - - ternary_diff_bitvector::ternary_diff_bitvector(uint64 n, unsigned num_bits) : - m_pos(n, num_bits), m_neg(num_bits) { } - - ternary_diff_bitvector::ternary_diff_bitvector(const ternary_bitvector & tbv) : - m_pos(tbv), m_neg(tbv.size()) { } - - bool ternary_diff_bitvector::contains(const ternary_diff_bitvector & other) const { - return m_pos.contains(other.m_pos) && other.m_neg.contains(m_neg); - } - - bool ternary_diff_bitvector::is_empty() const { - if (m_pos.is_empty()) - return true; - - return m_neg.contains(m_pos); - } - - ternary_diff_bitvector ternary_diff_bitvector::and(const ternary_diff_bitvector& other) const { - ternary_diff_bitvector result(m_pos.and(other.m_pos)); - result.m_neg.swap(m_neg.or(other.m_neg)); - return result; - } - - void ternary_diff_bitvector::neg(union_ternary_bitvector& result) const { - // not(A\B) <-> (T\A) U B - ternary_diff_bitvector negated(size(), true); - negated.m_neg.add_new_fact(m_pos); - result.add_fact(negated); - - for (union_ternary_bitvector::const_iterator I = m_neg.begin(), - E = m_neg.end(); I != E; ++I) { - result.add_fact(*I); - } - } - - void ternary_diff_bitvector::subtract(const union_ternary_bitvector& other, - union_ternary_bitvector& result) const { - ternary_diff_bitvector newfact(*this); - for (union_ternary_bitvector::const_iterator I = other.begin(), - E = other.end(); I != E; ++I) { - if (!I->m_neg.empty()) { - NOT_IMPLEMENTED_YET(); - } - newfact.m_neg.add_fact(I->m_pos); - } - - if (!newfact.is_empty()) - result.add_fact(newfact); - } - - void ternary_diff_bitvector::join(const ternary_diff_bitvector& other, - const unsigned_vector& cols1, - const unsigned_vector& cols2, - union_ternary_bitvector& result) const { - unsigned new_size = size() + other.size(); - ternary_diff_bitvector res(new_size); - - res.m_pos = m_pos; - res.m_pos.append(other.m_pos); - - for (unsigned i = 0; i < cols1.size(); ++i) { - unsigned idx1 = cols1[i]; - unsigned idx2 = size() + cols2[i]; - unsigned v1 = res.m_pos.get(idx1); - unsigned v2 = res.m_pos.get(idx2); - - if (v1 == BIT_x) { - if (v2 == BIT_x) { - // add to subtracted TBVs: 1xx0 and 0xx1 - { - ternary_bitvector r(new_size, true); - r.set(idx1, BIT_0); - r.set(idx2, BIT_1); - res.m_neg.add_new_fact(r); - } - { - ternary_bitvector r(new_size, true); - r.set(idx1, BIT_1); - r.set(idx2, BIT_0); - res.m_neg.add_new_fact(r); - } - } else { - res.m_pos.set(idx1, v2); - } - } else if (v2 == BIT_x) { - res.m_pos.set(idx2, v1); - } else if (v1 != v2) { - // columns don't match - return; - } - } - - // handle subtracted TBVs: 1010 -> 1010xxx - if (!m_neg.empty()) { - ternary_bitvector padding(other.size(), true); - for (union_ternary_bitvector::const_iterator I = m_neg.begin(), - E = m_neg.end(); I != E; ++I) { - ternary_bitvector BV(*I); - BV.append(padding); - res.m_neg.add_new_fact(BV); - } - } - - if (!other.m_neg.empty()) { - ternary_bitvector padding(size(), true); - for (union_ternary_bitvector::const_iterator I = other.m_neg.begin(), - E = other.m_neg.end(); I != E; ++I) { - ternary_bitvector BV(padding); - BV.append(*I); - res.m_neg.add_new_fact(BV); - } - } - - result.add_fact(res); - } - - bool ternary_diff_bitvector::project(const unsigned_vector& delcols, ternary_diff_bitvector& result) const { - m_pos.project(delcols, result.m_pos); - if (m_neg.empty()) - return true; - - ternary_bitvector newneg; - for (union_ternary_bitvector::const_iterator I = m_neg.begin(), - E = m_neg.end(); I != E; ++I) { - for (unsigned i = 0; i < delcols.size()-1; ++i) { - unsigned idx = delcols[i]; - if (I->get(idx) != BIT_x && m_pos.get(idx) == BIT_x) - goto skip_row; - } - newneg.reset(); - I->project(delcols, newneg); - result.m_neg.add_fact(newneg); -skip_row: ; - } - return !result.is_empty(); - } - - void ternary_diff_bitvector::rename(const unsigned_vector& cyclecols, - const unsigned_vector& out_of_cycle_cols, - const table_information& src_table, - const table_information& dst_table, - ternary_diff_bitvector& result) const { - m_pos.rename(cyclecols, out_of_cycle_cols, src_table, dst_table, result.m_pos); - m_neg.rename(cyclecols, out_of_cycle_cols, src_table, dst_table, result.m_neg); - } - - unsigned ternary_diff_bitvector::get(unsigned idx) { - return m_pos.get(idx); - } - - void ternary_diff_bitvector::set(unsigned idx, unsigned val) { - m_pos.set(idx, val); - } - - void ternary_diff_bitvector::swap(ternary_diff_bitvector & other) { - m_pos.swap(other.m_pos); - m_neg.swap(other.m_neg); - } - - void ternary_diff_bitvector::reset() { - m_pos.reset(); - m_neg.reset(); - } - - void ternary_diff_bitvector::display(std::ostream & out) const { - m_pos.display(out); - if (!m_neg.empty()) { - out << " \\ "; - if (m_neg.num_disjs() > 1) out << '('; - m_neg.display(out); - if (m_neg.num_disjs() > 1) out << ')'; - } - } - - unsigned ternary_diff_bitvector::size_in_bytes() const { - return m_pos.size_in_bytes() + m_neg.num_bytes(); - } - -#if Z3DEBUG - void ternary_diff_bitvector::expand(std::set & BVs) const { - m_pos.expand(BVs); - SASSERT(!BVs.empty()); - - std::set NegBVs; - m_neg.expand(NegBVs); - BVs.erase(NegBVs.begin(), NegBVs.end()); - } -#endif - - hassel_diff_table_plugin::hassel_diff_table_plugin(relation_manager & manager) - : common_hassel_table_plugin(symbol("hassel_diff"), manager) {} - -} diff --git a/src/muz_qe/dl_hassel_diff_table.h b/src/muz_qe/dl_hassel_diff_table.h deleted file mode 100755 index 07340c7e8..000000000 --- a/src/muz_qe/dl_hassel_diff_table.h +++ /dev/null @@ -1,87 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - dl_hassel_diff_table.h - -Abstract: - - - -Revision History: - ---*/ - -#ifndef _DL_HASSEL_DIFF_TABLE_H_ -#define _DL_HASSEL_DIFF_TABLE_H_ - -#include "dl_hassel_common.h" - -namespace datalog { - - class hassel_diff_table; - - class ternary_diff_bitvector { - // pos \ (neg0 \/ ... \/ negn) - ternary_bitvector m_pos; - union_ternary_bitvector m_neg; - - public: - ternary_diff_bitvector() : m_pos(), m_neg(0) {} - ternary_diff_bitvector(unsigned size) : m_pos(size), m_neg(size) {} - ternary_diff_bitvector(unsigned size, bool full); - ternary_diff_bitvector(uint64 n, unsigned num_bits); - ternary_diff_bitvector(const ternary_bitvector & tbv); - - bool contains(const ternary_diff_bitvector & other) const; - bool is_empty() const; - - ternary_diff_bitvector and(const ternary_diff_bitvector& other) const; - void neg(union_ternary_bitvector& result) const; - - static bool has_subtract() { return true; } - void subtract(const union_ternary_bitvector& other, - union_ternary_bitvector& result) const; - - void join(const ternary_diff_bitvector& other, const unsigned_vector& cols1, - const unsigned_vector& cols2, union_ternary_bitvector& result) const; - - bool project(const unsigned_vector& delcols, ternary_diff_bitvector& result) const; - - void rename(const unsigned_vector& cyclecols, const unsigned_vector& out_of_cycle_cols, - const table_information& src_table, const table_information& dst_table, - ternary_diff_bitvector& result) const; - - unsigned get(unsigned idx); - void set(unsigned idx, unsigned val); - - void swap(ternary_diff_bitvector & other); - void reset(); - - unsigned size() const { return m_pos.size(); } - - void display(std::ostream & out) const; - unsigned size_in_bytes() const; - -#if Z3DEBUG - void expand(std::set & BVs) const; -#endif - }; - - typedef union_ternary_bitvector union_ternary_diff_bitvector; - - class hassel_diff_table : public common_hassel_table { - public: - hassel_diff_table(table_plugin & p, const table_signature & sig) : - common_hassel_table(p, sig) {} - }; - - class hassel_diff_table_plugin : public common_hassel_table_plugin { - public: - hassel_diff_table_plugin(relation_manager & manager); - }; - -} - -#endif diff --git a/src/muz_qe/dl_hassel_table.cpp b/src/muz_qe/dl_hassel_table.cpp deleted file mode 100644 index 6ec28df87..000000000 --- a/src/muz_qe/dl_hassel_table.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - dl_hassel_table.cpp - -Abstract: - - - -Revision History: - ---*/ - -#include "ast_printer.h" -#include "dl_context.h" -#include "dl_util.h" -#include "dl_hassel_table.h" - - -namespace datalog { - - hassel_table_plugin::hassel_table_plugin(relation_manager & manager) - : common_hassel_table_plugin(symbol("hassel"), manager) {} - -} diff --git a/src/muz_qe/dl_hassel_table.h b/src/muz_qe/dl_hassel_table.h deleted file mode 100644 index 6c4e9c1fe..000000000 --- a/src/muz_qe/dl_hassel_table.h +++ /dev/null @@ -1,39 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - dl_hassel_table.h - -Abstract: - - - -Revision History: - ---*/ - -#ifndef _DL_HASSEL_TABLE_H_ -#define _DL_HASSEL_TABLE_H_ - -#include "dl_hassel_common.h" - -namespace datalog { - - class hassel_table; - typedef union_ternary_bitvector union_ternary_bitvectors; - - class hassel_table : public common_hassel_table { - public: - hassel_table(table_plugin & p, const table_signature & sig) : - common_hassel_table(p, sig) {} - }; - - class hassel_table_plugin : public common_hassel_table_plugin { - public: - hassel_table_plugin(relation_manager & manager); - }; - -} - -#endif diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index dcfbcca2b..76f744325 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -1561,15 +1561,16 @@ namespace pdr { m_fparams.m_arith_auto_config_simplex = true; m_fparams.m_arith_propagate_eqs = false; m_fparams.m_arith_eager_eq_axioms = false; - if (classify.is_utvpi() && m_params.use_utvpi()) { + if (classify.is_dl()) { + m_fparams.m_arith_mode = AS_DIFF_LOGIC; + m_fparams.m_arith_expand_eqs = true; + } + else if (classify.is_utvpi() && m_params.use_utvpi()) { IF_VERBOSE(1, verbose_stream() << "UTVPI\n";); m_fparams.m_arith_mode = AS_UTVPI; m_fparams.m_arith_expand_eqs = true; } - else if (classify.is_dl()) { - m_fparams.m_arith_mode = AS_DIFF_LOGIC; - m_fparams.m_arith_expand_eqs = true; - } + } if (!use_mc && m_params.use_inductive_generalizer()) { m_core_generalizers.push_back(alloc(core_bool_inductive_generalizer, *this, 0)); diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index 3d61c04f0..7aade28e2 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -19,7 +19,6 @@ Revision History: --*/ -#define Z3_HASSEL_TABLE #include"rel_context.h" #include"dl_context.h" @@ -33,10 +32,6 @@ Revision History: #include"dl_mk_karr_invariants.h" #include"dl_finite_product_relation.h" #include"dl_sparse_table.h" -#ifdef Z3_HASSEL_TABLE -# include"dl_hassel_table.h" -# include"dl_hassel_diff_table.h" -#endif #include"dl_table.h" #include"dl_table_relation.h" #include"aig_exporter.h" @@ -94,10 +89,6 @@ namespace datalog { get_rmanager().register_plugin(alloc(bitvector_table_plugin, get_rmanager())); get_rmanager().register_plugin(alloc(equivalence_table_plugin, get_rmanager())); -#ifdef Z3_HASSEL_TABLE - get_rmanager().register_plugin(alloc(hassel_table_plugin, get_rmanager())); - get_rmanager().register_plugin(alloc(hassel_diff_table_plugin, get_rmanager())); -#endif // register plugins for builtin relations diff --git a/src/smt/database.smt b/src/smt/database.smt index 186dd9b95..2f5e5e1c9 100644 --- a/src/smt/database.smt +++ b/src/smt/database.smt @@ -311,4 +311,4 @@ (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pats { (?select (?select (?asElems e) a) i) }) - ) \ No newline at end of file + ) diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 6c85e40b9..498a2a172 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -418,9 +418,6 @@ namespace smt { return FC_GIVEUP; } else { - m_graph.set_to_zero(to_var(m_zero_int), to_var(m_zero_real)); - m_graph.set_to_zero(neg(to_var(m_zero_int)), neg(to_var(m_zero_real))); - m_graph.set_to_zero(to_var(m_zero_int), neg(to_var(m_zero_int))); return FC_DONE; } } @@ -691,16 +688,33 @@ namespace smt { \brief adjust values for variables in the difference graph such that for variables of integer sort it is the case that x^+ - x^- is even. - The informal justification for the procedure enforce_parity is that - the graph does not contain a strongly connected component where - x^+ and x+- are connected. They can be independently changed. - Since we would like variables representing 0 (zero) map to 0, - we selectively update the subgraph that can be updated without - changing the value of zero (which should be 0). + The informal justification for the procedure enforce_parity relies + on a set of properties: + 1. the graph does not contain a strongly connected component where + x^+ and x+- are connected. They can be independently changed. + This is checked prior to enforce_parity. + 2. When x^+ - x^- is odd, the values are adjusted by first + decrementing the value of x^+, provided x^- is not 0-dependent. + Otherwise decrement x^-. + x^- is "0-dependent" if there is a set of tight + inequalities from x^+ to x^-. + 3. The affinity to x^+ (the same component of x^+) ensures that + the parity is broken only a finite number of times when + traversing that component. Namely, suppose that the parity of y + gets broken when fixing 'x'. Then first note that 'y' cannot + be equal to 'x'. If it were, then we have a state where: + parity(x^+) != parity(x^-) and + parity(y^+) == parity(y^-) + but x^+ and y^+ are tightly connected and x^- and y^- are + also tightly connected using two copies of the same inequalities. + This is a contradiction. + Thus, 'y' cannot be equal to 'x' if 'y's parity gets broken when + repairing 'x'. + */ template void theory_utvpi::enforce_parity() { - unsigned_vector todo; + unsigned_vector todo; unsigned sz = get_num_vars(); for (unsigned i = 0; i < sz; ++i) { @@ -712,6 +726,8 @@ namespace smt { if (todo.empty()) { return; } + IF_VERBOSE(2, verbose_stream() << "disparity: " << todo.size() << "\n";); + unsigned iter = 0; while (!todo.empty()) { unsigned i = todo.back(); todo.pop_back(); @@ -720,21 +736,17 @@ namespace smt { } th_var v1 = to_var(i); th_var v2 = neg(v1); - TRACE("utvpi", tout << "disparity: " << v1 << "\n";); + + // IF_VERBOSE(1, verbose_stream() << "disparity: " << v1 << "\n";); int_vector zero_v; m_graph.compute_zero_succ(v1, zero_v); - bool found0 = false; - for (unsigned j = 0; !found0 && j < zero_v.size(); ++j) { - found0 = - (to_var(m_zero_int) == zero_v[j]) || - (neg(to_var(m_zero_int)) == zero_v[j]); - } - // variables that are tightly connected - // to 0 should not have their values changed. - if (found0) { - zero_v.reset(); - m_graph.compute_zero_succ(v2, zero_v); + for (unsigned j = 0; j < zero_v.size(); ++j) { + if (zero_v[j] == v2) { + zero_v.reset(); + m_graph.compute_zero_succ(v2, zero_v); + } } + TRACE("utvpi", for (unsigned j = 0; j < zero_v.size(); ++j) { tout << "decrement: " << zero_v[j] << "\n"; @@ -745,10 +757,23 @@ namespace smt { m_graph.acc_assignment(v, numeral(-1)); th_var k = from_var(v); if (!is_parity_ok(k)) { - TRACE("utvpi", tout << "new disparity: " << k << "\n";); + // IF_VERBOSE(1, verbose_stream() << "new disparity: " << k << "\n";); todo.push_back(k); } - } + } + if (iter >= 10000) { + IF_VERBOSE(1, + verbose_stream() << "decrement: "; + for (unsigned j = 0; j < zero_v.size(); ++j) { + rational r = m_graph.get_assignment(zero_v[j]).get_rational(); + verbose_stream() << zero_v[j] << " (" << r << ") "; + } + verbose_stream() << "\n";); + if (!is_parity_ok(i)) { + IF_VERBOSE(1, verbose_stream() << "Parity not fixed\n";); + } + } + ++iter; } SASSERT(m_graph.is_feasible()); DEBUG_CODE( @@ -764,10 +789,13 @@ namespace smt { // models: template - void theory_utvpi::init_model(model_generator & m) { + void theory_utvpi::init_model(model_generator & m) { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); enforce_parity(); + m_graph.set_to_zero(to_var(m_zero_int), to_var(m_zero_real)); + m_graph.set_to_zero(neg(to_var(m_zero_int)), neg(to_var(m_zero_real))); + m_graph.set_to_zero(to_var(m_zero_int), neg(to_var(m_zero_int))); compute_delta(); DEBUG_CODE(validate_model();); } From 76a269c85a6522da30dcb603279e9470493e13d4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 1 Jun 2013 17:14:18 -0700 Subject: [PATCH 85/91] clean up parity computation Signed-off-by: unknown --- src/smt/theory_utvpi_def.h | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 498a2a172..8463eb17f 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -714,8 +714,7 @@ namespace smt { */ template void theory_utvpi::enforce_parity() { - unsigned_vector todo; - + unsigned_vector todo; unsigned sz = get_num_vars(); for (unsigned i = 0; i < sz; ++i) { enode* e = get_enode(i); @@ -726,8 +725,6 @@ namespace smt { if (todo.empty()) { return; } - IF_VERBOSE(2, verbose_stream() << "disparity: " << todo.size() << "\n";); - unsigned iter = 0; while (!todo.empty()) { unsigned i = todo.back(); todo.pop_back(); @@ -737,17 +734,18 @@ namespace smt { th_var v1 = to_var(i); th_var v2 = neg(v1); - // IF_VERBOSE(1, verbose_stream() << "disparity: " << v1 << "\n";); int_vector zero_v; m_graph.compute_zero_succ(v1, zero_v); for (unsigned j = 0; j < zero_v.size(); ++j) { if (zero_v[j] == v2) { zero_v.reset(); m_graph.compute_zero_succ(v2, zero_v); + break; } } TRACE("utvpi", + tout << "Disparity: " << v1 << "\n"; for (unsigned j = 0; j < zero_v.size(); ++j) { tout << "decrement: " << zero_v[j] << "\n"; }); @@ -757,23 +755,9 @@ namespace smt { m_graph.acc_assignment(v, numeral(-1)); th_var k = from_var(v); if (!is_parity_ok(k)) { - // IF_VERBOSE(1, verbose_stream() << "new disparity: " << k << "\n";); todo.push_back(k); } } - if (iter >= 10000) { - IF_VERBOSE(1, - verbose_stream() << "decrement: "; - for (unsigned j = 0; j < zero_v.size(); ++j) { - rational r = m_graph.get_assignment(zero_v[j]).get_rational(); - verbose_stream() << zero_v[j] << " (" << r << ") "; - } - verbose_stream() << "\n";); - if (!is_parity_ok(i)) { - IF_VERBOSE(1, verbose_stream() << "Parity not fixed\n";); - } - } - ++iter; } SASSERT(m_graph.is_feasible()); DEBUG_CODE( From ec121db5c1a8a41c38fbee3922493d2e0a5e740e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Jun 2013 12:02:35 -0700 Subject: [PATCH 86/91] addressing race condition on interrupts Signed-off-by: Nikolaj Bjorner --- src/shell/smtlib_frontend.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index a005462e2..b5d8635da 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -54,13 +54,19 @@ static void display_statistics() { } static void on_timeout() { - display_statistics(); - exit(0); + #pragma omp critical (g_display_stats) + { + display_statistics(); + exit(0); + } } static void on_ctrl_c(int) { signal (SIGINT, SIG_DFL); - display_statistics(); + #pragma omp critical (g_display_stats) + { + display_statistics(); + } raise(SIGINT); } @@ -83,9 +89,12 @@ unsigned read_smtlib_file(char const * benchmark_file) { } } - display_statistics(); - register_on_timeout_proc(0); - g_solver = 0; + #pragma omp critical (g_display_stats) + { + display_statistics(); + register_on_timeout_proc(0); + g_solver = 0; + } return solver.get_error_code(); } @@ -118,8 +127,12 @@ unsigned read_smtlib2_commands(char const * file_name) { result = parse_smt2_commands(ctx, std::cin, true); } - display_statistics(); - g_cmd_context = 0; + + #pragma omp critical (g_display_stats) + { + display_statistics(); + g_cmd_context = 0; + } return result ? 0 : 1; } From d569027e369ce834297873e990db098717155f88 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Jun 2013 20:54:01 -0700 Subject: [PATCH 87/91] fix reference count bugs in overflow/underflow APIs for bit-vectors Signed-off-by: Nikolaj Bjorner --- src/api/api_ast.cpp | 4 + src/api/api_bv.cpp | 133 +++++++++++++++++++++---- src/muz_qe/qe.cpp | 2 +- src/smt/params/theory_arith_params.cpp | 1 + src/smt/theory_arith_core.h | 2 +- 5 files changed, 120 insertions(+), 22 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index c4b5c97d7..6855f6209 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -1070,6 +1070,10 @@ extern "C" { case OP_BV2INT: return Z3_OP_BV2INT; case OP_CARRY: return Z3_OP_CARRY; case OP_XOR3: return Z3_OP_XOR3; + case OP_BSMUL_NO_OVFL: + case OP_BUMUL_NO_OVFL: + case OP_BSMUL_NO_UDFL: + return Z3_OP_UNINTERPRETED; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; diff --git a/src/api/api_bv.cpp b/src/api/api_bv.cpp index 8126c8e2a..07d4fda18 100644 --- a/src/api/api_bv.cpp +++ b/src/api/api_bv.cpp @@ -121,10 +121,20 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ unsigned sz = Z3_get_bv_sort_size(c, s); rational max_bound = power(rational(2), sz); Z3_ast bound = Z3_mk_numeral(c, max_bound.to_string().c_str(), int_s); - Z3_ast pred = Z3_mk_bvslt(c, n, Z3_mk_int(c, 0, s)); + Z3_inc_ref(c, bound); + Z3_ast zero = Z3_mk_int(c, 0, s); + Z3_inc_ref(c, zero); + Z3_ast pred = Z3_mk_bvslt(c, n, zero); + Z3_inc_ref(c, pred); // if n <_sigend 0 then r - s^sz else r Z3_ast args[2] = { r, bound }; - Z3_ast res = Z3_mk_ite(c, pred, Z3_mk_sub(c, 2, args), r); + Z3_ast sub = Z3_mk_sub(c, 2, args); + Z3_inc_ref(c, sub); + Z3_ast res = Z3_mk_ite(c, pred, sub, r); + Z3_dec_ref(c, bound); + Z3_dec_ref(c, pred); + Z3_dec_ref(c, sub); + Z3_dec_ref(c, zero); RETURN_Z3(res); } else { @@ -156,7 +166,14 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } - return Z3_mk_bvshl(c, Z3_mk_int64(c, 1, s), Z3_mk_int64(c, sz - 1, s)); + Z3_ast x = Z3_mk_int64(c, 1, s); + Z3_inc_ref(c, x); + Z3_ast y = Z3_mk_int64(c, sz - 1, s); + Z3_inc_ref(c, y); + Z3_ast result = Z3_mk_bvshl(c, x, y); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + return result; Z3_CATCH_RETURN(0); } @@ -177,17 +194,40 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ RESET_ERROR_CODE(); if (is_signed) { Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); + Z3_inc_ref(c, zero); Z3_ast r = Z3_mk_bvadd(c, t1, t2); - Z3_ast args[2] = { Z3_mk_bvslt(c, zero, t1), Z3_mk_bvslt(c, zero, t2) }; + Z3_inc_ref(c, r); + Z3_ast l1 = Z3_mk_bvslt(c, zero, t1); + Z3_inc_ref(c, l1); + Z3_ast l2 = Z3_mk_bvslt(c, zero, t2); + Z3_inc_ref(c, l2); + Z3_ast args[2] = { l1, l2 }; Z3_ast args_pos = Z3_mk_and(c, 2, args); - return Z3_mk_implies(c, args_pos, Z3_mk_bvslt(c, zero, r)); + Z3_inc_ref(c, args_pos); + Z3_ast result = Z3_mk_implies(c, args_pos, Z3_mk_bvslt(c, zero, r)); + Z3_dec_ref(c, r); + Z3_dec_ref(c, l1); + Z3_dec_ref(c, l2); + Z3_dec_ref(c, args_pos); + Z3_dec_ref(c, zero); + return result; } else { unsigned sz = Z3_get_bv_sort_size(c, Z3_get_sort(c, t1)); t1 = Z3_mk_zero_ext(c, 1, t1); + Z3_inc_ref(c, t1); t2 = Z3_mk_zero_ext(c, 1, t2); + Z3_inc_ref(c, t2); Z3_ast r = Z3_mk_bvadd(c, t1, t2); - return Z3_mk_eq(c, Z3_mk_extract(c, sz, sz, r), Z3_mk_int(c, 0, Z3_mk_bv_sort(c, 1))); + Z3_inc_ref(c, r); + Z3_ast ex = Z3_mk_extract(c, sz, sz, r); + Z3_inc_ref(c, ex); + Z3_ast result = Z3_mk_eq(c, ex, Z3_mk_int(c, 0, Z3_mk_bv_sort(c, 1))); + Z3_dec_ref(c, t1); + Z3_dec_ref(c, t2); + Z3_dec_ref(c, ex); + Z3_dec_ref(c, r); + return result; } Z3_CATCH_RETURN(0); } @@ -197,10 +237,26 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_TRY; RESET_ERROR_CODE(); Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); + Z3_inc_ref(c, zero); Z3_ast r = Z3_mk_bvadd(c, t1, t2); - Z3_ast args[2] = { Z3_mk_bvslt(c, t1, zero), Z3_mk_bvslt(c, t2, zero) }; + Z3_inc_ref(c, r); + Z3_ast l1 = Z3_mk_bvslt(c, t1, zero); + Z3_inc_ref(c, l1); + Z3_ast l2 = Z3_mk_bvslt(c, t2, zero); + Z3_inc_ref(c, l2); + Z3_ast args[2] = { l1, l2 }; Z3_ast args_neg = Z3_mk_and(c, 2, args); - return Z3_mk_implies(c, args_neg, Z3_mk_bvslt(c, r, zero)); + Z3_inc_ref(c, args_neg); + Z3_ast lt = Z3_mk_bvslt(c, r, zero); + Z3_inc_ref(c, lt); + Z3_ast result = Z3_mk_implies(c, args_neg, lt); + Z3_dec_ref(c, lt); + Z3_dec_ref(c, l1); + Z3_dec_ref(c, l2); + Z3_dec_ref(c, r); + Z3_dec_ref(c, args_neg); + Z3_dec_ref(c, zero); + return result; Z3_CATCH_RETURN(0); } @@ -208,12 +264,28 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_ast Z3_API Z3_mk_bvsub_no_overflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); - Z3_sort s = Z3_get_sort(c, t2); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); + Z3_inc_ref(c, minus_t2); + Z3_sort s = Z3_get_sort(c, t2); Z3_ast min = Z3_mk_bvsmin(c, s); - return Z3_mk_ite(c, Z3_mk_eq(c, t2, min), - Z3_mk_bvslt(c, t1, Z3_mk_int(c, 0, s)), - Z3_mk_bvadd_no_overflow(c, t1, minus_t2, true)); + Z3_inc_ref(c, min); + Z3_ast x = Z3_mk_eq(c, t2, min); + Z3_inc_ref(c, x); + Z3_ast zero = Z3_mk_int(c, 0, s); + Z3_inc_ref(c, zero); + Z3_ast y = Z3_mk_bvslt(c, t1, zero); + Z3_inc_ref(c, y); + Z3_ast z = Z3_mk_bvadd_no_overflow(c, t1, minus_t2, true); + Z3_inc_ref(c, z); + Z3_ast result = Z3_mk_ite(c, x, y, z); + mk_c(c)->save_ast_trail(to_app(result)); + Z3_dec_ref(c, minus_t2); + Z3_dec_ref(c, min); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + Z3_dec_ref(c, z); + Z3_dec_ref(c, zero); + return result; Z3_CATCH_RETURN(0); } @@ -222,10 +294,19 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ RESET_ERROR_CODE(); if (is_signed) { Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); - if (Z3_get_error_code(c) != Z3_OK) return 0; + Z3_inc_ref(c, zero); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); - if (Z3_get_error_code(c) != Z3_OK) return 0; - return Z3_mk_implies(c, Z3_mk_bvslt(c, zero, t2), Z3_mk_bvadd_no_underflow(c, t1, minus_t2)); + Z3_inc_ref(c, minus_t2); + Z3_ast x = Z3_mk_bvslt(c, zero, t2); + Z3_inc_ref(c, x); + Z3_ast y = Z3_mk_bvadd_no_underflow(c, t1, minus_t2); + Z3_inc_ref(c, y); + Z3_ast result = Z3_mk_implies(c, x, y); + Z3_dec_ref(c, zero); + Z3_dec_ref(c, minus_t2); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + return result; } else { return Z3_mk_bvule(c, t2, t1); @@ -267,12 +348,24 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_TRY; RESET_ERROR_CODE(); Z3_sort s = Z3_get_sort(c, t1); - if (Z3_get_error_code(c) != Z3_OK) return 0; Z3_ast min = Z3_mk_bvmsb(c, s); - if (Z3_get_error_code(c) != Z3_OK) return 0; - Z3_ast args[2] = { Z3_mk_eq(c, t1, min), - Z3_mk_eq(c, t2, Z3_mk_int(c, -1, s)) }; - return Z3_mk_not(c, Z3_mk_and(c, 2, args)); + Z3_inc_ref(c, min); + Z3_ast x = Z3_mk_eq(c, t1, min); + Z3_inc_ref(c, x); + Z3_ast y = Z3_mk_int(c, -1, s); + Z3_inc_ref(c, y); + Z3_ast z = Z3_mk_eq(c, t2, y); + Z3_inc_ref(c, z); + Z3_ast args[2] = { x, z }; + Z3_ast u = Z3_mk_and(c, 2, args); + Z3_inc_ref(c, u); + Z3_ast result = Z3_mk_not(c, u); + Z3_dec_ref(c, min); + Z3_dec_ref(c, x); + Z3_dec_ref(c, y); + Z3_dec_ref(c, z); + Z3_dec_ref(c, u); + return result; Z3_CATCH_RETURN(0); } diff --git a/src/muz_qe/qe.cpp b/src/muz_qe/qe.cpp index 894a935b5..5603b8102 100644 --- a/src/muz_qe/qe.cpp +++ b/src/muz_qe/qe.cpp @@ -2084,7 +2084,7 @@ namespace qe { flet fl1(m_fparams.m_model, true); flet fl2(m_fparams.m_simplify_bit2int, true); - flet fl3(m_fparams.m_arith_enum_const_mod, true); + //flet fl3(m_fparams.m_arith_enum_const_mod, true); flet fl4(m_fparams.m_bv_enable_int2bv2int, true); flet fl5(m_fparams.m_array_canonize_simplify, true); flet fl6(m_fparams.m_relevancy_lvl, 0); diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index 7281a5daa..dcffa83dc 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -33,6 +33,7 @@ void theory_arith_params::updt_params(params_ref const & _p) { m_arith_int_eq_branching = p.arith_int_eq_branch(); m_arith_ignore_int = p.arith_ignore_int(); m_arith_bound_prop = static_cast(p.arith_propagation_mode()); + m_arith_enum_const_mod = true; } diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index fafe73a79..195d78e25 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -408,7 +408,7 @@ namespace smt { mk_axiom(eqz, upper); rational k; if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) && - k.is_pos() && k < rational(512)) { + k.is_pos() && k < rational(8)) { rational j(0); #if 1 literal_buffer lits; From 56bfc06c4f7e33d4249551e269e443b19f71ef89 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Jun 2013 20:55:15 -0700 Subject: [PATCH 88/91] fix reference count bugs in overflow/underflow APIs for bit-vectors Signed-off-by: Nikolaj Bjorner --- src/muz_qe/qe.cpp | 2 +- src/smt/params/theory_arith_params.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/muz_qe/qe.cpp b/src/muz_qe/qe.cpp index 5603b8102..894a935b5 100644 --- a/src/muz_qe/qe.cpp +++ b/src/muz_qe/qe.cpp @@ -2084,7 +2084,7 @@ namespace qe { flet fl1(m_fparams.m_model, true); flet fl2(m_fparams.m_simplify_bit2int, true); - //flet fl3(m_fparams.m_arith_enum_const_mod, true); + flet fl3(m_fparams.m_arith_enum_const_mod, true); flet fl4(m_fparams.m_bv_enable_int2bv2int, true); flet fl5(m_fparams.m_array_canonize_simplify, true); flet fl6(m_fparams.m_relevancy_lvl, 0); diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index dcffa83dc..7281a5daa 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -33,7 +33,6 @@ void theory_arith_params::updt_params(params_ref const & _p) { m_arith_int_eq_branching = p.arith_int_eq_branch(); m_arith_ignore_int = p.arith_ignore_int(); m_arith_bound_prop = static_cast(p.arith_propagation_mode()); - m_arith_enum_const_mod = true; } From bd064bf5d06145a6638d78f565469f571929248e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 3 Jun 2013 11:46:13 -0700 Subject: [PATCH 89/91] enable UTVPI by default Signed-off-by: Nikolaj Bjorner --- src/muz_qe/fixedpoint_params.pyg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index c2d33cb05..d38c3c9b0 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -60,7 +60,7 @@ def_module_params('fixedpoint', ('print_answer', BOOL, False, 'print answer instance(s) to query'), ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), ('print_statistics', BOOL, False, 'print statistics'), - ('use_utvpi', BOOL, False, 'experimental use UTVPI strategy'), + ('use_utvpi', BOOL, True, 'PDR: Enable UTVPI strategy'), ('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), )) From b6d9d8a60127da23318dd776bc186539b1530014 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 4 Jun 2013 12:55:35 -0700 Subject: [PATCH 90/91] fix bugs reported by Nuno Lopes Signed-off-by: Nikolaj Bjorner --- src/muz_qe/qe_lite.cpp | 4 ++++ src/util/bit_vector.cpp | 10 ++++++---- src/util/bit_vector.h | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/muz_qe/qe_lite.cpp b/src/muz_qe/qe_lite.cpp index 50c3347ee..f840f19d6 100644 --- a/src/muz_qe/qe_lite.cpp +++ b/src/muz_qe/qe_lite.cpp @@ -1417,6 +1417,7 @@ namespace fm { fm(ast_manager & _m): m(_m), + m_is_variable(0), m_allocator("fm-elim"), m_util(m), m_bvar2expr(m), @@ -1424,6 +1425,9 @@ namespace fm { m_new_fmls(m), m_inconsistent_core(m) { m_cancel = false; + updt_params(); + m_counter = 0; + m_inconsistent = false; } ~fm() { diff --git a/src/util/bit_vector.cpp b/src/util/bit_vector.cpp index 0d67f1f79..6885b93e8 100644 --- a/src/util/bit_vector.cpp +++ b/src/util/bit_vector.cpp @@ -22,6 +22,8 @@ Revision History: #define DEFAULT_CAPACITY 2 +#define MK_MASK(_num_bits_) ((1U << _num_bits_) - 1) + void bit_vector::expand_to(unsigned new_capacity) { unsigned * new_data = alloc_svect(unsigned, new_capacity); memset(new_data, 0, new_capacity * sizeof(unsigned)); @@ -51,7 +53,7 @@ void bit_vector::resize(unsigned new_size, bool val) { unsigned ewidx = num_words(new_size); unsigned * begin = m_data + bwidx; unsigned pos = m_num_bits % 32; - unsigned mask = (1 << pos) - 1; + unsigned mask = MK_MASK(pos); int cval; if (val) { @@ -128,7 +130,7 @@ bool bit_vector::operator==(bit_vector const & source) const { return false; } unsigned bit_rest = source.m_num_bits % 32; - unsigned mask = (1 << bit_rest) - 1; + unsigned mask = MK_MASK(bit_rest); if (mask == 0) mask = UINT_MAX; return (m_data[i] & mask) == (source.m_data[i] & mask); } @@ -149,7 +151,7 @@ bit_vector & bit_vector::operator|=(bit_vector const & source) { unsigned i = 0; for (i = 0; i < n2 - 1; i++) m_data[i] |= source.m_data[i]; - unsigned mask = (1 << bit_rest) - 1; + unsigned mask = MK_MASK(bit_rest); m_data[i] |= source.m_data[i] & mask; } return *this; @@ -175,7 +177,7 @@ bit_vector & bit_vector::operator&=(bit_vector const & source) { else { for (i = 0; i < n2 - 1; i++) m_data[i] &= source.m_data[i]; - unsigned mask = (1 << bit_rest) - 1; + unsigned mask = MK_MASK(bit_rest); m_data[i] &= (source.m_data[i] & mask); } diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index cbe7a0618..0ccdeae9e 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -69,6 +69,7 @@ public: m_num_bits(0), m_capacity(num_words(reserve_num_bits)), m_data(alloc_svect(unsigned, m_capacity)) { + memset(m_data, 0, m_capacity * sizeof(unsigned)); } bit_vector(bit_vector const & source): From 110fa0b7fb711418fe2be67f033e8ed70b354972 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Wed, 5 Jun 2013 13:50:22 -0700 Subject: [PATCH 91/91] Fix issue http://z3.codeplex.com/workitem/45 Signed-off-by: Leonardo de Moura --- src/ast/simplifier/bv_simplifier_plugin.cpp | 40 +++++++++++++++---- src/ast/simplifier/poly_simplifier_plugin.cpp | 1 + 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/ast/simplifier/bv_simplifier_plugin.cpp b/src/ast/simplifier/bv_simplifier_plugin.cpp index 3ef55baba..8ee353a76 100644 --- a/src/ast/simplifier/bv_simplifier_plugin.cpp +++ b/src/ast/simplifier/bv_simplifier_plugin.cpp @@ -636,7 +636,31 @@ bool bv_simplifier_plugin::try_mk_extract(unsigned high, unsigned low, expr * ar if (!all_found) { return false; } - result = m_manager.mk_app(m_fid, a->get_decl_kind(), new_args.size(), new_args.c_ptr()); + // We should not use mk_app because it does not guarantee that the result would be in simplified form. + // result = m_manager.mk_app(m_fid, a->get_decl_kind(), new_args.size(), new_args.c_ptr()); + if (is_app_of(a, m_fid, OP_BAND)) + mk_bv_and(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BOR)) + mk_bv_or(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BXOR)) + mk_bv_xor(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BNOR)) + mk_bv_nor(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BNAND)) + mk_bv_nand(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BNOT)) { + SASSERT(new_args.size() == 1); + mk_bv_not(new_args[0], result); + } + else if (is_app_of(a, m_fid, OP_BADD)) + mk_add(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BMUL)) + mk_mul(new_args.size(), new_args.c_ptr(), result); + else if (is_app_of(a, m_fid, OP_BSUB)) + mk_sub(new_args.size(), new_args.c_ptr(), result); + else { + UNREACHABLE(); + } return true; } else if (m_manager.is_ite(a)) { @@ -747,16 +771,16 @@ void bv_simplifier_plugin::mk_bv_eq(expr* a1, expr* a2, expr_ref& result) { expr * arg1 = *it1; expr * arg2 = *it2; TRACE("expr_bv_util", tout << "low1: " << low1 << " low2: " << low2 << "\n"; - ast_ll_pp(tout, m_manager, arg1); - ast_ll_pp(tout, m_manager, arg2);); + tout << mk_pp(arg1, m_manager) << "\n"; + tout << mk_pp(arg2, m_manager) << "\n";); unsigned sz1 = get_bv_size(arg1); unsigned sz2 = get_bv_size(arg2); SASSERT(low1 < sz1 && low2 < sz2); unsigned rsz1 = sz1 - low1; unsigned rsz2 = sz2 - low2; TRACE("expr_bv_util", tout << "rsz1: " << rsz1 << " rsz2: " << rsz2 << "\n"; - ast_ll_pp(tout, m_manager, arg1); ast_ll_pp(tout, m_manager, arg2);); - + tout << mk_pp(arg1, m_manager) << "\n"; + tout << mk_pp(arg2, m_manager) << "\n";); if (rsz1 == rsz2) { mk_extract(sz1 - 1, low1, arg1, lhs); @@ -826,9 +850,9 @@ void bv_simplifier_plugin::mk_eq_core(expr * arg1, expr * arg2, expr_ref & resul } m_bsimp.mk_and(tmps.size(), tmps.c_ptr(), result); TRACE("mk_eq_bb", - ast_ll_pp(tout, m_manager, arg1); - ast_ll_pp(tout, m_manager, arg2); - ast_ll_pp(tout, m_manager, result);); + tout << mk_pp(arg1, m_manager) << "\n"; + tout << mk_pp(arg2, m_manager) << "\n"; + tout << mk_pp(result, m_manager) << "\n";); return; } #endif diff --git a/src/ast/simplifier/poly_simplifier_plugin.cpp b/src/ast/simplifier/poly_simplifier_plugin.cpp index 402b078a8..d64123e7b 100644 --- a/src/ast/simplifier/poly_simplifier_plugin.cpp +++ b/src/ast/simplifier/poly_simplifier_plugin.cpp @@ -285,6 +285,7 @@ bool poly_simplifier_plugin::merge_monomials(bool inv, expr * n1, expr * n2, exp else result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k1), b); } + TRACE("merge_monomials", tout << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\n" << mk_pp(result, m_manager) << "\n";); return true; }