mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-30 19:22:28 +00:00 
			
		
		
		
	include more qsat features
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									f175f864ec
								
							
						
					
					
						commit
						c4472ce717
					
				
					 15 changed files with 2197 additions and 190 deletions
				
			
		|  | @ -64,6 +64,13 @@ namespace nlsat { | |||
|             for (unsigned i = 0; i < sz; i++) | ||||
|                 push_back(ls[i]); | ||||
|         } | ||||
|         void append(scoped_literal_vector const& ls) { | ||||
|             append(ls.size(), ls.c_ptr()); | ||||
|         } | ||||
|         void swap(scoped_literal_vector& other) { | ||||
|             SASSERT(&m_solver == &other.m_solver); | ||||
|             m_lits.swap(other.m_lits); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,6 +30,8 @@ Notes: | |||
| #include"arith_decl_plugin.h" | ||||
| #include"tactic.h" | ||||
| #include"ast_smt2_pp.h" | ||||
| #include"polynomial.h" | ||||
| #include"algebraic_numbers.h" | ||||
| 
 | ||||
| struct goal2nlsat::imp { | ||||
|     struct nlsat_expr2polynomial : public expr2polynomial { | ||||
|  | @ -257,6 +259,7 @@ struct goal2nlsat::imp { | |||
|             process(g.form(i), g.dep(i)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| struct goal2nlsat::scoped_set_imp { | ||||
|  | @ -290,3 +293,153 @@ void goal2nlsat::operator()(goal const & g, params_ref const & p, nlsat::solver | |||
|     local_imp(g); | ||||
| } | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
| struct nlsat2goal::imp { | ||||
|     ast_manager& m; | ||||
|     arith_util   a; | ||||
|     u_map<expr*> const* m_x2t; | ||||
| public: | ||||
|     imp(ast_manager& m):m(m),a(m) {} | ||||
| 
 | ||||
|     expr_ref operator()(nlsat::solver& s, u_map<expr*> const& b2a, u_map<expr*> const& x2t, nlsat::literal l) { | ||||
|         m_x2t = &x2t; | ||||
|         expr_ref result(m); | ||||
|         expr* t; | ||||
|         if (b2a.find(l.var(), t)) { | ||||
|             result = t; | ||||
|         } | ||||
|         else { | ||||
|             nlsat::atom const* at = s.bool_var2atom(l.var()); | ||||
|             SASSERT(at != 0); | ||||
|             if (at->is_ineq_atom()) { | ||||
|                 nlsat::ineq_atom const* ia = to_ineq_atom(at); | ||||
|                 unsigned sz = ia->size(); | ||||
|                 expr_ref_vector ps(m); | ||||
|                 bool is_int = true; | ||||
|                 for (unsigned i = 0; is_int && i < sz; ++i) { | ||||
|                     is_int = poly_is_int(ia->p(i)); | ||||
|                 } | ||||
|                 for (unsigned i = 0; i < sz; ++i) { | ||||
|                     polynomial::polynomial* p = ia->p(i); | ||||
|                     expr_ref t = poly2expr(s, p, is_int); | ||||
|                     if (ia->is_even(i)) { | ||||
|                         t = a.mk_power(t, a.mk_numeral(rational(2), a.is_int(t))); | ||||
|                     } | ||||
|                     ps.push_back(t); | ||||
|                 } | ||||
|                 result = a.mk_mul_simplify(ps); | ||||
|                 expr_ref zero(m); | ||||
|                 zero = a.mk_numeral(rational(0), a.is_int(result)); | ||||
|                 switch (ia->get_kind()) { | ||||
|                 case nlsat::atom::EQ: | ||||
|                     result = m.mk_eq(result, zero); | ||||
|                     break; | ||||
|                 case nlsat::atom::LT: | ||||
|                     if (l.sign()) { | ||||
|                         l.neg(); | ||||
|                         result = a.mk_ge(result, zero); | ||||
|                     } | ||||
|                     else { | ||||
|                         result = a.mk_lt(result, zero); | ||||
|                     } | ||||
|                     break; | ||||
|                 case nlsat::atom::GT: | ||||
|                     if (l.sign()) { | ||||
|                         l.neg(); | ||||
|                         result = a.mk_le(result, zero); | ||||
|                     } | ||||
|                     else { | ||||
|                         result = a.mk_gt(result, zero); | ||||
|                     } | ||||
|                     break; | ||||
|                 default: | ||||
|                     UNREACHABLE(); | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 //nlsat::root_atom const* ra = nlsat::to_root_atom(at);
 | ||||
|                 //ra->i();
 | ||||
|                 //expr_ref p = poly2expr(s, ra->p());
 | ||||
|                 //expr*    x = m_x2t->find(ra->x());
 | ||||
|                 std::ostringstream strm; | ||||
|                 s.display(strm, l.sign()?~l:l); | ||||
|                 result = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (l.sign()) { | ||||
|             result = m.mk_not(result); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     expr_ref poly2expr(nlsat::solver& s, polynomial::polynomial* p, bool is_int) { | ||||
|         expr_ref result(m); | ||||
|         unsigned sz = polynomial::manager::size(p); | ||||
|         expr_ref_vector args(m); | ||||
|         for (unsigned i = 0; i < sz; ++i) { | ||||
|             args.push_back(mono2expr(s,  | ||||
|                                      polynomial::manager::coeff(p, i),  | ||||
|                                      polynomial::manager::get_monomial(p, i), is_int)); | ||||
|         } | ||||
|         result = a.mk_add_simplify(args); | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     expr_ref mono2expr(nlsat::solver& s, polynomial::numeral const& c, polynomial::monomial* mon, bool is_int) { | ||||
|         expr_ref result(m); | ||||
|         expr_ref_vector args(m); | ||||
|         unsigned sz = polynomial::manager::size(mon); | ||||
|         for (unsigned i = 0; i < sz; ++i) { | ||||
|             unsigned d = polynomial::manager::degree(mon, i); | ||||
|             expr* t = m_x2t->find(polynomial::manager::get_var(mon, i)); | ||||
|             SASSERT(d >= 1); | ||||
|             if (d == 1) { | ||||
|                 args.push_back(t); | ||||
|             } | ||||
|             else { | ||||
|                 args.push_back(a.mk_power(t, a.mk_numeral(rational(d), a.is_int(t)))); | ||||
|             } | ||||
|         } | ||||
|         if (!s.pm().m().is_one(c)) { | ||||
|             args.push_back(a.mk_numeral(c, is_int)); | ||||
|         } | ||||
|         result = a.mk_mul_simplify(args); | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     bool poly_is_int(polynomial::polynomial* p) { | ||||
|         bool is_int = true; | ||||
|         unsigned sz = polynomial::manager::size(p); | ||||
|         for (unsigned i = 0; is_int && i < sz; ++i) { | ||||
|             is_int = mono_is_int(polynomial::manager::get_monomial(p, i)); | ||||
|         } | ||||
|         return is_int; | ||||
|     } | ||||
| 
 | ||||
|     bool mono_is_int(polynomial::monomial* mon) { | ||||
|         bool is_int = true; | ||||
|         unsigned sz = polynomial::manager::size(mon); | ||||
|         for (unsigned i = 0; is_int && i < sz; ++i) { | ||||
|             is_int = a.is_int(m_x2t->find(polynomial::manager::get_var(mon, i))); | ||||
|         } | ||||
|         return is_int; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| nlsat2goal::nlsat2goal(ast_manager& m) { | ||||
|     m_imp = alloc(imp, m); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| nlsat2goal::~nlsat2goal() { | ||||
|     dealloc(m_imp); | ||||
| } | ||||
| 
 | ||||
| expr_ref nlsat2goal::operator()(nlsat::solver& s, u_map<expr*> const& b2a, u_map<expr*> const& x2t, nlsat::literal l) { | ||||
|     return (*m_imp)(s, b2a, x2t, l); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -57,16 +57,15 @@ class nlsat2goal { | |||
|     struct imp; | ||||
|     imp *  m_imp; | ||||
| public: | ||||
|     nlsat2goal(); | ||||
|     nlsat2goal(ast_manager& m); | ||||
|     ~nlsat2goal(); | ||||
| 
 | ||||
|     static void collect_param_descrs(param_descrs & r); | ||||
| 
 | ||||
|     /**
 | ||||
|        \brief Translate the state of the nlsat engine back into a goal. | ||||
|        \brief Translate a literal into a formula. | ||||
|     */    | ||||
|     void operator()(nlsat::solver const & s, expr2var const & a2b, expr2var const & t2x, | ||||
|                     params_ref const & p, goal & g, model_converter_ref & mc); | ||||
|     expr_ref operator()(nlsat::solver& s, u_map<expr*> const& b2a, u_map<expr*> const& x2t, nlsat::literal l); | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,7 +36,6 @@ Revision History: | |||
| #include "expr_functors.h" | ||||
| #include "quant_hoist.h" | ||||
| #include "bool_rewriter.h" | ||||
| #include "qe_util.h" | ||||
| #include "th_rewriter.h" | ||||
| #include "smt_kernel.h" | ||||
| #include "model_evaluator.h" | ||||
|  | @ -2281,7 +2280,7 @@ namespace qe { | |||
|         }     | ||||
|     } | ||||
| 
 | ||||
|     static void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars) { | ||||
|     void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars) { | ||||
|         ast_manager& m = new_body.get_manager(); | ||||
|         expr_ref tmp(m); | ||||
|         unsigned nd = q->get_num_decls(); | ||||
|  |  | |||
|  | @ -223,6 +223,8 @@ namespace qe { | |||
| 
 | ||||
|     qe_solver_plugin* mk_arith_plugin(i_solver_context& ctx, bool produce_models, smt_params& p); | ||||
| 
 | ||||
|     void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars); | ||||
| 
 | ||||
|     class def_vector { | ||||
|         func_decl_ref_vector m_vars; | ||||
|         expr_ref_vector      m_defs; | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -9,16 +9,33 @@ Copyright (c) 2015 Microsoft Corporation | |||
| #define QE_ARITH_H_ | ||||
| 
 | ||||
| #include "model.h" | ||||
| #include "arith_decl_plugin.h" | ||||
| #include "qe_mbp.h" | ||||
| 
 | ||||
| namespace qe { | ||||
| 
 | ||||
|     /**
 | ||||
|        Loos-Weispfenning model-based projection for a basic conjunction. | ||||
|        Lits is a vector of literals. | ||||
|        return vector of variables that could not be projected. | ||||
|      */ | ||||
|     expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits); | ||||
| 
 | ||||
|     expr_ref arith_project(model& model, app_ref_vector& vars, expr* fml); | ||||
|     class arith_project_plugin : public project_plugin { | ||||
|         struct imp; | ||||
|         imp* m_imp; | ||||
|     public: | ||||
|         arith_project_plugin(ast_manager& m); | ||||
|         virtual ~arith_project_plugin(); | ||||
|         virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); | ||||
|         virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); | ||||
|         virtual family_id get_family_id(); | ||||
|     }; | ||||
| 
 | ||||
|     bool arith_project(model& model, app* var, expr_ref_vector& lits); | ||||
| 
 | ||||
|     // match e := t mod k = 0.
 | ||||
|     bool is_divides(arith_util& a, expr* e, rational& k, expr_ref& t); | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ Revision History: | |||
| #include "nlarith_util.h" | ||||
| #include "model_evaluator.h" | ||||
| #include "smt_kernel.h" | ||||
| #include "qe_arith.h" | ||||
| 
 | ||||
| namespace qe { | ||||
| 
 | ||||
|  | @ -266,23 +267,8 @@ namespace qe { | |||
|         // 
 | ||||
|         // match 0 == p mod k, p mod k == 0
 | ||||
|         //
 | ||||
|         bool is_divides(app* e, numeral& k, expr_ref& p) { | ||||
|             expr* e1, *e2; | ||||
|             if (!m.is_eq(e, e1, e2)) { | ||||
|                 return false; | ||||
|             } | ||||
|             return is_divides(e1, e2, k, p) || is_divides(e2, e1, k, p); | ||||
|         } | ||||
|      | ||||
|         bool is_divides(expr* e1, expr* e2, numeral& k, expr_ref& p) {   | ||||
|             if (m_arith.is_mod(e2) &&  | ||||
|                 m_arith.is_numeral(e1, k) &&  | ||||
|                 k.is_zero() && | ||||
|                 m_arith.is_numeral(to_app(e2)->get_arg(1), k)) { | ||||
|                 p = to_app(e2)->get_arg(0); | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         bool is_divides(expr* e, numeral& k, expr_ref& p) { | ||||
|             return qe::is_divides(m_arith, e, k, p); | ||||
|         } | ||||
| 
 | ||||
|         bool is_not_divides(app* e, app_ref& n, numeral& k, expr_ref& p) { | ||||
|  |  | |||
							
								
								
									
										427
									
								
								src/qe/qe_arrays.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										427
									
								
								src/qe/qe_arrays.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,427 @@ | |||
| /*++
 | ||||
| Copyright (c) 2015 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     qe_arrays.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Model based projection for arrays | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2015-06-13 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| 
 | ||||
| #include "qe_arrays.h" | ||||
| #include "rewriter_def.h" | ||||
| #include "expr_functors.h" | ||||
| #include "expr_safe_replace.h" | ||||
| #include "lbool.h" | ||||
| #include "ast_util.h" | ||||
| #include "ast_pp.h" | ||||
| 
 | ||||
| namespace qe { | ||||
| 
 | ||||
|     struct array_project_plugin::imp { | ||||
| 
 | ||||
|         // rewriter or direct procedure.
 | ||||
|         struct rw_cfg : public default_rewriter_cfg { | ||||
|             ast_manager&    m; | ||||
|             array_util&     a; | ||||
|             expr_ref_vector m_lits; | ||||
|             model*          m_model;  | ||||
|             imp*            m_imp; | ||||
|              | ||||
|             rw_cfg(ast_manager& m, array_util& a): | ||||
|                 m(m), a(a), m_lits(m), m_model(0) {} | ||||
|              | ||||
|             br_status reduce_app(func_decl* f, unsigned n, expr* const* args, expr_ref& result, proof_ref & pr) { | ||||
|                 if (a.is_select(f) && a.is_store(args[0])) {                     | ||||
|                     expr_ref val1(m), val2(m);  | ||||
|                     app* b = to_app(args[0]); | ||||
|                     SASSERT(b->get_num_args() == n + 1); | ||||
|                     for (unsigned i = 1; i < n; ++i) { | ||||
|                         expr* arg1 = args[i]; | ||||
|                         expr* arg2 = b->get_arg(i); | ||||
|                         if (arg1 == arg2) { | ||||
|                             val1 = val2 = arg1; | ||||
|                         } | ||||
|                         else { | ||||
|                             VERIFY(m_model->eval(arg1, val1)); | ||||
|                             VERIFY(m_model->eval(arg2, val2)); | ||||
|                         } | ||||
|                         switch(compare(val1, val2)) { | ||||
|                         case l_true: | ||||
|                             if (arg1 != arg2) { | ||||
|                                 m_lits.push_back(m.mk_eq(arg1, arg2)); | ||||
|                             } | ||||
|                             break; | ||||
|                         case l_false: { | ||||
|                             ptr_vector<expr> new_args; | ||||
|                             if (i > 0) { | ||||
|                                 m_lits.resize(m_lits.size() - i); | ||||
|                             } | ||||
|                             m_lits.push_back(m.mk_not(m.mk_eq(arg1, arg2))); | ||||
|                             new_args.push_back(b->get_arg(0)); | ||||
|                             new_args.append(n-1, args+1); | ||||
|                             result = m.mk_app(f, n, new_args.c_ptr()); | ||||
|                             return BR_REWRITE1; | ||||
|                         } | ||||
|                         case l_undef: | ||||
|                             return BR_FAILED; | ||||
|                         } | ||||
|                     } | ||||
|                     result = b->get_arg(n); | ||||
|                     return BR_DONE; | ||||
|                 } | ||||
|                 return BR_FAILED; | ||||
|             } | ||||
| 
 | ||||
|             lbool compare(expr* x, expr* y) { | ||||
|                 NOT_IMPLEMENTED_YET(); | ||||
|                 return l_undef; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         struct indices { | ||||
|             expr_ref_vector m_values; | ||||
|             expr* const*    m_vars; | ||||
| 
 | ||||
|             indices(ast_manager& m, model& model, unsigned n, expr* const* vars):  | ||||
|                 m_values(m), m_vars(vars) { | ||||
|                 expr_ref val(m); | ||||
|                 for (unsigned i = 0; i < n; ++i) { | ||||
|                     VERIFY(model.eval(vars[i], val)); | ||||
|                     m_values.push_back(val); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|         ast_manager& m; | ||||
|         array_util   a; | ||||
|         scoped_ptr<contains_app> m_var; | ||||
|          | ||||
|         imp(ast_manager& m): m(m), a(m) {} | ||||
|         ~imp() {} | ||||
| 
 | ||||
|         virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
| 
 | ||||
|             TRACE("qe", tout << mk_pp(var, m) << "\n" << lits;); | ||||
|             m_var = alloc(contains_app, m, var); | ||||
| 
 | ||||
|             // reduce select-store redeces based on model.
 | ||||
|             // rw_cfg rw(m);
 | ||||
|             // rw(lits);
 | ||||
| 
 | ||||
|             // try first to solve for var.
 | ||||
|             if (solve_eq(model, vars, lits)) { | ||||
|                 return true; | ||||
|             }  | ||||
| 
 | ||||
|             app_ref_vector selects(m); | ||||
| 
 | ||||
|             // check that only non-select occurrences are in disequalities.
 | ||||
|             if (!check_diseqs(lits, selects)) { | ||||
|                 TRACE("qe", tout << "Could not project " << mk_pp(var, m) << " for:\n" << lits << "\n";); | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             // remove disequalities.
 | ||||
|             elim_diseqs(lits); | ||||
| 
 | ||||
|             // Ackerman reduction on remaining select occurrences
 | ||||
|             // either replace occurrences by model value or other node
 | ||||
|             // that is congruent to model value.
 | ||||
| 
 | ||||
|             ackermanize_select(model, selects, vars, lits); | ||||
|             | ||||
|             TRACE("qe", tout << selects << "\n" << lits << "\n";); | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         void ackermanize_select(model& model, app_ref_vector const& selects, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|             expr_ref_vector vals(m), reps(m); | ||||
|             expr_ref val(m); | ||||
|             expr_safe_replace sub(m); | ||||
| 
 | ||||
|             if (selects.empty()) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             app_ref sel(m); | ||||
|             for (unsigned i = 0; i < selects.size(); ++i) {                 | ||||
|                 sel = m.mk_fresh_const("sel", m.get_sort(selects[i])); | ||||
|                 VERIFY (model.eval(selects[i], val)); | ||||
|                 model.register_decl(sel->get_decl(), val); | ||||
|                 vals.push_back(to_app(val)); | ||||
|                 reps.push_back(val);               // TODO: direct pass could handle nested selects.
 | ||||
|                 vars.push_back(sel); | ||||
|                 sub.insert(selects[i], val); | ||||
|             } | ||||
| 
 | ||||
|             sub(lits); | ||||
|             remove_true(lits); | ||||
|             project_plugin::partition_args(model, selects, lits); | ||||
|             project_plugin::partition_values(model, reps, lits); | ||||
|         } | ||||
| 
 | ||||
|         void remove_true(expr_ref_vector& lits) { | ||||
|             for (unsigned i = 0; i < lits.size(); ++i) { | ||||
|                 if (m.is_true(lits[i].get())) { | ||||
|                     project_plugin::erase(lits, i); | ||||
|                 } | ||||
|             }             | ||||
|         } | ||||
| 
 | ||||
|         bool contains_x(expr* e) { | ||||
|             return (*m_var)(e); | ||||
|         } | ||||
| 
 | ||||
|         void mk_eq(indices& x, indices y, expr_ref_vector& lits) { | ||||
|             unsigned n = x.m_values.size(); | ||||
|             for (unsigned j = 0; j < n; ++j) { | ||||
|                 lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         // check that x occurs only under selects or in disequalities.
 | ||||
|         bool check_diseqs(expr_ref_vector const& lits, app_ref_vector& selects) { | ||||
|             expr_mark mark; | ||||
|             ptr_vector<app> todo; | ||||
|             app* e; | ||||
|             for (unsigned i = 0; i < lits.size(); ++i) { | ||||
|                 e = to_app(lits[i]); | ||||
|                 if (is_diseq_x(e)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (contains_x(e)) { | ||||
|                     todo.push_back(e); | ||||
|                 } | ||||
|             } | ||||
|             while (!todo.empty()) { | ||||
|                 e = todo.back(); | ||||
|                 todo.pop_back();                     | ||||
|                 if (mark.is_marked(e)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 mark.mark(e); | ||||
|                 if (m_var->x() == e) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 unsigned start = 0; | ||||
|                 if (a.is_select(e)) { | ||||
|                     if (e->get_arg(0) == m_var->x()) { | ||||
|                         start = 1; | ||||
|                         selects.push_back(e); | ||||
|                     }  | ||||
|                 } | ||||
|                 for (unsigned i = start; i < e->get_num_args(); ++i) { | ||||
|                     todo.push_back(to_app(e->get_arg(i))); | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         void elim_diseqs(expr_ref_vector& lits) { | ||||
|             for (unsigned i = 0; i < lits.size(); ++i) { | ||||
|                 if (is_diseq_x(lits[i].get())) { | ||||
|                     project_plugin::erase(lits, i); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         bool is_update_x(app* e) { | ||||
|             do { | ||||
|                 if (m_var->x() == e) { | ||||
|                     return true; | ||||
|                 } | ||||
|                 if (a.is_store(e) && contains_x(e->get_arg(0))) { | ||||
|                     for (unsigned i = 1; i < e->get_num_args(); ++i) { | ||||
|                         if (contains_x(e->get_arg(i))) { | ||||
|                             return false; | ||||
|                         } | ||||
|                     } | ||||
|                     e = to_app(e->get_arg(0)); | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|             while (false); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         bool is_diseq_x(expr* e) { | ||||
|             expr *f, * s, *t; | ||||
|             if (m.is_not(e, f) && m.is_eq(f, s, t)) { | ||||
|                 if (contains_x(s) && !contains_x(t) && is_update_x(to_app(s))) return true; | ||||
|                 if (contains_x(t) && !contains_x(s) && is_update_x(to_app(t))) return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|             // find an equality to solve for.
 | ||||
|             expr* s, *t; | ||||
|             for (unsigned i = 0; i < lits.size(); ++i) { | ||||
|                 if (m.is_eq(lits[i].get(), s, t)) { | ||||
|                     vector<indices> idxs; | ||||
|                     expr_ref save(m), back(m); | ||||
|                     save = lits[i].get(); | ||||
|                     back = lits.back(); | ||||
|                     lits[i] = back; | ||||
|                     lits.pop_back(); | ||||
|                     unsigned sz = lits.size(); | ||||
|                     if (contains_x(s) && !contains_x(t) && is_app(s)) { | ||||
|                         if (solve(model, to_app(s), t, idxs, vars, lits)) { | ||||
|                             return true; | ||||
|                         } | ||||
|                     } | ||||
|                     else if (contains_x(t) && !contains_x(s) && is_app(t)) { | ||||
|                         if (solve(model, to_app(t), s, idxs, vars, lits)) { | ||||
|                             return true; | ||||
|                         } | ||||
|                     } | ||||
|                     // put back the equality literal.
 | ||||
|                     lits.resize(sz); | ||||
|                     lits.push_back(back); | ||||
|                     lits[i] = save; | ||||
|                 } | ||||
|                 // TBD: not distinct?
 | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         bool solve(model& model, app* s, expr* t, vector<indices>& idxs, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|             SASSERT(contains_x(s)); | ||||
|             SASSERT(!contains_x(t)); | ||||
| 
 | ||||
|             if (s == m_var->x()) { | ||||
|                 expr_ref result(s, m); | ||||
|                 expr_ref_vector args(m); | ||||
|                 sort* range = get_array_range(m.get_sort(s)); | ||||
|                 for (unsigned i = 0; i < idxs.size(); ++i) { | ||||
|                     app_ref var(m); | ||||
|                     var = m.mk_fresh_const("value", range); | ||||
|                     vars.push_back(var); | ||||
|                     args.reset(); | ||||
|                     args.push_back(result); | ||||
|                     args.append(idxs[i].m_values.size(), idxs[i].m_vars); | ||||
|                     args.push_back(var); | ||||
|                     result = a.mk_store(args.size(), args.c_ptr()); | ||||
|                 } | ||||
|                 expr_safe_replace sub(m); | ||||
|                 sub.insert(s, result); | ||||
|                 for (unsigned i = 0; i < lits.size(); ++i) { | ||||
|                     sub(lits[i].get(), result); | ||||
|                     lits[i] = result; | ||||
|                 } | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             if (a.is_store(s)) { | ||||
|                 unsigned n = s->get_num_args()-2; | ||||
|                 indices idx(m, model, n, s->get_args()+1); | ||||
|                 for (unsigned i = 1; i < n; ++i) { | ||||
|                     if (contains_x(s->get_arg(i))) { | ||||
|                         return false; | ||||
|                     } | ||||
|                 }  | ||||
|                 unsigned i; | ||||
|                 expr_ref_vector args(m); | ||||
|                 switch (contains(idx, idxs, i)) { | ||||
|                 case l_true: | ||||
|                     mk_eq(idx, idxs[i], lits); | ||||
|                     return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); | ||||
|                 case l_false: | ||||
|                     for (unsigned i = 0; i < idxs.size(); ++i) { | ||||
|                         expr_ref_vector eqs(m); | ||||
|                         mk_eq(idx, idxs[i], eqs); | ||||
|                         lits.push_back(m.mk_not(mk_and(eqs))); // TBD: extract single index of difference based on model.
 | ||||
|                     } | ||||
|                     args.push_back(t); | ||||
|                     args.append(n, s->get_args()+1); | ||||
|                     lits.push_back(m.mk_eq(a.mk_select(args.size(), args.c_ptr()), s->get_arg(n+1))); | ||||
|                     idxs.push_back(idx); | ||||
|                     return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); | ||||
|                 case l_undef: | ||||
|                     return false; | ||||
|                 }                 | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         lbool contains(indices const& idx, vector<indices> const& idxs, unsigned& j) { | ||||
|             for (unsigned i = 0; i < idxs.size(); ++i) { | ||||
|                 switch (compare(idx, idxs[i])) { | ||||
|                 case l_true:  | ||||
|                     j = i; | ||||
|                     return l_true; | ||||
|                 case l_false: | ||||
|                     break; | ||||
|                 case l_undef: | ||||
|                     return l_undef;                 | ||||
|                 } | ||||
|             } | ||||
|             return l_false; | ||||
|         } | ||||
| 
 | ||||
|         lbool compare(indices const& idx1, indices const& idx2) { | ||||
|             unsigned n = idx1.m_values.size(); | ||||
|             for (unsigned i = 0; i < n; ++i) { | ||||
|                 switch (compare(idx1.m_values[i], idx2.m_values[i])) { | ||||
|                 case l_true: | ||||
|                     break; | ||||
|                 case l_false: | ||||
|                     return l_false; | ||||
|                 case l_undef: | ||||
|                     return l_undef; | ||||
|                 } | ||||
|             } | ||||
|             return l_true; | ||||
|         } | ||||
| 
 | ||||
|         lbool compare(expr* val1, expr* val2) { | ||||
|             if (val1 == val2) { | ||||
|                 return l_true; | ||||
|             } | ||||
|             if (is_uninterp(val1) || | ||||
|                 is_uninterp(val2)) { | ||||
|                 // TBD chase definition of nested array.
 | ||||
|                 return l_undef; | ||||
|             } | ||||
|             return l_true; | ||||
|         }             | ||||
|     }; | ||||
|      | ||||
|      | ||||
|     array_project_plugin::array_project_plugin(ast_manager& m) { | ||||
|         m_imp = alloc(imp, m); | ||||
|     } | ||||
|      | ||||
|     array_project_plugin::~array_project_plugin() { | ||||
|         dealloc(m_imp); | ||||
|     } | ||||
|      | ||||
|     bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|         return (*m_imp)(model, var, vars, lits); | ||||
|     } | ||||
| 
 | ||||
|     bool array_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|         return m_imp->solve(model, vars, lits); | ||||
|     } | ||||
|      | ||||
|     family_id array_project_plugin::get_family_id() { | ||||
|         return m_imp->a.get_family_id(); | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
							
								
								
									
										42
									
								
								src/qe/qe_arrays.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/qe/qe_arrays.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| /*++
 | ||||
| Copyright (c) 2015 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     qe_arrays.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Model based projection for arrays | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2015-06-13 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef __QE_ARRAYS_H_ | ||||
| #define __QE_ARRAYS_H_ | ||||
| 
 | ||||
| #include "array_decl_plugin.h" | ||||
| #include "qe_mbp.h" | ||||
| 
 | ||||
| namespace qe { | ||||
| 
 | ||||
|     class array_project_plugin : public project_plugin { | ||||
|         struct imp; | ||||
|         imp* m_imp; | ||||
|     public: | ||||
|         array_project_plugin(ast_manager& m); | ||||
|         virtual ~array_project_plugin(); | ||||
|         virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); | ||||
|         virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); | ||||
|         virtual family_id get_family_id(); | ||||
|     }; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										312
									
								
								src/qe/qe_datatypes.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								src/qe/qe_datatypes.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,312 @@ | |||
| /*++
 | ||||
| Copyright (c) 2015 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     qe_datatypes.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Simple projection function for algebraic datatypes. | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2015-06-13 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #include "qe_arith.h" | ||||
| #include "ast_pp.h" | ||||
| #include "th_rewriter.h" | ||||
| #include "expr_functors.h" | ||||
| #include "model_v2_pp.h" | ||||
| #include "expr_safe_replace.h" | ||||
| #include "obj_pair_hashtable.h" | ||||
| #include "qe_datatypes.h" | ||||
| 
 | ||||
| namespace qe { | ||||
|      | ||||
|     struct datatype_project_plugin::imp  { | ||||
|         ast_manager&              m; | ||||
|         datatype_util             dt; | ||||
|         app_ref                   m_val; | ||||
|         scoped_ptr<contains_app>  m_var; | ||||
|          | ||||
|         imp(ast_manager& m): | ||||
|             m(m), dt(m), m_val(m) {} | ||||
|          | ||||
|         virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|             return lift_foreign(vars, lits); | ||||
|         } | ||||
| 
 | ||||
|         bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|             expr_ref val(m); | ||||
|             VERIFY(model.eval(var, val)); | ||||
|             SASSERT(is_app(val)); | ||||
|             TRACE("qe", tout << mk_pp(var, m) << " := " << val << "\n";); | ||||
|             m_val = to_app(val); | ||||
|             if (!dt.is_constructor(m_val)) { | ||||
|                 // assert: var does not occur in lits.
 | ||||
|                 return true; | ||||
|             } | ||||
|             m_var = alloc(contains_app, m, var); | ||||
| 
 | ||||
| 
 | ||||
|             try { | ||||
|                 if (dt.is_recursive(m.get_sort(var))) { | ||||
|                     project_rec(model, vars, lits); | ||||
|                 } | ||||
|                 else { | ||||
|                     project_nonrec(model, vars, lits); | ||||
|                 } | ||||
|             } | ||||
|             catch (cant_project) { | ||||
|                 TRACE("qe", tout << "can't project:" << mk_pp(var, m) << "\n";); | ||||
|                 return false; | ||||
|             } | ||||
|             return true; | ||||
|         }         | ||||
|      | ||||
|         void project_nonrec(model& model, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|             expr_ref tmp(m), val(m); | ||||
|             expr_ref_vector args(m); | ||||
|             app_ref arg(m); | ||||
|             SASSERT(dt.is_constructor(m_val)); | ||||
|             func_decl* f = m_val->get_decl(); | ||||
|             ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(f); | ||||
|             for (unsigned i = 0; i < acc.size(); ++i) { | ||||
|                 arg = m.mk_fresh_const(acc[i]->get_name().str().c_str(), acc[i]->get_range()); | ||||
|                 model.register_decl(arg->get_decl(), m_val->get_arg(i)); | ||||
|                 args.push_back(arg); | ||||
|             } | ||||
|             val = m.mk_app(f, args.size(), args.c_ptr()); | ||||
|             TRACE("qe", tout << mk_pp(m_var->x(), m) << " |-> " << val << "\n";); | ||||
|             reduce(val, lits); | ||||
|         } | ||||
| 
 | ||||
|         void project_rec(model& model, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|             func_decl* f = m_val->get_decl(); | ||||
|             expr_ref rhs(m); | ||||
|             expr_ref_vector eqs(m); | ||||
|             for (unsigned i = 0; i < lits.size(); ++i) { | ||||
|                 if (solve(model, vars, lits[i].get(), rhs, eqs)) { | ||||
|                     project_plugin::erase(lits, i); | ||||
|                     reduce(rhs, lits); | ||||
|                     lits.append(eqs); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // otherwise, unfold the constructor associated with m_var
 | ||||
|             // once according to the model. In this way, selector-constructor
 | ||||
|             // redexes are reduced and disequalities are eventually solved
 | ||||
|             // by virtue of the constructors being different.
 | ||||
|             project_nonrec(model, vars, lits); | ||||
|         } | ||||
| 
 | ||||
|         void reduce(expr* val, expr_ref_vector& lits) { | ||||
|             expr_safe_replace sub(m); | ||||
|             th_rewriter rw(m); | ||||
|             expr_ref tmp(m); | ||||
|             sub.insert(m_var->x(), val); | ||||
|             TRACE("qe", tout << mk_pp(m_var->x(), m) << " = " << mk_pp(val, m) << "\n"; | ||||
|                   tout << lits << "\n";); | ||||
|             for (unsigned i = 0; i < lits.size(); ++i) {                 | ||||
|                 sub(lits[i].get(), tmp); | ||||
|                 rw(tmp); | ||||
|                 lits[i] = tmp; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         bool contains_x(expr* e) { | ||||
|             return (*m_var)(e); | ||||
|         } | ||||
| 
 | ||||
|         bool solve(model& model, app_ref_vector& vars, expr* fml, expr_ref& t, expr_ref_vector& eqs) { | ||||
|             expr* t1, *t2; | ||||
|             if (m.is_eq(fml, t1, t2)) { | ||||
|                 if (contains_x(t1) && !contains_x(t2) && is_app(t1)) { | ||||
|                     return solve(model, vars, to_app(t1), t2, t, eqs); | ||||
|                 } | ||||
|                 if (contains_x(t2) && !contains_x(t1) && is_app(t2)) { | ||||
|                     return solve(model, vars, to_app(t2), t1, t, eqs); | ||||
|                 } | ||||
|             } | ||||
|             if (m.is_not(fml, t1) && m.is_distinct(t1)) { | ||||
|                 expr_ref eq = project_plugin::pick_equality(m, model, t1); | ||||
|                 return solve(model, vars, eq, t, eqs); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         bool solve(model& model, app_ref_vector& vars, app* a, expr* b, expr_ref& t, expr_ref_vector& eqs) { | ||||
|             SASSERT(contains_x(a)); | ||||
|             SASSERT(!contains_x(b)); | ||||
|             if (m_var->x() == a) { | ||||
|                 t = b; | ||||
|                 return true; | ||||
|             } | ||||
|             if (!dt.is_constructor(a)) { | ||||
|                 return false; | ||||
|             } | ||||
|             func_decl* c = a->get_decl(); | ||||
|             func_decl* rec = dt.get_constructor_recognizer(c); | ||||
|             ptr_vector<func_decl> const & acc = *dt.get_constructor_accessors(c); | ||||
|             SASSERT(acc.size() == a->get_num_args()); | ||||
|             //
 | ||||
|             // It suffices to solve just the first available equality.
 | ||||
|             // The others are determined by the first.
 | ||||
|             //
 | ||||
|             for (unsigned i = 0; i < a->get_num_args(); ++i) { | ||||
|                 expr* l = a->get_arg(i); | ||||
|                 if (is_app(l) && contains_x(l)) { | ||||
|                     expr_ref r(m); | ||||
|                     r = access(c, i, acc, b); | ||||
|                     if (solve(model, vars, to_app(l), r, t, eqs)) { | ||||
|                         for (unsigned j = 0; j < c->get_arity(); ++j) { | ||||
|                             if (i != j) { | ||||
|                                 eqs.push_back(m.mk_eq(access(c, j, acc, b), a->get_arg(j))); | ||||
|                             } | ||||
|                         } | ||||
|                         if (!is_app_of(b, c)) { | ||||
|                             eqs.push_back(m.mk_app(rec, b)); | ||||
|                         } | ||||
| 
 | ||||
|                         return true; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         expr* access(func_decl* c, unsigned i, ptr_vector<func_decl> const& acc, expr* e) { | ||||
|             if (is_app_of(e,c)) { | ||||
|                 return to_app(e)->get_arg(i); | ||||
|             } | ||||
|             else { | ||||
|                 return m.mk_app(acc[i], e); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         bool lift_foreign(app_ref_vector const& vars, expr_ref_vector& lits) { | ||||
| 
 | ||||
|             bool reduced = false; | ||||
|             expr_mark visited; | ||||
|             expr_mark has_var; | ||||
|             bool inserted = false; | ||||
|             for (unsigned i = 0; i < vars.size(); ++i) {  | ||||
|                 if (m.is_bool(vars[i])) continue; | ||||
|                 if (dt.is_datatype(m.get_sort(vars[i]))) continue; | ||||
|                 inserted = true; | ||||
|                 has_var.mark(vars[i]); | ||||
|                 visited.mark(vars[i]); | ||||
|             } | ||||
|             if (inserted) { | ||||
|                 for (unsigned i = 0; i < lits.size(); ++i) { | ||||
|                     expr* e = lits[i].get(), *l, *r; | ||||
|                     if (m.is_eq(e, l, r) && reduce_eq(visited, has_var, l, r, lits)) { | ||||
|                         project_plugin::erase(lits, i); | ||||
|                         reduced = true; | ||||
|                     } | ||||
|                 } | ||||
|                 CTRACE("qe", reduced, tout << vars << "\n" << lits << "\n";); | ||||
|             } | ||||
|             return reduced; | ||||
|         } | ||||
| 
 | ||||
|         bool reduce_eq(expr_mark& has_var, expr_mark& visited, expr* l, expr* r, expr_ref_vector& lits) { | ||||
|             if (!is_app(l) || !is_app(r)) { | ||||
|                 return false; | ||||
|             } | ||||
|             bool reduce = false; | ||||
|             if (dt.is_constructor(to_app(r)) && contains_foreign(has_var, visited, r)) { | ||||
|                 std::swap(l, r); | ||||
|                 reduce = true; | ||||
|             } | ||||
|             reduce |= dt.is_constructor(to_app(l)) && contains_foreign(has_var, visited, l); | ||||
|             if (!reduce) { | ||||
|                 return false; | ||||
|             } | ||||
|             func_decl* c = to_app(l)->get_decl(); | ||||
|             ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(c); | ||||
|             if (!is_app_of(r, c)) { | ||||
|                 lits.push_back(m.mk_app(dt.get_constructor_recognizer(c), r)); | ||||
|             } | ||||
|             for (unsigned i = 0; i < acc.size(); ++i) { | ||||
|                 lits.push_back(m.mk_eq(to_app(l)->get_arg(i), access(c, i, acc, r))); | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         ptr_vector<expr> todo; | ||||
| 
 | ||||
|         bool contains_foreign(expr_mark& has_var, expr_mark& visited, expr* e) { | ||||
|             todo.push_back(e); | ||||
|             while (!todo.empty()) { | ||||
|                 expr* _f = todo.back(); | ||||
|                 if (visited.is_marked(_f)) { | ||||
|                     todo.pop_back(); | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (!is_app(_f)) { | ||||
|                     visited.mark(_f);                    | ||||
|                     todo.pop_back(); | ||||
|                     continue; | ||||
|                 } | ||||
|                 app* f = to_app(_f); | ||||
|                 bool has_new = false, has_v = false; | ||||
|                 for (unsigned i = 0; i < f->get_num_args(); ++i) { | ||||
|                     expr* arg = f->get_arg(i); | ||||
|                     if (!visited.is_marked(arg)) { | ||||
|                         todo.push_back(arg); | ||||
|                         has_new = true; | ||||
|                     } | ||||
|                     else { | ||||
|                         has_v |= has_var.is_marked(arg); | ||||
|                     } | ||||
|                 } | ||||
|                 if (has_new) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 todo.pop_back(); | ||||
|                 if (has_v) { | ||||
|                     has_var.mark(f); | ||||
|                 } | ||||
|                 TRACE("qe", tout << "contains: " << mk_pp(f, m) << " " << has_var.is_marked(f) << "\n";); | ||||
|                 visited.mark(f); | ||||
|             } | ||||
|             TRACE("qe", tout << "contains: " << mk_pp(e, m) << " " << has_var.is_marked(e) << "\n";); | ||||
|             return has_var.is_marked(e); | ||||
|         } | ||||
|          | ||||
|     }; | ||||
|      | ||||
|     datatype_project_plugin::datatype_project_plugin(ast_manager& m) { | ||||
|         m_imp = alloc(imp, m); | ||||
|     } | ||||
|      | ||||
|     datatype_project_plugin::~datatype_project_plugin() { | ||||
|         dealloc(m_imp); | ||||
|     } | ||||
|      | ||||
|     bool datatype_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|         return (*m_imp)(model, var, vars, lits); | ||||
|     } | ||||
| 
 | ||||
|     bool datatype_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|         return m_imp->solve(model, vars, lits); | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     family_id datatype_project_plugin::get_family_id() { | ||||
|         return m_imp->dt.get_family_id(); | ||||
|     } | ||||
|      | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										42
									
								
								src/qe/qe_datatypes.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/qe/qe_datatypes.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| /*++
 | ||||
| Copyright (c) 2015 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     qe_datatypes.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Model based projection for algebraic datatypes | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2015-06-13 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef __QE_DATATYPES_H_ | ||||
| #define __QE_DATATYPES_H_ | ||||
| 
 | ||||
| #include "datatype_decl_plugin.h" | ||||
| #include "qe_mbp.h" | ||||
| 
 | ||||
| namespace qe { | ||||
| 
 | ||||
|     class datatype_project_plugin : public project_plugin { | ||||
|         struct imp; | ||||
|         imp* m_imp; | ||||
|     public: | ||||
|         datatype_project_plugin(ast_manager& m); | ||||
|         virtual ~datatype_project_plugin(); | ||||
|         virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); | ||||
|         virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); | ||||
|         virtual family_id get_family_id(); | ||||
|     }; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  | @ -31,7 +31,6 @@ Revision History: | |||
| #include "var_subst.h" | ||||
| #include "uint_set.h" | ||||
| #include "ast_util.h" | ||||
| #include "qe_util.h" | ||||
| #include "th_rewriter.h" | ||||
| #include "for_each_expr.h" | ||||
| #include "expr_safe_replace.h" | ||||
|  |  | |||
							
								
								
									
										369
									
								
								src/qe/qe_mbp.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										369
									
								
								src/qe/qe_mbp.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,369 @@ | |||
| /*++
 | ||||
| Copyright (c) 2015 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     qe_mbp.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Model-based projection utilities | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2015-5-29 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #include "qe_mbp.h" | ||||
| #include "qe_arith.h" | ||||
| #include "qe_arrays.h" | ||||
| #include "qe_datatypes.h" | ||||
| #include "expr_safe_replace.h" | ||||
| #include "ast_pp.h" | ||||
| #include "ast_util.h" | ||||
| #include "th_rewriter.h" | ||||
| #include "model_v2_pp.h" | ||||
| #include "expr_functors.h" | ||||
| 
 | ||||
| 
 | ||||
| using namespace qe; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|    \brief return two terms that are equal in the model. | ||||
|    The distinct term t is false in model, so there  | ||||
|    are at least two arguments of t that are equal in the model. | ||||
| */ | ||||
| expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) { | ||||
|     SASSERT(m.is_distinct(t)); | ||||
|     expr_ref val(m); | ||||
|     expr_ref_vector vals(m); | ||||
|     obj_map<expr, expr*> val2expr; | ||||
|     app* alit = to_app(t); | ||||
|     for (unsigned i = 0; i < alit->get_num_args(); ++i) { | ||||
|         expr* e1 = alit->get_arg(i), *e2; | ||||
|         VERIFY(model.eval(e1, val)); | ||||
|         if (val2expr.find(val, e2)) { | ||||
|             return expr_ref(m.mk_eq(e1, e2), m); | ||||
|         } | ||||
|         val2expr.insert(val, e1); | ||||
|         vals.push_back(val); | ||||
|     } | ||||
|     UNREACHABLE(); | ||||
|     return expr_ref(0, m); | ||||
| } | ||||
| 
 | ||||
| void project_plugin::partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits) { | ||||
|     ast_manager& m = vals.get_manager(); | ||||
|     expr_ref val(m); | ||||
|     expr_ref_vector trail(m), reps(m); | ||||
|     obj_map<expr, expr*> roots; | ||||
|     for (unsigned i = 0; i < vals.size(); ++i) { | ||||
|         expr* v = vals[i], *root; | ||||
|         VERIFY (model.eval(v, val)); | ||||
|         if (roots.find(val, root)) { | ||||
|             lits.push_back(m.mk_eq(v, root)); | ||||
|         } | ||||
|         else { | ||||
|             roots.insert(val, v); | ||||
|             trail.push_back(val); | ||||
|             reps.push_back(v); | ||||
|         } | ||||
|     } | ||||
|     if (reps.size() > 1) {                 | ||||
|         lits.push_back(mk_distinct(reps)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void project_plugin::partition_args(model& model, app_ref_vector const& selects, expr_ref_vector& lits) { | ||||
|     ast_manager& m = selects.get_manager(); | ||||
|     if (selects.empty()) return; | ||||
|     unsigned num_args = selects[0]->get_decl()->get_arity(); | ||||
|     for (unsigned j = 1; j < num_args; ++j) { | ||||
|         expr_ref_vector args(m);             | ||||
|         for (unsigned i = 0; i < selects.size(); ++i) { | ||||
|             args.push_back(selects[i]->get_arg(j)); | ||||
|         } | ||||
|         project_plugin::partition_values(model, args, lits); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void project_plugin::erase(expr_ref_vector& lits, unsigned& i) { | ||||
|     lits[i] = lits.back(); | ||||
|     lits.pop_back(); | ||||
|     --i; | ||||
| } | ||||
| 
 | ||||
| void project_plugin::push_back(expr_ref_vector& lits, expr* e) { | ||||
|     if (lits.get_manager().is_true(e)) return; | ||||
|     lits.push_back(e); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class mbp::impl { | ||||
|     ast_manager& m; | ||||
|     ptr_vector<project_plugin> m_plugins; | ||||
| 
 | ||||
|     void add_plugin(project_plugin* p) { | ||||
|         family_id fid = p->get_family_id(); | ||||
|         SASSERT(!m_plugins.get(fid, 0)); | ||||
|         m_plugins.setx(fid, p, 0); | ||||
|     } | ||||
| 
 | ||||
|     project_plugin* get_plugin(app* var) { | ||||
|         family_id fid = m.get_sort(var)->get_family_id(); | ||||
|         return m_plugins.get(fid, 0); | ||||
|     } | ||||
| 
 | ||||
|     bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { | ||||
|         expr_mark is_var, is_rem; | ||||
|         if (vars.empty()) { | ||||
|             return false; | ||||
|         } | ||||
|         bool reduced = false; | ||||
|         for (unsigned i = 0; i < vars.size(); ++i) {  | ||||
|             is_var.mark(vars[i].get()); | ||||
|         } | ||||
|         expr_ref tmp(m), t(m), v(m);             | ||||
|         th_rewriter rw(m); | ||||
|         for (unsigned i = 0; i < lits.size(); ++i) { | ||||
|             expr* e = lits[i].get(), *l, *r; | ||||
|             if (m.is_eq(e, l, r) && reduce_eq(is_var, l, r, v, t)) { | ||||
|                 reduced = true; | ||||
|                 lits[i] = lits.back(); | ||||
|                 lits.pop_back(); | ||||
|                 --i; | ||||
|                 expr_safe_replace sub(m); | ||||
|                 sub.insert(v, t); | ||||
|                 is_rem.mark(v); | ||||
|                 for (unsigned j = 0; j < lits.size(); ++j) { | ||||
|                     sub(lits[j].get(), tmp); | ||||
|                     rw(tmp); | ||||
|                     lits[j] = tmp; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (reduced) { | ||||
|             for (unsigned i = 0; i < vars.size(); ++i) { | ||||
|                 if (is_rem.is_marked(vars[i].get())) { | ||||
|                     vars[i] = vars.back(); | ||||
|                     vars.pop_back(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return reduced; | ||||
|     } | ||||
| 
 | ||||
|     bool reduce_eq(expr_mark& is_var, expr* l, expr* r, expr_ref& v, expr_ref& t) { | ||||
|         if (is_var.is_marked(r)) { | ||||
|             std::swap(l, r); | ||||
|         } | ||||
|         if (is_var.is_marked(l)) { | ||||
|             contains_app cont(m, to_app(l)); | ||||
|             if (!cont(r)) { | ||||
|                 v = to_app(l); | ||||
|                 t = r; | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     void extract_literals(model& model, expr_ref_vector& fmls) { | ||||
|         expr_ref val(m); | ||||
|         for (unsigned i = 0; i < fmls.size(); ++i) { | ||||
|             expr* fml = fmls[i].get(), *nfml, *f1, *f2, *f3; | ||||
|             if (m.is_not(fml, nfml) && m.is_distinct(nfml)) { | ||||
|                 fmls[i] = project_plugin::pick_equality(m, model, nfml); | ||||
|                 --i; | ||||
|             } | ||||
|             else if (m.is_or(fml)) { | ||||
|                 for (unsigned j = 0; j < to_app(fml)->get_num_args(); ++j) { | ||||
|                     VERIFY (model.eval(to_app(fml)->get_arg(j), val)); | ||||
|                     if (m.is_true(val)) { | ||||
|                         fmls[i] = to_app(fml)->get_arg(j); | ||||
|                         --i; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else if (m.is_and(fml)) { | ||||
|                 fmls.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); | ||||
|                 project_plugin::erase(fmls, i); | ||||
|             } | ||||
|             else if (m.is_iff(fml, f1, f2) || (m.is_not(fml, nfml) && m.is_xor(nfml, f1, f2))) { | ||||
|                 VERIFY (model.eval(f1, val)); | ||||
|                 if (m.is_false(val)) { | ||||
|                     f1 = mk_not(m, f1); | ||||
|                     f2 = mk_not(m, f2); | ||||
|                 } | ||||
|                 project_plugin::push_back(fmls, f1); | ||||
|                 project_plugin::push_back(fmls, f2); | ||||
|                 project_plugin::erase(fmls, i); | ||||
|             } | ||||
|             else if (m.is_implies(fml, f1, f2)) { | ||||
|                 VERIFY (model.eval(f2, val)); | ||||
|                 if (m.is_true(val)) { | ||||
|                     project_plugin::push_back(fmls, f2); | ||||
|                 } | ||||
|                 else { | ||||
|                     project_plugin::push_back(fmls, mk_not(m, f1)); | ||||
|                 } | ||||
|                 project_plugin::erase(fmls, i); | ||||
|             } | ||||
|             else if (m.is_ite(fml, f1, f2, f3)) { | ||||
|                 VERIFY (model.eval(f1, val));                 | ||||
|                 if (m.is_true(val)) { | ||||
|                     project_plugin::push_back(fmls, f2); | ||||
|                 } | ||||
|                 else { | ||||
|                     project_plugin::push_back(fmls, f3); | ||||
|                 } | ||||
|                 project_plugin::erase(fmls, i); | ||||
|             } | ||||
|             else if (m.is_not(fml, nfml) && m.is_not(nfml, nfml)) { | ||||
|                 project_plugin::push_back(fmls, nfml); | ||||
|                 project_plugin::erase(fmls, i); | ||||
|             } | ||||
|             else if (m.is_not(fml, nfml) && m.is_and(nfml)) { | ||||
|                 for (unsigned j = 0; j < to_app(nfml)->get_num_args(); ++j) { | ||||
|                     VERIFY (model.eval(to_app(nfml)->get_arg(j), val)); | ||||
|                     if (m.is_false(val)) { | ||||
|                         fmls[i] = mk_not(m, to_app(nfml)->get_arg(j)); | ||||
|                         --i; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else if (m.is_not(fml, nfml) && m.is_or(nfml)) { | ||||
|                 for (unsigned j = 0; j < to_app(nfml)->get_num_args(); ++j) { | ||||
|                     project_plugin::push_back(fmls, mk_not(m, to_app(nfml)->get_arg(j))); | ||||
|                 } | ||||
|                 project_plugin::erase(fmls, i);                 | ||||
|             } | ||||
|             else if ((m.is_not(fml, nfml) && m.is_iff(nfml, f1, f2)) || m.is_xor(fml, f1, f2)) { | ||||
|                 VERIFY (model.eval(f1, val)); | ||||
|                 if (m.is_true(val)) { | ||||
|                     f2 = mk_not(m, f2); | ||||
|                 } | ||||
|                 else { | ||||
|                     f1 = mk_not(m, f1); | ||||
|                 } | ||||
|                 project_plugin::push_back(fmls, f1); | ||||
|                 project_plugin::push_back(fmls, f2); | ||||
|                 project_plugin::erase(fmls, i); | ||||
|             } | ||||
|             else if (m.is_not(fml, nfml) && m.is_implies(nfml, f1, f2)) { | ||||
|                 project_plugin::push_back(fmls, f1); | ||||
|                 project_plugin::push_back(fmls, mk_not(m, f2)); | ||||
|                 project_plugin::erase(fmls, i); | ||||
|             } | ||||
|             else if (m.is_not(fml, nfml) && m.is_ite(nfml, f1, f2, f3)) { | ||||
|                 VERIFY (model.eval(f1, val));                 | ||||
|                 if (m.is_true(val)) { | ||||
|                     project_plugin::push_back(fmls, mk_not(m, f2)); | ||||
|                 } | ||||
|                 else { | ||||
|                     project_plugin::push_back(fmls, mk_not(m, f3)); | ||||
|                 } | ||||
|                 project_plugin::erase(fmls, i); | ||||
|             } | ||||
|             else { | ||||
|                 // TBD other Boolean operations.
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl(ast_manager& m):m(m) { | ||||
|         add_plugin(alloc(arith_project_plugin, m)); | ||||
|         add_plugin(alloc(datatype_project_plugin, m)); | ||||
|         add_plugin(alloc(array_project_plugin, m)); | ||||
|     } | ||||
| 
 | ||||
|     ~impl() { | ||||
|         std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc<project_plugin>()); | ||||
|     } | ||||
| 
 | ||||
|     void preprocess_solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { | ||||
|         extract_literals(model, fmls); | ||||
|         bool change = true; | ||||
|         while (change && !vars.empty()) { | ||||
|             change = solve(model, vars, fmls); | ||||
|             for (unsigned i = 0; i < m_plugins.size(); ++i) { | ||||
|                 if (m_plugins[i] && m_plugins[i]->solve(model, vars, fmls)) { | ||||
|                     change = true; | ||||
|                 } | ||||
|             } | ||||
|         }         | ||||
|     } | ||||
| 
 | ||||
|     void operator()(bool force_elim, app_ref_vector& vars, model& model, expr_ref_vector& fmls) { | ||||
|         expr_ref val(m), tmp(m); | ||||
|         app_ref var(m); | ||||
|         th_rewriter rw(m); | ||||
|         bool progress = true; | ||||
|         while (progress && !vars.empty()) { | ||||
|             preprocess_solve(model, vars, fmls); | ||||
|             app_ref_vector new_vars(m); | ||||
|             progress = false; | ||||
|             while (!vars.empty()) { | ||||
|                 var = vars.back(); | ||||
|                 vars.pop_back(); | ||||
|                 project_plugin* p = get_plugin(var); | ||||
|                 if (p && (*p)(model, var, vars, fmls)) { | ||||
|                     progress = true; | ||||
|                 } | ||||
|                 else { | ||||
|                     new_vars.push_back(var); | ||||
|                 } | ||||
|             } | ||||
|             if (!progress && !new_vars.empty() && force_elim) { | ||||
|                 var = new_vars.back(); | ||||
|                 new_vars.pop_back(); | ||||
|                 expr_safe_replace sub(m); | ||||
|                 VERIFY(model.eval(var, val)); | ||||
|                 sub.insert(var, val); | ||||
|                 for (unsigned i = 0; i < fmls.size(); ++i) { | ||||
|                     sub(fmls[i].get(), tmp); | ||||
|                     rw(tmp); | ||||
|                     if (m.is_true(tmp)) { | ||||
|                         fmls[i] = fmls.back(); | ||||
|                         fmls.pop_back(); | ||||
|                         --i; | ||||
|                     } | ||||
|                     else { | ||||
|                         fmls[i] = tmp; | ||||
|                     } | ||||
|                 }             | ||||
|                 progress = true; | ||||
|             } | ||||
|             vars.append(new_vars); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|      | ||||
| mbp::mbp(ast_manager& m) { | ||||
|     m_impl = alloc(impl, m); | ||||
| } | ||||
|          | ||||
| mbp::~mbp() { | ||||
|     dealloc(m_impl); | ||||
| } | ||||
|          | ||||
| void mbp::operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls) { | ||||
|     (*m_impl)(force_elim, vars, mdl, fmls); | ||||
| } | ||||
| 
 | ||||
| void mbp::solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { | ||||
|     m_impl->preprocess_solve(model, vars, fmls); | ||||
| } | ||||
|          | ||||
| void mbp::extract_literals(model& model, expr_ref_vector& lits) { | ||||
|     m_impl->extract_literals(model, lits); | ||||
| } | ||||
							
								
								
									
										76
									
								
								src/qe/qe_mbp.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/qe/qe_mbp.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,76 @@ | |||
| /*++
 | ||||
| Copyright (c) 2015 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     qe_mbp.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Model-based projection utilities | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2015-5-28 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #ifndef __QE_MBP_H__ | ||||
| #define __QE_MBP_H__ | ||||
| 
 | ||||
| #include "ast.h" | ||||
| #include "params.h" | ||||
| #include "model.h" | ||||
| 
 | ||||
| 
 | ||||
| namespace qe { | ||||
| 
 | ||||
|     struct cant_project {}; | ||||
| 
 | ||||
|     class project_plugin { | ||||
|     public: | ||||
|         virtual ~project_plugin() {} | ||||
|         virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) = 0; | ||||
|         virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) = 0; | ||||
|         virtual family_id get_family_id() = 0; | ||||
| 
 | ||||
|         static expr_ref pick_equality(ast_manager& m, model& model, expr* t); | ||||
|         static void partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits); | ||||
|         static void partition_args(model& model, app_ref_vector const& sels, expr_ref_vector& lits); | ||||
|         static void erase(expr_ref_vector& lits, unsigned& i); | ||||
|         static void push_back(expr_ref_vector& lits, expr* lit); | ||||
|     }; | ||||
| 
 | ||||
|     class mbp { | ||||
|         class impl; | ||||
|         impl * m_impl; | ||||
|     public: | ||||
|         mbp(ast_manager& m); | ||||
|          | ||||
|         ~mbp(); | ||||
|          | ||||
|         /**
 | ||||
|            \brief | ||||
|            Apply model-based qe on constants provided as vector of variables.  | ||||
|            Return the updated formula and updated set of variables that were not eliminated.            | ||||
|         */ | ||||
|         void operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls); | ||||
| 
 | ||||
|         /**
 | ||||
|            \brief | ||||
|            Solve as many variables as possible using "cheap" quantifier elimination" | ||||
|         */ | ||||
|         void solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); | ||||
| 
 | ||||
|         /**
 | ||||
|            \brief  | ||||
|            Extract literals from formulas based on model. | ||||
|          */ | ||||
|         void extract_literals(model& model, expr_ref_vector& lits); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif  | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue