/*++ Copyright (c) 2010 Microsoft Corporation Module Name: qe.h Abstract: Quantifier-elimination procedures Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: --*/ #ifndef __QE_H__ #define __QE_H__ #include "ast.h" #include "front_end_params.h" #include "statistics.h" #include "lbool.h" #include "expr_functors.h" #include "simplifier.h" #include "rewriter.h" #include "model.h" #include "params.h" namespace qe { class i_nnf_atom { public: virtual void operator()(expr* e, bool pol, expr_ref& result) = 0; }; typedef obj_hashtable atom_set; class qe_solver_plugin; class i_solver_context { protected: class is_relevant : public i_expr_pred { i_solver_context& m_s; public: is_relevant(i_solver_context& s):m_s(s) {} virtual bool operator()(expr* e); }; class mk_atom_fn : public i_nnf_atom { i_solver_context& m_s; public: mk_atom_fn(i_solver_context& s) : m_s(s) {} void operator()(expr* e, bool p, expr_ref& result); }; is_relevant m_is_relevant; mk_atom_fn m_mk_atom; ptr_vector m_plugins; // fid -> plugin public: i_solver_context():m_is_relevant(*this), m_mk_atom(*this) {} virtual ~i_solver_context(); void add_plugin(qe_solver_plugin* p); bool has_plugin(app* x); qe_solver_plugin& plugin(app* x); qe_solver_plugin& plugin(unsigned fid) { return *m_plugins[fid]; } void mk_atom(expr* e, bool p, expr_ref& result); virtual ast_manager& get_manager() = 0; // set of atoms in current formula. virtual atom_set const& pos_atoms() const = 0; virtual atom_set const& neg_atoms() const = 0; // Access current set of variables to solve virtual unsigned get_num_vars() const = 0; virtual app* get_var(unsigned idx) const = 0; virtual app*const* get_vars() const = 0; virtual bool is_var(expr* e, unsigned& idx) const; virtual contains_app& contains(unsigned idx) = 0; // callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml' virtual void elim_var(unsigned idx, expr* fml, expr* def) = 0; // callback to add new variable to branch. virtual void add_var(app* x) = 0; // callback to add constraints in branch. virtual void add_constraint(bool use_var, expr* l1 = 0, expr* l2 = 0, expr* l3 = 0) = 0; // eliminate finite domain variable 'var' from fml. virtual void blast_or(app* var, expr_ref& fml) = 0; i_expr_pred& get_is_relevant() { return m_is_relevant; } i_nnf_atom& get_mk_atom() { return m_mk_atom; } }; class conj_enum { ast_manager& m; expr_ref_vector m_conjs; public: conj_enum(ast_manager& m, expr* e); class iterator { conj_enum* m_super; unsigned m_index; public: iterator(conj_enum& c, bool first) : m_super(&c), m_index(first?0:c.m_conjs.size()) {} expr* operator*() { return m_super->m_conjs[m_index].get(); } iterator& operator++() { m_index++; return *this; } bool operator==(iterator const& it) const { return m_index == it.m_index; } bool operator!=(iterator const& it) const { return m_index != it.m_index; } }; void extract_equalities(expr_ref_vector& eqs); iterator begin() { return iterator(*this, true); } iterator end() { return iterator(*this, false); } }; // // interface for plugins to eliminate quantified variables. // class qe_solver_plugin { protected: ast_manager& m; family_id m_fid; i_solver_context& m_ctx; public: qe_solver_plugin(ast_manager& m, family_id fid, i_solver_context& ctx) : m(m), m_fid(fid), m_ctx(ctx) {} virtual ~qe_solver_plugin() {} family_id get_family_id() { return m_fid; } /** \brief Return number of case splits for variable x in fml. */ virtual bool get_num_branches(contains_app& x, expr* fml, rational& num_branches) = 0; /** \brief Given a case split index 'vl' assert the constraints associated with it. */ virtual void assign(contains_app& x, expr* fml, rational const& vl) = 0; /** \brief The case splits associated with 'vl' are satisfiable. Apply the elimination for fml corresponding to case split. If def is non-null, then retrieve the realizer corresponding to the case split. */ virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) = 0; /** \brief solve quantified variable. */ virtual bool solve(conj_enum& conjs, expr* fml) = 0; /** \brief project variable x for fml given model. Assumption: model |= fml[x] Projection updates fml to fml', such that: - fml' -> fml - model |= fml' - fml' does not contain x return false if the method is not implemented. */ virtual bool project(contains_app& x, model_ref& model, expr_ref& fml) { return false; } /** \brief assign branching penalty to variable x for 'fml'. */ virtual unsigned get_weight(contains_app& x, expr* fml) { return UINT_MAX; } /** \brief simplify formula. */ virtual bool simplify(expr_ref& fml) { return false; } /** \brief Normalize literal during NNF conversion. */ virtual bool mk_atom(expr* e, bool p, expr_ref& result) { return false; } /** \brief Determine whether sub-term is uninterpreted with respect to quantifier elimination. */ virtual bool is_uninterpreted(app* f) { return true; } }; qe_solver_plugin* mk_datatype_plugin(i_solver_context& ctx); qe_solver_plugin* mk_bool_plugin(i_solver_context& ctx); qe_solver_plugin* mk_bv_plugin(i_solver_context& ctx); qe_solver_plugin* mk_dl_plugin(i_solver_context& ctx); qe_solver_plugin* mk_array_plugin(i_solver_context& ctx); qe_solver_plugin* mk_arith_plugin(i_solver_context& ctx, bool produce_models, front_end_params& p); class def_vector { func_decl_ref_vector m_vars; expr_ref_vector m_defs; public: def_vector(ast_manager& m): m_vars(m), m_defs(m) {} void push_back(func_decl* v, expr* e) { m_vars.push_back(v); m_defs.push_back(e); } void reset() { m_vars.reset(); m_defs.reset(); } void append(def_vector const& o) { m_vars.append(o.m_vars); m_defs.append(o.m_defs); } unsigned size() const { return m_defs.size(); } void shrink(unsigned sz) { m_vars.shrink(sz); m_defs.shrink(sz); } bool empty() const { return m_defs.empty(); } func_decl* var(unsigned i) const { return m_vars[i]; } expr* def(unsigned i) const { return m_defs[i]; } expr_ref_vector::element_ref def_ref(unsigned i) { return m_defs[i]; } }; class quant_elim; class expr_quant_elim { ast_manager& m; front_end_params const& m_fparams; params_ref m_params; expr_ref_vector m_trail; obj_map m_visited; quant_elim* m_qe; expr* m_assumption; bool m_use_new_qe; public: expr_quant_elim(ast_manager& m, front_end_params const& fp, params_ref const& p = params_ref()); ~expr_quant_elim(); void operator()(expr* assumption, expr* fml, expr_ref& result); void collect_statistics(statistics & st) const; void updt_params(params_ref const& p); void collect_param_descrs(param_descrs& r); /** \brief try to eliminate 'vars' and find first satisfying assignment. return l_true if satisfiable, l_false if unsatisfiable, l_undef if the formula could not be satisfied modulo eliminating the quantified variables. */ lbool first_elim(unsigned num_vars, app* const* vars, expr_ref& fml, def_vector& defs); /** \brief solve for (exists (var) fml). Return false if operation failed. Return true and list of pairs (t_i, fml_i) in such that fml[t_1] \/ ... \/ fml[t_n] == (exists (var) fml) and fml_i == fml[t_1] */ bool solve_for_var(app* var, expr* fml, expr_ref_vector& terms, expr_ref_vector& fmls); void set_cancel(bool f); private: void instantiate_expr(expr_ref_vector& bound, expr_ref& fml); void abstract_expr(unsigned sz, expr* const* bound, expr_ref& fml); void elim(expr_ref& result); void init_qe(); }; class expr_quant_elim_star1 : public simplifier { protected: expr_quant_elim m_quant_elim; expr* m_assumption; virtual bool visit_quantifier(quantifier * q); virtual void reduce1_quantifier(quantifier * q); virtual bool is_target(quantifier * q) const { return q->get_num_patterns() == 0 && q->get_num_no_patterns() == 0; } public: expr_quant_elim_star1(ast_manager & m, front_end_params const& p); virtual ~expr_quant_elim_star1() {} void collect_statistics(statistics & st) const { m_quant_elim.collect_statistics(st); } void reduce_with_assumption(expr* ctx, expr* fml, expr_ref& result); lbool first_elim(unsigned num_vars, app* const* vars, expr_ref& fml, def_vector& defs) { return m_quant_elim.first_elim(num_vars, vars, fml, defs); } void set_cancel(bool f) {} // TBD: replace simplifier by rewriter }; void hoist_exists(expr_ref& fml, app_ref_vector& vars); void mk_exists(unsigned num_vars, app* const* vars, expr_ref& fml); void get_nnf(expr_ref& fml, i_expr_pred& pred, i_nnf_atom& mk_atom, atom_set& pos, atom_set& neg); class simplify_rewriter_cfg : public default_rewriter_cfg { class impl; impl* imp; public: simplify_rewriter_cfg(ast_manager& m); ~simplify_rewriter_cfg(); bool reduce_quantifier( quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr); bool pre_visit(expr* e); }; class simplify_rewriter_star : public rewriter_tpl { simplify_rewriter_cfg m_cfg; public: simplify_rewriter_star(ast_manager& m): rewriter_tpl(m, false, m_cfg), m_cfg(m) {} }; }; #endif