diff --git a/src/muz/transforms/CMakeLists.txt b/src/muz/transforms/CMakeLists.txt index 6a0a1ac9c..5e8829d42 100644 --- a/src/muz/transforms/CMakeLists.txt +++ b/src/muz/transforms/CMakeLists.txt @@ -21,6 +21,8 @@ z3_add_component(transforms dl_mk_unbound_compressor.cpp dl_mk_unfold.cpp dl_transforms.cpp + dl_mk_array_eq_rewrite.cpp + dl_mk_array_instantiation.cpp COMPONENT_DEPENDENCIES dataflow hilbert diff --git a/src/muz/transforms/dl_mk_array_eq_rewrite.cpp b/src/muz/transforms/dl_mk_array_eq_rewrite.cpp new file mode 100644 index 000000000..c9caa6148 --- /dev/null +++ b/src/muz/transforms/dl_mk_array_eq_rewrite.cpp @@ -0,0 +1,140 @@ +/*++ + +Module Name: + + dl_mk_array_eq_rewrite.h + +Abstract: + Selects a representative for array equivalence classes. + +Author: + + Julien Braine + +Revision History: + +--*/ + + +#include "dl_mk_array_eq_rewrite.h" +#include "dl_context.h" +#include "pattern_inference.h" +#include "dl_context.h" +#include "expr_safe_replace.h" +#include "expr_abstract.h" +#include"fixedpoint_params.hpp" +#include "../spacer/obj_equiv_class.h" + +namespace datalog { + + mk_array_eq_rewrite::mk_array_eq_rewrite( + context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx), + m_a(m) + { + } + + rule_set * mk_array_eq_rewrite::operator()(rule_set const & source) + { + src_set = &source; + rule_set * result = alloc(rule_set, m_ctx); + result->inherit_predicates(source); + dst=result; + unsigned nbrules = source.get_num_rules(); + src_manager = &source.get_rule_manager(); + for(unsigned i =0;iget_counter().get_max_rule_var(r)+1; + + + expr_ref_vector new_tail(m); + unsigned nb_predicates = r.get_uninterpreted_tail_size(); + unsigned tail_size = r.get_tail_size(); + for(unsigned i=0;imk_rule(m.mk_implies(m.mk_and(res_conjs.size(), res_conjs.c_ptr()), r.get_head()), pr, dest, r.name()); + } + + expr* mk_array_eq_rewrite::replace(expr* e, expr* new_val, expr* old_val) + { + if(e==old_val) + return new_val; + else if(!is_app(e)) + { + return e; + } + app*f = to_app(e); + ptr_vector n_args; + for(unsigned i=0;iget_num_args();i++) + { + n_args.push_back(replace(f->get_arg(i), new_val, old_val)); + } + return m.mk_app(f->get_decl(), n_args.size(), n_args.c_ptr()); + } + +} diff --git a/src/muz/transforms/dl_mk_array_eq_rewrite.h b/src/muz/transforms/dl_mk_array_eq_rewrite.h new file mode 100644 index 000000000..0229ff487 --- /dev/null +++ b/src/muz/transforms/dl_mk_array_eq_rewrite.h @@ -0,0 +1,54 @@ +/*++ + +Module Name: + + dl_mk_array_eq_rewrite.h + +Abstract: + Selects a representative for array equivalence classes. + +Author: + + Julien Braine + +Revision History: + +--*/ + +#ifndef DL_MK_ARRAY_EQ_REWRITE_H_ +#define DL_MK_ARRAY_EQ_REWRITE_H_ + + +#include "dl_rule_transformer.h" +#include "../spacer/obj_equiv_class.h" + +namespace datalog { + + class context; + class mk_array_eq_rewrite : public rule_transformer::plugin { + //Context objects + ast_manager& m; + context& m_ctx; + array_util m_a; + + //Rule set context + const rule_set*src_set; + rule_set*dst; + rule_manager* src_manager; + unsigned cnt;//Index for new variables + + expr* replace(expr* e, expr* new_val, expr* old_val); + void instantiate_rule(const rule& r, rule_set & dest); + + public: + mk_array_eq_rewrite(context & ctx, unsigned priority); + rule_set * operator()(rule_set const & source); + virtual ~mk_array_eq_rewrite(){} + }; + + + +}; + +#endif /* DL_MK_ARRAY_EQ_REWRITE_H_ */ + diff --git a/src/muz/transforms/dl_mk_array_instantiation.cpp b/src/muz/transforms/dl_mk_array_instantiation.cpp new file mode 100644 index 000000000..762884545 --- /dev/null +++ b/src/muz/transforms/dl_mk_array_instantiation.cpp @@ -0,0 +1,324 @@ +/*++ + +Module Name: + + dl_mk_array_instantiation.h + +Abstract: + + Does array instantiation + +Author: + + Julien Braine + +Revision History: + +--*/ + + +#include "dl_mk_array_instantiation.h" +#include "dl_context.h" +#include "pattern_inference.h" +#include "dl_context.h" +#include "expr_safe_replace.h" +#include "expr_abstract.h" +#include"fixedpoint_params.hpp" +#include "../spacer/obj_equiv_class.h" + +namespace datalog { + + mk_array_instantiation::mk_array_instantiation( + context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx), + m_a(m), + eq_classes(m), + ownership(m) + { + } + + rule_set * mk_array_instantiation::operator()(rule_set const & source) + { + std::cout<<"Array Instantiation called with parameters :" + <<" enforce="<display(std::cout); + return result; + } + + void mk_array_instantiation::instantiate_rule(const rule& r, rule_set & dest) + { + //Reset everything + selects.reset(); + eq_classes.reset(); + cnt = src_manager->get_counter().get_max_rule_var(r)+1; + done_selects.reset(); + ownership.reset(); + + expr_ref_vector phi(m); + expr_ref_vector preds(m); + expr_ref new_head = create_head(to_app(r.get_head())); + unsigned nb_predicates = r.get_uninterpreted_tail_size(); + unsigned tail_size = r.get_tail_size(); + for(unsigned i=0;i::iterator it = done_selects.begin(); it!=done_selects.end(); ++it) + { + expr_ref tmp(m); + tmp = &it->get_key(); + new_tail.push_back(m.mk_eq(it->get_value(), tmp)); + } + proof_ref pr(m); + src_manager->mk_rule(m.mk_implies(m.mk_and(new_tail.size(), new_tail.c_ptr()), new_head), pr, dest, r.name()); + } + + expr_ref mk_array_instantiation::create_head(app* old_head) + { + expr_ref_vector new_args(m); + for(unsigned i=0;iget_num_args();i++) + { + expr*arg = old_head->get_arg(i); + if(m_a.is_array(get_sort(arg))) + { + for(unsigned k=0; k< m_ctx.get_params().xform_instantiate_arrays_nb_quantifier();k++) + { + expr_ref_vector dummy_args(m); + dummy_args.push_back(arg); + for(unsigned i=0;i()); + selects[arg].push_back(select); + } + if(!m_ctx.get_params().xform_instantiate_arrays_enforce()) + new_args.push_back(arg); + } + else + new_args.push_back(arg); + } + return create_pred(old_head, new_args); + } + + + void mk_array_instantiation::retrieve_selects(expr* e) + { + //If the expression is not a function application, we ignore it + if (!is_app(e)) { + return; + } + app*f=to_app(e); + //Call the function recursively on all arguments + unsigned nbargs = f->get_num_args(); + for(unsigned i=0;iget_arg(i)); + } + //If it is a select, then add it to selects + if(m_a.is_select(f)) + { + SASSERT(!m_a.is_array(get_sort(e))); + selects.insert_if_not_there(f->get_arg(0), ptr_vector()); + selects[f->get_arg(0)].push_back(e); + } + //If it is a condition between arrays, for example the result of a store, then add it to the equiv_classes + if(m_a.is_store(f)) + { + eq_classes.merge(e, f->get_arg(0)); + } + else if(m.is_eq(f) && m_a.is_array(get_sort(f->get_arg(0)))) + { + eq_classes.merge(f->get_arg(0), f->get_arg(1)); + } + } + + + expr_ref_vector mk_array_instantiation::getId(app*old_pred, const expr_ref_vector& n_args) + { + expr_ref_vector res(m); + for(unsigned i=0;iget_num_args();j++) + { + res.push_back(select->get_arg(j)); + } + } + } + return res; + } + + expr_ref mk_array_instantiation::create_pred(app*old_pred, expr_ref_vector& n_args) + { + expr_ref_vector new_args(m); + new_args.append(n_args); + new_args.append(getId(old_pred, n_args)); + for(unsigned i=0;iget_decl()->get_name().str()+"!inst").c_str()), new_sorts.size(), new_sorts.c_ptr(), old_pred->get_decl()->get_range()); + m_ctx.register_predicate(fun_decl, false); + if(src_set->is_output_predicate(old_pred->get_decl())) + dst->set_output_predicate(fun_decl); + res=m.mk_app(fun_decl,new_args.size(), new_args.c_ptr()); + return res; + } + + var * mk_array_instantiation::mk_select_var(expr* select) + { + var*result; + if(!done_selects.find(select, result)) + { + ownership.push_back(select); + result = m.mk_var(cnt, get_sort(select)); + cnt++; + done_selects.insert(select, result); + } + return result; + } + + expr_ref mk_array_instantiation::rewrite_select(expr*array, expr*select) + { + app*s = to_app(select); + expr_ref res(m); + expr_ref_vector args(m); + args.push_back(array); + for(unsigned i=1; iget_num_args();i++) + { + args.push_back(s->get_arg(i)); + } + res = m_a.mk_select(args.size(), args.c_ptr()); + return res; + } + + expr_ref_vector mk_array_instantiation::retrieve_all_selects(expr*array) + { + expr_ref_vector all_selects(m); + for(spacer::expr_equiv_class::iterator it = eq_classes.begin(array); + it != eq_classes.end(array); ++it) + { + selects.insert_if_not_there(*it, ptr_vector()); + ptr_vector& select_ops = selects[*it]; + for(unsigned i=0;iget_num_args(); + //Stores, for each old position, the list of a new possible arguments + vector arg_correspondance; + for(unsigned i=0;iget_arg(i), m); + if(m_a.is_array(get_sort(arg))) + { + vector arg_possibilities(m_ctx.get_params().xform_instantiate_arrays_nb_quantifier(), retrieve_all_selects(arg)); + arg_correspondance.append(arg_possibilities); + if(!m_ctx.get_params().xform_instantiate_arrays_enforce()) + { + expr_ref_vector tmp(m); + tmp.push_back(arg); + arg_correspondance.push_back(tmp); + } + } + else + { + expr_ref_vector tmp(m); + tmp.push_back(arg); + arg_correspondance.push_back(tmp); + } + } + //Now, we need to deal with every combination + + expr_ref_vector res(m); + + svector chosen(arg_correspondance.size(), 0u); + while(1) + { + expr_ref_vector new_args(m); + for(unsigned i=0;i=arg_correspondance[pos].size()); + chosen[pos]++; + } + } +} diff --git a/src/muz/transforms/dl_mk_array_instantiation.h b/src/muz/transforms/dl_mk_array_instantiation.h new file mode 100644 index 000000000..0af57ee84 --- /dev/null +++ b/src/muz/transforms/dl_mk_array_instantiation.h @@ -0,0 +1,123 @@ +/*++ + +Module Name: + + dl_mk_array_instantiation.h + +Abstract: + Transforms predicates so that array invariants can be discovered. + + Motivation : Given a predicate P(a), no quantifier-free solution can express that P(a) <=> forall i, P(a[i]) = 0 + + Solution : Introduce a fresh variable i, and transform P(a) into P!inst(i, a). + Now, (P!inst(i,a) := a[i] = 0) <=> P(a) := forall i, a[i] = 0. + + Transformation on Horn rules: + P(a, args) /\ phi(a, args, args') => P'(args') (for simplicity, assume there are no arrays in args'). + Is transformed into: + (/\_r in read_indices(phi) P!inst(r, a, args)) /\ phi(a, args, args') => P'(args') + + Limitations : This technique can only discover invariants on arrays that depend on one quantifier. + Related work : Techniques relying on adding quantifiers and eliminating them. See dl_mk_quantifier_abstraction and dl_mk_quantifier_instantiation + +Implementation: + The implementation follows the solution suggested above, with more options. The addition of options implies that in the simple + case described above, we in fact have P(a) transformed into P(i, a[i], a). + + 1) Dealing with multiple quantifiers -> The options fixedpoint.xform.instantiate_arrays.nb_quantifier gives the number of quantifiers per array. + + 2) Inforcing the instantiation -> We suggest an option (enforce_instantiation) to enforce this abstraction. This transforms + P(a) into P(i, a[i]). This enforces the solver to limit the space search at the cost of imprecise results. This option + corresponds to fixedpoint.xform.instantiate_arrays.enforce + + 3) Adding slices in the mix -> We wish to have the possibility to further restrict the search space: we want to smash cells, given a smashing rule. + For example, in for loops j=0; j P'(...) is transformed into + (/\_r in read_indices(phi) P!inst(id_r, a[r], a) /\ GetId(r) = id_r) /\ phi(a, ...) => P'(...). + Note : when no slicing is done, GetId(i) = i. + This option corresponds to fixedpoint.xform.instantiate_arrays.slice_technique + + Although we described GetId as returning integers, there is no reason to restrict the type of ids to integers. A more direct method, + for the 0<=i > selects; + spacer::expr_equiv_class eq_classes; + unsigned cnt;//Index for new variables + obj_map done_selects; + expr_ref_vector ownership; + + //Helper functions + void instantiate_rule(const rule& r, rule_set & dest);//Instantiates the rule + void retrieve_selects(expr* e);//Retrieves all selects (fills the selects and eq_classes members) + expr_ref rewrite_select(expr*array, expr*select);//Rewrites select(a, args) to select(array, args) + expr_ref_vector retrieve_all_selects(expr*array);//Retrieves all selects linked to a given array (using eq classes and selects) + expr_ref_vector instantiate_pred(app*old_pred);//Returns all the instantiation of a given predicate + expr_ref create_pred(app*old_pred, expr_ref_vector& new_args);//Creates a predicate + expr_ref create_head(app* old_head);//Creates the new head + var * mk_select_var(expr* select); + + /*Given the old predicate, and the new arguments for the new predicate, returns the new setId arguments. + By default getId(P(x, y, a, b), (x, y, a[i], a[j], a, b[k], b[l], b)) (nb_quantifier=2, enforce=false) + returns (i,j,k,l) + So that the final created predicate is P!inst(x, y, a[i], a[j], a, b[k], b[l], b, i, j, k, l) + */ + expr_ref_vector getId(app*old_pred, const expr_ref_vector& new_args); + public: + mk_array_instantiation(context & ctx, unsigned priority); + rule_set * operator()(rule_set const & source); + virtual ~mk_array_instantiation(){} + }; + + + +}; + +#endif /* DL_MK_ARRAY_INSTANTIATION_H_ */ diff --git a/src/muz/transforms/dl_transforms.cpp b/src/muz/transforms/dl_transforms.cpp index c727a29d4..4569cb572 100644 --- a/src/muz/transforms/dl_transforms.cpp +++ b/src/muz/transforms/dl_transforms.cpp @@ -33,7 +33,8 @@ Revision History: #include "muz/transforms/dl_mk_quantifier_instantiation.h" #include "muz/transforms/dl_mk_subsumption_checker.h" #include "muz/transforms/dl_mk_scale.h" -#include"fixedpoint_params.hpp" +#include "muz/base/fixedpoint_params.hpp" +#include "muz/transforms/dl_mk_array_eq_rewrite.h" namespace datalog { @@ -46,6 +47,11 @@ namespace datalog { transf.register_plugin(alloc(datalog::mk_coi_filter, ctx)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx)); + if (ctx.get_params().xform_instantiate_arrays()) { + transf.register_plugin(alloc(datalog::mk_array_instantiation, ctx, 34999)); + } + if(ctx.get_params().xform_transform_arrays()) + transf.register_plugin(alloc(datalog::mk_array_eq_rewrite, ctx, 34998)); if (ctx.get_params().xform_quantify_arrays()) { transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, ctx, 38000)); } @@ -83,7 +89,7 @@ namespace datalog { transf.register_plugin(alloc(datalog::mk_karr_invariants, ctx, 36010)); transf.register_plugin(alloc(datalog::mk_scale, ctx, 36030)); if (!ctx.get_params().xform_quantify_arrays()) { - transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 36000)); + transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 35999)); } if (ctx.get_params().xform_magic()) { transf.register_plugin(alloc(datalog::mk_magic_symbolic, ctx, 36020));