From 8f46179deffa7f671d5f9ec5cd92496c8bfc72f2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 8 Apr 2013 13:50:56 -0700 Subject: [PATCH 1/6] 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 90c808bde91fc010b699003076345327017f074b Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 8 Apr 2013 17:14:43 -0700 Subject: [PATCH 2/6] [datalog] fix memory leak in union instructions the source operand was never cleaned up Signed-off-by: Nuno Lopes --- src/muz_qe/dl_compiler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index c898f7964..acedc3619 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -707,6 +707,7 @@ namespace datalog { //update the head relation make_union(new_head_reg, head_reg, delta_reg, use_widening, acc); + make_dealloc_non_void(new_head_reg, acc); } finish: From 93297fa9e72f750fc8115f0568217f474888563e Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 8 Apr 2013 18:00:43 -0700 Subject: [PATCH 3/6] Fix bug in purify_arith reported at https://z3.codeplex.com/workitem/32 Signed-off-by: Leonardo de Moura --- src/tactic/arith/purify_arith_tactic.cpp | 61 +++++++++++------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index 169e27927..1a38ac10e 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -29,6 +29,7 @@ Revision History: #include"th_rewriter.h" #include"filter_model_converter.h" #include"ast_smt2_pp.h" +#include"expr_replacer.h" /* ---- @@ -131,18 +132,16 @@ struct purify_arith_proc { proof_ref_vector m_new_cnstr_prs; expr_ref m_subst; proof_ref m_subst_pr; - bool m_in_q; - unsigned m_var_idx; + expr_ref_vector m_new_vars; - rw_cfg(purify_arith_proc & o, bool in_q): + rw_cfg(purify_arith_proc & o): m_owner(o), m_pinned(o.m()), m_new_cnstrs(o.m()), m_new_cnstr_prs(o.m()), m_subst(o.m()), m_subst_pr(o.m()), - m_in_q(in_q), - m_var_idx(0) { + m_new_vars(o.m()) { } ast_manager & m() { return m_owner.m(); } @@ -155,14 +154,9 @@ struct purify_arith_proc { bool elim_inverses() const { return m_owner.m_elim_inverses; } expr * mk_fresh_var(bool is_int) { - if (m_in_q) { - unsigned idx = m_var_idx; - m_var_idx++; - return m().mk_var(idx, is_int ? u().mk_int() : u().mk_real()); - } - else { - return m().mk_fresh_const(0, is_int ? u().mk_int() : u().mk_real()); - } + expr * r = m().mk_fresh_const(0, is_int ? u().mk_int() : u().mk_real()); + m_new_vars.push_back(r); + return r; } expr * mk_fresh_real_var() { return mk_fresh_var(false); } @@ -596,9 +590,9 @@ struct purify_arith_proc { struct rw : public rewriter_tpl { rw_cfg m_cfg; - rw(purify_arith_proc & o, bool in_q): + rw(purify_arith_proc & o): rewriter_tpl(o.m(), o.m_produce_proofs, m_cfg), - m_cfg(o, in_q) { + m_cfg(o) { } }; @@ -661,40 +655,43 @@ struct purify_arith_proc { void process_quantifier(quantifier * q, expr_ref & result, proof_ref & result_pr) { result_pr = 0; - num_vars_proc p(u(), m_elim_root_objs); - expr_ref body(m()); - unsigned num_vars = p(q->get_expr()); - if (num_vars > 0) { - // open space for aux vars - var_shifter shifter(m()); - shifter(q->get_expr(), num_vars, body); - } - else { - body = q->get_expr(); - } - - rw r(*this, true); + rw r(*this); expr_ref new_body(m()); proof_ref new_body_pr(m()); - r(body, new_body, new_body_pr); + r(q->get_expr(), new_body, new_body_pr); + unsigned num_vars = r.cfg().m_new_vars.size(); TRACE("purify_arith", tout << "num_vars: " << num_vars << "\n"; - tout << "body: " << mk_ismt2_pp(body, m()) << "\nnew_body: " << mk_ismt2_pp(new_body, m()) << "\n";); + tout << "body: " << mk_ismt2_pp(q->get_expr(), m()) << "\nnew_body: " << mk_ismt2_pp(new_body, m()) << "\n";); if (num_vars == 0) { + SASSERT(r.cfg().m_new_cnstrs.empty()); result = m().update_quantifier(q, new_body); if (m_produce_proofs) result_pr = m().mk_quant_intro(q, to_quantifier(result.get()), result_pr); } else { + // Add new constraints expr_ref_vector & cnstrs = r.cfg().m_new_cnstrs; cnstrs.push_back(new_body); new_body = m().mk_and(cnstrs.size(), cnstrs.c_ptr()); + // Open space for new variables + var_shifter shifter(m()); + shifter(new_body, num_vars, new_body); + // Rename fresh constants in r.cfg().m_new_vars to variables ptr_buffer sorts; buffer names; + expr_substitution subst(m(), false, false); for (unsigned i = 0; i < num_vars; i++) { - sorts.push_back(u().mk_real()); + expr * c = r.cfg().m_new_vars.get(i); + sort * s = get_sort(c); + sorts.push_back(s); names.push_back(m().mk_fresh_var_name("x")); + unsigned idx = num_vars - i - 1; + subst.insert(c, m().mk_var(idx, s)); } + scoped_ptr replacer = mk_default_expr_replacer(m()); + replacer->set_substitution(&subst); + (*replacer)(new_body, new_body); new_body = m().mk_exists(num_vars, sorts.c_ptr(), names.c_ptr(), new_body); result = m().update_quantifier(q, new_body); if (m_produce_proofs) { @@ -708,7 +705,7 @@ struct purify_arith_proc { } void operator()(goal & g, model_converter_ref & mc, bool produce_models) { - rw r(*this, false); + rw r(*this); // purify expr_ref new_curr(m()); proof_ref new_pr(m()); From 8627f6f1d5e5afc23335c29ad150ef375d07cb16 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Mon, 8 Apr 2013 18:02:28 -0700 Subject: [PATCH 4/6] Remove dead code Signed-off-by: Leonardo de Moura --- src/tactic/arith/purify_arith_tactic.cpp | 57 ------------------------ 1 file changed, 57 deletions(-) diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index 1a38ac10e..f2ddd86ce 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -595,63 +595,6 @@ struct purify_arith_proc { m_cfg(o) { } }; - - /** - \brief Return the number of (auxiliary) variables needed for converting an expression. - */ - struct num_vars_proc { - arith_util & m_util; - expr_fast_mark1 m_visited; - ptr_vector m_todo; - unsigned m_num_vars; - bool m_elim_root_objs; - - num_vars_proc(arith_util & u, bool elim_root_objs): - m_util(u), - m_elim_root_objs(elim_root_objs) { - } - - void visit(expr * t) { - if (m_visited.is_marked(t)) - return; - m_visited.mark(t); - m_todo.push_back(t); - } - - void process(app * t) { - if (t->get_family_id() == m_util.get_family_id()) { - if (m_util.is_power(t)) { - rational k; - if (m_util.is_numeral(t->get_arg(1), k) && (k.is_zero() || !k.is_int())) { - m_num_vars++; - } - } - else if (m_util.is_div(t) || - m_util.is_idiv(t) || - m_util.is_mod(t) || - m_util.is_to_int(t) || - (m_util.is_irrational_algebraic_numeral(t) && m_elim_root_objs)) { - m_num_vars++; - } - } - unsigned num_args = t->get_num_args(); - for (unsigned i = 0; i < num_args; i++) - visit(t->get_arg(i)); - } - - unsigned operator()(expr * t) { - m_num_vars = 0; - visit(t); - while (!m_todo.empty()) { - expr * t = m_todo.back(); - m_todo.pop_back(); - if (is_app(t)) - process(to_app(t)); - } - m_visited.reset(); - return m_num_vars; - } - }; void process_quantifier(quantifier * q, expr_ref & result, proof_ref & result_pr) { result_pr = 0; From d26f0e1c28e9784e5c931dc326cc300089d8d03b Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Tue, 9 Apr 2013 08:42:14 -0700 Subject: [PATCH 5/6] Fix bug in the SAT solver. Signed-off-by: Leonardo de Moura --- src/sat/sat_solver.cpp | 54 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 399fb2c61..3e9b60260 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1745,6 +1745,12 @@ namespace sat { mark_lit(m_lemma[i]); } + literal l0 = m_lemma[0]; + // l0 is the FUIP, and we never remove the FUIP. + // + // In the following loop, we use unmark_lit(l) to remove a + // literal from m_lemma. + for (unsigned i = 0; i < sz; i++) { literal l = m_lemma[i]; if (!is_marked_lit(l)) @@ -1754,9 +1760,15 @@ namespace sat { watch_list::const_iterator it = wlist.begin(); watch_list::const_iterator end = wlist.end(); for (; it != end; ++it) { + // In this for-loop, the conditions l0 != ~l2 and l0 != ~l3 + // are not really needed if the solver does not miss unit propagations. + // However, we add them anyway because we don't want to rely on this + // property of the propagator. + // For example, if this property is relaxed in the future, then the code + // without the conditions l0 != ~l2 and l0 != ~l3 may remove the FUIP if (it->is_binary_clause()) { literal l2 = it->get_literal(); - if (is_marked_lit(~l2)) { + if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } @@ -1764,11 +1776,11 @@ namespace sat { else if (it->is_ternary_clause()) { literal l2 = it->get_literal1(); literal l3 = it->get_literal2(); - if (is_marked_lit(l2) && is_marked_lit(~l3)) { + if (is_marked_lit(l2) && is_marked_lit(~l3) && l0 != ~l3) { // eliminate ~l3 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l3); } - else if (is_marked_lit(~l2) && is_marked_lit(l3)) { + else if (is_marked_lit(~l2) && is_marked_lit(l3) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l2); } @@ -1786,7 +1798,41 @@ namespace sat { literal_vector::iterator end = implied_lits->end(); for (; it != end; ++it) { literal l2 = *it; - if (is_marked_lit(~l2)) { + // Here, we must check l0 != ~l2. + // l \/ l2 is an implied binary clause. + // However, it may have been deduced using a lemma that has been deleted. + // For example, consider the following sequence of events: + // + // 1. Initial clause database: + // + // l \/ ~p1 + // p1 \/ ~p2 + // p2 \/ ~p3 + // p3 \/ ~p4 + // q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // ~q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // ~q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // ... + // + // 2. Now suppose we learned the lemma + // + // p1 \/ p2 \/ p3 \/ p4 \/ l2 (*) + // + // 3. Probing is executed and we notice hat (~l => l2) when we assign l to false. + // That is, l \/ l2 is an implied clause. Note that probing does not add + // this clause to the clause database (there are too many). + // + // 4. Lemma (*) is deleted (garbage collected). + // + // 5. l is decided to be false, p1, p2, p3 and p4 are propagated using BCP, + // but l2 is not since the lemma (*) was deleted. + // + // Probing module still "knows" that l \/ l2 is valid binary clause + // + // 6. A new lemma is created where ~l2 is the FUIP and the lemma also contains l. + // If we remove l0 != ~l2 may try to delete the FUIP. + if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } From d5a14c0b51bf5a7a25dc450b81288dda56c915fb Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Tue, 9 Apr 2013 08:49:04 -0700 Subject: [PATCH 6/6] Fix problem reported at http://stackoverflow.com/questions/15882140/z3-smt2-in-get-z3-version/15882868#comment22637420_15882868 Signed-off-by: Leonardo de Moura --- src/cmd_context/basic_cmds.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 21a986fda..9f18608d3 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -568,7 +568,7 @@ public: ctx.regular_stream() << "(:authors \"Leonardo de Moura and Nikolaj Bjorner\")" << std::endl; } else if (opt == m_version) { - ctx.regular_stream() << "(:version \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "\")" << std::endl; + ctx.regular_stream() << "(:version \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER << "\")" << std::endl; } else if (opt == m_status) { ctx.regular_stream() << "(:status " << ctx.get_status() << ")" << std::endl;