z3-z3-4.13.0.src.sat.smt.arith_solver.h Maven / Gradle / Ivy
The newest version!
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
arith_solver.h
Abstract:
Theory plugin for arithmetic
Author:
Nikolaj Bjorner (nbjorner) 2020-09-08
--*/
#pragma once
#include "util/obj_pair_set.h"
#include "ast/ast_trail.h"
#include "ast/arith_decl_plugin.h"
#include "math/lp/indexed_value.h"
#include "math/lp/lar_solver.h"
#include "math/lp/nla_solver.h"
#include "math/lp/lp_types.h"
#include "math/lp/lp_api.h"
#include "math/polynomial/algebraic_numbers.h"
#include "math/polynomial/polynomial.h"
#include "sat/smt/sat_th.h"
#include "sat/smt/arith_sls.h"
#include "sat/sat_ddfw.h"
namespace euf {
class solver;
}
namespace arith {
typedef ptr_vector> lp_bounds;
typedef lp::lpvar lpvar;
typedef euf::theory_var theory_var;
typedef euf::theory_id theory_id;
typedef euf::enode enode;
typedef euf::enode_pair enode_pair;
typedef sat::literal literal;
typedef sat::bool_var bool_var;
typedef sat::literal_vector literal_vector;
typedef lp_api::bound api_bound;
enum class hint_type {
farkas_h,
bound_h,
cut_h,
implied_eq_h,
nla_h
};
struct arith_proof_hint : public euf::th_proof_hint {
hint_type m_ty;
unsigned m_lit_head, m_lit_tail, m_eq_head, m_eq_tail;
arith_proof_hint(hint_type t, unsigned lh, unsigned lt, unsigned eh, unsigned et):
m_ty(t), m_lit_head(lh), m_lit_tail(lt), m_eq_head(eh), m_eq_tail(et) {}
expr* get_hint(euf::solver& s) const override;
};
class arith_proof_hint_builder {
vector> m_literals;
svector> m_eqs;
hint_type m_ty;
unsigned m_lit_head = 0, m_lit_tail = 0, m_eq_head = 0, m_eq_tail = 0;
void reset() { m_lit_head = m_lit_tail; m_eq_head = m_eq_tail; }
void add(euf::enode* a, euf::enode* b, bool is_eq) {
if (m_eq_tail < m_eqs.size())
m_eqs[m_eq_tail] = { a, b, is_eq };
else
m_eqs.push_back({a, b, is_eq });
m_eq_tail++;
}
public:
void set_type(euf::solver& ctx, hint_type ty);
void add_eq(euf::enode* a, euf::enode* b) { add(a, b, true); }
void add_diseq(euf::enode* a, euf::enode* b) { add(a, b, false); }
void add_lit(rational const& coeff, literal lit) {
if (m_lit_tail < m_literals.size())
m_literals[m_lit_tail] = {coeff, lit};
else
m_literals.push_back({coeff, lit});
m_lit_tail++;
}
std::pair const& lit(unsigned i) const { return m_literals[i]; }
std::tuple const& eq(unsigned i) const { return m_eqs[i]; }
arith_proof_hint* mk(euf::solver& s);
};
class solver : public euf::th_euf_solver {
friend struct arith_proof_hint;
friend class sls;
struct scope {
unsigned m_bounds_lim;
unsigned m_asserted_qhead;
unsigned m_asserted_lim;
};
class resource_limit : public lp::lp_resource_limit {
solver& m_imp;
public:
resource_limit(solver& i) : m_imp(i) { }
bool get_cancel_flag() override { return !m_imp.m.inc(); }
};
struct var_value_eq {
solver& m_th;
var_value_eq(solver& th) :m_th(th) {}
bool operator()(theory_var v1, theory_var v2) const {
if (m_th.is_int(v1) != m_th.is_int(v2)) {
return false;
}
return m_th.is_eq(v1, v2);
}
};
struct var_value_hash {
solver& m_th;
var_value_hash(solver& th) :m_th(th) {}
unsigned operator()(theory_var v) const {
if (m_th.use_nra_model()) {
return m_th.is_int(v);
}
else {
return (unsigned)std::hash()(m_th.get_ivalue(v));
}
}
};
int_hashtable m_model_eqs;
bool m_new_eq = false;
// temporary values kept during internalization
struct internalize_state {
expr_ref_vector m_terms;
vector m_coeffs;
svector m_vars;
ptr_vector m_to_ensure_enode, m_to_ensure_var;
internalize_state(ast_manager& m) : m_terms(m) {}
void reset() {
m_terms.reset();
m_coeffs.reset();
m_vars.reset();
m_to_ensure_enode.reset();
m_to_ensure_var.reset();
}
};
scoped_ptr_vector m_internalize_states;
unsigned m_internalize_head = 0;
class scoped_internalize_state {
solver& m_imp;
internalize_state& m_st;
internalize_state& push_internalize(solver& i) {
if (i.m_internalize_head == i.m_internalize_states.size()) {
i.m_internalize_states.push_back(alloc(internalize_state, i.m));
}
internalize_state& st = *i.m_internalize_states[i.m_internalize_head++];
st.reset();
return st;
}
public:
scoped_internalize_state(solver& i) : m_imp(i), m_st(push_internalize(i)) {}
~scoped_internalize_state() { --m_imp.m_internalize_head; }
expr_ref_vector& terms() { return m_st.m_terms; }
vector& coeffs() { return m_st.m_coeffs; }
svector& vars() { return m_st.m_vars; }
ptr_vector& to_ensure_enode() { return m_st.m_to_ensure_enode; }
ptr_vector& to_ensure_var() { return m_st.m_to_ensure_var; }
void push(expr* e, rational c) { m_st.m_terms.push_back(e); m_st.m_coeffs.push_back(c); }
void set_back(unsigned i) {
if (terms().size() == i + 1) return;
terms()[i] = terms().back();
coeffs()[i] = coeffs().back();
terms().pop_back();
coeffs().pop_back();
}
};
sls m_local_search;
typedef vector> var_coeffs;
vector m_columns;
var_coeffs m_left_side; // constraint left side
lpvar m_one_var = UINT_MAX;
lpvar m_zero_var = UINT_MAX;
lpvar m_rone_var = UINT_MAX;
lpvar m_rzero_var = UINT_MAX;
enum constraint_source {
inequality_source,
equality_source,
definition_source,
null_source
};
svector m_constraint_sources;
svector m_inequalities; // asserted rows corresponding to inequality literals.
svector m_equalities; // asserted rows corresponding to equalities.
svector m_definitions; // asserted rows corresponding to definitions
svector> m_delayed_eqs;
literal_vector m_asserted;
expr* m_not_handled = nullptr;
ptr_vector m_underspecified;
ptr_vector m_idiv_terms;
ptr_vector m_bv_terms;
vector > m_use_list; // bounds where variables are used.
// attributes for incremental version:
u_map m_bool_var2bound;
vector m_bounds;
unsigned_vector m_unassigned_bounds;
unsigned_vector m_bounds_trail;
unsigned m_asserted_qhead = 0;
svector > m_assume_eq_candidates;
unsigned m_assume_eq_head = 0;
indexed_uint_set m_tmp_var_set;
unsigned m_num_conflicts = 0;
lp_api::stats m_stats;
svector m_scopes;
// non-linear arithmetic
scoped_ptr m_nla;
// integer arithmetic
scoped_ptr m_lia;
scoped_ptr m_solver;
resource_limit m_resource_limit;
lp_bounds m_new_bounds;
symbol m_farkas;
std_vector m_implied_bounds;
lp::lp_bound_propagator m_bp;
mutable vector> m_todo_terms;
// lemmas
lp::explanation m_explanation;
literal_vector m_core, m_core2;
vector m_coeffs;
svector m_eqs;
vector m_params;
nla::lemma m_lemma;
arith_util a;
bool is_int(theory_var v) const { return is_int(var2enode(v)); }
bool is_int(euf::enode* n) const { return a.is_int(n->get_expr()); }
bool is_real(theory_var v) const { return is_real(var2enode(v)); }
bool is_real(euf::enode* n) const { return a.is_real(n->get_expr()); }
bool is_bool(theory_var v) const { return is_bool(var2enode(v)); }
bool is_bool(euf::enode* n) const { return m.is_bool(n->get_expr()); }
// internalize
bool m_internalize_initialized { false };
void init_internalize();
lpvar add_const(int c, lpvar& var, bool is_int);
lpvar get_one(bool is_int);
lpvar get_zero(bool is_int);
void ensure_nla();
void found_unsupported(expr* n);
void found_underspecified(expr* n);
void linearize_term(expr* term, scoped_internalize_state& st);
void linearize_ineq(expr* lhs, expr* rhs, scoped_internalize_state& st);
void linearize(scoped_internalize_state& st);
void add_eq_constraint(lp::constraint_index index, enode* n1, enode* n2);
void add_ineq_constraint(lp::constraint_index index, literal lit);
void add_def_constraint(lp::constraint_index index);
void add_def_constraint(lp::constraint_index index, theory_var v);
void add_def_constraint_and_equality(lpvar vi, lp::lconstraint_kind kind, const rational& bound);
void internalize_args(app* t, bool force = false);
void ensure_arg_vars(app* t);
theory_var internalize_power(app* t, app* n, unsigned p);
theory_var internalize_mul(app* t);
theory_var internalize_numeral(app* t, rational const& v);
theory_var internalize_def(expr* term);
theory_var internalize_def(expr* term, scoped_internalize_state& st);
theory_var internalize_linearized_def(expr* term, scoped_internalize_state& st);
void init_left_side(scoped_internalize_state& st);
bool internalize_term(expr* term);
bool internalize_atom(expr* atom);
bool is_unit_var(scoped_internalize_state& st);
bool is_one(scoped_internalize_state& st);
bool is_zero(scoped_internalize_state& st);
expr* mk_sub(expr* a, expr* b);
enode* mk_enode(expr* e);
lpvar register_theory_var_in_lar_solver(theory_var v);
theory_var mk_evar(expr* e);
bool has_var(expr* e);
bool reflect(expr* n) const;
lpvar get_lpvar(theory_var v) const;
lp::lpvar get_column(theory_var v) const;
// axioms
void mk_div_axiom(expr* p, expr* q);
void mk_to_int_axiom(app* n);
void mk_abs_axiom(app* n);
void mk_is_int_axiom(expr* n);
void mk_idiv_mod_axioms(expr* p, expr* q);
void mk_rem_axiom(expr* dividend, expr* divisor);
void mk_bound_axioms(api_bound& b);
void mk_bound_axiom(api_bound& b1, api_bound& b2);
void mk_power0_axioms(app* t, app* n);
void mk_bv_axiom(app* n);
void flush_bound_axioms();
void add_farkas_clause(sat::literal l1, sat::literal l2);
// bounds
struct compare_bounds {
bool operator()(api_bound* a1, api_bound* a2) const { return a1->get_value() < a2->get_value(); }
};
typedef lp_bounds::iterator iterator;
lp_bounds::iterator first(
lp_api::bound_kind kind,
iterator it,
iterator end);
lp_bounds::iterator next_inf(
api_bound* a1,
lp_api::bound_kind kind,
iterator it,
iterator end,
bool& found_compatible);
lp_bounds::iterator next_sup(
api_bound* a1,
lp_api::bound_kind kind,
iterator it,
iterator end,
bool& found_compatible);
void propagate_eqs(lp::lpvar t, lp::constraint_index ci, lp::lconstraint_kind k, api_bound& b, rational const& value);
void propagate_basic_bounds(unsigned qhead);
void propagate_bounds_with_lp_solver();
void propagate_bound(literal lit, api_bound& b);
void propagate_lp_solver_bound(const lp::implied_bound& be);
void refine_bound(theory_var v, const lp::implied_bound& be);
literal is_bound_implied(lp::lconstraint_kind k, rational const& value, api_bound const& b) const;
void assert_bound(bool is_true, api_bound& b);
void mk_eq_axiom(bool is_eq, euf::th_eq const& eq);
void mk_diseq_axiom(theory_var v1, theory_var v2);
void assert_idiv_mod_axioms(theory_var u, theory_var v, theory_var w, rational const& r);
api_bound* mk_var_bound(sat::literal lit, theory_var v, lp_api::bound_kind bk, rational const& bound);
lp::lconstraint_kind bound2constraint_kind(bool is_int, lp_api::bound_kind bk, bool is_true);
void fixed_var_eh(theory_var v1, u_dependency* dep, rational const& bound);
bool set_upper_bound(lp::lpvar t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, false); }
bool set_lower_bound(lp::lpvar t, lp::constraint_index ci, rational const& v) { return set_bound(t, ci, v, true); }
bool set_bound(lp::lpvar tv, lp::constraint_index ci, rational const& v, bool is_lower);
typedef std::pair constraint_bound;
vector m_lower_terms;
vector m_upper_terms;
vector m_history;
bool can_get_value(theory_var v) const {
return is_registered_var(v) && m_model_is_initialized;
}
vector m_fixed_values;
map m_value2var;
struct undo_value;
void register_fixed_var(theory_var v, rational const& value);
// solving
void report_equality_of_fixed_vars(unsigned vi1, unsigned vi2);
void reserve_bounds(theory_var v);
void updt_unassigned_bounds(theory_var v, int inc);
void pop_core(unsigned n) override;
void push_core() override;
void del_bounds(unsigned old_size);
bool all_zeros(vector const& v) const;
bound_prop_mode propagation_mode() const;
bool is_registered_var(theory_var v) const;
void ensure_column(theory_var v);
lp::impq get_ivalue(theory_var v) const;
rational get_value(theory_var v) const;
void random_update();
bool assume_eqs();
bool delayed_assume_eqs();
bool is_eq(theory_var v1, theory_var v2);
bool use_nra_model();
bool include_func_interp(enode* n) const;
lbool make_feasible();
bool check_delayed_eqs();
lbool check_lia();
lbool check_nla();
bool check_bv_terms();
bool check_bv_term(app* n);
void add_lemmas();
void propagate_nla();
void add_equality(lpvar v, rational const& k, lp::explanation const& exp);
bool is_infeasible() const;
nlsat::anum const& nl_value(theory_var v, scoped_anum& r) const;
bool has_bound(lpvar vi, u_dependency*& ci, rational const& bound, bool is_lower);
bool has_lower_bound(lpvar vi, u_dependency*& ci, rational const& bound);
bool has_upper_bound(lpvar vi, u_dependency*& ci, rational const& bound);
/*
* Facility to put a small box around integer variables used in branch and bounds.
*/
struct bound_info {
rational m_offset;
unsigned m_range;
bound_info() {}
bound_info(rational const& o, unsigned r) :m_offset(o), m_range(r) {}
};
unsigned m_bounded_range_idx; // current size of bounded range.
literal m_bounded_range_lit; // current bounded range literal
expr_ref_vector m_bound_terms; // predicates used for bounds
expr_ref m_bound_predicate;
obj_map m_predicate2term;
obj_map m_term2bound_info;
bool m_model_is_initialized = false;
unsigned small_lemma_size() const { return get_config().m_arith_small_lemma_size; }
bool propagate_eqs() const { return get_config().m_arith_propagate_eqs && m_num_conflicts < get_config().m_arith_propagation_threshold; }
bool should_propagate() const { return bound_prop_mode::BP_NONE != propagation_mode(); }
bool should_refine_bounds() const { return bound_prop_mode::BP_REFINE == propagation_mode() && s().at_search_lvl(); }
unsigned init_range() const { return 5; }
unsigned max_range() const { return 20; }
void reset_evidence();
bool check_idiv_bounds();
bool is_bounded(expr* n);
app_ref mk_bound(lp::lar_term const& term, rational const& k, bool lower_bound);
app_ref mk_bound(lp::lar_term const& term, rational const& k, bool lower_bound, rational& offset, expr_ref& t);
literal mk_eq(lp::lar_term const& term, rational const& offset);
rational gcd_reduce(u_map& coeffs);
app_ref mk_term(lp::lar_term const& term, bool is_int);
app_ref coeffs2app(u_map const& coeffs, rational const& offset, bool is_int);
void term2coeffs(lp::lar_term const& term, u_map& coeffs, rational const& coeff);
void term2coeffs(lp::lar_term const& term, u_map& coeffs);
void get_infeasibility_explanation_and_set_conflict();
void set_conflict(hint_type ty);
void set_conflict_or_lemma(hint_type ty, literal_vector const& core, bool is_conflict);
void set_evidence(lp::constraint_index idx);
void assign(literal lit, literal_vector const& core, svector const& eqs, euf::th_proof_hint const* pma);
sat::literal mk_ineq_literal(nla::ineq const& ineq);
void false_case_of_check_nla(const nla::lemma& l);
void dbg_finalize_model(model& mdl);
arith_proof_hint_builder m_arith_hint;
arith_proof_hint const* explain(hint_type ty, sat::literal lit = sat::null_literal);
arith_proof_hint const* explain_implied_eq(lp::explanation const& e, euf::enode* a, euf::enode* b);
arith_proof_hint const* explain_trichotomy(sat::literal le, sat::literal ge, sat::literal eq);
arith_proof_hint const* explain_conflict(hint_type ty, sat::literal_vector const& core, euf::enode_pair_vector const& eqs);
void explain_assumptions(lp::explanation const& e);
bool validate_conflict();
public:
solver(euf::solver& ctx, theory_id id);
~solver() override;
bool is_external(bool_var v) override { return false; }
void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) override;
void asserted(literal l) override;
sat::check_result check() override;
void simplify() override {}
void init_search() override {}
std::ostream& display(std::ostream& out) const override;
std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override;
std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override;
void collect_statistics(statistics& st) const override;
euf::th_solver* clone(euf::solver& ctx) override;
bool use_diseqs() const override { return true; }
void new_eq_eh(euf::th_eq const& eq) override;
void new_diseq_eh(euf::th_eq const& de) override;
bool unit_propagate() override;
void init_model() override;
void finalize_model(model& mdl) override { DEBUG_CODE(dbg_finalize_model(mdl);); }
void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override;
bool add_dep(euf::enode* n, top_sort& dep) override;
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
void eq_internalized(euf::enode* n) override;
void apply_sort_cnstr(euf::enode* n, sort* s) override {}
bool is_shared(theory_var v) const override;
lbool get_phase(bool_var v) override;
bool include_func_interp(func_decl* f) const override;
bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_add(n->get_expr()); }
bool has_unhandled() const override { return m_not_handled != nullptr; }
void set_bool_search(sat::ddfw* ddfw) override { m_local_search.set(ddfw); }
// bounds and equality propagation callbacks
lp::lar_solver& lp() { return *m_solver; }
lp::lar_solver const& lp() const { return *m_solver; }
bool is_equal(theory_var x, theory_var y) const;
bool add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed);
void consume(rational const& v, lp::constraint_index j);
bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const;
bool get_value(euf::enode* n, expr_ref& val);
};
}