/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_utvpi.h Abstract: use Bellman Ford traversal algorithm for UTVPI. Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: The implementaton is derived from theory_diff_logic. --*/ #pragma once #include "smt/theory_diff_logic.h" namespace smt { class utvpi_tester { ast_manager& m; arith_util a; ptr_vector m_todo; ast_mark m_mark; obj_map m_coeff_map; rational m_weight; vector > m_terms; public: utvpi_tester(ast_manager& m); // test if formula is in the Horn inequality fragment: bool operator()(expr* fml); bool operator()(unsigned num_fmls, expr* const* fmls); // linearize inequality/equality bool linearize(expr* e); bool linearize(expr* e1, expr* e2); // retrieve linearization vector > const& get_linearization() const; rational const& get_weight() const { return m_weight; } private: bool linearize(); }; template class theory_utvpi : public theory, private Ext { typedef typename Ext::numeral numeral; typedef theory_var th_var; typedef svector th_var_vector; typedef vector > coeffs; class assignment_trail; class parent_trail; struct GExt : public Ext { typedef std::pair explanation; }; class atom { protected: bool_var m_bvar; bool m_true; int m_pos; int m_neg; public: atom(bool_var bv, int pos, int neg) : m_bvar(bv), m_true(false), m_pos(pos), m_neg(neg) {} bool_var get_bool_var() const { return m_bvar; } void assign_eh(bool is_true) { m_true = is_true; } int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } int get_pos() const { return m_pos; } int get_neg() const { return m_neg; } std::ostream& display(theory_utvpi const& th, std::ostream& out) const; }; typedef svector atoms; struct scope { unsigned m_atoms_lim; unsigned m_asserted_atoms_lim; unsigned m_asserted_qhead_old; }; struct stats { unsigned m_num_conflicts; unsigned m_num_assertions; unsigned m_num_core2th_eqs; unsigned m_num_core2th_diseqs; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; // Functor used to collect the proofs for a conflict due to // a negative cycle. class nc_functor { literal_vector m_antecedents; unsigned_vector m_coeffs; theory_utvpi& m_super; public: nc_functor(theory_utvpi& s) : m_super(s) {} void reset() { m_antecedents.reset(); m_coeffs.reset(); } literal_vector const& get_lits() const { return m_antecedents; } unsigned_vector const& get_coeffs() const { return m_coeffs; } void operator()(std::pair const & ex) { if (ex.first != null_literal) { m_antecedents.push_back(ex.first); m_coeffs.push_back(ex.second); } } void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { m_super.new_edge(src, dst, num_edges, edges); } }; stats m_stats; smt_params m_params; arith_util a; arith_eq_adapter m_arith_eq_adapter; bool m_consistent; th_var m_izero, m_rzero; //cache the variable representing the zero variable. dl_graph m_graph; nc_functor m_nc_functor; atoms m_atoms; unsigned_vector m_asserted_atoms; // set of asserted atoms unsigned m_asserted_qhead; u_map m_bool_var2atom; svector m_scopes; double m_agility; bool m_lia; bool m_lra; bool m_non_utvpi_exprs; utvpi_tester m_test; arith_factory * m_factory; rational m_delta; struct var_value_hash; friend struct var_value_hash; struct var_value_hash { theory_utvpi & m_th; var_value_hash(theory_utvpi & th):m_th(th) {} unsigned operator()(theory_var v) const { return m_th.mk_value(v, false).hash(); } }; struct var_value_eq; friend struct var_value_eq; struct var_value_eq { theory_utvpi & m_th; var_value_eq(theory_utvpi & th):m_th(th) {} bool operator()(theory_var v1, theory_var v2) const { return m_th.mk_value(v1, false) == m_th.mk_value(v2, false) && m_th.is_int(v1) == m_th.is_int(v2); } }; typedef int_hashtable var_value_table; var_value_table m_var_value_table; // Set a conflict due to a negative cycle. void set_conflict(); void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {} // Create a new theory variable. th_var mk_var(enode* n) override; virtual th_var mk_var(expr* n); void compute_delta(); void found_non_utvpi_expr(expr * n); bool is_interpreted(app* n) const { return n->get_family_id() == get_family_id(); } public: theory_utvpi(context& ctx); ~theory_utvpi() override; theory * mk_fresh(context * new_ctx) override; char const * get_name() const override { return "utvpi-logic"; } void init() override { init_zero(); } /** \brief See comment in theory::mk_eq_atom */ app * mk_eq_atom(expr * lhs, expr * rhs) override { return a.mk_eq(lhs, rhs); } bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void internalize_eq_eh(app * atom, bool_var v) override; void assign_eh(bool_var v, bool is_true) override; void new_eq_eh(th_var v1, th_var v2) override { m_stats.m_num_core2th_eqs++; m_arith_eq_adapter.new_eq_eh(v1, v2); } bool use_diseqs() const override { return true; } void new_diseq_eh(th_var v1, th_var v2) override { m_arith_eq_adapter.new_diseq_eh(v1, v2); } void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override { m_arith_eq_adapter.restart_eh(); } void relevant_eh(app* e) override {} void init_search_eh() override { m_arith_eq_adapter.init_search_eh(); } final_check_status final_check_eh() override; bool is_shared(th_var v) const override { return false; } bool can_propagate() override { SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); return m_asserted_qhead != m_asserted_atoms.size(); } void propagate() override; justification * why_is_diseq(th_var v1, th_var v2) override { UNREACHABLE(); return nullptr; } void reset_eh() override; void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; void display(std::ostream & out) const override; void collect_statistics(::statistics & st) const override; private: void init_model(); bool assume_eqs_core(); rational mk_value(theory_var v, bool is_int); bool is_parity_ok(unsigned v) const; void enforce_parity(); bool eval(expr* e); void model_validate(); rational eval_num(expr* e); bool check_z_consistency(); bool has_shared(); virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { m_stats.m_num_core2th_eqs++; new_eq_or_diseq(true, v1, v2, j); } virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { m_stats.m_num_core2th_diseqs++; new_eq_or_diseq(false, v1, v2, j); } void negate(coeffs& coeffs, rational& weight); numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); void del_atoms(unsigned old_size); bool propagate_atom(atom const& a); th_var mk_term(app* n); th_var mk_num(app* n, rational const& r); bool is_consistent() const; th_var expand(bool pos, th_var v, rational & k); void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); bool is_int(theory_var v) const { return a.is_int(get_enode(v)->get_expr()); } th_var get_zero(sort* s) { return a.is_int(s) ? m_izero : m_rzero; } th_var get_zero(expr* e) { return get_zero(e->get_sort()); } void init_zero(); void inc_conflicts(); edge_id add_ineq(vector > const& terms, numeral const& weight, literal l); bool enable_edge(edge_id id); th_var to_var(th_var v) const { return 2*v; } th_var from_var(th_var v) const { return v/2; } th_var pos(th_var v) const { return v & 0xFFFFFFFE; } th_var neg(th_var v) const { return v | 0x1; } }; typedef theory_utvpi theory_rutvpi; typedef theory_utvpi theory_iutvpi; };