diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 341b3fb12..f11330d1b 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -259,9 +259,9 @@ public: bool is_uminus(expr const * n) const { return is_app_of(n, m_afid, OP_UMINUS); } bool is_mul(expr const * n) const { return is_app_of(n, m_afid, OP_MUL); } bool is_div(expr const * n) const { return is_app_of(n, m_afid, OP_DIV); } - bool is_div0(expr const * n) const { return is_app_of(n, m_afid, OP_DIV_0); } + //bool is_div0(expr const * n) const { return is_app_of(n, m_afid, OP_DIV_0); } bool is_idiv(expr const * n) const { return is_app_of(n, m_afid, OP_IDIV); } - bool is_idiv0(expr const * n) const { return is_app_of(n, m_afid, OP_IDIV_0); } + //bool is_idiv0(expr const * n) const { return is_app_of(n, m_afid, OP_IDIV_0); } bool is_mod(expr const * n) const { return is_app_of(n, m_afid, OP_MOD); } bool is_rem(expr const * n) const { return is_app_of(n, m_afid, OP_REM); } bool is_to_real(expr const * n) const { return is_app_of(n, m_afid, OP_TO_REAL); } @@ -535,3 +535,4 @@ inline app_ref operator>(app_ref const& x, app_ref const& y) { } #endif /* ARITH_DECL_PLUGIN_H_ */ + diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 15de24ec5..1b1be9b52 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1714,6 +1714,7 @@ ast * ast_manager::register_node_core(ast * n) { n->m_id = is_decl(n) ? m_decl_id_gen.mk() : m_expr_id_gen.mk(); + TRACE("ast", tout << "Object " << n->m_id << " was created.\n";); TRACE("mk_var_bug", tout << "mk_ast: " << n->m_id << "\n";); // increment reference counters diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 9c166eb63..744c8a235 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -800,58 +800,9 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu result = m_util.mk_numeral(div(v1, v2), is_int); return BR_DONE; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { - result = m_util.mk_idiv0(arg1); - return BR_REWRITE1; - } - expr_ref quot(m()); - if (divides(arg1, arg2, quot)) { - result = m_util.mk_mul(quot, m_util.mk_idiv(arg1, arg1)); - return BR_REWRITE2; - } return BR_FAILED; } -bool arith_rewriter::divides(expr* d, expr* n, expr_ref& quot) { - if (d == n) { - quot = m_util.mk_numeral(rational(1), m_util.is_int(d)); - return true; - } - if (m_util.is_mul(n)) { - expr_ref_vector muls(m()); - muls.push_back(n); - expr* n1, *n2; - rational r1, r2; - for (unsigned i = 0; i < muls.size(); ++i) { - if (m_util.is_mul(muls[i].get(), n1, n2)) { - muls[i] = n1; - muls.push_back(n2); - --i; - } - } - if (m_util.is_numeral(d, r1) && !r1.is_zero()) { - for (unsigned i = 0; i < muls.size(); ++i) { - if (m_util.is_numeral(muls[i].get(), r2) && (r2 / r1).is_int()) { - muls[i] = m_util.mk_numeral(r2 / r1, m_util.is_int(d)); - quot = m_util.mk_mul(muls.size(), muls.c_ptr()); - return true; - } - } - } - else { - for (unsigned i = 0; i < muls.size(); ++i) { - if (d == muls[i].get()) { - muls[i] = muls.back(); - muls.pop_back(); - quot = m_util.mk_mul(muls.size(), muls.c_ptr()); - return true; - } - } - } - } - return false; -} - br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(m().get_sort(arg1)); numeral v1, v2; diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h index 65fbfb43f..95668ea44 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -95,7 +95,6 @@ class arith_rewriter : public poly_rewriter { expr_ref neg_monomial(expr * e) const; expr * mk_sin_value(rational const & k); app * mk_sqrt(rational const & k); - bool divides(expr* d, expr* n, expr_ref& quot); public: arith_rewriter(ast_manager & m, params_ref const & p = params_ref()): diff --git a/src/math/polynomial/polynomial.h b/src/math/polynomial/polynomial.h index 1f6386f2b..374a51084 100644 --- a/src/math/polynomial/polynomial.h +++ b/src/math/polynomial/polynomial.h @@ -19,16 +19,17 @@ Notes: #ifndef POLYNOMIAL_H_ #define POLYNOMIAL_H_ -#include"util/mpz.h" -#include"util/rational.h" -#include"util/obj_ref.h" -#include"util/ref_vector.h" -#include"util/z3_exception.h" -#include"util/scoped_numeral.h" -#include"util/scoped_numeral_vector.h" -#include"util/params.h" -#include"util/mpbqi.h" -#include"util/rlimit.h" +#include "util/mpz.h" +#include "util/rational.h" +#include "util/obj_ref.h" +#include "util/ref_vector.h" +#include "util/z3_exception.h" +#include "util/scoped_numeral.h" +#include "util/scoped_numeral_vector.h" +#include "util/params.h" +#include "util/mpbqi.h" +#include "util/rlimit.h" +#include "util/lbool.h" class small_object_allocator; diff --git a/src/nlsat/nlsat_solver.h b/src/nlsat/nlsat_solver.h index 85a9adea7..4ba1225bd 100644 --- a/src/nlsat/nlsat_solver.h +++ b/src/nlsat/nlsat_solver.h @@ -21,10 +21,10 @@ Revision History: #ifndef NLSAT_SOLVER_H_ #define NLSAT_SOLVER_H_ -#include"nlsat/nlsat_types.h" -#include"util/params.h" -#include"util/statistics.h" -#include"util/rlimit.h" +#include "nlsat/nlsat_types.h" +#include "util/params.h" +#include "util/statistics.h" +#include "util/rlimit.h" namespace nlsat { diff --git a/src/nlsat/nlsat_types.h b/src/nlsat/nlsat_types.h index 1583da5be..647a5e3ee 100644 --- a/src/nlsat/nlsat_types.h +++ b/src/nlsat/nlsat_types.h @@ -19,10 +19,10 @@ Revision History: #ifndef NLSAT_TYPES_H_ #define NLSAT_TYPES_H_ -#include"math/polynomial/polynomial.h" -#include"util/buffer.h" -#include"sat/sat_types.h" -#include"util/z3_exception.h" +#include "math/polynomial/polynomial.h" +#include "util/buffer.h" +#include "sat/sat_types.h" +#include "util/z3_exception.h" namespace algebraic_numbers { class anum; diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index bd321ee6d..d15aba793 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -19,12 +19,12 @@ Revision History: #ifndef SAT_TYPES_H_ #define SAT_TYPES_H_ -#include"util/debug.h" -#include"util/approx_set.h" -#include"util/lbool.h" -#include"util/z3_exception.h" -#include"util/common_msgs.h" -#include"util/vector.h" +#include "util/debug.h" +#include "util/approx_set.h" +#include "util/lbool.h" +#include "util/z3_exception.h" +#include "util/common_msgs.h" +#include "util/vector.h" #include namespace sat { diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp index 9145f8adf..718f59854 100644 --- a/src/shell/lp_frontend.cpp +++ b/src/shell/lp_frontend.cpp @@ -17,7 +17,7 @@ Author: #include "util/gparams.h" #include -static lp::lp_solver* g_solver = 0; +static lp::lp_solver* g_solver = nullptr; static void display_statistics() { if (g_solver && g_solver->settings().print_statistics) { @@ -80,7 +80,8 @@ void run_solver(lp_params & params, char const * mps_file_name) { solver->settings().set_message_ostream(&std::cout); solver->settings().report_frequency = params.rep_freq(); solver->settings().print_statistics = params.print_stats(); - solver->settings().simplex_strategy() = lp::simplex_strategy_enum::lu; + solver->settings().simplex_strategy() = lp:: simplex_strategy_enum::lu; + solver->find_maximal_solution(); *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; diff --git a/src/shell/main.cpp b/src/shell/main.cpp index d367b8ec6..a0444a919 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -296,6 +296,9 @@ void parse_cmd_line_args(int argc, char ** argv) { int STD_CALL main(int argc, char ** argv) { try{ + DEBUG_CODE( for (int i = 0; i < argc; i++) + std::cout << argv[i] << " "; + std::cout << std::endl;); unsigned return_value = 0; memory::initialize(0); memory::exit_when_out_of_memory(true, "ERROR: out of memory"); diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index d2571de91..d55a50d4a 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -979,7 +979,7 @@ namespace smt { if (st.num_theories() == 2 && st.has_uf() && is_arith(st)) { if (!st.m_has_real) setup_QF_UFLIA(st); - else if (!st.m_has_int && st.m_num_non_linear == 0) + else if (!st.m_has_int) setup_QF_UFLRA(); else setup_unknown(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 1dccc11a4..47f5d3028 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1,23 +1,23 @@ /*++ -Copyright (c) 2016 Microsoft Corporation + Copyright (c) 2016 Microsoft Corporation -Module Name: + Module Name: - theory_lra.cpp + theory_lra.cpp -Abstract: + Abstract: - + -Author: + Author: - Lev Nachmanson (levnach) 2016-25-3 - Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) 2016-25-3 + Nikolaj Bjorner (nbjorner) -Revision History: + Revision History: ---*/ + --*/ #include "util/stopwatch.h" #include "util/lp/lp_solver.h" #include "util/lp/lp_primal_simplex.h" @@ -28,7 +28,6 @@ Revision History: #include "util/optional.h" #include "util/lp/lp_params.hpp" #include "util/inf_rational.h" -#include "ast/ast_pp.h" #include "smt/smt_theory.h" #include "smt/smt_context.h" #include "smt/theory_lra.h" @@ -40,2852 +39,2863 @@ Revision History: #include "tactic/filter_model_converter.h" #include "math/polynomial/algebraic_numbers.h" #include "math/polynomial/polynomial.h" +#include "ast/ast_pp.h" +#include "util/cancel_eh.h" +#include "util/scoped_timer.h" namespace lp_api { - enum bound_kind { lower_t, upper_t }; +enum bound_kind { lower_t, upper_t }; - std::ostream& operator<<(std::ostream& out, bound_kind const& k) { - switch (k) { - case lower_t: return out << "<="; - case upper_t: return out << ">="; - } - return out; +std::ostream& operator<<(std::ostream& out, bound_kind const& k) { + switch (k) { + case lower_t: return out << "<="; + case upper_t: return out << ">="; } + return out; +} - class bound { - smt::bool_var m_bv; - smt::theory_var m_var; - bool m_is_int; - rational m_value; - bound_kind m_bound_kind; +class bound { + smt::bool_var m_bv; + smt::theory_var m_var; + bool m_is_int; + rational m_value; + bound_kind m_bound_kind; - public: - bound(smt::bool_var bv, smt::theory_var v, bool is_int, rational const & val, bound_kind k): - m_bv(bv), - m_var(v), - m_is_int(is_int), - m_value(val), - m_bound_kind(k) { - } - virtual ~bound() {} - smt::theory_var get_var() const { return m_var; } - smt::bool_var get_bv() const { return m_bv; } - bound_kind get_bound_kind() const { return m_bound_kind; } - bool is_int() const { return m_is_int; } - rational const& get_value() const { return m_value; } - inf_rational get_value(bool is_true) const { - if (is_true) return inf_rational(m_value); // v >= value or v <= value - - if (m_is_int) { - SASSERT(m_value.is_int()); - if (m_bound_kind == lower_t) return inf_rational(m_value - rational::one()); // v <= value - 1 - return inf_rational(m_value + rational::one()); // v >= value + 1 - } - else { - if (m_bound_kind == lower_t) return inf_rational(m_value, false); // v <= value - epsilon - return inf_rational(m_value, true); // v >= value + epsilon - } - } - virtual std::ostream& display(std::ostream& out) const { - return out << m_value << " " << get_bound_kind() << " v" << get_var(); - } - }; - - std::ostream& operator<<(std::ostream& out, bound const& b) { - return b.display(out); +public: + bound(smt::bool_var bv, smt::theory_var v, bool is_int, rational const & val, bound_kind k): + m_bv(bv), + m_var(v), + m_is_int(is_int), + m_value(val), + m_bound_kind(k) { } - - struct stats { - unsigned m_assert_lower; - unsigned m_assert_upper; - unsigned m_add_rows; - unsigned m_bounds_propagations; - unsigned m_num_iterations; - unsigned m_num_iterations_with_no_progress; - unsigned m_need_to_solve_inf; - unsigned m_fixed_eqs; - unsigned m_conflicts; - unsigned m_bound_propagations1; - unsigned m_bound_propagations2; - unsigned m_assert_diseq; - unsigned m_gomory_cuts; - stats() { reset(); } - void reset() { - memset(this, 0, sizeof(*this)); + virtual ~bound() {} + smt::theory_var get_var() const { return m_var; } + smt::bool_var get_bv() const { return m_bv; } + bound_kind get_bound_kind() const { return m_bound_kind; } + bool is_int() const { return m_is_int; } + rational const& get_value() const { return m_value; } + inf_rational get_value(bool is_true) const { + if (is_true) return inf_rational(m_value); // v >= value or v <= value + if (m_is_int) { + SASSERT(m_value.is_int()); + if (m_bound_kind == lower_t) return inf_rational(m_value - rational::one()); // v <= value - 1 + return inf_rational(m_value + rational::one()); // v >= value + 1 } - }; + else { + if (m_bound_kind == lower_t) return inf_rational(m_value, false); // v <= value - epsilon + return inf_rational(m_value, true); // v >= value + epsilon + } + } + virtual std::ostream& display(std::ostream& out) const { + return out << m_value << " " << get_bound_kind() << " v" << get_var(); + } +}; - typedef optional opt_inf_rational; +std::ostream& operator<<(std::ostream& out, bound const& b) { + return b.display(out); +} + +struct stats { + unsigned m_assert_lower; + unsigned m_assert_upper; + unsigned m_add_rows; + unsigned m_bounds_propagations; + unsigned m_num_iterations; + unsigned m_num_iterations_with_no_progress; + unsigned m_need_to_solve_inf; + unsigned m_fixed_eqs; + unsigned m_conflicts; + unsigned m_bound_propagations1; + unsigned m_bound_propagations2; + unsigned m_assert_diseq; + unsigned m_gomory_cuts; + stats() { reset(); } + void reset() { + memset(this, 0, sizeof(*this)); + } +}; + +typedef optional opt_inf_rational; } namespace smt { - typedef ptr_vector lp_bounds; +typedef ptr_vector lp_bounds; - class theory_lra::imp { +class theory_lra::imp { - struct scope { - unsigned m_bounds_lim; - unsigned m_asserted_qhead; - unsigned m_asserted_atoms_lim; - unsigned m_underspecified_lim; - unsigned m_var_trail_lim; - expr* m_not_handled; - }; + struct scope { + unsigned m_bounds_lim; + unsigned m_asserted_qhead; + unsigned m_asserted_atoms_lim; + unsigned m_underspecified_lim; + unsigned m_var_trail_lim; + expr* m_not_handled; + }; - struct delayed_atom { - unsigned m_bv; - bool m_is_true; - delayed_atom(unsigned b, bool t): m_bv(b), m_is_true(t) {} - }; + struct delayed_atom { + unsigned m_bv; + bool m_is_true; + delayed_atom(unsigned b, bool t): m_bv(b), m_is_true(t) {} + }; - class resource_limit : public lp::lp_resource_limit { - imp& m_imp; - public: - resource_limit(imp& i): m_imp(i) { } - bool get_cancel_flag() override { return m_imp.m.canceled(); } - }; + class resource_limit : public lp::lp_resource_limit { + imp& m_imp; + public: + resource_limit(imp& i): m_imp(i) { } + virtual bool get_cancel_flag() { return m_imp.m.canceled(); } + }; - theory_lra& th; - ast_manager& m; - theory_arith_params& m_arith_params; - arith_util a; - arith_eq_adapter m_arith_eq_adapter; - vector m_columns; + theory_lra& th; + ast_manager& m; + theory_arith_params& m_arith_params; + arith_util a; + arith_eq_adapter m_arith_eq_adapter; + vector m_columns; - // temporary values kept during internalization - struct internalize_state { - expr_ref_vector m_terms; - vector m_coeffs; - svector m_vars; - rational m_coeff; - ptr_vector m_terms_to_internalize; - internalize_state(ast_manager& m): m_terms(m) {} - void reset() { - m_terms.reset(); - m_coeffs.reset(); - m_coeff.reset(); - m_vars.reset(); - m_terms_to_internalize.reset(); - } - }; - ptr_vector m_internalize_states; - unsigned m_internalize_head; - - class scoped_internalize_state { - imp& m_imp; - internalize_state& m_st; - - internalize_state& push_internalize(imp& 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(imp& 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; } - rational& coeff() { return m_st.m_coeff; } - ptr_vector& terms_to_internalize() { return m_st.m_terms_to_internalize; } - 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(); - } - }; - - typedef vector> var_coeffs; - - svector m_theory_var2var_index; // translate from theory variables to lar vars - svector m_var_index2theory_var; // reverse map from lp_solver variables to theory variables - svector m_term_index2theory_var; // reverse map from lp_solver variables to theory variables - var_coeffs m_left_side; // constraint left side - mutable std::unordered_map m_variable_values; // current model - - 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_asserted_atoms; - expr* m_not_handled; - ptr_vector m_underspecified; - unsigned_vector m_var_trail; - 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; - - svector m_to_check; // rows that should be checked for theory propagation - - svector > m_assume_eq_candidates; - unsigned m_assume_eq_head; - - unsigned m_num_conflicts; - - // non-linear arithmetic - scoped_ptr m_nra; - bool m_use_nra_model; - scoped_ptr m_a1, m_a2; - - // integer arithmetic - scoped_ptr m_lia; - - - struct var_value_eq { - imp & m_th; - var_value_eq(imp & 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 { - imp & m_th; - var_value_hash(imp & th):m_th(th) {} - unsigned operator()(theory_var v) const { - if (m_th.m_use_nra_model) { - return m_th.is_int(v); - } - else { - return (unsigned)std::hash()(m_th.get_ivalue(v)); - } - } - }; - int_hashtable m_model_eqs; - - - svector m_scopes; - lp_api::stats m_stats; - arith_factory* m_factory; - scoped_ptr m_solver; - resource_limit m_resource_limit; - lp_bounds m_new_bounds; - - - context& ctx() const { return th.get_context(); } - theory_id get_id() const { return th.get_id(); } - bool is_int(theory_var v) const { return is_int(get_enode(v)); } - bool is_int(enode* n) const { return a.is_int(n->get_owner()); } - enode* get_enode(theory_var v) const { return th.get_enode(v); } - enode* get_enode(expr* e) const { return ctx().get_enode(e); } - expr* get_owner(theory_var v) const { return get_enode(v)->get_owner(); } - - void init_solver() { - if (m_solver) return; - lp_params lp(ctx().get_params()); - m_solver = alloc(lp::lar_solver); - m_theory_var2var_index.reset(); - m_solver->settings().set_resource_limit(m_resource_limit); - m_solver->settings().simplex_strategy() = static_cast(lp.simplex_strategy()); - reset_variable_values(); - m_solver->settings().bound_propagation() = BP_NONE != propagation_mode(); - m_solver->set_track_pivoted_rows(lp.bprop_on_pivoted_rows()); - m_solver->settings().m_int_branch_cut_gomory_threshold = ctx().get_fparams().m_arith_branch_cut_ratio; - m_solver->settings().m_run_gcd_test = ctx().get_fparams().m_arith_gcd_test; - m_solver->settings().set_random_seed(ctx().get_fparams().m_random_seed); - //m_solver->settings().set_ostream(0); - m_lia = alloc(lp::int_solver, m_solver.get()); + // temporary values kept during internalization + struct internalize_state { + expr_ref_vector m_terms; + vector m_coeffs; + svector m_vars; + rational m_coeff; + ptr_vector m_terms_to_internalize; + internalize_state(ast_manager& m): m_terms(m) {} + void reset() { + m_terms.reset(); + m_coeffs.reset(); + m_coeff.reset(); + m_vars.reset(); + m_terms_to_internalize.reset(); } + }; + ptr_vector m_internalize_states; + unsigned m_internalize_head; - void ensure_nra() { - if (!m_nra) { - m_nra = alloc(nra::solver, *m_solver.get(), m.limit(), ctx().get_params()); - for (unsigned i = 0; i < m_scopes.size(); ++i) { - m_nra->push(); - } - } - } + class scoped_internalize_state { + imp& m_imp; + internalize_state& m_st; - void found_not_handled(expr* n) { - if (a.is_div0(n)) { - return; - } - m_not_handled = n; - if (is_app(n) && is_underspecified(to_app(n))) { - TRACE("arith", tout << "Unhandled: " << mk_pp(n, m) << "\n";); - m_underspecified.push_back(to_app(n)); + internalize_state& push_internalize(imp& 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; } - - bool is_numeral(expr* term, rational& r) { - rational mul(1); - do { - if (a.is_numeral(term, r)) { - r *= mul; - return true; - } - if (a.is_uminus(term, term)) { - mul.neg(); - continue; - } - if (a.is_to_real(term, term)) { - continue; - } - return false; - } - while (false); - return false; - } - - void linearize_term(expr* term, scoped_internalize_state& st) { - st.push(term, rational::one()); - linearize(st); - } - - void linearize_ineq(expr* lhs, expr* rhs, scoped_internalize_state& st) { - st.push(lhs, rational::one()); - st.push(rhs, rational::minus_one()); - linearize(st); - } - - void linearize(scoped_internalize_state& st) { - expr_ref_vector & terms = st.terms(); - svector& vars = st.vars(); - vector& coeffs = st.coeffs(); - rational& coeff = st.coeff(); - rational r; - expr* n1, *n2; - unsigned index = 0; - while (index < terms.size()) { - SASSERT(index >= vars.size()); - expr* n = terms[index].get(); - st.terms_to_internalize().push_back(n); - if (a.is_add(n)) { - unsigned sz = to_app(n)->get_num_args(); - for (unsigned i = 0; i < sz; ++i) { - st.push(to_app(n)->get_arg(i), coeffs[index]); - } - st.set_back(index); - } - else if (a.is_sub(n)) { - unsigned sz = to_app(n)->get_num_args(); - terms[index] = to_app(n)->get_arg(0); - for (unsigned i = 1; i < sz; ++i) { - st.push(to_app(n)->get_arg(i), -coeffs[index]); - } - } - else if (a.is_mul(n, n1, n2) && is_numeral(n1, r)) { - coeffs[index] *= r; - terms[index] = n2; - st.terms_to_internalize().push_back(n1); - } - else if (a.is_mul(n, n1, n2) && is_numeral(n2, r)) { - coeffs[index] *= r; - terms[index] = n1; - st.terms_to_internalize().push_back(n2); - } - else if (a.is_mul(n)) { - theory_var v; - internalize_mul(to_app(n), v, r); - coeffs[index] *= r; - coeffs[vars.size()] = coeffs[index]; - vars.push_back(v); - ++index; - } - else if (a.is_numeral(n, r)) { - coeff += coeffs[index]*r; - ++index; - } - else if (a.is_uminus(n, n1)) { - coeffs[index].neg(); - terms[index] = n1; - } - else if (is_app(n) && a.get_family_id() == to_app(n)->get_family_id()) { - app* t = to_app(n); - if (a.is_div(n, n1, n2) && is_numeral(n2, r)) { - // skip - } - else { - found_not_handled(n); - } - internalize_args(t); - mk_enode(t); - theory_var v = mk_var(n); - coeffs[vars.size()] = coeffs[index]; - vars.push_back(v); - ++index; - } - else { - if (is_app(n)) { - internalize_args(to_app(n)); - } - if (a.is_int(n)) { - found_not_handled(n); - } - theory_var v = mk_var(n); - coeffs[vars.size()] = coeffs[index]; - vars.push_back(v); - ++index; - } - } - for (unsigned i = st.terms_to_internalize().size(); i > 0; ) { - --i; - expr* n = st.terms_to_internalize()[i]; - if (is_app(n)) { - mk_enode(to_app(n)); - } - } - st.terms_to_internalize().reset(); - } - - void internalize_args(app* t) { - for (unsigned i = 0; reflect(t) && i < t->get_num_args(); ++i) { - if (!ctx().e_internalized(t->get_arg(i))) { - ctx().internalize(t->get_arg(i), false); - } - } - } - - void internalize_mul(app* t, theory_var& v, rational& r) { - SASSERT(a.is_mul(t)); - bool _has_var = has_var(t); - if (!_has_var) { - internalize_args(t); - mk_enode(t); - } - r = rational::one(); - rational r1; - v = mk_var(t); - svector vars; - ptr_vector todo; - todo.push_back(t); - while (!todo.empty()) { - expr* n = todo.back(); - todo.pop_back(); - expr* n1, *n2; - if (a.is_mul(n, n1, n2)) { - todo.push_back(n1); - todo.push_back(n2); - } - else if (a.is_numeral(n, r1)) { - r *= r1; - } - else { - if (!ctx().e_internalized(n)) { - ctx().internalize(n, false); - } - vars.push_back(get_var_index(mk_var(n))); - } - } - TRACE("arith", tout << mk_pp(t, m) << "\n";); - if (!_has_var) { - ensure_nra(); - m_nra->add_monomial(get_var_index(v), vars.size(), vars.c_ptr()); - } - } - - enode * mk_enode(app * n) { - if (ctx().e_internalized(n)) { - return get_enode(n); - } - else { - return ctx().mk_enode(n, !reflect(n), false, enable_cgc_for(n)); - } - } - - bool enable_cgc_for(app * n) const { - // Congruence closure is not enabled for (+ ...) and (* ...) applications. - return !(n->get_family_id() == get_id() && (n->get_decl_kind() == OP_ADD || n->get_decl_kind() == OP_MUL)); - } - - - void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) { - TRACE("arith", literal lits[2]; lits[0] = l1; lits[1] = l2; ctx().display_literals_verbose(tout, 2, lits); tout << "\n";); - ctx().mk_th_axiom(get_id(), l1, l2, num_params, params); - } - - void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { - TRACE("arith", literal lits[3]; lits[0] = l1; lits[1] = l2; lits[2] = l3; ctx().display_literals_verbose(tout, 3, lits); tout << "\n";); - ctx().mk_th_axiom(get_id(), l1, l2, l3, num_params, params); - } - - bool is_underspecified(app* n) const { - if (n->get_family_id() == get_id()) { - switch (n->get_decl_kind()) { - case OP_DIV: - case OP_IDIV: - case OP_REM: - case OP_MOD: - return true; - default: - break; - } - } - return false; - } - - bool reflect(app* n) const { - return m_arith_params.m_arith_reflect || is_underspecified(n); - } - - bool has_var(expr* n) { - if (!ctx().e_internalized(n)) { - return false; - } - enode* e = get_enode(n); - return th.is_attached_to_var(e); - } - - theory_var mk_var(expr* n, bool internalize = true) { - if (!ctx().e_internalized(n)) { - ctx().internalize(n, false); - } - enode* e = get_enode(n); - theory_var v; - if (!th.is_attached_to_var(e)) { - v = th.mk_var(e); - SASSERT(m_bounds.size() <= static_cast(v) || m_bounds[v].empty()); - if (m_bounds.size() <= static_cast(v)) { - m_bounds.push_back(lp_bounds()); - m_unassigned_bounds.push_back(0); - } - ctx().attach_th_var(e, &th, v); - } - else { - v = e->get_th_var(get_id()); - } - SASSERT(null_theory_var != v); - return v; - } - - lp::var_index get_var_index(theory_var v) { - lp::var_index result = UINT_MAX; - if (m_theory_var2var_index.size() > static_cast(v)) { - result = m_theory_var2var_index[v]; - } - if (result == UINT_MAX) { - result = m_solver->add_var(v, is_int(v)); - m_theory_var2var_index.setx(v, result, UINT_MAX); - m_var_index2theory_var.setx(result, v, UINT_MAX); - m_var_trail.push_back(v); - } - return result; - } - - void init_left_side(scoped_internalize_state& st) { - SASSERT(all_zeros(m_columns)); - svector const& vars = st.vars(); - vector const& coeffs = st.coeffs(); - for (unsigned i = 0; i < vars.size(); ++i) { - theory_var var = vars[i]; - rational const& coeff = coeffs[i]; - if (m_columns.size() <= static_cast(var)) { - m_columns.setx(var, coeff, rational::zero()); - } - else { - m_columns[var] += coeff; - } - } - m_left_side.clear(); - // reset the coefficients after they have been used. - for (unsigned i = 0; i < vars.size(); ++i) { - theory_var var = vars[i]; - rational const& r = m_columns[var]; - if (!r.is_zero()) { - m_left_side.push_back(std::make_pair(r, get_var_index(var))); - m_columns[var].reset(); - } - } - SASSERT(all_zeros(m_columns)); - } - - bool all_zeros(vector const& v) const { - for (rational const& r : v) { - if (!r.is_zero()) { - return false; - } - } - return true; - } - - void add_eq_constraint(lp::constraint_index index, enode* n1, enode* n2) { - m_constraint_sources.setx(index, equality_source, null_source); - m_equalities.setx(index, enode_pair(n1, n2), enode_pair(0, 0)); - ++m_stats.m_add_rows; - } - - void add_ineq_constraint(lp::constraint_index index, literal lit) { - m_constraint_sources.setx(index, inequality_source, null_source); - m_inequalities.setx(index, lit, null_literal); - ++m_stats.m_add_rows; - TRACE("arith", m_solver->print_constraint(index, tout); tout << "\n";); - } - - void add_def_constraint(lp::constraint_index index, theory_var v) { - m_constraint_sources.setx(index, definition_source, null_source); - m_definitions.setx(index, v, null_theory_var); - ++m_stats.m_add_rows; - } - - void internalize_eq(theory_var v1, theory_var v2) { - enode* n1 = get_enode(v1); - enode* n2 = get_enode(v2); - scoped_internalize_state st(*this); - st.vars().push_back(v1); - st.vars().push_back(v2); - st.coeffs().push_back(rational::one()); - st.coeffs().push_back(rational::minus_one()); - init_left_side(st); - add_eq_constraint(m_solver->add_constraint(m_left_side, lp::EQ, rational::zero()), n1, n2); - TRACE("arith", - tout << "v" << v1 << " = " << "v" << v2 << ": " - << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n";); - } - - void del_bounds(unsigned old_size) { - for (unsigned i = m_bounds_trail.size(); i > old_size; ) { - --i; - unsigned v = m_bounds_trail[i]; - lp_api::bound* b = m_bounds[v].back(); - // del_use_lists(b); - dealloc(b); - m_bounds[v].pop_back(); - } - m_bounds_trail.shrink(old_size); - } - - void updt_unassigned_bounds(theory_var v, int inc) { - TRACE("arith", tout << "v" << v << " " << m_unassigned_bounds[v] << " += " << inc << "\n";); - ctx().push_trail(vector_value_trail(m_unassigned_bounds, v)); - m_unassigned_bounds[v] += inc; - } - - bool is_unit_var(scoped_internalize_state& st) { - return st.coeff().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one(); - } - - theory_var internalize_def(app* term, scoped_internalize_state& st) { - linearize_term(term, st); - if (is_unit_var(st)) { - return st.vars()[0]; - } - else { - theory_var v = mk_var(term); - SASSERT(null_theory_var != v); - st.coeffs().resize(st.vars().size() + 1); - st.coeffs()[st.vars().size()] = rational::minus_one(); - st.vars().push_back(v); - return v; - } - } - - // term - v = 0 - theory_var internalize_def(app* term) { - scoped_internalize_state st(*this); - linearize_term(term, st); - if (is_unit_var(st)) { - return st.vars()[0]; - } - else { - init_left_side(st); - theory_var v = mk_var(term); - lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); - if (vi == UINT_MAX) { - vi = m_solver->add_term(m_left_side, st.coeff()); - m_theory_var2var_index.setx(v, vi, UINT_MAX); - if (m_solver->is_term(vi)) { - m_term_index2theory_var.setx(m_solver->adjust_term_index(vi), v, UINT_MAX); - } - else { - m_var_index2theory_var.setx(vi, v, UINT_MAX); - } - m_var_trail.push_back(v); - TRACE("arith_verbose", tout << "v" << v << " := " << mk_pp(term, m) << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; - m_solver->print_term(m_solver->get_term(vi), tout); tout << "\n";); - } - rational val; - if (a.is_numeral(term, val)) { - m_fixed_var_table.insert(value_sort_pair(val, is_int(v)), v); - } - return v; - } - } - - public: - imp(theory_lra& th, ast_manager& m, theory_arith_params& ap): - th(th), m(m), - m_arith_params(ap), - a(m), - m_arith_eq_adapter(th, ap, a), - m_internalize_head(0), - m_not_handled(0), - m_asserted_qhead(0), - m_assume_eq_head(0), - m_num_conflicts(0), - m_use_nra_model(false), - m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), - m_solver(0), - m_resource_limit(*this) { + scoped_internalize_state(imp& 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; } + rational& coeff() { return m_st.m_coeff; } + ptr_vector& terms_to_internalize() { return m_st.m_terms_to_internalize; } + 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(); } + }; + + typedef vector> var_coeffs; - ~imp() { - del_bounds(0); - std::for_each(m_internalize_states.begin(), m_internalize_states.end(), delete_proc()); - } + svector m_theory_var2var_index; // translate from theory variables to lar vars + svector m_var_index2theory_var; // reverse map from lp_solver variables to theory variables + svector m_term_index2theory_var; // reverse map from lp_solver variables to theory variables + var_coeffs m_left_side; // constraint left side + mutable std::unordered_map m_variable_values; // current model - void init(context* ctx) { - init_solver(); - } + 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 - bool internalize_atom(app * atom, bool gate_ctx) { - return internalize_atom_strict(atom, gate_ctx); - - } + svector m_asserted_atoms; + expr* m_not_handled; + ptr_vector m_underspecified; + unsigned_vector m_var_trail; + vector > m_use_list; // bounds where variables are used. - bool internalize_atom_strict(app * atom, bool gate_ctx) { - SASSERT(!ctx().b_internalized(atom)); - bool_var bv = ctx().mk_bool_var(atom); - ctx().set_var_theory(bv, get_id()); - expr* n1 = nullptr, *n2 = nullptr; - rational r; - lp_api::bound_kind k; - theory_var v = null_theory_var; - if (a.is_le(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { - v = internalize_def(to_app(n1)); - k = lp_api::upper_t; + // 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; + + svector m_to_check; // rows that should be checked for theory propagation + + svector > m_assume_eq_candidates; + unsigned m_assume_eq_head; + + unsigned m_num_conflicts; + + // non-linear arithmetic + scoped_ptr m_nra; + bool m_use_nra_model; + scoped_ptr m_a1, m_a2; + + // integer arithmetic + scoped_ptr m_lia; + + + struct var_value_eq { + imp & m_th; + var_value_eq(imp & 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 { + imp & m_th; + var_value_hash(imp & th):m_th(th) {} + unsigned operator()(theory_var v) const { + if (m_th.m_use_nra_model) { + return m_th.is_int(v); } - else if (a.is_ge(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { - v = internalize_def(to_app(n1)); - k = lp_api::lower_t; - } else { - TRACE("arith", tout << "Could not internalize " << mk_pp(atom, m) << "\n";); - found_not_handled(atom); + return (unsigned)std::hash()(m_th.get_ivalue(v)); + } + } + }; + int_hashtable m_model_eqs; + + + svector m_scopes; + lp_api::stats m_stats; + arith_factory* m_factory; + scoped_ptr m_solver; + resource_limit m_resource_limit; + lp_bounds m_new_bounds; + + + context& ctx() const { return th.get_context(); } + theory_id get_id() const { return th.get_id(); } + bool is_int(theory_var v) const { return is_int(get_enode(v)); } + bool is_int(enode* n) const { return a.is_int(n->get_owner()); } + enode* get_enode(theory_var v) const { return th.get_enode(v); } + enode* get_enode(expr* e) const { return ctx().get_enode(e); } + expr* get_owner(theory_var v) const { return get_enode(v)->get_owner(); } + + void init_solver() { + if (m_solver) return; + lp_params lp(ctx().get_params()); + m_solver = alloc(lp::lar_solver); + m_theory_var2var_index.reset(); + m_solver->settings().set_resource_limit(m_resource_limit); + m_solver->settings().simplex_strategy() = static_cast(lp.simplex_strategy()); + reset_variable_values(); + m_solver->settings().bound_propagation() = BP_NONE != propagation_mode(); + m_solver->set_track_pivoted_rows(lp.bprop_on_pivoted_rows()); + m_solver->settings().m_int_branch_cut_gomory_threshold = ctx().get_fparams().m_arith_branch_cut_ratio; + m_solver->settings().m_int_branch_cut_solver = std::max(4u, ctx().get_fparams().m_arith_branch_cut_ratio); + m_solver->settings().m_run_gcd_test = ctx().get_fparams().m_arith_gcd_test; + m_solver->settings().set_random_seed(ctx().get_fparams().m_random_seed); + //m_solver->settings().set_ostream(0); + m_lia = alloc(lp::int_solver, m_solver.get()); + } + + void ensure_nra() { + if (!m_nra) { + m_nra = alloc(nra::solver, *m_solver.get(), m.limit(), ctx().get_params()); + for (unsigned i = 0; i < m_scopes.size(); ++i) { + m_nra->push(); + } + } + } + + + void found_not_handled(expr* n) { + m_not_handled = n; + if (is_app(n) && is_underspecified(to_app(n))) { + TRACE("arith", tout << "Unhandled: " << mk_pp(n, m) << "\n";); + m_underspecified.push_back(to_app(n)); + } + } + + bool is_numeral(expr* term, rational& r) { + rational mul(1); + do { + if (a.is_numeral(term, r)) { + r *= mul; return true; } - lp_api::bound* b = alloc(lp_api::bound, bv, v, is_int(v), r, k); - m_bounds[v].push_back(b); - updt_unassigned_bounds(v, +1); - m_bounds_trail.push_back(v); - m_bool_var2bound.insert(bv, b); - TRACE("arith_verbose", tout << "Internalized " << mk_pp(atom, m) << "\n";); - mk_bound_axioms(*b); - //add_use_lists(b); - return true; - } - - bool internalize_term(app * term) { - if (ctx().e_internalized(term) && th.is_attached_to_var(ctx().get_enode(term))) { - // skip - } - else { - internalize_def(term); - } - return true; - } - - void internalize_eq_eh(app * atom, bool_var) { - expr* lhs = nullptr, *rhs = nullptr; - VERIFY(m.is_eq(atom, lhs, rhs)); - enode * n1 = get_enode(lhs); - enode * n2 = get_enode(rhs); - if (n1->get_th_var(get_id()) != null_theory_var && - n2->get_th_var(get_id()) != null_theory_var && - n1 != n2) { - TRACE("arith_verbose", tout << mk_pp(atom, m) << "\n";); - m_arith_eq_adapter.mk_axioms(n1, n2); - } - } - - void assign_eh(bool_var v, bool is_true) { - TRACE("arith", tout << mk_pp(ctx().bool_var2expr(v), m) << " " << (is_true?"true":"false") << "\n";); - m_asserted_atoms.push_back(delayed_atom(v, is_true)); - } - - void new_eq_eh(theory_var v1, theory_var v2) { - // or internalize_eq(v1, v2); - m_arith_eq_adapter.new_eq_eh(v1, v2); - } - - bool use_diseqs() const { - return true; - } - - void new_diseq_eh(theory_var v1, theory_var v2) { - TRACE("arith", tout << "v" << v1 << " != " << "v" << v2 << "\n";); - ++m_stats.m_assert_diseq; - m_arith_eq_adapter.new_diseq_eh(v1, v2); - } - - void push_scope_eh() { - m_scopes.push_back(scope()); - scope& s = m_scopes.back(); - s.m_bounds_lim = m_bounds_trail.size(); - s.m_asserted_qhead = m_asserted_qhead; - s.m_asserted_atoms_lim = m_asserted_atoms.size(); - s.m_not_handled = m_not_handled; - s.m_underspecified_lim = m_underspecified.size(); - s.m_var_trail_lim = m_var_trail.size(); - m_solver->push(); - if (m_nra) m_nra->push(); - } - - void pop_scope_eh(unsigned num_scopes) { - if (num_scopes == 0) { - return; - } - unsigned old_size = m_scopes.size() - num_scopes; - del_bounds(m_scopes[old_size].m_bounds_lim); - for (unsigned i = m_scopes[old_size].m_var_trail_lim; i < m_var_trail.size(); ++i) { - lp::var_index vi = m_theory_var2var_index[m_var_trail[i]]; - if (m_solver->is_term(vi)) { - unsigned ti = m_solver->adjust_term_index(vi); - m_term_index2theory_var[ti] = UINT_MAX; - } - else if (vi < m_var_index2theory_var.size()) { - m_var_index2theory_var[vi] = UINT_MAX; - } - m_theory_var2var_index[m_var_trail[i]] = UINT_MAX; - } - m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim); - m_asserted_qhead = m_scopes[old_size].m_asserted_qhead; - m_underspecified.shrink(m_scopes[old_size].m_underspecified_lim); - m_var_trail.shrink(m_scopes[old_size].m_var_trail_lim); - m_not_handled = m_scopes[old_size].m_not_handled; - m_scopes.resize(old_size); - m_solver->pop(num_scopes); - // VERIFY(l_false != make_feasible()); - m_new_bounds.reset(); - m_to_check.reset(); - if (m_nra) m_nra->pop(num_scopes); - TRACE("arith", tout << "num scopes: " << num_scopes << " new scope level: " << m_scopes.size() << "\n";); - } - - void restart_eh() { - m_arith_eq_adapter.restart_eh(); - } - - void relevant_eh(app* n) { - TRACE("arith", tout << mk_pp(n, m) << "\n";); - expr* n1 = nullptr, *n2 = nullptr; - if (a.is_mod(n, n1, n2)) - mk_idiv_mod_axioms(n1, n2); - else if (a.is_rem(n, n1, n2)) - mk_rem_axiom(n1, n2); - else if (a.is_div(n, n1, n2)) - mk_div_axiom(n1, n2); - else if (a.is_to_int(n)) - mk_to_int_axiom(n); - else if (a.is_is_int(n)) - mk_is_int_axiom(n); - } - - // n < 0 || rem(a, n) = mod(a, n) - // !n < 0 || rem(a, n) = -mod(a, n) - void mk_rem_axiom(expr* dividend, expr* divisor) { - expr_ref zero(a.mk_int(0), m); - expr_ref rem(a.mk_rem(dividend, divisor), m); - expr_ref mod(a.mk_mod(dividend, divisor), m); - expr_ref mmod(a.mk_uminus(mod), m); - literal dgez = mk_literal(a.mk_ge(divisor, zero)); - mk_axiom(~dgez, th.mk_eq(rem, mod, false)); - mk_axiom( dgez, th.mk_eq(rem, mmod, false)); - } - - // q = 0 or q * (p div q) = p - void mk_div_axiom(expr* p, expr* q) { - if (a.is_zero(q)) return; - literal eqz = th.mk_eq(q, a.mk_real(0), false); - literal eq = th.mk_eq(a.mk_mul(q, a.mk_div(p, q)), p, false); - mk_axiom(eqz, eq); - } - - // to_int (to_real x) = x - // to_real(to_int(x)) <= x < to_real(to_int(x)) + 1 - void mk_to_int_axiom(app* n) { - expr* x = nullptr, *y = nullptr; - VERIFY (a.is_to_int(n, x)); - if (a.is_to_real(x, y)) { - mk_axiom(th.mk_eq(y, n, false)); - } - else { - expr_ref to_r(a.mk_to_real(n), m); - expr_ref lo(a.mk_le(a.mk_sub(to_r, x), a.mk_real(0)), m); - expr_ref hi(a.mk_ge(a.mk_sub(x, to_r), a.mk_real(1)), m); - mk_axiom(mk_literal(lo)); - mk_axiom(~mk_literal(hi)); - } - } - - // is_int(x) <=> to_real(to_int(x)) = x - void mk_is_int_axiom(app* n) { - expr* x = nullptr; - VERIFY(a.is_is_int(n, x)); - literal eq = th.mk_eq(a.mk_to_real(a.mk_to_int(x)), x, false); - literal is_int = ctx().get_literal(n); - mk_axiom(~is_int, eq); - mk_axiom(is_int, ~eq); - } - - void mk_idiv_mod_axioms(expr * p, expr * q) { - if (a.is_zero(q)) { - return; - } - // if q is zero, then idiv and mod are uninterpreted functions. - expr_ref div(a.mk_idiv(p, q), m); - expr_ref mod(a.mk_mod(p, q), m); - expr_ref zero(a.mk_int(0), m); - literal q_ge_0 = mk_literal(a.mk_ge(q, zero)); - literal q_le_0 = mk_literal(a.mk_le(q, zero)); - // literal eqz = th.mk_eq(q, zero, false); - literal eq = th.mk_eq(a.mk_add(a.mk_mul(q, div), mod), p, false); - literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero)); - // q >= 0 or p = (p mod q) + q * (p div q) - // q <= 0 or p = (p mod q) + q * (p div q) - // q >= 0 or (p mod q) >= 0 - // q <= 0 or (p mod q) >= 0 - // q <= 0 or (p mod q) < q - // q >= 0 or (p mod q) < -q - mk_axiom(q_ge_0, eq); - mk_axiom(q_le_0, eq); - mk_axiom(q_ge_0, mod_ge_0); - mk_axiom(q_le_0, mod_ge_0); - mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero))); - mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero))); - rational k; - if (m_arith_params.m_arith_enum_const_mod && a.is_numeral(q, k) && - k.is_pos() && k < rational(8)) { - unsigned _k = k.get_unsigned(); - literal_buffer lits; - for (unsigned j = 0; j < _k; ++j) { - literal mod_j = th.mk_eq(mod, a.mk_int(j), false); - lits.push_back(mod_j); - ctx().mark_as_relevant(mod_j); - } - ctx().mk_th_axiom(get_id(), lits.size(), lits.begin()); - } - } - - void mk_axiom(literal l) { - ctx().mk_th_axiom(get_id(), false_literal, l); - if (ctx().relevancy()) { - ctx().mark_as_relevant(l); - } - } - - void mk_axiom(literal l1, literal l2) { - if (l1 == false_literal) { - mk_axiom(l2); - return; - } - ctx().mk_th_axiom(get_id(), l1, l2); - if (ctx().relevancy()) { - ctx().mark_as_relevant(l1); - expr_ref e(m); - ctx().literal2expr(l2, e); - ctx().add_rel_watch(~l1, e); - } - } - - void mk_axiom(literal l1, literal l2, literal l3) { - ctx().mk_th_axiom(get_id(), l1, l2, l3); - if (ctx().relevancy()) { - expr_ref e(m); - ctx().mark_as_relevant(l1); - ctx().literal2expr(l2, e); - ctx().add_rel_watch(~l1, e); - ctx().literal2expr(l3, e); - ctx().add_rel_watch(~l2, e); - } - } - - literal mk_literal(expr* e) { - expr_ref pinned(e, m); - if (!ctx().e_internalized(e)) { - ctx().internalize(e, false); - } - return ctx().get_literal(e); - } - - - void init_search_eh() { - m_arith_eq_adapter.init_search_eh(); - m_num_conflicts = 0; - } - - bool can_get_value(theory_var v) const { - return - (v != null_theory_var) && - (v < static_cast(m_theory_var2var_index.size())) && - (UINT_MAX != m_theory_var2var_index[v]) && - (m_solver->is_term(m_theory_var2var_index[v]) || m_variable_values.count(m_theory_var2var_index[v]) > 0); - } - - - bool can_get_ivalue(theory_var v) const { - if (v == null_theory_var || (v >= static_cast(m_theory_var2var_index.size()))) - return false; - return m_solver->var_is_registered(m_theory_var2var_index[v]); - } - - mutable vector> m_todo_terms; - - lp::impq get_ivalue(theory_var v) const { - lp_assert(can_get_ivalue(v)); - lp::var_index vi = m_theory_var2var_index[v]; - if (!m_solver->is_term(vi)) - return m_solver->get_column_value(vi); - m_todo_terms.push_back(std::make_pair(vi, rational::one())); - lp::impq result(0); - while (!m_todo_terms.empty()) { - vi = m_todo_terms.back().first; - rational coeff = m_todo_terms.back().second; - m_todo_terms.pop_back(); - if (m_solver->is_term(vi)) { - const lp::lar_term& term = m_solver->get_term(vi); - result += term.m_v * coeff; - for (const auto & i: term.m_coeffs) { - m_todo_terms.push_back(std::make_pair(i.first, coeff * i.second)); - } - } - else { - result += m_solver->get_column_value(vi) * coeff; - } - } - return result; - } - - rational get_value(theory_var v) const { - if (!can_get_value(v)) return rational::zero(); - lp::var_index vi = m_theory_var2var_index[v]; - if (m_variable_values.count(vi) > 0) { - return m_variable_values[vi]; - } - m_todo_terms.push_back(std::make_pair(vi, rational::one())); - rational result(0); - while (!m_todo_terms.empty()) { - lp::var_index wi = m_todo_terms.back().first; - rational coeff = m_todo_terms.back().second; - m_todo_terms.pop_back(); - if (m_solver->is_term(wi)) { - const lp::lar_term& term = m_solver->get_term(wi); - result += term.m_v * coeff; - for (auto const& i : term.m_coeffs) { - m_todo_terms.push_back(std::make_pair(i.first, i.second * coeff)); - } - } - else { - result += m_variable_values[wi] * coeff; - } - } - m_variable_values[vi] = result; - return result; - } - - void init_variable_values() { - if (m_solver.get() && th.get_num_vars() > 0) { - m_solver->get_model(m_variable_values); - } - } - - void reset_variable_values() { - m_variable_values.clear(); - } - - bool assume_eqs() { - svector vars; - theory_var sz = static_cast(th.get_num_vars()); - for (theory_var v = 0; v < sz; ++v) { - if (th.is_relevant_and_shared(get_enode(v))) { - vars.push_back(m_theory_var2var_index[v]); - } - } - if (vars.empty()) { - return false; - } - TRACE("arith", - for (theory_var v = 0; v < sz; ++v) { - if (th.is_relevant_and_shared(get_enode(v))) { - tout << "v" << v << " " << m_theory_var2var_index[v] << " "; - } - } - tout << "\n"; - ); - if (!m_use_nra_model) { - m_solver->random_update(vars.size(), vars.c_ptr()); - } - m_model_eqs.reset(); - TRACE("arith", display(tout);); - - unsigned old_sz = m_assume_eq_candidates.size(); - bool result = false; - int start = ctx().get_random_value(); - for (theory_var i = 0; i < sz; ++i) { - theory_var v = (i + start) % sz; - enode* n1 = get_enode(v); - if (!th.is_relevant_and_shared(n1)) { - continue; - } - if (!can_get_ivalue(v)) { - continue; - } - theory_var other = m_model_eqs.insert_if_not_there(v); - if (other == v) { - continue; - } - enode* n2 = get_enode(other); - if (n1->get_root() != n2->get_root()) { - TRACE("arith", tout << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n"; - tout << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n"; - tout << "v" << v << " = " << "v" << other << "\n";); - m_assume_eq_candidates.push_back(std::make_pair(v, other)); - result = true; - } - } - - if (result) { - ctx().push_trail(restore_size_trail, false>(m_assume_eq_candidates, old_sz)); - } - - return delayed_assume_eqs(); - } - - bool delayed_assume_eqs() { - if (m_assume_eq_head == m_assume_eq_candidates.size()) - return false; - - ctx().push_trail(value_trail(m_assume_eq_head)); - while (m_assume_eq_head < m_assume_eq_candidates.size()) { - std::pair const & p = m_assume_eq_candidates[m_assume_eq_head]; - theory_var v1 = p.first; - theory_var v2 = p.second; - enode* n1 = get_enode(v1); - enode* n2 = get_enode(v2); - m_assume_eq_head++; - CTRACE("arith", - is_eq(v1, v2) && n1->get_root() != n2->get_root(), - tout << "assuming eq: v" << v1 << " = v" << v2 << "\n";); - if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) { - return true; - } + if (a.is_uminus(term, term)) { + mul.neg(); + continue; } + if (a.is_to_real(term, term)) { + continue; + } return false; } + while (false); + return false; + } - bool is_eq(theory_var v1, theory_var v2) { - if (m_use_nra_model) { - return m_nra->am().eq(nl_value(v1, *m_a1), nl_value(v2, *m_a2)); + void linearize_term(expr* term, scoped_internalize_state& st) { + st.push(term, rational::one()); + linearize(st); + } + + void linearize_ineq(expr* lhs, expr* rhs, scoped_internalize_state& st) { + st.push(lhs, rational::one()); + st.push(rhs, rational::minus_one()); + linearize(st); + } + + void linearize(scoped_internalize_state& st) { + expr_ref_vector & terms = st.terms(); + svector& vars = st.vars(); + vector& coeffs = st.coeffs(); + rational& coeff = st.coeff(); + rational r; + expr* n1, *n2; + unsigned index = 0; + while (index < terms.size()) { + SASSERT(index >= vars.size()); + expr* n = terms[index].get(); + st.terms_to_internalize().push_back(n); + if (a.is_add(n)) { + unsigned sz = to_app(n)->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + st.push(to_app(n)->get_arg(i), coeffs[index]); + } + st.set_back(index); + } + else if (a.is_sub(n)) { + unsigned sz = to_app(n)->get_num_args(); + terms[index] = to_app(n)->get_arg(0); + for (unsigned i = 1; i < sz; ++i) { + st.push(to_app(n)->get_arg(i), -coeffs[index]); + } + } + else if (a.is_mul(n, n1, n2) && is_numeral(n1, r)) { + coeffs[index] *= r; + terms[index] = n2; + st.terms_to_internalize().push_back(n1); + } + else if (a.is_mul(n, n1, n2) && is_numeral(n2, r)) { + coeffs[index] *= r; + terms[index] = n1; + st.terms_to_internalize().push_back(n2); + } + else if (a.is_mul(n)) { + theory_var v; + internalize_mul(to_app(n), v, r); + coeffs[index] *= r; + coeffs[vars.size()] = coeffs[index]; + vars.push_back(v); + ++index; + } + else if (a.is_numeral(n, r)) { + coeff += coeffs[index]*r; + ++index; + } + else if (a.is_uminus(n, n1)) { + coeffs[index].neg(); + terms[index] = n1; + } + else if (is_app(n) && a.get_family_id() == to_app(n)->get_family_id()) { + app* t = to_app(n); + found_not_handled(n); + internalize_args(t); + mk_enode(t); + theory_var v = mk_var(n); + coeffs[vars.size()] = coeffs[index]; + vars.push_back(v); + ++index; } else { - return get_ivalue(v1) == get_ivalue(v2); + if (is_app(n)) { + internalize_args(to_app(n)); + } + theory_var v = mk_var(n); + coeffs[vars.size()] = coeffs[index]; + vars.push_back(v); + ++index; } } - - bool has_delayed_constraints() const { - return !m_asserted_atoms.empty(); + for (unsigned i = st.terms_to_internalize().size(); i > 0; ) { + --i; + expr* n = st.terms_to_internalize()[i]; + if (is_app(n)) { + mk_enode(to_app(n)); + } } + st.terms_to_internalize().reset(); + } - final_check_status final_check_eh() { - m_use_nra_model = false; - lbool is_sat = l_true; - if (m_solver->get_status() != lp::lp_status::OPTIMAL) { - is_sat = make_feasible(); + void internalize_args(app* t) { + for (unsigned i = 0; reflect(t) && i < t->get_num_args(); ++i) { + if (!ctx().e_internalized(t->get_arg(i))) { + ctx().internalize(t->get_arg(i), false); } - final_check_status st = FC_DONE; - switch (is_sat) { - case l_true: - - if (delayed_assume_eqs()) { - return FC_CONTINUE; - } - if (assume_eqs()) { - return FC_CONTINUE; - } - - switch (check_lia()) { - case l_true: - break; - case l_false: - return FC_CONTINUE; - case l_undef: - st = FC_GIVEUP; - break; - } - - switch (check_nra()) { - case l_true: - break; - case l_false: - return FC_CONTINUE; - case l_undef: - st = FC_GIVEUP; - break; - } - if (m_not_handled != 0) { - st = FC_GIVEUP; - } - - return st; - case l_false: - set_conflict(); - return FC_CONTINUE; - case l_undef: - return m.canceled() ? FC_CONTINUE : FC_GIVEUP; - default: - UNREACHABLE(); - break; - } - return FC_GIVEUP; } + } - // create a bound atom representing term <= k - app_ref mk_bound(lp::lar_term const& term, rational const& k) { - app_ref t = mk_term(term, k.is_int()); - app_ref atom(a.mk_le(t, a.mk_numeral(k, k.is_int())), m); - expr_ref atom1(m); - proof_ref atomp(m); - ctx().get_simplifier()(atom, atom1, atomp); - atom = to_app(atom1); - TRACE("arith", tout << atom << "\n"; - m_solver->print_term(term, tout << "bound atom: "); tout << " <= " << k << "\n"; - display(tout); - ); - ctx().internalize(atom, true); - ctx().mark_as_relevant(atom.get()); - return atom; + void internalize_mul(app* t, theory_var& v, rational& r) { + SASSERT(a.is_mul(t)); + bool _has_var = has_var(t); + if (!_has_var) { + internalize_args(t); + mk_enode(t); } - - lbool check_lia() { - if (m.canceled()) { - TRACE("arith", tout << "canceled\n";); - return l_undef; + r = rational::one(); + rational r1; + v = mk_var(t); + svector vars; + ptr_vector todo; + todo.push_back(t); + while (!todo.empty()) { + expr* n = todo.back(); + todo.pop_back(); + expr* n1, *n2; + if (a.is_mul(n, n1, n2)) { + todo.push_back(n1); + todo.push_back(n2); } - lp::lar_term term; - lp::mpq k; - lp::explanation ex; // TBD, this should be streamlined accross different explanations - switch(m_lia->check(term, k, ex)) { - case lp::lia_move::ok: - return l_true; - case lp::lia_move::branch: { - (void)mk_bound(term, k); - // branch on term <= k - // at this point we have a new unassigned atom that the - // SAT core assigns a value to - return l_false; + else if (a.is_numeral(n, r1)) { + r *= r1; } - case lp::lia_move::cut: { - ++m_stats.m_gomory_cuts; - // m_explanation implies term <= k - app_ref b = mk_bound(term, k); - m_eqs.reset(); - m_core.reset(); - m_params.reset(); - for (auto const& ev : ex.m_explanation) { - if (!ev.first.is_zero()) { - set_evidence(ev.second); - } + else { + if (!ctx().e_internalized(n)) { + ctx().internalize(n, false); } - assign(literal(ctx().get_bool_var(b), false)); - return l_false; + vars.push_back(get_var_index(mk_var(n))); } - case lp::lia_move::conflict: - // ex contains unsat core - m_explanation = ex.m_explanation; - set_conflict1(); - return l_false; - case lp::lia_move::give_up: - TRACE("arith", tout << "lia giveup\n";); - return l_undef; - default: - UNREACHABLE(); - } - return l_undef; } + TRACE("arith", tout << mk_pp(t, m) << "\n";); + if (!_has_var) { + ensure_nra(); + m_nra->add_monomial(get_var_index(v), vars.size(), vars.c_ptr()); + } + } - lbool check_nra() { - m_use_nra_model = false; - if (m.canceled()) { - TRACE("arith", tout << "canceled\n";); - return l_undef; - } - if (!m_nra) return l_true; - if (!m_nra->need_check()) return l_true; - m_a1 = 0; m_a2 = 0; - lbool r = m_nra->check(m_explanation); - m_a1 = alloc(scoped_anum, m_nra->am()); - m_a2 = alloc(scoped_anum, m_nra->am()); - switch (r) { - case l_false: - set_conflict1(); - break; - case l_true: - m_use_nra_model = true; - if (assume_eqs()) { - return l_false; - } - break; + enode * mk_enode(app * n) { + if (ctx().e_internalized(n)) { + return get_enode(n); + } + else { + return ctx().mk_enode(n, !reflect(n), false, enable_cgc_for(n)); + } + } + + bool enable_cgc_for(app * n) const { + // Congruence closure is not enabled for (+ ...) and (* ...) applications. + return !(n->get_family_id() == get_id() && (n->get_decl_kind() == OP_ADD || n->get_decl_kind() == OP_MUL)); + } + + + void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) { + TRACE("arith", literal lits[2]; lits[0] = l1; lits[1] = l2; ctx().display_literals_verbose(tout, 2, lits); tout << "\n";); + ctx().mk_th_axiom(get_id(), l1, l2, num_params, params); + } + + void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { + TRACE("arith", literal lits[3]; lits[0] = l1; lits[1] = l2; lits[2] = l3; ctx().display_literals_verbose(tout, 3, lits); tout << "\n";); + ctx().mk_th_axiom(get_id(), l1, l2, l3, num_params, params); + } + + bool is_underspecified(app* n) const { + if (n->get_family_id() == get_id()) { + switch (n->get_decl_kind()) { + case OP_DIV: + case OP_IDIV: + case OP_REM: + case OP_MOD: + return true; default: break; } - return r; } + return false; + } - /** - \brief We must redefine this method, because theory of arithmetic contains - underspecified operators such as division by 0. - (/ a b) is essentially an uninterpreted function when b = 0. - Thus, 'a' must be considered a shared var if it is the child of an underspecified operator. + bool reflect(app* n) const { + return m_arith_params.m_arith_reflect || is_underspecified(n); + } - if merge(a / b, x + y) and a / b is root, then x + y become shared and all z + u in equivalence class of x + y. + bool has_var(expr* n) { + if (!ctx().e_internalized(n)) { + return false; + } + enode* e = get_enode(n); + return th.is_attached_to_var(e); + } - - TBD: when the set of underspecified subterms is small, compute the shared variables below it. - Recompute the set if there are merges that invalidate it. - Use the set to determine if a variable is shared. - */ - bool is_shared(theory_var v) const { - if (m_underspecified.empty()) { + theory_var mk_var(expr* n, bool internalize = true) { + if (!ctx().e_internalized(n)) { + ctx().internalize(n, false); + } + enode* e = get_enode(n); + theory_var v; + if (!th.is_attached_to_var(e)) { + v = th.mk_var(e); + SASSERT(m_bounds.size() <= static_cast(v) || m_bounds[v].empty()); + if (m_bounds.size() <= static_cast(v)) { + m_bounds.push_back(lp_bounds()); + m_unassigned_bounds.push_back(0); + } + ctx().attach_th_var(e, &th, v); + } + else { + v = e->get_th_var(get_id()); + } + SASSERT(null_theory_var != v); + return v; + } + + lp::var_index get_var_index(theory_var v) { + lp::var_index result = UINT_MAX; + if (m_theory_var2var_index.size() > static_cast(v)) { + result = m_theory_var2var_index[v]; + } + if (result == UINT_MAX) { + result = m_solver->add_var(v, is_int(v)); + m_theory_var2var_index.setx(v, result, UINT_MAX); + m_var_index2theory_var.setx(result, v, UINT_MAX); + m_var_trail.push_back(v); + } + return result; + } + + void init_left_side(scoped_internalize_state& st) { + SASSERT(all_zeros(m_columns)); + svector const& vars = st.vars(); + vector const& coeffs = st.coeffs(); + for (unsigned i = 0; i < vars.size(); ++i) { + theory_var var = vars[i]; + rational const& coeff = coeffs[i]; + if (m_columns.size() <= static_cast(var)) { + m_columns.setx(var, coeff, rational::zero()); + } + else { + m_columns[var] += coeff; + } + } + m_left_side.clear(); + // reset the coefficients after they have been used. + for (unsigned i = 0; i < vars.size(); ++i) { + theory_var var = vars[i]; + rational const& r = m_columns[var]; + if (!r.is_zero()) { + m_left_side.push_back(std::make_pair(r, get_var_index(var))); + m_columns[var].reset(); + } + } + SASSERT(all_zeros(m_columns)); + } + + bool all_zeros(vector const& v) const { + for (rational const& r : v) { + if (!r.is_zero()) { return false; } - enode * n = get_enode(v); - enode * r = n->get_root(); - unsigned usz = m_underspecified.size(); - TRACE("shared", tout << ctx().get_scope_level() << " " << v << " " << r->get_num_parents() << "\n";); - if (r->get_num_parents() > 2*usz) { - for (unsigned i = 0; i < usz; ++i) { - app* u = m_underspecified[i]; - unsigned sz = u->get_num_args(); - for (unsigned j = 0; j < sz; ++j) { - if (ctx().get_enode(u->get_arg(j))->get_root() == r) { - return true; - } - } + } + return true; + } + + void add_eq_constraint(lp::constraint_index index, enode* n1, enode* n2) { + m_constraint_sources.setx(index, equality_source, null_source); + m_equalities.setx(index, enode_pair(n1, n2), enode_pair(0, 0)); + ++m_stats.m_add_rows; + } + + void add_ineq_constraint(lp::constraint_index index, literal lit) { + m_constraint_sources.setx(index, inequality_source, null_source); + m_inequalities.setx(index, lit, null_literal); + ++m_stats.m_add_rows; + TRACE("arith", m_solver->print_constraint(index, tout); tout << "\n";); + } + + void add_def_constraint(lp::constraint_index index, theory_var v) { + m_constraint_sources.setx(index, definition_source, null_source); + m_definitions.setx(index, v, null_theory_var); + ++m_stats.m_add_rows; + } + + void internalize_eq(theory_var v1, theory_var v2) { + enode* n1 = get_enode(v1); + enode* n2 = get_enode(v2); + scoped_internalize_state st(*this); + st.vars().push_back(v1); + st.vars().push_back(v2); + st.coeffs().push_back(rational::one()); + st.coeffs().push_back(rational::minus_one()); + init_left_side(st); + add_eq_constraint(m_solver->add_constraint(m_left_side, lp::EQ, rational::zero()), n1, n2); + TRACE("arith", + tout << "v" << v1 << " = " << "v" << v2 << ": " + << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n";); + } + + void del_bounds(unsigned old_size) { + for (unsigned i = m_bounds_trail.size(); i > old_size; ) { + --i; + unsigned v = m_bounds_trail[i]; + lp_api::bound* b = m_bounds[v].back(); + // del_use_lists(b); + dealloc(b); + m_bounds[v].pop_back(); + } + m_bounds_trail.shrink(old_size); + } + + void updt_unassigned_bounds(theory_var v, int inc) { + TRACE("arith", tout << "v" << v << " " << m_unassigned_bounds[v] << " += " << inc << "\n";); + ctx().push_trail(vector_value_trail(m_unassigned_bounds, v)); + m_unassigned_bounds[v] += inc; + } + + bool is_unit_var(scoped_internalize_state& st) { + return st.coeff().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one(); + } + + theory_var internalize_def(app* term, scoped_internalize_state& st) { + linearize_term(term, st); + if (is_unit_var(st)) { + return st.vars()[0]; + } + else { + theory_var v = mk_var(term); + SASSERT(null_theory_var != v); + st.coeffs().resize(st.vars().size() + 1); + st.coeffs()[st.vars().size()] = rational::minus_one(); + st.vars().push_back(v); + return v; + } + } + + // term - v = 0 + theory_var internalize_def(app* term) { + scoped_internalize_state st(*this); + linearize_term(term, st); + if (is_unit_var(st)) { + return st.vars()[0]; + } + else { + init_left_side(st); + theory_var v = mk_var(term); + lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); + if (vi == UINT_MAX) { + vi = m_solver->add_term(m_left_side, st.coeff()); + m_theory_var2var_index.setx(v, vi, UINT_MAX); + if (m_solver->is_term(vi)) { + m_term_index2theory_var.setx(m_solver->adjust_term_index(vi), v, UINT_MAX); + } + else { + m_var_index2theory_var.setx(vi, v, UINT_MAX); + } + m_var_trail.push_back(v); + TRACE("arith_verbose", tout << "v" << v << " := " << mk_pp(term, m) << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; + m_solver->print_term(m_solver->get_term(vi), tout); tout << "\n";); + } + rational val; + if (a.is_numeral(term, val)) { + m_fixed_var_table.insert(value_sort_pair(val, is_int(v)), v); + } + return v; + } + } + + +public: + imp(theory_lra& th, ast_manager& m, theory_arith_params& ap): + th(th), m(m), + m_arith_params(ap), + a(m), + m_arith_eq_adapter(th, ap, a), + m_internalize_head(0), + m_not_handled(0), + m_asserted_qhead(0), + m_assume_eq_head(0), + m_num_conflicts(0), + m_use_nra_model(false), + m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), + m_solver(0), + m_resource_limit(*this) { + } + + ~imp() { + del_bounds(0); + std::for_each(m_internalize_states.begin(), m_internalize_states.end(), delete_proc()); + } + + void init(context* ctx) { + init_solver(); + } + + bool internalize_atom(app * atom, bool gate_ctx) { + return internalize_atom_strict(atom, gate_ctx); + + } + + bool internalize_atom_strict(app * atom, bool gate_ctx) { + SASSERT(!ctx().b_internalized(atom)); + bool_var bv = ctx().mk_bool_var(atom); + ctx().set_var_theory(bv, get_id()); + expr* n1, *n2; + rational r; + lp_api::bound_kind k; + theory_var v = null_theory_var; + if (a.is_le(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { + v = internalize_def(to_app(n1)); + k = lp_api::upper_t; + } + else if (a.is_ge(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { + v = internalize_def(to_app(n1)); + k = lp_api::lower_t; + } + else { + TRACE("arith", tout << "Could not internalize " << mk_pp(atom, m) << "\n";); + found_not_handled(atom); + return true; + } + lp_api::bound* b = alloc(lp_api::bound, bv, v, is_int(v), r, k); + m_bounds[v].push_back(b); + updt_unassigned_bounds(v, +1); + m_bounds_trail.push_back(v); + m_bool_var2bound.insert(bv, b); + TRACE("arith_verbose", tout << "Internalized " << mk_pp(atom, m) << "\n";); + mk_bound_axioms(*b); + //add_use_lists(b); + return true; + } + + bool internalize_term(app * term) { + if (ctx().e_internalized(term) && th.is_attached_to_var(ctx().get_enode(term))) { + // skip + } + else { + internalize_def(term); + } + return true; + } + + void internalize_eq_eh(app * atom, bool_var) { + expr* lhs = 0, *rhs = 0; + VERIFY(m.is_eq(atom, lhs, rhs)); + enode * n1 = get_enode(lhs); + enode * n2 = get_enode(rhs); + if (n1->get_th_var(get_id()) != null_theory_var && + n2->get_th_var(get_id()) != null_theory_var && + n1 != n2) { + TRACE("arith_verbose", tout << mk_pp(atom, m) << "\n";); + m_arith_eq_adapter.mk_axioms(n1, n2); + } + } + + void assign_eh(bool_var v, bool is_true) { + TRACE("arith", tout << mk_pp(ctx().bool_var2expr(v), m) << " " << (is_true?"true":"false") << "\n";); + m_asserted_atoms.push_back(delayed_atom(v, is_true)); + } + + void new_eq_eh(theory_var v1, theory_var v2) { + // or internalize_eq(v1, v2); + m_arith_eq_adapter.new_eq_eh(v1, v2); + } + + bool use_diseqs() const { + return true; + } + + void new_diseq_eh(theory_var v1, theory_var v2) { + TRACE("arith", tout << "v" << v1 << " != " << "v" << v2 << "\n";); + ++m_stats.m_assert_diseq; + m_arith_eq_adapter.new_diseq_eh(v1, v2); + } + + void push_scope_eh() { + m_scopes.push_back(scope()); + scope& s = m_scopes.back(); + s.m_bounds_lim = m_bounds_trail.size(); + s.m_asserted_qhead = m_asserted_qhead; + s.m_asserted_atoms_lim = m_asserted_atoms.size(); + s.m_not_handled = m_not_handled; + s.m_underspecified_lim = m_underspecified.size(); + s.m_var_trail_lim = m_var_trail.size(); + m_solver->push(); + if (m_nra) m_nra->push(); + } + + void pop_scope_eh(unsigned num_scopes) { + if (num_scopes == 0) { + return; + } + unsigned old_size = m_scopes.size() - num_scopes; + del_bounds(m_scopes[old_size].m_bounds_lim); + for (unsigned i = m_scopes[old_size].m_var_trail_lim; i < m_var_trail.size(); ++i) { + lp::var_index vi = m_theory_var2var_index[m_var_trail[i]]; + if (m_solver->is_term(vi)) { + unsigned ti = m_solver->adjust_term_index(vi); + m_term_index2theory_var[ti] = UINT_MAX; + } + else if (vi < m_var_index2theory_var.size()) { + m_var_index2theory_var[vi] = UINT_MAX; + } + m_theory_var2var_index[m_var_trail[i]] = UINT_MAX; + } + m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim); + m_asserted_qhead = m_scopes[old_size].m_asserted_qhead; + m_underspecified.shrink(m_scopes[old_size].m_underspecified_lim); + m_var_trail.shrink(m_scopes[old_size].m_var_trail_lim); + m_not_handled = m_scopes[old_size].m_not_handled; + m_scopes.resize(old_size); + m_solver->pop(num_scopes); + // VERIFY(l_false != make_feasible()); + m_new_bounds.reset(); + m_to_check.reset(); + if (m_nra) m_nra->pop(num_scopes); + TRACE("arith", tout << "num scopes: " << num_scopes << " new scope level: " << m_scopes.size() << "\n";); + } + + void restart_eh() { + m_arith_eq_adapter.restart_eh(); + } + + void relevant_eh(app* n) { + TRACE("arith", tout << mk_pp(n, m) << "\n";); + expr* n1, *n2; + if (a.is_mod(n, n1, n2)) + mk_idiv_mod_axioms(n1, n2); + else if (a.is_rem(n, n1, n2)) + mk_rem_axiom(n1, n2); + else if (a.is_div(n, n1, n2)) + mk_div_axiom(n1, n2); + else if (a.is_to_int(n)) + mk_to_int_axiom(n); + else if (a.is_is_int(n)) + mk_is_int_axiom(n); + } + + // n < 0 || rem(a, n) = mod(a, n) + // !n < 0 || rem(a, n) = -mod(a, n) + void mk_rem_axiom(expr* dividend, expr* divisor) { + expr_ref zero(a.mk_int(0), m); + expr_ref rem(a.mk_rem(dividend, divisor), m); + expr_ref mod(a.mk_mod(dividend, divisor), m); + expr_ref mmod(a.mk_uminus(mod), m); + literal dgez = mk_literal(a.mk_ge(divisor, zero)); + mk_axiom(~dgez, th.mk_eq(rem, mod, false)); + mk_axiom( dgez, th.mk_eq(rem, mmod, false)); + } + + // q = 0 or q * (p div q) = p + void mk_div_axiom(expr* p, expr* q) { + if (a.is_zero(q)) return; + literal eqz = th.mk_eq(q, a.mk_real(0), false); + literal eq = th.mk_eq(a.mk_mul(q, a.mk_div(p, q)), p, false); + mk_axiom(eqz, eq); + } + + // to_int (to_real x) = x + // to_real(to_int(x)) <= x < to_real(to_int(x)) + 1 + void mk_to_int_axiom(app* n) { + expr* x = 0, *y = 0; + VERIFY (a.is_to_int(n, x)); + if (a.is_to_real(x, y)) { + mk_axiom(th.mk_eq(y, n, false)); + } + else { + expr_ref to_r(a.mk_to_real(n), m); + expr_ref lo(a.mk_le(a.mk_sub(to_r, x), a.mk_real(0)), m); + expr_ref hi(a.mk_ge(a.mk_sub(x, to_r), a.mk_real(1)), m); + mk_axiom(mk_literal(lo)); + mk_axiom(~mk_literal(hi)); + } + } + + // is_int(x) <=> to_real(to_int(x)) = x + void mk_is_int_axiom(app* n) { + expr* x = 0; + VERIFY(a.is_is_int(n, x)); + literal eq = th.mk_eq(a.mk_to_real(a.mk_to_int(x)), x, false); + literal is_int = ctx().get_literal(n); + mk_axiom(~is_int, eq); + mk_axiom(is_int, ~eq); + } + + void mk_idiv_mod_axioms(expr * p, expr * q) { + if (a.is_zero(q)) { + return; + } + // if q is zero, then idiv and mod are uninterpreted functions. + expr_ref div(a.mk_idiv(p, q), m); + expr_ref mod(a.mk_mod(p, q), m); + expr_ref zero(a.mk_int(0), m); + literal q_ge_0 = mk_literal(a.mk_ge(q, zero)); + literal q_le_0 = mk_literal(a.mk_le(q, zero)); + // literal eqz = th.mk_eq(q, zero, false); + literal eq = th.mk_eq(a.mk_add(a.mk_mul(q, div), mod), p, false); + literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero)); + // q >= 0 or p = (p mod q) + q * (p div q) + // q <= 0 or p = (p mod q) + q * (p div q) + // q >= 0 or (p mod q) >= 0 + // q <= 0 or (p mod q) >= 0 + // q <= 0 or (p mod q) < q + // q >= 0 or (p mod q) < -q + mk_axiom(q_ge_0, eq); + mk_axiom(q_le_0, eq); + mk_axiom(q_ge_0, mod_ge_0); + mk_axiom(q_le_0, mod_ge_0); + mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero))); + mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero))); + rational k; + if (m_arith_params.m_arith_enum_const_mod && a.is_numeral(q, k) && + k.is_pos() && k < rational(8)) { + unsigned _k = k.get_unsigned(); + literal_buffer lits; + for (unsigned j = 0; j < _k; ++j) { + literal mod_j = th.mk_eq(mod, a.mk_int(j), false); + lits.push_back(mod_j); + ctx().mark_as_relevant(mod_j); + } + ctx().mk_th_axiom(get_id(), lits.size(), lits.begin()); + } + } + + void mk_axiom(literal l) { + ctx().mk_th_axiom(get_id(), false_literal, l); + if (ctx().relevancy()) { + ctx().mark_as_relevant(l); + } + } + + void mk_axiom(literal l1, literal l2) { + if (l1 == false_literal) { + mk_axiom(l2); + return; + } + ctx().mk_th_axiom(get_id(), l1, l2); + if (ctx().relevancy()) { + ctx().mark_as_relevant(l1); + expr_ref e(m); + ctx().literal2expr(l2, e); + ctx().add_rel_watch(~l1, e); + } + } + + void mk_axiom(literal l1, literal l2, literal l3) { + ctx().mk_th_axiom(get_id(), l1, l2, l3); + if (ctx().relevancy()) { + expr_ref e(m); + ctx().mark_as_relevant(l1); + ctx().literal2expr(l2, e); + ctx().add_rel_watch(~l1, e); + ctx().literal2expr(l3, e); + ctx().add_rel_watch(~l2, e); + } + } + + literal mk_literal(expr* e) { + expr_ref pinned(e, m); + if (!ctx().e_internalized(e)) { + ctx().internalize(e, false); + } + return ctx().get_literal(e); + } + + + void init_search_eh() { + m_arith_eq_adapter.init_search_eh(); + m_num_conflicts = 0; + } + + bool can_get_value(theory_var v) const { + return + (v != null_theory_var) && + (v < static_cast(m_theory_var2var_index.size())) && + (UINT_MAX != m_theory_var2var_index[v]) && + (m_solver->is_term(m_theory_var2var_index[v]) || m_variable_values.count(m_theory_var2var_index[v]) > 0); + } + + + bool can_get_ivalue(theory_var v) const { + if (v == null_theory_var || (v >= static_cast(m_theory_var2var_index.size()))) + return false; + return m_solver->var_is_registered(m_theory_var2var_index[v]); + } + + mutable vector> m_todo_terms; + + lp::impq get_ivalue(theory_var v) const { + lp_assert(can_get_ivalue(v)); + lp::var_index vi = m_theory_var2var_index[v]; + if (!m_solver->is_term(vi)) + return m_solver->get_column_value(vi); + m_todo_terms.push_back(std::make_pair(vi, rational::one())); + lp::impq result(0); + while (!m_todo_terms.empty()) { + vi = m_todo_terms.back().first; + rational coeff = m_todo_terms.back().second; + m_todo_terms.pop_back(); + if (m_solver->is_term(vi)) { + const lp::lar_term& term = m_solver->get_term(vi); + result += term.m_v * coeff; + for (const auto & i: term.m_coeffs) { + m_todo_terms.push_back(std::make_pair(i.first, coeff * i.second)); + } + } + else { + result += m_solver->get_column_value(vi) * coeff; + } + } + return result; + } + + rational get_value(theory_var v) const { + if (!can_get_value(v)) return rational::zero(); + lp::var_index vi = m_theory_var2var_index[v]; + if (m_variable_values.count(vi) > 0) { + return m_variable_values[vi]; + } + m_todo_terms.push_back(std::make_pair(vi, rational::one())); + rational result(0); + while (!m_todo_terms.empty()) { + lp::var_index wi = m_todo_terms.back().first; + rational coeff = m_todo_terms.back().second; + m_todo_terms.pop_back(); + if (m_solver->is_term(wi)) { + const lp::lar_term& term = m_solver->get_term(wi); + result += term.m_v * coeff; + for (auto const& i : term.m_coeffs) { + m_todo_terms.push_back(std::make_pair(i.first, i.second * coeff)); } } else { - enode_vector::const_iterator it = r->begin_parents(); - enode_vector::const_iterator end = r->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; - if (is_underspecified(parent->get_owner())) { + result += m_variable_values[wi] * coeff; + } + } + m_variable_values[vi] = result; + return result; + } + + void init_variable_values() { + if (m_solver.get() && th.get_num_vars() > 0) { + m_solver->get_model(m_variable_values); + } + } + + void reset_variable_values() { + m_variable_values.clear(); + } + + bool assume_eqs() { + svector vars; + theory_var sz = static_cast(th.get_num_vars()); + for (theory_var v = 0; v < sz; ++v) { + if (th.is_relevant_and_shared(get_enode(v))) { + vars.push_back(m_theory_var2var_index[v]); + } + } + if (vars.empty()) { + return false; + } + TRACE("arith", + for (theory_var v = 0; v < sz; ++v) { + if (th.is_relevant_and_shared(get_enode(v))) { + tout << "v" << v << " " << m_theory_var2var_index[v] << " "; + } + } + tout << "\n"; + ); + if (!m_use_nra_model) { + m_solver->random_update(vars.size(), vars.c_ptr()); + } + m_model_eqs.reset(); + TRACE("arith", display(tout);); + + unsigned old_sz = m_assume_eq_candidates.size(); + bool result = false; + int start = ctx().get_random_value(); + for (theory_var i = 0; i < sz; ++i) { + theory_var v = (i + start) % sz; + enode* n1 = get_enode(v); + if (!th.is_relevant_and_shared(n1)) { + continue; + } + if (!can_get_ivalue(v)) { + continue; + } + theory_var other = m_model_eqs.insert_if_not_there(v); + if (other == v) { + continue; + } + enode* n2 = get_enode(other); + if (n1->get_root() != n2->get_root()) { + TRACE("arith", tout << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n"; + tout << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n"; + tout << "v" << v << " = " << "v" << other << "\n";); + m_assume_eq_candidates.push_back(std::make_pair(v, other)); + result = true; + } + } + + if (result) { + ctx().push_trail(restore_size_trail, false>(m_assume_eq_candidates, old_sz)); + } + + return delayed_assume_eqs(); + } + + bool delayed_assume_eqs() { + if (m_assume_eq_head == m_assume_eq_candidates.size()) + return false; + + ctx().push_trail(value_trail(m_assume_eq_head)); + while (m_assume_eq_head < m_assume_eq_candidates.size()) { + std::pair const & p = m_assume_eq_candidates[m_assume_eq_head]; + theory_var v1 = p.first; + theory_var v2 = p.second; + enode* n1 = get_enode(v1); + enode* n2 = get_enode(v2); + m_assume_eq_head++; + CTRACE("arith", + is_eq(v1, v2) && n1->get_root() != n2->get_root(), + tout << "assuming eq: v" << v1 << " = v" << v2 << "\n";); + if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) { + return true; + } + } + return false; + } + + bool is_eq(theory_var v1, theory_var v2) { + if (m_use_nra_model) { + return m_nra->am().eq(nl_value(v1, *m_a1), nl_value(v2, *m_a2)); + } + else { + return get_ivalue(v1) == get_ivalue(v2); + } + } + + bool has_delayed_constraints() const { + return !m_asserted_atoms.empty(); + } + + final_check_status final_check_eh() { + m_use_nra_model = false; + lbool is_sat = l_true; + if (m_solver->get_status() != lp::lp_status::OPTIMAL) { + is_sat = make_feasible(); + } + final_check_status st = FC_DONE; + switch (is_sat) { + case l_true: + + if (delayed_assume_eqs()) { + return FC_CONTINUE; + } + if (assume_eqs()) { + return FC_CONTINUE; + } + + switch (check_lia()) { + case l_true: + break; + case l_false: + return FC_CONTINUE; + case l_undef: + TRACE("arith", tout << "check-lia giveup\n";); + st = FC_GIVEUP; + break; + } + + switch (check_nra()) { + case l_true: + break; + case l_false: + return FC_CONTINUE; + case l_undef: + TRACE("arith", tout << "check-nra giveup\n";); + st = FC_GIVEUP; + break; + } + if (m_not_handled != 0) { + TRACE("arith", tout << "unhandled operator " << mk_pp(m_not_handled, m) << "\n";); + st = FC_GIVEUP; + } + + return st; + case l_false: + set_conflict(); + return FC_CONTINUE; + case l_undef: + TRACE("arith", tout << "check feasiable is undef\n";); + return m.canceled() ? FC_CONTINUE : FC_GIVEUP; + default: + UNREACHABLE(); + break; + } + TRACE("arith", tout << "default giveup\n";); + return FC_GIVEUP; + } + + // create a bound atom representing term <= k + app_ref mk_bound(lp::lar_term const& term, rational const& k) { + app_ref t = mk_term(term, k.is_int()); + app_ref atom(a.mk_le(t, a.mk_numeral(k, k.is_int())), m); + expr_ref atom1(m); + proof_ref atomp(m); + ctx().get_rewriter()(atom, atom1, atomp); + atom = to_app(atom1); + TRACE("arith", tout << atom << "\n"; + m_solver->print_term(term, tout << "bound atom: "); tout << " <= " << k << "\n"; + display(tout); + ); + ctx().internalize(atom, true); + ctx().mark_as_relevant(atom.get()); + return atom; + } + + lbool check_lia() { + if (m.canceled()) { + TRACE("arith", tout << "canceled\n";); + return l_undef; + } + lp::lar_term term; + lp::mpq k; + lp::explanation ex; // TBD, this should be streamlined accross different explanations + switch(m_lia->check(term, k, ex)) { + case lp::lia_move::ok: + return l_true; + case lp::lia_move::branch: { + (void)mk_bound(term, k); + // branch on term <= k + // at this point we have a new unassigned atom that the + // SAT core assigns a value to + return l_false; + } + case lp::lia_move::cut: { + ++m_stats.m_gomory_cuts; + // m_explanation implies term <= k + app_ref b = mk_bound(term, k); + m_eqs.reset(); + m_core.reset(); + m_params.reset(); + for (auto const& ev : ex.m_explanation) { + if (!ev.first.is_zero()) { + set_evidence(ev.second); + } + } + assign(literal(ctx().get_bool_var(b), false)); + return l_false; + } + case lp::lia_move::conflict: + // ex contains unsat core + m_explanation = ex.m_explanation; + set_conflict1(); + return l_false; + case lp::lia_move::give_up: + TRACE("arith", tout << "lia giveup\n";); + return l_undef; + default: + UNREACHABLE(); + } + UNREACHABLE(); + return l_undef; + } + + lbool check_nra() { + m_use_nra_model = false; + if (m.canceled()) { + TRACE("arith", tout << "canceled\n";); + return l_undef; + } + if (!m_nra) return l_true; + if (!m_nra->need_check()) return l_true; + m_a1 = 0; m_a2 = 0; + lbool r = m_nra->check(m_explanation); + m_a1 = alloc(scoped_anum, m_nra->am()); + m_a2 = alloc(scoped_anum, m_nra->am()); + switch (r) { + case l_false: + set_conflict1(); + break; + case l_true: + m_use_nra_model = true; + if (assume_eqs()) { + return l_false; + } + break; + case l_undef: + TRACE("arith", tout << "nra-undef\n";); + default: + break; + } + return r; + } + + /** + \brief We must redefine this method, because theory of arithmetic contains + underspecified operators such as division by 0. + (/ a b) is essentially an uninterpreted function when b = 0. + Thus, 'a' must be considered a shared var if it is the child of an underspecified operator. + + if merge(a / b, x + y) and a / b is root, then x + y become shared and all z + u in equivalence class of x + y. + + + TBD: when the set of underspecified subterms is small, compute the shared variables below it. + Recompute the set if there are merges that invalidate it. + Use the set to determine if a variable is shared. + */ + bool is_shared(theory_var v) const { + if (m_underspecified.empty()) { + return false; + } + enode * n = get_enode(v); + enode * r = n->get_root(); + unsigned usz = m_underspecified.size(); + TRACE("shared", tout << ctx().get_scope_level() << " " << v << " " << r->get_num_parents() << "\n";); + if (r->get_num_parents() > 2*usz) { + for (unsigned i = 0; i < usz; ++i) { + app* u = m_underspecified[i]; + unsigned sz = u->get_num_args(); + for (unsigned j = 0; j < sz; ++j) { + if (ctx().get_enode(u->get_arg(j))->get_root() == r) { return true; } } } - return false; } + else { + enode_vector::const_iterator it = r->begin_parents(); + enode_vector::const_iterator end = r->end_parents(); + for (; it != end; ++it) { + enode * parent = *it; + if (is_underspecified(parent->get_owner())) { + return true; + } + } + } + return false; + } - bool can_propagate() { + bool can_propagate() { #if 0 - if (ctx().at_base_level() && has_delayed_constraints()) { - // we could add the delayed constraints here directly to the tableau instead of using bounds variables. - } -#endif - return m_asserted_atoms.size() > m_asserted_qhead; + if (ctx().at_base_level() && has_delayed_constraints()) { + // we could add the delayed constraints here directly to the tableau instead of using bounds variables. } - - void propagate() { - flush_bound_axioms(); - if (!can_propagate()) { - return; - } - while (m_asserted_qhead < m_asserted_atoms.size() && !ctx().inconsistent()) { - bool_var bv = m_asserted_atoms[m_asserted_qhead].m_bv; - bool is_true = m_asserted_atoms[m_asserted_qhead].m_is_true; - -#if 1 - m_to_check.push_back(bv); -#else - propagate_bound(bv, is_true, b); #endif - lp_api::bound& b = *m_bool_var2bound.find(bv); - assert_bound(bv, is_true, b); + return m_asserted_atoms.size() > m_asserted_qhead; + } + + void propagate() { + flush_bound_axioms(); + if (!can_propagate()) { + return; + } + while (m_asserted_qhead < m_asserted_atoms.size() && !ctx().inconsistent()) { + bool_var bv = m_asserted_atoms[m_asserted_qhead].m_bv; + bool is_true = m_asserted_atoms[m_asserted_qhead].m_is_true; + +#if 1 + m_to_check.push_back(bv); +#else + propagate_bound(bv, is_true, b); +#endif + lp_api::bound& b = *m_bool_var2bound.find(bv); + assert_bound(bv, is_true, b); - ++m_asserted_qhead; - } - if (ctx().inconsistent()) { - m_to_check.reset(); - return; - } - /*for (; qhead < m_asserted_atoms.size() && !ctx().inconsistent(); ++qhead) { - bool_var bv = m_asserted_atoms[qhead].m_bv; - bool is_true = m_asserted_atoms[qhead].m_is_true; - lp_api::bound& b = *m_bool_var2bound.find(bv); - propagate_bound_compound(bv, is_true, b); - }*/ - - lbool lbl = make_feasible(); - - switch(lbl) { - case l_false: - TRACE("arith", tout << "propagation conflict\n";); - set_conflict(); - break; - case l_true: - propagate_basic_bounds(); - propagate_bounds_with_lp_solver(); - break; - case l_undef: - break; - } + ++m_asserted_qhead; } + if (ctx().inconsistent()) { + m_to_check.reset(); + return; + } + /*for (; qhead < m_asserted_atoms.size() && !ctx().inconsistent(); ++qhead) { + bool_var bv = m_asserted_atoms[qhead].m_bv; + bool is_true = m_asserted_atoms[qhead].m_is_true; + lp_api::bound& b = *m_bool_var2bound.find(bv); + propagate_bound_compound(bv, is_true, b); + }*/ - void propagate_bounds_with_lp_solver() { - if (BP_NONE == propagation_mode()) { - return; - } - int num_of_p = m_solver->settings().st().m_num_of_implied_bounds; - (void)num_of_p; - local_bound_propagator bp(*this); - m_solver->propagate_bounds_for_touched_rows(bp); - if (m.canceled()) { - return; - } - int new_num_of_p = m_solver->settings().st().m_num_of_implied_bounds; - (void)new_num_of_p; - CTRACE("arith", new_num_of_p > num_of_p, tout << "found " << new_num_of_p << " implied bounds\n";); - if (m_solver->get_status() == lp::lp_status::INFEASIBLE) { - set_conflict(); - } - else { - for (unsigned i = 0; !m.canceled() && !ctx().inconsistent() && i < bp.m_ibounds.size(); ++i) { - propagate_lp_solver_bound(bp.m_ibounds[i]); - } + lbool lbl = make_feasible(); + + switch(lbl) { + case l_false: + TRACE("arith", tout << "propagation conflict\n";); + set_conflict(); + break; + case l_true: + propagate_basic_bounds(); + propagate_bounds_with_lp_solver(); + break; + case l_undef: + break; + } + + } + + void propagate_bounds_with_lp_solver() { + if (BP_NONE == propagation_mode()) { + return; + } + int num_of_p = m_solver->settings().st().m_num_of_implied_bounds; + (void)num_of_p; + local_bound_propagator bp(*this); + m_solver->propagate_bounds_for_touched_rows(bp); + if (m.canceled()) { + return; + } + int new_num_of_p = m_solver->settings().st().m_num_of_implied_bounds; + (void)new_num_of_p; + CTRACE("arith", new_num_of_p > num_of_p, tout << "found " << new_num_of_p << " implied bounds\n";); + if (m_solver->get_status() == lp::lp_status::INFEASIBLE) { + set_conflict(); + } + else { + for (unsigned i = 0; !m.canceled() && !ctx().inconsistent() && i < bp.m_ibounds.size(); ++i) { + propagate_lp_solver_bound(bp.m_ibounds[i]); } } + } - bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational & bval) const { - theory_var v; - if (m_solver->is_term(vi)) { - v = m_term_index2theory_var.get(m_solver->adjust_term_index(vi), null_theory_var); - } - else { - v = m_var_index2theory_var.get(vi, null_theory_var); - } + bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational & bval) const { + theory_var v; + if (m_solver->is_term(vi)) { + v = m_term_index2theory_var.get(m_solver->adjust_term_index(vi), null_theory_var); + } + else { + v = m_var_index2theory_var.get(vi, null_theory_var); + } - if (v == null_theory_var) return false; + if (v == null_theory_var) return false; - if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { - TRACE("arith", tout << "return\n";); - return false; - } - lp_bounds const& bounds = m_bounds[v]; - for (unsigned i = 0; i < bounds.size(); ++i) { - lp_api::bound* b = bounds[i]; - if (ctx().get_assignment(b->get_bv()) != l_undef) { - continue; - } - literal lit = is_bound_implied(kind, bval, *b); - if (lit == null_literal) { - continue; - } - return true; - } + if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { + TRACE("arith", tout << "return\n";); return false; } - - struct local_bound_propagator: public lp::bound_propagator { - imp & m_imp; - local_bound_propagator(imp& i) : lp_bound_propagator(*i.m_solver), m_imp(i) {} - - bool bound_is_interesting(unsigned j, lp::lconstraint_kind kind, const rational & v) { - return m_imp.bound_is_interesting(j, kind, v); + lp_bounds const& bounds = m_bounds[v]; + for (unsigned i = 0; i < bounds.size(); ++i) { + lp_api::bound* b = bounds[i]; + if (ctx().get_assignment(b->get_bv()) != l_undef) { + continue; } - - void consume(rational const& v, unsigned j) override { - m_imp.set_evidence(j); - m_imp.m_explanation.push_back(std::make_pair(v, j)); + literal lit = is_bound_implied(kind, bval, *b); + if (lit == null_literal) { + continue; } - }; + return true; + } + return false; + } + + struct local_bound_propagator: public lp::bound_propagator { + imp & m_imp; + local_bound_propagator(imp& i) : bound_propagator(*i.m_solver), m_imp(i) {} + + bool bound_is_interesting(unsigned j, lp::lconstraint_kind kind, const rational & v) { + return m_imp.bound_is_interesting(j, kind, v); + } + + virtual void consume(rational const& v, unsigned j) { + m_imp.set_evidence(j); + m_imp.m_explanation.push_back(std::make_pair(v, j)); + } + }; - void propagate_lp_solver_bound(lp::implied_bound& be) { + void propagate_lp_solver_bound(lp::implied_bound& be) { - theory_var v; - lp::var_index vi = be.m_j; - if (m_solver->is_term(vi)) { - v = m_term_index2theory_var.get(m_solver->adjust_term_index(vi), null_theory_var); + theory_var v; + lp::var_index vi = be.m_j; + if (m_solver->is_term(vi)) { + v = m_term_index2theory_var.get(m_solver->adjust_term_index(vi), null_theory_var); + } + else { + v = m_var_index2theory_var.get(vi, null_theory_var); + } + + if (v == null_theory_var) return; + TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n"; + // if (m_unassigned_bounds[v] == 0) m_solver->print_bound_evidence(be, tout); + ); + + + if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { + TRACE("arith", tout << "return\n";); + return; + } + lp_bounds const& bounds = m_bounds[v]; + bool first = true; + for (unsigned i = 0; i < bounds.size(); ++i) { + lp_api::bound* b = bounds[i]; + if (ctx().get_assignment(b->get_bv()) != l_undef) { + continue; + } + literal lit = is_bound_implied(be.kind(), be.m_bound, *b); + if (lit == null_literal) { + continue; + } + TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); + + m_solver->settings().st().m_num_of_implied_bounds ++; + if (first) { + first = false; + m_core.reset(); + m_eqs.reset(); + m_params.reset(); + m_explanation.clear(); + local_bound_propagator bp(*this); + m_solver->explain_implied_bound(be, bp); + } + CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); + updt_unassigned_bounds(v, -1); + TRACE("arith", + ctx().display_literals_verbose(tout, m_core); + tout << "\n --> "; + ctx().display_literal_verbose(tout, lit); + tout << "\n"; + display_evidence(tout, m_explanation); + m_solver->print_implied_bound(be, tout); + ); + DEBUG_CODE( + for (auto& lit : m_core) { + SASSERT(ctx().get_assignment(lit) == l_true); + }); + ++m_stats.m_bound_propagations1; + assign(lit); + } + } + + literal_vector m_core2; + + void assign(literal lit) { + // SASSERT(validate_assign(lit)); + if (m_core.size() < small_lemma_size() && m_eqs.empty()) { + m_core2.reset(); + for (auto const& c : m_core) { + m_core2.push_back(~c); + } + m_core2.push_back(lit); + justification * js = 0; + if (proofs_enabled()) { + js = alloc(theory_lemma_justification, get_id(), ctx(), m_core2.size(), m_core2.c_ptr(), + m_params.size(), m_params.c_ptr()); + } + ctx().mk_clause(m_core2.size(), m_core2.c_ptr(), js, CLS_AUX_LEMMA, 0); + } + else { + ctx().assign( + lit, ctx().mk_justification( + ext_theory_propagation_justification( + get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), + m_eqs.size(), m_eqs.c_ptr(), lit, m_params.size(), m_params.c_ptr()))); + } + } + + literal is_bound_implied(lp::lconstraint_kind k, rational const& value, lp_api::bound const& b) const { + if ((k == lp::LE || k == lp::LT) && b.get_bound_kind() == lp_api::upper_t && value <= b.get_value()) { + TRACE("arith", tout << "v <= value <= b.get_value() => v <= b.get_value() \n";); + return literal(b.get_bv(), false); + } + if ((k == lp::GE || k == lp::GT) && b.get_bound_kind() == lp_api::lower_t && b.get_value() <= value) { + TRACE("arith", tout << "b.get_value() <= value <= v => b.get_value() <= v \n";); + return literal(b.get_bv(), false); + } + if (k == lp::LE && b.get_bound_kind() == lp_api::lower_t && value < b.get_value()) { + TRACE("arith", tout << "v <= value < b.get_value() => v < b.get_value()\n";); + return literal(b.get_bv(), true); + } + if (k == lp::LT && b.get_bound_kind() == lp_api::lower_t && value <= b.get_value()) { + TRACE("arith", tout << "v < value <= b.get_value() => v < b.get_value()\n";); + return literal(b.get_bv(), true); + } + if (k == lp::GE && b.get_bound_kind() == lp_api::upper_t && b.get_value() < value) { + TRACE("arith", tout << "b.get_value() < value <= v => b.get_value() < v\n";); + return literal(b.get_bv(), true); + } + if (k == lp::GT && b.get_bound_kind() == lp_api::upper_t && b.get_value() <= value) { + TRACE("arith", tout << "b.get_value() <= value < v => b.get_value() < v\n";); + return literal(b.get_bv(), true); + } + + return null_literal; + } + + void mk_bound_axioms(lp_api::bound& b) { + if (!ctx().is_searching()) { + // + // NB. We make an assumption that user push calls propagation + // before internal scopes are pushed. This flushes all newly + // asserted atoms into the right context. + // + m_new_bounds.push_back(&b); + return; + } + theory_var v = b.get_var(); + lp_api::bound_kind kind1 = b.get_bound_kind(); + rational const& k1 = b.get_value(); + lp_bounds & bounds = m_bounds[v]; + + lp_api::bound* end = 0; + lp_api::bound* lo_inf = end, *lo_sup = end; + lp_api::bound* hi_inf = end, *hi_sup = end; + + for (unsigned i = 0; i < bounds.size(); ++i) { + lp_api::bound& other = *bounds[i]; + if (&other == &b) continue; + if (b.get_bv() == other.get_bv()) continue; + lp_api::bound_kind kind2 = other.get_bound_kind(); + rational const& k2 = other.get_value(); + if (k1 == k2 && kind1 == kind2) { + // the bounds are equivalent. + continue; + } + + SASSERT(k1 != k2 || kind1 != kind2); + if (kind2 == lp_api::lower_t) { + if (k2 < k1) { + if (lo_inf == end || k2 > lo_inf->get_value()) { + lo_inf = &other; + } + } + else if (lo_sup == end || k2 < lo_sup->get_value()) { + lo_sup = &other; + } + } + else if (k2 < k1) { + if (hi_inf == end || k2 > hi_inf->get_value()) { + hi_inf = &other; + } + } + else if (hi_sup == end || k2 < hi_sup->get_value()) { + hi_sup = &other; + } + } + if (lo_inf != end) mk_bound_axiom(b, *lo_inf); + if (lo_sup != end) mk_bound_axiom(b, *lo_sup); + if (hi_inf != end) mk_bound_axiom(b, *hi_inf); + if (hi_sup != end) mk_bound_axiom(b, *hi_sup); + } + + + void mk_bound_axiom(lp_api::bound& b1, lp_api::bound& b2) { + theory_var v = b1.get_var(); + literal l1(b1.get_bv()); + literal l2(b2.get_bv()); + rational const& k1 = b1.get_value(); + rational const& k2 = b2.get_value(); + lp_api::bound_kind kind1 = b1.get_bound_kind(); + lp_api::bound_kind kind2 = b2.get_bound_kind(); + bool v_is_int = is_int(v); + SASSERT(v == b2.get_var()); + if (k1 == k2 && kind1 == kind2) return; + SASSERT(k1 != k2 || kind1 != kind2); + parameter coeffs[3] = { parameter(symbol("farkas")), + parameter(rational(1)), parameter(rational(1)) }; + + if (kind1 == lp_api::lower_t) { + if (kind2 == lp_api::lower_t) { + if (k2 <= k1) { + mk_clause(~l1, l2, 3, coeffs); + } + else { + mk_clause(l1, ~l2, 3, coeffs); + } + } + else if (k1 <= k2) { + // k1 <= k2, k1 <= x or x <= k2 + mk_clause(l1, l2, 3, coeffs); } else { - v = m_var_index2theory_var.get(vi, null_theory_var); + // k1 > hi_inf, k1 <= x => ~(x <= hi_inf) + mk_clause(~l1, ~l2, 3, coeffs); + if (v_is_int && k1 == k2 + rational(1)) { + // k1 <= x or x <= k1-1 + mk_clause(l1, l2, 3, coeffs); + } } - - if (v == null_theory_var) return; - TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n"; - // if (m_unassigned_bounds[v] == 0) m_solver->print_bound_evidence(be, tout); - ); - - - if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { - TRACE("arith", tout << "return\n";); - return; + } + else if (kind2 == lp_api::lower_t) { + if (k1 >= k2) { + // k1 >= lo_inf, k1 >= x or lo_inf <= x + mk_clause(l1, l2, 3, coeffs); } - lp_bounds const& bounds = m_bounds[v]; - bool first = true; + else { + // k1 < k2, k2 <= x => ~(x <= k1) + mk_clause(~l1, ~l2, 3, coeffs); + if (v_is_int && k1 == k2 - rational(1)) { + // x <= k1 or k1+l <= x + mk_clause(l1, l2, 3, coeffs); + } + + } + } + else { + // kind1 == A_UPPER, kind2 == A_UPPER + if (k1 >= k2) { + // k1 >= k2, x <= k2 => x <= k1 + mk_clause(l1, ~l2, 3, coeffs); + } + else { + // k1 <= hi_sup , x <= k1 => x <= hi_sup + mk_clause(~l1, l2, 3, coeffs); + } + } + } + + typedef lp_bounds::iterator iterator; + + void flush_bound_axioms() { + CTRACE("arith", !m_new_bounds.empty(), tout << "flush bound axioms\n";); + + while (!m_new_bounds.empty()) { + lp_bounds atoms; + atoms.push_back(m_new_bounds.back()); + m_new_bounds.pop_back(); + theory_var v = atoms.back()->get_var(); + for (unsigned i = 0; i < m_new_bounds.size(); ++i) { + if (m_new_bounds[i]->get_var() == v) { + atoms.push_back(m_new_bounds[i]); + m_new_bounds[i] = m_new_bounds.back(); + m_new_bounds.pop_back(); + --i; + } + } + CTRACE("arith_verbose", !atoms.empty(), + for (unsigned i = 0; i < atoms.size(); ++i) { + atoms[i]->display(tout); tout << "\n"; + }); + lp_bounds occs(m_bounds[v]); + + std::sort(atoms.begin(), atoms.end(), compare_bounds()); + std::sort(occs.begin(), occs.end(), compare_bounds()); + + iterator begin1 = occs.begin(); + iterator begin2 = occs.begin(); + iterator end = occs.end(); + begin1 = first(lp_api::lower_t, begin1, end); + begin2 = first(lp_api::upper_t, begin2, end); + + iterator lo_inf = begin1, lo_sup = begin1; + iterator hi_inf = begin2, hi_sup = begin2; + iterator lo_inf1 = begin1, lo_sup1 = begin1; + iterator hi_inf1 = begin2, hi_sup1 = begin2; + bool flo_inf, fhi_inf, flo_sup, fhi_sup; + ptr_addr_hashtable visited; + for (unsigned i = 0; i < atoms.size(); ++i) { + lp_api::bound* a1 = atoms[i]; + lo_inf1 = next_inf(a1, lp_api::lower_t, lo_inf, end, flo_inf); + hi_inf1 = next_inf(a1, lp_api::upper_t, hi_inf, end, fhi_inf); + lo_sup1 = next_sup(a1, lp_api::lower_t, lo_sup, end, flo_sup); + hi_sup1 = next_sup(a1, lp_api::upper_t, hi_sup, end, fhi_sup); + if (lo_inf1 != end) lo_inf = lo_inf1; + if (lo_sup1 != end) lo_sup = lo_sup1; + if (hi_inf1 != end) hi_inf = hi_inf1; + if (hi_sup1 != end) hi_sup = hi_sup1; + if (!flo_inf) lo_inf = end; + if (!fhi_inf) hi_inf = end; + if (!flo_sup) lo_sup = end; + if (!fhi_sup) hi_sup = end; + visited.insert(a1); + if (lo_inf1 != end && lo_inf != end && !visited.contains(*lo_inf)) mk_bound_axiom(*a1, **lo_inf); + if (lo_sup1 != end && lo_sup != end && !visited.contains(*lo_sup)) mk_bound_axiom(*a1, **lo_sup); + if (hi_inf1 != end && hi_inf != end && !visited.contains(*hi_inf)) mk_bound_axiom(*a1, **hi_inf); + if (hi_sup1 != end && hi_sup != end && !visited.contains(*hi_sup)) mk_bound_axiom(*a1, **hi_sup); + } + } + } + + struct compare_bounds { + bool operator()(lp_api::bound* a1, lp_api::bound* a2) const { return a1->get_value() < a2->get_value(); } + }; + + + lp_bounds::iterator first( + lp_api::bound_kind kind, + iterator it, + iterator end) { + for (; it != end; ++it) { + lp_api::bound* a = *it; + if (a->get_bound_kind() == kind) return it; + } + return end; + } + + lp_bounds::iterator next_inf( + lp_api::bound* a1, + lp_api::bound_kind kind, + iterator it, + iterator end, + bool& found_compatible) { + rational const & k1(a1->get_value()); + iterator result = end; + found_compatible = false; + for (; it != end; ++it) { + lp_api::bound * a2 = *it; + if (a1 == a2) continue; + if (a2->get_bound_kind() != kind) continue; + rational const & k2(a2->get_value()); + found_compatible = true; + if (k2 <= k1) { + result = it; + } + else { + break; + } + } + return result; + } + + lp_bounds::iterator next_sup( + lp_api::bound* a1, + lp_api::bound_kind kind, + iterator it, + iterator end, + bool& found_compatible) { + rational const & k1(a1->get_value()); + found_compatible = false; + for (; it != end; ++it) { + lp_api::bound * a2 = *it; + if (a1 == a2) continue; + if (a2->get_bound_kind() != kind) continue; + rational const & k2(a2->get_value()); + found_compatible = true; + if (k1 < k2) { + return it; + } + } + return end; + } + + void propagate_basic_bounds() { + for (auto const& bv : m_to_check) { + lp_api::bound& b = *m_bool_var2bound.find(bv); + propagate_bound(bv, ctx().get_assignment(bv) == l_true, b); + if (ctx().inconsistent()) break; + + } + m_to_check.reset(); + } + + // for glb lo': lo' < lo: + // lo <= x -> lo' <= x + // lo <= x -> ~(x <= lo') + // for lub hi': hi' > hi + // x <= hi -> x <= hi' + // x <= hi -> ~(x >= hi') + + void propagate_bound(bool_var bv, bool is_true, lp_api::bound& b) { + if (BP_NONE == propagation_mode()) { + return; + } + lp_api::bound_kind k = b.get_bound_kind(); + theory_var v = b.get_var(); + inf_rational val = b.get_value(is_true); + lp_bounds const& bounds = m_bounds[v]; + SASSERT(!bounds.empty()); + if (bounds.size() == 1) return; + if (m_unassigned_bounds[v] == 0) return; + + literal lit1(bv, !is_true); + literal lit2 = null_literal; + bool find_glb = (is_true == (k == lp_api::lower_t)); + if (find_glb) { + rational glb; + lp_api::bound* lb = 0; for (unsigned i = 0; i < bounds.size(); ++i) { - lp_api::bound* b = bounds[i]; - if (ctx().get_assignment(b->get_bv()) != l_undef) { - continue; + lp_api::bound* b2 = bounds[i]; + if (b2 == &b) continue; + rational const& val2 = b2->get_value(); + if ((is_true ? val2 < val : val2 <= val) && (!lb || glb < val2)) { + lb = b2; + glb = val2; } - literal lit = is_bound_implied(be.kind(), be.m_bound, *b); - if (lit == null_literal) { - continue; + } + if (!lb) return; + bool sign = lb->get_bound_kind() != lp_api::lower_t; + lit2 = literal(lb->get_bv(), sign); + } + else { + rational lub; + lp_api::bound* ub = 0; + for (unsigned i = 0; i < bounds.size(); ++i) { + lp_api::bound* b2 = bounds[i]; + if (b2 == &b) continue; + rational const& val2 = b2->get_value(); + if ((is_true ? val < val2 : val <= val2) && (!ub || val2 < lub)) { + ub = b2; + lub = val2; } - TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); + } + if (!ub) return; + bool sign = ub->get_bound_kind() != lp_api::upper_t; + lit2 = literal(ub->get_bv(), sign); + } + TRACE("arith", + ctx().display_literal_verbose(tout, lit1); + ctx().display_literal_verbose(tout << " => ", lit2); + tout << "\n";); + updt_unassigned_bounds(v, -1); + ++m_stats.m_bound_propagations2; + m_params.reset(); + m_core.reset(); + m_eqs.reset(); + m_core.push_back(lit2); + m_params.push_back(parameter(symbol("farkas"))); + m_params.push_back(parameter(rational(1))); + m_params.push_back(parameter(rational(1))); + assign(lit2); + ++m_stats.m_bounds_propagations; + } - m_solver->settings().st().m_num_of_implied_bounds ++; - if (first) { - first = false; - m_core.reset(); - m_eqs.reset(); - m_params.reset(); - m_explanation.clear(); - local_bound_propagator bp(*this); - m_solver->explain_implied_bound(be, bp); + svector m_todo_vars; + + void add_use_lists(lp_api::bound* b) { + theory_var v = b->get_var(); + lp::var_index vi = get_var_index(v); + if (!m_solver->is_term(vi)) { + return; + } + m_todo_vars.push_back(vi); + while (!m_todo_vars.empty()) { + vi = m_todo_vars.back(); + m_todo_vars.pop_back(); + lp::lar_term const& term = m_solver->get_term(vi); + for (auto i = term.m_coeffs.begin(); i != term.m_coeffs.end(); ++i) { + lp::var_index wi = i->first; + if (m_solver->is_term(wi)) { + m_todo_vars.push_back(wi); } - CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); - updt_unassigned_bounds(v, -1); + else { + unsigned w = m_var_index2theory_var[wi]; + m_use_list.reserve(w + 1, ptr_vector()); + m_use_list[w].push_back(b); + } + } + } + } + + void del_use_lists(lp_api::bound* b) { + theory_var v = b->get_var(); + lp::var_index vi = m_theory_var2var_index[v]; + if (!m_solver->is_term(vi)) { + return; + } + m_todo_vars.push_back(vi); + while (!m_todo_vars.empty()) { + vi = m_todo_vars.back(); + m_todo_vars.pop_back(); + lp::lar_term const& term = m_solver->get_term(vi); + for (auto i = term.m_coeffs.begin(); i != term.m_coeffs.end(); ++i) { + lp::var_index wi = i->first; + if (m_solver->is_term(wi)) { + m_todo_vars.push_back(wi); + } + else { + unsigned w = m_var_index2theory_var[wi]; + SASSERT(m_use_list[w].back() == b); + m_use_list[w].pop_back(); + } + } + } + } + + // + // propagate bounds to compound terms + // The idea is that if bounds on all variables in an inequality ax + by + cz >= k + // have been assigned we may know the truth value of the inequality by using simple + // bounds propagation. + // + void propagate_bound_compound(bool_var bv, bool is_true, lp_api::bound& b) { + theory_var v = b.get_var(); + TRACE("arith", tout << mk_pp(get_owner(v), m) << "\n";); + if (static_cast(v) >= m_use_list.size()) { + return; + } + for (auto const& vb : m_use_list[v]) { + if (ctx().get_assignment(vb->get_bv()) != l_undef) { + TRACE("arith_verbose", display_bound(tout << "assigned ", *vb) << "\n";); + continue; + } + inf_rational r; + // x + y + // x >= 0, y >= 1 -> x + y >= 1 + // x <= 0, y <= 2 -> x + y <= 2 + literal lit = null_literal; + if (lp_api::lower_t == vb->get_bound_kind()) { + if (get_glb(*vb, r) && r >= vb->get_value()) { // vb is assigned true + lit = literal(vb->get_bv(), false); + } + else if (get_lub(*vb, r) && r < vb->get_value()) { // vb is assigned false + lit = literal(vb->get_bv(), true); + } + } + else { + if (get_glb(*vb, r) && r > vb->get_value()) { // VB <= value < val(VB) + lit = literal(vb->get_bv(), true); + } + else if (get_lub(*vb, r) && r <= vb->get_value()) { // val(VB) <= value + lit = literal(vb->get_bv(), false); + } + } + + if (lit != null_literal) { TRACE("arith", ctx().display_literals_verbose(tout, m_core); tout << "\n --> "; ctx().display_literal_verbose(tout, lit); tout << "\n"; - display_evidence(tout, m_explanation); - m_solver->print_implied_bound(be, tout); ); - DEBUG_CODE( - for (auto& lit : m_core) { - SASSERT(ctx().get_assignment(lit) == l_true); - }); - ++m_stats.m_bound_propagations1; + + assign(lit); } - } - - literal_vector m_core2; - - void assign(literal lit) { - SASSERT(validate_assign(lit)); - if (m_core.size() < small_lemma_size() && m_eqs.empty()) { - m_core2.reset(); - for (auto const& c : m_core) { - m_core2.push_back(~c); - } - m_core2.push_back(lit); - justification * js = nullptr; - if (proofs_enabled()) { - js = alloc(theory_lemma_justification, get_id(), ctx(), m_core2.size(), m_core2.c_ptr(), - m_params.size(), m_params.c_ptr()); - } - ctx().mk_clause(m_core2.size(), m_core2.c_ptr(), js, CLS_AUX_LEMMA, nullptr); - } else { - ctx().assign( - lit, ctx().mk_justification( - ext_theory_propagation_justification( - get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), - m_eqs.size(), m_eqs.c_ptr(), lit, m_params.size(), m_params.c_ptr()))); + TRACE("arith_verbose", display_bound(tout << "skip ", *vb) << "\n";); } } + } - literal is_bound_implied(lp::lconstraint_kind k, rational const& value, lp_api::bound const& b) const { - if ((k == lp::LE || k == lp::LT) && b.get_bound_kind() == lp_api::upper_t && value <= b.get_value()) { - TRACE("arith", tout << "v <= value <= b.get_value() => v <= b.get_value() \n";); - return literal(b.get_bv(), false); - } - if ((k == lp::GE || k == lp::GT) && b.get_bound_kind() == lp_api::lower_t && b.get_value() <= value) { - TRACE("arith", tout << "b.get_value() <= value <= v => b.get_value() <= v \n";); - return literal(b.get_bv(), false); - } - if (k == lp::LE && b.get_bound_kind() == lp_api::lower_t && value < b.get_value()) { - TRACE("arith", tout << "v <= value < b.get_value() => v < b.get_value()\n";); - return literal(b.get_bv(), true); - } - if (k == lp::LT && b.get_bound_kind() == lp_api::lower_t && value <= b.get_value()) { - TRACE("arith", tout << "v < value <= b.get_value() => v < b.get_value()\n";); - return literal(b.get_bv(), true); - } - if (k == lp::GE && b.get_bound_kind() == lp_api::upper_t && b.get_value() < value) { - TRACE("arith", tout << "b.get_value() < value <= v => b.get_value() < v\n";); - return literal(b.get_bv(), true); - } - if (k == lp::GT && b.get_bound_kind() == lp_api::upper_t && b.get_value() <= value) { - TRACE("arith", tout << "b.get_value() <= value < v => b.get_value() < v\n";); - return literal(b.get_bv(), true); - } + bool get_lub(lp_api::bound const& b, inf_rational& lub) { + return get_bound(b, lub, true); + } - return null_literal; - } + bool get_glb(lp_api::bound const& b, inf_rational& glb) { + return get_bound(b, glb, false); + } - void mk_bound_axioms(lp_api::bound& b) { - if (!ctx().is_searching()) { - // - // NB. We make an assumption that user push calls propagation - // before internal scopes are pushed. This flushes all newly - // asserted atoms into the right context. - // - m_new_bounds.push_back(&b); - return; - } - theory_var v = b.get_var(); - lp_api::bound_kind kind1 = b.get_bound_kind(); - rational const& k1 = b.get_value(); - lp_bounds & bounds = m_bounds[v]; + std::ostream& display_bound(std::ostream& out, lp_api::bound const& b) { + return out << mk_pp(ctx().bool_var2expr(b.get_bv()), m); + } - lp_api::bound* end = 0; - lp_api::bound* lo_inf = end, *lo_sup = end; - lp_api::bound* hi_inf = end, *hi_sup = end; - - for (unsigned i = 0; i < bounds.size(); ++i) { - lp_api::bound& other = *bounds[i]; - if (&other == &b) continue; - if (b.get_bv() == other.get_bv()) continue; - lp_api::bound_kind kind2 = other.get_bound_kind(); - rational const& k2 = other.get_value(); - if (k1 == k2 && kind1 == kind2) { - // the bounds are equivalent. - continue; - } - - SASSERT(k1 != k2 || kind1 != kind2); - if (kind2 == lp_api::lower_t) { - if (k2 < k1) { - if (lo_inf == end || k2 > lo_inf->get_value()) { - lo_inf = &other; - } - } - else if (lo_sup == end || k2 < lo_sup->get_value()) { - lo_sup = &other; - } - } - else if (k2 < k1) { - if (hi_inf == end || k2 > hi_inf->get_value()) { - hi_inf = &other; - } - } - else if (hi_sup == end || k2 < hi_sup->get_value()) { - hi_sup = &other; - } - } - if (lo_inf != end) mk_bound_axiom(b, *lo_inf); - if (lo_sup != end) mk_bound_axiom(b, *lo_sup); - if (hi_inf != end) mk_bound_axiom(b, *hi_inf); - if (hi_sup != end) mk_bound_axiom(b, *hi_sup); - } - - - void mk_bound_axiom(lp_api::bound& b1, lp_api::bound& b2) { - theory_var v = b1.get_var(); - literal l1(b1.get_bv()); - literal l2(b2.get_bv()); - rational const& k1 = b1.get_value(); - rational const& k2 = b2.get_value(); - lp_api::bound_kind kind1 = b1.get_bound_kind(); - lp_api::bound_kind kind2 = b2.get_bound_kind(); - bool v_is_int = is_int(v); - SASSERT(v == b2.get_var()); - if (k1 == k2 && kind1 == kind2) return; - SASSERT(k1 != k2 || kind1 != kind2); - parameter coeffs[3] = { parameter(symbol("farkas")), - parameter(rational(1)), parameter(rational(1)) }; - - if (kind1 == lp_api::lower_t) { - if (kind2 == lp_api::lower_t) { - if (k2 <= k1) { - mk_clause(~l1, l2, 3, coeffs); - } - else { - mk_clause(l1, ~l2, 3, coeffs); - } - } - else if (k1 <= k2) { - // k1 <= k2, k1 <= x or x <= k2 - mk_clause(l1, l2, 3, coeffs); - } - else { - // k1 > hi_inf, k1 <= x => ~(x <= hi_inf) - mk_clause(~l1, ~l2, 3, coeffs); - if (v_is_int && k1 == k2 + rational(1)) { - // k1 <= x or x <= k1-1 - mk_clause(l1, l2, 3, coeffs); - } - } - } - else if (kind2 == lp_api::lower_t) { - if (k1 >= k2) { - // k1 >= lo_inf, k1 >= x or lo_inf <= x - mk_clause(l1, l2, 3, coeffs); - } - else { - // k1 < k2, k2 <= x => ~(x <= k1) - mk_clause(~l1, ~l2, 3, coeffs); - if (v_is_int && k1 == k2 - rational(1)) { - // x <= k1 or k1+l <= x - mk_clause(l1, l2, 3, coeffs); - } - - } - } - else { - // kind1 == A_UPPER, kind2 == A_UPPER - if (k1 >= k2) { - // k1 >= k2, x <= k2 => x <= k1 - mk_clause(l1, ~l2, 3, coeffs); - } - else { - // k1 <= hi_sup , x <= k1 => x <= hi_sup - mk_clause(~l1, l2, 3, coeffs); - } - } - } - - typedef lp_bounds::iterator iterator; - - void flush_bound_axioms() { - CTRACE("arith", !m_new_bounds.empty(), tout << "flush bound axioms\n";); - - while (!m_new_bounds.empty()) { - lp_bounds atoms; - atoms.push_back(m_new_bounds.back()); - m_new_bounds.pop_back(); - theory_var v = atoms.back()->get_var(); - for (unsigned i = 0; i < m_new_bounds.size(); ++i) { - if (m_new_bounds[i]->get_var() == v) { - atoms.push_back(m_new_bounds[i]); - m_new_bounds[i] = m_new_bounds.back(); - m_new_bounds.pop_back(); - --i; - } - } - CTRACE("arith_verbose", !atoms.empty(), - for (unsigned i = 0; i < atoms.size(); ++i) { - atoms[i]->display(tout); tout << "\n"; - }); - lp_bounds occs(m_bounds[v]); - - std::sort(atoms.begin(), atoms.end(), compare_bounds()); - std::sort(occs.begin(), occs.end(), compare_bounds()); - - iterator begin1 = occs.begin(); - iterator begin2 = occs.begin(); - iterator end = occs.end(); - begin1 = first(lp_api::lower_t, begin1, end); - begin2 = first(lp_api::upper_t, begin2, end); - - iterator lo_inf = begin1, lo_sup = begin1; - iterator hi_inf = begin2, hi_sup = begin2; - iterator lo_inf1 = begin1, lo_sup1 = begin1; - iterator hi_inf1 = begin2, hi_sup1 = begin2; - bool flo_inf, fhi_inf, flo_sup, fhi_sup; - ptr_addr_hashtable visited; - for (unsigned i = 0; i < atoms.size(); ++i) { - lp_api::bound* a1 = atoms[i]; - lo_inf1 = next_inf(a1, lp_api::lower_t, lo_inf, end, flo_inf); - hi_inf1 = next_inf(a1, lp_api::upper_t, hi_inf, end, fhi_inf); - lo_sup1 = next_sup(a1, lp_api::lower_t, lo_sup, end, flo_sup); - hi_sup1 = next_sup(a1, lp_api::upper_t, hi_sup, end, fhi_sup); - if (lo_inf1 != end) lo_inf = lo_inf1; - if (lo_sup1 != end) lo_sup = lo_sup1; - if (hi_inf1 != end) hi_inf = hi_inf1; - if (hi_sup1 != end) hi_sup = hi_sup1; - if (!flo_inf) lo_inf = end; - if (!fhi_inf) hi_inf = end; - if (!flo_sup) lo_sup = end; - if (!fhi_sup) hi_sup = end; - visited.insert(a1); - if (lo_inf1 != end && lo_inf != end && !visited.contains(*lo_inf)) mk_bound_axiom(*a1, **lo_inf); - if (lo_sup1 != end && lo_sup != end && !visited.contains(*lo_sup)) mk_bound_axiom(*a1, **lo_sup); - if (hi_inf1 != end && hi_inf != end && !visited.contains(*hi_inf)) mk_bound_axiom(*a1, **hi_inf); - if (hi_sup1 != end && hi_sup != end && !visited.contains(*hi_sup)) mk_bound_axiom(*a1, **hi_sup); - } - } - } - - struct compare_bounds { - bool operator()(lp_api::bound* a1, lp_api::bound* a2) const { return a1->get_value() < a2->get_value(); } - }; - - - lp_bounds::iterator first( - lp_api::bound_kind kind, - iterator it, - iterator end) { - for (; it != end; ++it) { - lp_api::bound* a = *it; - if (a->get_bound_kind() == kind) return it; - } - return end; - } - - lp_bounds::iterator next_inf( - lp_api::bound* a1, - lp_api::bound_kind kind, - iterator it, - iterator end, - bool& found_compatible) { - rational const & k1(a1->get_value()); - iterator result = end; - found_compatible = false; - for (; it != end; ++it) { - lp_api::bound * a2 = *it; - if (a1 == a2) continue; - if (a2->get_bound_kind() != kind) continue; - rational const & k2(a2->get_value()); - found_compatible = true; - if (k2 <= k1) { - result = it; - } - else { - break; - } - } - return result; - } - - lp_bounds::iterator next_sup( - lp_api::bound* a1, - lp_api::bound_kind kind, - iterator it, - iterator end, - bool& found_compatible) { - rational const & k1(a1->get_value()); - found_compatible = false; - for (; it != end; ++it) { - lp_api::bound * a2 = *it; - if (a1 == a2) continue; - if (a2->get_bound_kind() != kind) continue; - rational const & k2(a2->get_value()); - found_compatible = true; - if (k1 < k2) { - return it; - } - } - return end; - } - - void propagate_basic_bounds() { - for (auto const& bv : m_to_check) { - lp_api::bound& b = *m_bool_var2bound.find(bv); - propagate_bound(bv, ctx().get_assignment(bv) == l_true, b); - if (ctx().inconsistent()) break; - - } - m_to_check.reset(); - } - - // for glb lo': lo' < lo: - // lo <= x -> lo' <= x - // lo <= x -> ~(x <= lo') - // for lub hi': hi' > hi - // x <= hi -> x <= hi' - // x <= hi -> ~(x >= hi') - - void propagate_bound(bool_var bv, bool is_true, lp_api::bound& b) { - if (BP_NONE == propagation_mode()) { - return; - } - lp_api::bound_kind k = b.get_bound_kind(); - theory_var v = b.get_var(); - inf_rational val = b.get_value(is_true); - lp_bounds const& bounds = m_bounds[v]; - SASSERT(!bounds.empty()); - if (bounds.size() == 1) return; - if (m_unassigned_bounds[v] == 0) return; - - literal lit1(bv, !is_true); - literal lit2 = null_literal; - bool find_glb = (is_true == (k == lp_api::lower_t)); - if (find_glb) { - rational glb; - lp_api::bound* lb = 0; - for (unsigned i = 0; i < bounds.size(); ++i) { - lp_api::bound* b2 = bounds[i]; - if (b2 == &b) continue; - rational const& val2 = b2->get_value(); - if ((is_true ? val2 < val : val2 <= val) && (!lb || glb < val2)) { - lb = b2; - glb = val2; - } - } - if (!lb) return; - bool sign = lb->get_bound_kind() != lp_api::lower_t; - lit2 = literal(lb->get_bv(), sign); - } - else { - rational lub; - lp_api::bound* ub = 0; - for (unsigned i = 0; i < bounds.size(); ++i) { - lp_api::bound* b2 = bounds[i]; - if (b2 == &b) continue; - rational const& val2 = b2->get_value(); - if ((is_true ? val < val2 : val <= val2) && (!ub || val2 < lub)) { - ub = b2; - lub = val2; - } - } - if (!ub) return; - bool sign = ub->get_bound_kind() != lp_api::upper_t; - lit2 = literal(ub->get_bv(), sign); - } - TRACE("arith", - ctx().display_literal_verbose(tout, lit1); - ctx().display_literal_verbose(tout << " => ", lit2); - tout << "\n";); - updt_unassigned_bounds(v, -1); - ++m_stats.m_bound_propagations2; - m_params.reset(); - m_core.reset(); - m_eqs.reset(); - m_core.push_back(lit2); - m_params.push_back(parameter(symbol("farkas"))); - m_params.push_back(parameter(rational(1))); - m_params.push_back(parameter(rational(1))); - assign(lit2); - ++m_stats.m_bounds_propagations; - } - - svector m_todo_vars; - - void add_use_lists(lp_api::bound* b) { - theory_var v = b->get_var(); - lp::var_index vi = get_var_index(v); - if (!m_solver->is_term(vi)) { - return; - } - m_todo_vars.push_back(vi); - while (!m_todo_vars.empty()) { - vi = m_todo_vars.back(); - m_todo_vars.pop_back(); - lp::lar_term const& term = m_solver->get_term(vi); - for (auto i = term.m_coeffs.begin(); i != term.m_coeffs.end(); ++i) { - lp::var_index wi = i->first; - if (m_solver->is_term(wi)) { - m_todo_vars.push_back(wi); - } - else { - unsigned w = m_var_index2theory_var[wi]; - m_use_list.reserve(w + 1, ptr_vector()); - m_use_list[w].push_back(b); - } - } - } - } - - void del_use_lists(lp_api::bound* b) { - theory_var v = b->get_var(); - lp::var_index vi = m_theory_var2var_index[v]; - if (!m_solver->is_term(vi)) { - return; - } - m_todo_vars.push_back(vi); - while (!m_todo_vars.empty()) { - vi = m_todo_vars.back(); - m_todo_vars.pop_back(); - lp::lar_term const& term = m_solver->get_term(vi); - for (auto i = term.m_coeffs.begin(); i != term.m_coeffs.end(); ++i) { - lp::var_index wi = i->first; - if (m_solver->is_term(wi)) { - m_todo_vars.push_back(wi); - } - else { - unsigned w = m_var_index2theory_var[wi]; - SASSERT(m_use_list[w].back() == b); - m_use_list[w].pop_back(); - } - } - } - } - - // - // propagate bounds to compound terms - // The idea is that if bounds on all variables in an inequality ax + by + cz >= k - // have been assigned we may know the truth value of the inequality by using simple - // bounds propagation. - // - void propagate_bound_compound(bool_var bv, bool is_true, lp_api::bound& b) { - theory_var v = b.get_var(); - TRACE("arith", tout << mk_pp(get_owner(v), m) << "\n";); - if (static_cast(v) >= m_use_list.size()) { - return; - } - for (auto const& vb : m_use_list[v]) { - if (ctx().get_assignment(vb->get_bv()) != l_undef) { - TRACE("arith_verbose", display_bound(tout << "assigned ", *vb) << "\n";); - continue; - } - inf_rational r; - // x + y - // x >= 0, y >= 1 -> x + y >= 1 - // x <= 0, y <= 2 -> x + y <= 2 - literal lit = null_literal; - if (lp_api::lower_t == vb->get_bound_kind()) { - if (get_glb(*vb, r) && r >= vb->get_value()) { // vb is assigned true - lit = literal(vb->get_bv(), false); - } - else if (get_lub(*vb, r) && r < vb->get_value()) { // vb is assigned false - lit = literal(vb->get_bv(), true); - } - } - else { - if (get_glb(*vb, r) && r > vb->get_value()) { // VB <= value < val(VB) - lit = literal(vb->get_bv(), true); - } - else if (get_lub(*vb, r) && r <= vb->get_value()) { // val(VB) <= value - lit = literal(vb->get_bv(), false); - } - } - - if (lit != null_literal) { - TRACE("arith", - ctx().display_literals_verbose(tout, m_core); - tout << "\n --> "; - ctx().display_literal_verbose(tout, lit); - tout << "\n"; - ); - - - assign(lit); - } - else { - TRACE("arith_verbose", display_bound(tout << "skip ", *vb) << "\n";); - } - } - } - - bool get_lub(lp_api::bound const& b, inf_rational& lub) { - return get_bound(b, lub, true); - } - - bool get_glb(lp_api::bound const& b, inf_rational& glb) { - return get_bound(b, glb, false); - } - - std::ostream& display_bound(std::ostream& out, lp_api::bound const& b) { - return out << mk_pp(ctx().bool_var2expr(b.get_bv()), m); - } - - bool get_bound(lp_api::bound const& b, inf_rational& r, bool is_lub) { - m_core.reset(); - m_eqs.reset(); - m_params.reset(); - r.reset(); - theory_var v = b.get_var(); - lp::var_index vi = m_theory_var2var_index[v]; - SASSERT(m_solver->is_term(vi)); - lp::lar_term const& term = m_solver->get_term(vi); - for (auto const coeff : term.m_coeffs) { - lp::var_index wi = coeff.first; - lp::constraint_index ci; - rational value; - bool is_strict; - if (m_solver->is_term(wi)) { - return false; - } - if (coeff.second.is_neg() == is_lub) { - // -3*x ... <= lub based on lower bound for x. - if (!m_solver->has_lower_bound(wi, ci, value, is_strict)) { - return false; - } - if (is_strict) { - r += inf_rational(rational::zero(), coeff.second.is_pos()); - } - } - else { - if (!m_solver->has_upper_bound(wi, ci, value, is_strict)) { - return false; - } - if (is_strict) { - r += inf_rational(rational::zero(), coeff.second.is_pos()); - } - } - r += value * coeff.second; - set_evidence(ci); - } - TRACE("arith_verbose", tout << (is_lub?"lub":"glb") << " is " << r << "\n";); - return true; - } - - void assert_bound(bool_var bv, bool is_true, lp_api::bound& b) { - if (m_solver->get_status() == lp::lp_status::INFEASIBLE) { - return; - } - scoped_internalize_state st(*this); - st.vars().push_back(b.get_var()); - st.coeffs().push_back(rational::one()); - init_left_side(st); - lp::lconstraint_kind k = lp::EQ; - bool is_int = b.is_int(); - switch (b.get_bound_kind()) { - case lp_api::lower_t: - k = is_true ? lp::GE : (is_int ? lp::LE : lp::LT); - break; - case lp_api::upper_t: - k = is_true ? lp::LE : (is_int ? lp::GE : lp::GT); - break; - } - if (k == lp::LT || k == lp::LE) { - ++m_stats.m_assert_lower; - } - else { - ++m_stats.m_assert_upper; - } - auto vi = get_var_index(b.get_var()); - rational bound = b.get_value(); + bool get_bound(lp_api::bound const& b, inf_rational& r, bool is_lub) { + m_core.reset(); + m_eqs.reset(); + m_params.reset(); + r.reset(); + theory_var v = b.get_var(); + lp::var_index vi = m_theory_var2var_index[v]; + SASSERT(m_solver->is_term(vi)); + lp::lar_term const& term = m_solver->get_term(vi); + for (auto const coeff : term.m_coeffs) { + lp::var_index wi = coeff.first; lp::constraint_index ci; - if (is_int && !is_true) { - rational bound = b.get_value(false).get_rational(); - ci = m_solver->add_var_bound(vi, k, bound); + rational value; + bool is_strict; + if (m_solver->is_term(wi)) { + return false; } - else { - ci = m_solver->add_var_bound(vi, k, b.get_value()); - } - TRACE("arith", tout << "v" << b.get_var() << "\n";); - add_ineq_constraint(ci, literal(bv, !is_true)); - - propagate_eqs(vi, ci, k, b); - } - - // - // fixed equalities. - // A fixed equality is inferred if there are two variables v1, v2 whose - // upper and lower bounds coincide. - // Then the equality v1 == v2 is propagated to the core. - // - - typedef std::pair constraint_bound; - vector m_lower_terms; - vector m_upper_terms; - typedef std::pair value_sort_pair; - typedef pair_hash, bool_hash> value_sort_pair_hash; - typedef map > value2var; - value2var m_fixed_var_table; - - void propagate_eqs(lp::var_index vi, lp::constraint_index ci, lp::lconstraint_kind k, lp_api::bound& b) { - if (propagate_eqs()) { - rational const& value = b.get_value(); - if (k == lp::GE) { - set_lower_bound(vi, ci, value); - if (has_upper_bound(vi, ci, value)) { - fixed_var_eh(b.get_var(), value); - } - } - else if (k == lp::LE) { - set_upper_bound(vi, ci, value); - if (has_lower_bound(vi, ci, value)) { - fixed_var_eh(b.get_var(), value); - } - } - } - } - - bool dump_lemmas() const { return m_arith_params.m_arith_dump_lemmas; } - - bool propagate_eqs() const { return m_arith_params.m_arith_propagate_eqs && m_num_conflicts < m_arith_params.m_arith_propagation_threshold; } - - bound_prop_mode propagation_mode() const { return m_num_conflicts < m_arith_params.m_arith_propagation_threshold ? m_arith_params.m_arith_bound_prop : BP_NONE; } - - unsigned small_lemma_size() const { return m_arith_params.m_arith_small_lemma_size; } - - bool proofs_enabled() const { return m.proofs_enabled(); } - - bool use_tableau() const { return lp_params(ctx().get_params()).simplex_strategy() < 2; } - - void set_upper_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { set_bound(vi, ci, v, false); } - - void set_lower_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { set_bound(vi, ci, v, true); } - - void set_bound(lp::var_index vi, lp::constraint_index ci, rational const& v, bool is_lower) { - if (!m_solver->is_term(vi)) { - // m_solver already tracks bounds on proper variables, but not on terms. - return; - } - lp::var_index ti = m_solver->adjust_term_index(vi); - auto& vec = is_lower ? m_lower_terms : m_upper_terms; - if (vec.size() <= ti) { - vec.resize(ti + 1, constraint_bound(UINT_MAX, rational())); - } - constraint_bound& b = vec[ti]; - if (b.first == UINT_MAX || (is_lower? b.second < v : b.second > v)) { - ctx().push_trail(vector_value_trail(vec, ti)); - b.first = ci; - b.second = v; - } - } - - bool has_upper_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, false); } - - bool has_lower_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } - - bool has_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound, bool is_lower) { - - if (m_solver->is_term(vi)) { - - lp::var_index ti = m_solver->adjust_term_index(vi); - theory_var v = m_term_index2theory_var.get(ti, null_theory_var); - rational val; - TRACE("arith", tout << vi << " " << v << "\n";); - if (v != null_theory_var && a.is_numeral(get_owner(v), val) && bound == val) { - ci = UINT_MAX; - return bound == val; - } - - auto& vec = is_lower ? m_lower_terms : m_upper_terms; - if (vec.size() > ti) { - constraint_bound& b = vec[ti]; - ci = b.first; - return ci != UINT_MAX && bound == b.second; - } - else { + if (coeff.second.is_neg() == is_lub) { + // -3*x ... <= lub based on lower bound for x. + if (!m_solver->has_lower_bound(wi, ci, value, is_strict)) { return false; - + } + if (is_strict) { + r += inf_rational(rational::zero(), coeff.second.is_pos()); } } else { - bool is_strict = false; - rational b; - if (is_lower) { - return m_solver->has_lower_bound(vi, ci, b, is_strict) && b == bound && !is_strict; + if (!m_solver->has_upper_bound(wi, ci, value, is_strict)) { + return false; } - else { - return m_solver->has_upper_bound(vi, ci, b, is_strict) && b == bound && !is_strict; + if (is_strict) { + r += inf_rational(rational::zero(), coeff.second.is_pos()); + } + } + r += value * coeff.second; + set_evidence(ci); + } + TRACE("arith_verbose", tout << (is_lub?"lub":"glb") << " is " << r << "\n";); + return true; + } + + void assert_bound(bool_var bv, bool is_true, lp_api::bound& b) { + if (m_solver->get_status() == lp::lp_status::INFEASIBLE) { + return; + } + scoped_internalize_state st(*this); + st.vars().push_back(b.get_var()); + st.coeffs().push_back(rational::one()); + init_left_side(st); + lp::lconstraint_kind k = lp::EQ; + bool is_int = b.is_int(); + switch (b.get_bound_kind()) { + case lp_api::lower_t: + k = is_true ? lp::GE : (is_int ? lp::LE : lp::LT); + break; + case lp_api::upper_t: + k = is_true ? lp::LE : (is_int ? lp::GE : lp::GT); + break; + } + if (k == lp::LT || k == lp::LE) { + ++m_stats.m_assert_lower; + } + else { + ++m_stats.m_assert_upper; + } + auto vi = get_var_index(b.get_var()); + rational bound = b.get_value(); + lp::constraint_index ci; + if (is_int && !is_true) { + rational bound = b.get_value(false).get_rational(); + ci = m_solver->add_var_bound(vi, k, bound); + } + else { + ci = m_solver->add_var_bound(vi, k, b.get_value()); + } + TRACE("arith", tout << "v" << b.get_var() << "\n";); + add_ineq_constraint(ci, literal(bv, !is_true)); + + propagate_eqs(vi, ci, k, b); + } + + // + // fixed equalities. + // A fixed equality is inferred if there are two variables v1, v2 whose + // upper and lower bounds coincide. + // Then the equality v1 == v2 is propagated to the core. + // + + typedef std::pair constraint_bound; + vector m_lower_terms; + vector m_upper_terms; + typedef std::pair value_sort_pair; + typedef pair_hash, bool_hash> value_sort_pair_hash; + typedef map > value2var; + value2var m_fixed_var_table; + + void propagate_eqs(lp::var_index vi, lp::constraint_index ci, lp::lconstraint_kind k, lp_api::bound& b) { + if (propagate_eqs()) { + rational const& value = b.get_value(); + if (k == lp::GE) { + set_lower_bound(vi, ci, value); + if (has_upper_bound(vi, ci, value)) { + fixed_var_eh(b.get_var(), value); + } + } + else if (k == lp::LE) { + set_upper_bound(vi, ci, value); + if (has_lower_bound(vi, ci, value)) { + fixed_var_eh(b.get_var(), value); } } } + } + bool dump_lemmas() const { return m_arith_params.m_arith_dump_lemmas; } - bool is_equal(theory_var x, theory_var y) const { return get_enode(x)->get_root() == get_enode(y)->get_root(); } + bool propagate_eqs() const { return m_arith_params.m_arith_propagate_eqs && m_num_conflicts < m_arith_params.m_arith_propagation_threshold; } + bound_prop_mode propagation_mode() const { return m_num_conflicts < m_arith_params.m_arith_propagation_threshold ? m_arith_params.m_arith_bound_prop : BP_NONE; } - void fixed_var_eh(theory_var v1, rational const& bound) { - theory_var v2; - value_sort_pair key(bound, is_int(v1)); - if (m_fixed_var_table.find(key, v2)) { - if (static_cast(v2) < th.get_num_vars() && !is_equal(v1, v2)) { - auto vi1 = get_var_index(v1); - auto vi2 = get_var_index(v2); - lp::constraint_index ci1, ci2, ci3, ci4; - TRACE("arith", tout << "fixed: " << mk_pp(get_owner(v1), m) << " " << mk_pp(get_owner(v2), m) << " " << bound << " " << has_lower_bound(vi2, ci3, bound) << "\n";); - if (has_lower_bound(vi2, ci3, bound) && has_upper_bound(vi2, ci4, bound)) { - VERIFY (has_lower_bound(vi1, ci1, bound)); - VERIFY (has_upper_bound(vi1, ci2, bound)); - ++m_stats.m_fixed_eqs; - m_core.reset(); - m_eqs.reset(); - set_evidence(ci1); - set_evidence(ci2); - set_evidence(ci3); - set_evidence(ci4); - enode* x = get_enode(v1); - enode* y = get_enode(v2); - justification* js = - ctx().mk_justification( - ext_theory_eq_propagation_justification( - get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), x, y, 0, nullptr)); + unsigned small_lemma_size() const { return m_arith_params.m_arith_small_lemma_size; } - TRACE("arith", - for (unsigned i = 0; i < m_core.size(); ++i) { - ctx().display_detailed_literal(tout, m_core[i]); - tout << "\n"; - } - for (unsigned i = 0; i < m_eqs.size(); ++i) { - tout << mk_pp(m_eqs[i].first->get_owner(), m) << " = " << mk_pp(m_eqs[i].second->get_owner(), m) << "\n"; - } - tout << " ==> "; - tout << mk_pp(x->get_owner(), m) << " = " << mk_pp(y->get_owner(), m) << "\n"; - ); + bool proofs_enabled() const { return m.proofs_enabled(); } - // parameters are TBD. - SASSERT(validate_eq(x, y)); - ctx().assign_eq(x, y, eq_justification(js)); - } - } - else { - // bounds on v2 were changed. - m_fixed_var_table.insert(key, v1); - } - } - else { - m_fixed_var_table.insert(key, v1); - } + bool use_tableau() const { return lp_params(ctx().get_params()).simplex_strategy() < 2; } + + void set_upper_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { set_bound(vi, ci, v, false); } + + void set_lower_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { set_bound(vi, ci, v, true); } + + void set_bound(lp::var_index vi, lp::constraint_index ci, rational const& v, bool is_lower) { + if (!m_solver->is_term(vi)) { + // m_solver already tracks bounds on proper variables, but not on terms. + return; } + lp::var_index ti = m_solver->adjust_term_index(vi); + auto& vec = is_lower ? m_lower_terms : m_upper_terms; + if (vec.size() <= ti) { + vec.resize(ti + 1, constraint_bound(UINT_MAX, rational())); + } + constraint_bound& b = vec[ti]; + if (b.first == UINT_MAX || (is_lower? b.second < v : b.second > v)) { + ctx().push_trail(vector_value_trail(vec, ti)); + b.first = ci; + b.second = v; + } + } - lbool make_feasible() { - auto status = m_solver->find_feasible_solution(); - TRACE("arith_verbose", display(tout);); - switch (status) { - case lp::lp_status::INFEASIBLE: - return l_false; - case lp::lp_status::FEASIBLE: - case lp::lp_status::OPTIMAL: - // SASSERT(m_solver->all_constraints_hold()); - return l_true; - case lp::lp_status::TIME_EXHAUSTED: + bool has_upper_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, false); } + + bool has_lower_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } + + bool has_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound, bool is_lower) { + + if (m_solver->is_term(vi)) { - default: - TRACE("arith", tout << "status treated as inconclusive: " << status << "\n";); - // TENTATIVE_UNBOUNDED, UNBOUNDED, TENTATIVE_DUAL_UNBOUNDED, DUAL_UNBOUNDED, - // FLOATING_POINT_ERROR, TIME_EXAUSTED, ITERATIONS_EXHAUSTED, EMPTY, UNSTABLE - return l_undef; + lp::var_index ti = m_solver->adjust_term_index(vi); + theory_var v = m_term_index2theory_var.get(ti, null_theory_var); + rational val; + TRACE("arith", tout << vi << " " << v << "\n";); + if (v != null_theory_var && a.is_numeral(get_owner(v), val) && bound == val) { + ci = UINT_MAX; + return bound == val; } - } - - vector> m_explanation; - literal_vector m_core; - svector m_eqs; - vector m_params; - // lp::constraint_index const null_constraint_index = UINT_MAX; // not sure what a correct fix is - - void set_evidence(lp::constraint_index idx) { - if (idx == UINT_MAX) { - return; - } - switch (m_constraint_sources[idx]) { - case inequality_source: { - literal lit = m_inequalities[idx]; - SASSERT(lit != null_literal); - m_core.push_back(lit); - break; - } - case equality_source: { - SASSERT(m_equalities[idx].first != nullptr); - SASSERT(m_equalities[idx].second != nullptr); - m_eqs.push_back(m_equalities[idx]); - break; - } - case definition_source: { - // skip definitions (these are treated as hard constraints) - break; - } - default: - UNREACHABLE(); - break; - } - } - - void set_conflict() { - m_explanation.clear(); - m_solver->get_infeasibility_explanation(m_explanation); - set_conflict1(); - } - - void set_conflict1() { - m_eqs.reset(); - m_core.reset(); - m_params.reset(); - // m_solver->shrink_explanation_to_minimum(m_explanation); // todo, enable when perf is fixed - /* - static unsigned cn = 0; - static unsigned num_l = 0; - num_l+=m_explanation.size(); - std::cout << num_l / (++cn) << "\n"; - */ - ++m_num_conflicts; - ++m_stats.m_conflicts; - TRACE("arith", tout << "scope: " << ctx().get_scope_level() << "\n"; display_evidence(tout, m_explanation); ); - TRACE("arith", display(tout);); - for (auto const& ev : m_explanation) { - if (!ev.first.is_zero()) { - set_evidence(ev.second); - } - } - SASSERT(validate_conflict()); - ctx().set_conflict( - ctx().mk_justification( - ext_theory_conflict_justification( - get_id(), ctx().get_region(), - m_core.size(), m_core.c_ptr(), - m_eqs.size(), m_eqs.c_ptr(), m_params.size(), m_params.c_ptr()))); - } - - justification * why_is_diseq(theory_var v1, theory_var v2) { - return nullptr; - } - - void reset_eh() { - m_arith_eq_adapter.reset_eh(); - m_solver = nullptr; - m_not_handled = nullptr; - del_bounds(0); - m_unassigned_bounds.reset(); - m_asserted_qhead = 0; - m_scopes.reset(); - m_stats.reset(); - m_to_check.reset(); - } - - void init_model(model_generator & mg) { - init_variable_values(); - m_factory = alloc(arith_factory, m); - mg.register_factory(m_factory); - TRACE("arith", display(tout);); - } - - nlsat::anum const& nl_value(theory_var v, scoped_anum& r) { - SASSERT(m_nra); - SASSERT(m_use_nra_model); - lp::var_index vi = m_theory_var2var_index[v]; - if (m_solver->is_term(vi)) { - - m_todo_terms.push_back(std::make_pair(vi, rational::one())); - - m_nra->am().set(r, 0); - while (!m_todo_terms.empty()) { - rational wcoeff = m_todo_terms.back().second; - // lp::var_index wi = m_todo_terms.back().first; // todo : got a warning "wi is not used" - m_todo_terms.pop_back(); - lp::lar_term const& term = m_solver->get_term(vi); - scoped_anum r1(m_nra->am()); - rational c1 = term.m_v * wcoeff; - m_nra->am().set(r1, c1.to_mpq()); - m_nra->am().add(r, r1, r); - for (auto const coeff : term.m_coeffs) { - lp::var_index wi = coeff.first; - c1 = coeff.second * wcoeff; - if (m_solver->is_term(wi)) { - m_todo_terms.push_back(std::make_pair(wi, c1)); - } - else { - m_nra->am().set(r1, c1.to_mpq()); - m_nra->am().mul(m_nra->value(wi), r1, r1); - m_nra->am().add(r1, r, r); - } - } - } - return r; - } - else { - return m_nra->value(vi); - } - } - - model_value_proc * mk_value(enode * n, model_generator & mg) { - theory_var v = n->get_th_var(get_id()); - expr* o = n->get_owner(); - if (m_use_nra_model) { - anum const& an = nl_value(v, *m_a1); - if (a.is_int(o) && !m_nra->am().is_int(an)) { - return alloc(expr_wrapper_proc, a.mk_numeral(rational::zero(), a.is_int(o))); - } - return alloc(expr_wrapper_proc, a.mk_numeral(nl_value(v, *m_a1), a.is_int(o))); - } - else { - rational r = get_value(v); - if (a.is_int(o) && !r.is_int()) r = floor(r); - return alloc(expr_wrapper_proc, m_factory->mk_value(r, m.get_sort(o))); - } - } - - bool get_value(enode* n, expr_ref& r) { - theory_var v = n->get_th_var(get_id()); - if (can_get_value(v)) { - r = a.mk_numeral(get_value(v), is_int(n)); - return true; + auto& vec = is_lower ? m_lower_terms : m_upper_terms; + if (vec.size() > ti) { + constraint_bound& b = vec[ti]; + ci = b.first; + return ci != UINT_MAX && bound == b.second; } else { return false; + } } - - bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { - SASSERT(v1 != null_theory_var); - SASSERT(v2 != null_theory_var); - return (get_value(v1) == get_value(v2)) == is_true; - } - - // Auxiliary verification utilities. - - struct scoped_arith_mode { - smt_params& p; - scoped_arith_mode(smt_params& p) : p(p) { - p.m_arith_mode = AS_ARITH; - } - ~scoped_arith_mode() { - p.m_arith_mode = AS_LRA; - } - }; - - bool validate_conflict() { - if (dump_lemmas()) { - ctx().display_lemma_as_smt_problem(m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), false_literal); - } - if (m_arith_params.m_arith_mode != AS_LRA) return true; - scoped_arith_mode _sa(ctx().get_fparams()); - context nctx(m, ctx().get_fparams(), ctx().get_params()); - add_background(nctx); - bool result = l_true != nctx.check(); - CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), false_literal); - display(tout);); - return result; - } - - bool validate_assign(literal lit) { - if (dump_lemmas()) { - ctx().display_lemma_as_smt_problem(m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); - } - if (m_arith_params.m_arith_mode != AS_LRA) return true; - scoped_arith_mode _sa(ctx().get_fparams()); - context nctx(m, ctx().get_fparams(), ctx().get_params()); - m_core.push_back(~lit); - add_background(nctx); - m_core.pop_back(); - bool result = l_true != nctx.check(); - CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); - display(tout);); - return result; - } - - bool validate_eq(enode* x, enode* y) { - if (m_arith_params.m_arith_mode == AS_LRA) return true; - context nctx(m, ctx().get_fparams(), ctx().get_params()); - add_background(nctx); - nctx.assert_expr(m.mk_not(m.mk_eq(x->get_owner(), y->get_owner()))); - return l_true != nctx.check(); - } - - void add_background(context& nctx) { - for (unsigned i = 0; i < m_core.size(); ++i) { - expr_ref tmp(m); - ctx().literal2expr(m_core[i], tmp); - nctx.assert_expr(tmp); - } - for (unsigned i = 0; i < m_eqs.size(); ++i) { - nctx.assert_expr(m.mk_eq(m_eqs[i].first->get_owner(), m_eqs[i].second->get_owner())); - } - } - - theory_lra::inf_eps value(theory_var v) { - lp::impq ival = get_ivalue(v); - return inf_eps(0, inf_rational(ival.x, ival.y)); - } - - theory_lra::inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) { - lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); - vector > coeffs; - rational coeff(0); - // - // TBD: change API for maximize_term to take a proper term as input. - // - if (m_solver->is_term(vi)) { - const lp::lar_term& term = m_solver->get_term(vi); - for (auto & ti : term.m_coeffs) { - SASSERT(!m_solver->is_term(ti.first)); - coeffs.push_back(std::make_pair(ti.second, ti.first)); - } - coeff = term.m_v; + else { + bool is_strict = false; + rational b; + if (is_lower) { + return m_solver->has_lower_bound(vi, ci, b, is_strict) && b == bound && !is_strict; } else { - coeffs.push_back(std::make_pair(rational::one(), vi)); - coeff = rational::zero(); - } - lp::impq term_max; - if (m_solver->maximize_term(coeffs, term_max)) { - blocker = mk_gt(v); - inf_rational val(term_max.x + coeff, term_max.y); - return inf_eps(rational::zero(), val); - } - else { - TRACE("arith", tout << "Unbounded " << v << "\n";); - has_shared = false; - blocker = m.mk_false(); - return inf_eps(rational::one(), inf_rational()); + return m_solver->has_upper_bound(vi, ci, b, is_strict) && b == bound && !is_strict; } } + } - expr_ref mk_gt(theory_var v) { - lp::impq val = get_ivalue(v); - expr* obj = get_enode(v)->get_owner(); - rational r = val.x; - expr_ref e(m); - if (a.is_int(m.get_sort(obj))) { - if (r.is_int()) { - r += rational::one(); + + bool is_equal(theory_var x, theory_var y) const { return get_enode(x)->get_root() == get_enode(y)->get_root(); } + + + void fixed_var_eh(theory_var v1, rational const& bound) { + theory_var v2; + value_sort_pair key(bound, is_int(v1)); + if (m_fixed_var_table.find(key, v2)) { + if (static_cast(v2) < th.get_num_vars() && !is_equal(v1, v2)) { + auto vi1 = get_var_index(v1); + auto vi2 = get_var_index(v2); + lp::constraint_index ci1, ci2, ci3, ci4; + TRACE("arith", tout << "fixed: " << mk_pp(get_owner(v1), m) << " " << mk_pp(get_owner(v2), m) << " " << bound << " " << has_lower_bound(vi2, ci3, bound) << "\n";); + if (has_lower_bound(vi2, ci3, bound) && has_upper_bound(vi2, ci4, bound)) { + VERIFY (has_lower_bound(vi1, ci1, bound)); + VERIFY (has_upper_bound(vi1, ci2, bound)); + ++m_stats.m_fixed_eqs; + m_core.reset(); + m_eqs.reset(); + set_evidence(ci1); + set_evidence(ci2); + set_evidence(ci3); + set_evidence(ci4); + enode* x = get_enode(v1); + enode* y = get_enode(v2); + justification* js = + ctx().mk_justification( + ext_theory_eq_propagation_justification( + get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), x, y, 0, 0)); + + TRACE("arith", + for (unsigned i = 0; i < m_core.size(); ++i) { + ctx().display_detailed_literal(tout, m_core[i]); + tout << "\n"; + } + for (unsigned i = 0; i < m_eqs.size(); ++i) { + tout << mk_pp(m_eqs[i].first->get_owner(), m) << " = " << mk_pp(m_eqs[i].second->get_owner(), m) << "\n"; + } + tout << " ==> "; + tout << mk_pp(x->get_owner(), m) << " = " << mk_pp(y->get_owner(), m) << "\n"; + ); + + // parameters are TBD. + // SASSERT(validate_eq(x, y)); + ctx().assign_eq(x, y, eq_justification(js)); } - else { - r = ceil(r); + } + else { + // bounds on v2 were changed. + m_fixed_var_table.insert(key, v1); + } + } + else { + m_fixed_var_table.insert(key, v1); + } + } + + lbool make_feasible() { + auto status = m_solver->find_feasible_solution(); + TRACE("arith_verbose", display(tout);); + switch (status) { + case lp::lp_status::INFEASIBLE: + return l_false; + case lp::lp_status::FEASIBLE: + case lp::lp_status::OPTIMAL: + // SASSERT(m_solver->all_constraints_hold()); + return l_true; + case lp::lp_status::TIME_EXHAUSTED: + + default: + TRACE("arith", tout << "status treated as inconclusive: " << status << "\n";); + // TENTATIVE_UNBOUNDED, UNBOUNDED, TENTATIVE_DUAL_UNBOUNDED, DUAL_UNBOUNDED, + // FLOATING_POINT_ERROR, TIME_EXAUSTED, ITERATIONS_EXHAUSTED, EMPTY, UNSTABLE + return l_undef; + } + } + + vector> m_explanation; + literal_vector m_core; + svector m_eqs; + vector m_params; + + // lp::constraint_index const null_constraint_index = UINT_MAX; // not sure what a correct fix is + + void set_evidence(lp::constraint_index idx) { + if (idx == UINT_MAX) { + return; + } + switch (m_constraint_sources[idx]) { + case inequality_source: { + literal lit = m_inequalities[idx]; + SASSERT(lit != null_literal); + m_core.push_back(lit); + break; + } + case equality_source: { + SASSERT(m_equalities[idx].first != nullptr); + SASSERT(m_equalities[idx].second != nullptr); + m_eqs.push_back(m_equalities[idx]); + break; + } + case definition_source: { + // skip definitions (these are treated as hard constraints) + break; + } + default: + UNREACHABLE(); + break; + } + } + + void set_conflict() { + m_explanation.clear(); + m_solver->get_infeasibility_explanation(m_explanation); + set_conflict1(); + } + + void set_conflict1() { + m_eqs.reset(); + m_core.reset(); + m_params.reset(); + // m_solver->shrink_explanation_to_minimum(m_explanation); // todo, enable when perf is fixed + /* + static unsigned cn = 0; + static unsigned num_l = 0; + num_l+=m_explanation.size(); + std::cout << num_l / (++cn) << "\n"; + */ + ++m_num_conflicts; + ++m_stats.m_conflicts; + TRACE("arith", tout << "scope: " << ctx().get_scope_level() << "\n"; display_evidence(tout, m_explanation); ); + TRACE("arith", display(tout);); + for (auto const& ev : m_explanation) { + if (!ev.first.is_zero()) { + set_evidence(ev.second); + } + } + // SASSERT(validate_conflict()); + ctx().set_conflict( + ctx().mk_justification( + ext_theory_conflict_justification( + get_id(), ctx().get_region(), + m_core.size(), m_core.c_ptr(), + m_eqs.size(), m_eqs.c_ptr(), m_params.size(), m_params.c_ptr()))); + } + + justification * why_is_diseq(theory_var v1, theory_var v2) { + return 0; + } + + void reset_eh() { + m_arith_eq_adapter.reset_eh(); + m_solver = 0; + m_not_handled = nullptr; + del_bounds(0); + m_unassigned_bounds.reset(); + m_asserted_qhead = 0; + m_scopes.reset(); + m_stats.reset(); + m_to_check.reset(); + } + + void init_model(model_generator & mg) { + init_variable_values(); + m_factory = alloc(arith_factory, m); + mg.register_factory(m_factory); + TRACE("arith", display(tout);); + } + + nlsat::anum const& nl_value(theory_var v, scoped_anum& r) { + SASSERT(m_nra); + SASSERT(m_use_nra_model); + lp::var_index vi = m_theory_var2var_index[v]; + if (m_solver->is_term(vi)) { + + m_todo_terms.push_back(std::make_pair(vi, rational::one())); + + m_nra->am().set(r, 0); + while (!m_todo_terms.empty()) { + rational wcoeff = m_todo_terms.back().second; + // lp::var_index wi = m_todo_terms.back().first; // todo : got a warning "wi is not used" + m_todo_terms.pop_back(); + lp::lar_term const& term = m_solver->get_term(vi); + scoped_anum r1(m_nra->am()); + rational c1 = term.m_v * wcoeff; + m_nra->am().set(r1, c1.to_mpq()); + m_nra->am().add(r, r1, r); + for (auto const coeff : term.m_coeffs) { + lp::var_index wi = coeff.first; + c1 = coeff.second * wcoeff; + if (m_solver->is_term(wi)) { + m_todo_terms.push_back(std::make_pair(wi, c1)); + } + else { + m_nra->am().set(r1, c1.to_mpq()); + m_nra->am().mul(m_nra->value(wi), r1, r1); + m_nra->am().add(r1, r, r); + } } - e = a.mk_numeral(r, m.get_sort(obj)); + } + return r; + } + else { + return m_nra->value(vi); + } + } + + model_value_proc * mk_value(enode * n, model_generator & mg) { + theory_var v = n->get_th_var(get_id()); + expr* o = n->get_owner(); + if (m_use_nra_model) { + anum const& an = nl_value(v, *m_a1); + if (a.is_int(o) && !m_nra->am().is_int(an)) { + return alloc(expr_wrapper_proc, a.mk_numeral(rational::zero(), a.is_int(o))); + } + return alloc(expr_wrapper_proc, a.mk_numeral(nl_value(v, *m_a1), a.is_int(o))); + } + else { + rational r = get_value(v); + if (a.is_int(o) && !r.is_int()) r = floor(r); + return alloc(expr_wrapper_proc, m_factory->mk_value(r, m.get_sort(o))); + } + } + + bool get_value(enode* n, expr_ref& r) { + theory_var v = n->get_th_var(get_id()); + if (can_get_value(v)) { + r = a.mk_numeral(get_value(v), is_int(n)); + return true; + } + else { + return false; + } + } + + bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { + SASSERT(v1 != null_theory_var); + SASSERT(v2 != null_theory_var); + return (get_value(v1) == get_value(v2)) == is_true; + } + + // Auxiliary verification utilities. + + struct scoped_arith_mode { + smt_params& p; + scoped_arith_mode(smt_params& p) : p(p) { + p.m_arith_mode = AS_ARITH; + } + ~scoped_arith_mode() { + p.m_arith_mode = AS_LRA; + } + }; + + bool validate_conflict() { + if (dump_lemmas()) { + ctx().display_lemma_as_smt_problem(m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), false_literal); + } + if (m_arith_params.m_arith_mode != AS_LRA) return true; + scoped_arith_mode _sa(ctx().get_fparams()); + context nctx(m, ctx().get_fparams(), ctx().get_params()); + add_background(nctx); + cancel_eh eh(m.limit()); + scoped_timer timer(1000, &eh); + bool result = l_true != nctx.check(); + CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), false_literal);); + return result; + } + + bool validate_assign(literal lit) { + if (dump_lemmas()) { + ctx().display_lemma_as_smt_problem(m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); + } + if (m_arith_params.m_arith_mode != AS_LRA) return true; + scoped_arith_mode _sa(ctx().get_fparams()); + context nctx(m, ctx().get_fparams(), ctx().get_params()); + m_core.push_back(~lit); + add_background(nctx); + m_core.pop_back(); + cancel_eh eh(m.limit()); + scoped_timer timer(1000, &eh); + bool result = l_true != nctx.check(); + CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); + display(tout);); + return result; + } + + bool validate_eq(enode* x, enode* y) { + if (m_arith_params.m_arith_mode == AS_LRA) return true; + context nctx(m, ctx().get_fparams(), ctx().get_params()); + add_background(nctx); + nctx.assert_expr(m.mk_not(m.mk_eq(x->get_owner(), y->get_owner()))); + cancel_eh eh(m.limit()); + scoped_timer timer(1000, &eh); + return l_true != nctx.check(); + } + + void add_background(context& nctx) { + for (unsigned i = 0; i < m_core.size(); ++i) { + expr_ref tmp(m); + ctx().literal2expr(m_core[i], tmp); + nctx.assert_expr(tmp); + } + for (unsigned i = 0; i < m_eqs.size(); ++i) { + nctx.assert_expr(m.mk_eq(m_eqs[i].first->get_owner(), m_eqs[i].second->get_owner())); + } + } + + theory_lra::inf_eps value(theory_var v) { + lp::impq ival = get_ivalue(v); + return inf_eps(0, inf_rational(ival.x, ival.y)); + } + + theory_lra::inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) { + lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); + vector > coeffs; + rational coeff(0); + // + // TBD: change API for maximize_term to take a proper term as input. + // + if (m_solver->is_term(vi)) { + const lp::lar_term& term = m_solver->get_term(vi); + for (auto & ti : term.m_coeffs) { + SASSERT(!m_solver->is_term(ti.first)); + coeffs.push_back(std::make_pair(ti.second, ti.first)); + } + coeff = term.m_v; + } + else { + coeffs.push_back(std::make_pair(rational::one(), vi)); + coeff = rational::zero(); + } + lp::impq term_max; + if (m_solver->maximize_term(coeffs, term_max)) { + blocker = mk_gt(v); + inf_rational val(term_max.x + coeff, term_max.y); + return inf_eps(rational::zero(), val); + } + else { + TRACE("arith", tout << "Unbounded " << v << "\n";); + has_shared = false; + blocker = m.mk_false(); + return inf_eps(rational::one(), inf_rational()); + } + } + + expr_ref mk_gt(theory_var v) { + lp::impq val = get_ivalue(v); + expr* obj = get_enode(v)->get_owner(); + rational r = val.x; + expr_ref e(m); + if (a.is_int(m.get_sort(obj))) { + if (r.is_int()) { + r += rational::one(); + } + else { + r = ceil(r); + } + e = a.mk_numeral(r, m.get_sort(obj)); + e = a.mk_ge(obj, e); + } + else { + e = a.mk_numeral(r, m.get_sort(obj)); + if (val.y.is_neg()) { e = a.mk_ge(obj, e); } else { - e = a.mk_numeral(r, m.get_sort(obj)); - if (val.y.is_neg()) { - e = a.mk_ge(obj, e); - } - else { - e = a.mk_gt(obj, e); - } + e = a.mk_gt(obj, e); } - TRACE("opt", tout << "v" << v << " " << val << " " << r << " " << e << "\n";); - return e; } + TRACE("opt", tout << "v" << v << " " << val << " " << r << " " << e << "\n";); + return e; + } - theory_var add_objective(app* term) { - return internalize_def(term); - } + theory_var add_objective(app* term) { + return internalize_def(term); + } - app_ref mk_term(lp::lar_term const& term, bool is_int) { - expr_ref_vector args(m); - for (auto & ti : term.m_coeffs) { - theory_var w; - if (m_solver->is_term(ti.first)) { - w = m_term_index2theory_var[m_solver->adjust_term_index(ti.first)]; - } - else { - w = m_var_index2theory_var[ti.first]; - } - expr* o = get_enode(w)->get_owner(); - if (ti.second.is_one()) { - args.push_back(o); - } - else { - args.push_back(a.mk_mul(a.mk_numeral(ti.second, is_int), o)); - } - } - if (!term.m_v.is_zero()) { - args.push_back(a.mk_numeral(term.m_v, is_int)); - } - if (args.size() == 1) { - return app_ref(to_app(args[0].get()), m); - } - return app_ref(a.mk_add(args.size(), args.c_ptr()), m); - } - - app_ref mk_obj(theory_var v) { - lp::var_index vi = m_theory_var2var_index[v]; - bool is_int = a.is_int(get_enode(v)->get_owner()); - if (m_solver->is_term(vi)) { - return mk_term(m_solver->get_term(vi), is_int); + app_ref mk_term(lp::lar_term const& term, bool is_int) { + expr_ref_vector args(m); + for (auto & ti : term.m_coeffs) { + theory_var w; + if (m_solver->is_term(ti.first)) { + w = m_term_index2theory_var[m_solver->adjust_term_index(ti.first)]; } else { - theory_var w = m_var_index2theory_var[vi]; - return app_ref(get_enode(w)->get_owner(), m); + w = m_var_index2theory_var[ti.first]; } - } - - expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val) { - rational r = val.get_rational(); - bool is_strict = val.get_infinitesimal().is_pos(); - app_ref b(m); - bool is_int = a.is_int(get_enode(v)->get_owner()); - if (is_strict) { - b = a.mk_le(mk_obj(v), a.mk_numeral(r, is_int)); + expr* o = get_enode(w)->get_owner(); + if (ti.second.is_one()) { + args.push_back(o); } else { - b = a.mk_ge(mk_obj(v), a.mk_numeral(r, is_int)); - } - if (!ctx().b_internalized(b)) { - fm.hide(b); - bool_var bv = ctx().mk_bool_var(b); - ctx().set_var_theory(bv, get_id()); - // ctx().set_enode_flag(bv, true); - lp_api::bound_kind bkind = lp_api::bound_kind::lower_t; - if (is_strict) bkind = lp_api::bound_kind::upper_t; - lp_api::bound* a = alloc(lp_api::bound, bv, v, is_int, r, bkind); - mk_bound_axioms(*a); - updt_unassigned_bounds(v, +1); - m_bounds[v].push_back(a); - m_bounds_trail.push_back(v); - m_bool_var2bound.insert(bv, a); - TRACE("arith", tout << mk_pp(b, m) << "\n";); - } - if (is_strict) { - b = m.mk_not(b); - } - TRACE("arith", tout << b << "\n";); - return expr_ref(b, m); - - } - - - void display(std::ostream & out) const { - if (m_solver) { - m_solver->print_constraints(out); - m_solver->print_terms(out); - } - unsigned nv = th.get_num_vars(); - for (unsigned v = 0; v < nv; ++v) { - out << "v" << v; - if (can_get_value(v)) out << ", value: " << get_value(v); - out << ", shared: " << ctx().is_shared(get_enode(v)) - << ", rel: " << ctx().is_relevant(get_enode(v)) - << ", def: "; th.display_var_flat_def(out, v) << "\n"; + args.push_back(a.mk_mul(a.mk_numeral(ti.second, is_int), o)); } } + if (!term.m_v.is_zero()) { + args.push_back(a.mk_numeral(term.m_v, is_int)); + } + if (args.size() == 1) { + return app_ref(to_app(args[0].get()), m); + } + return app_ref(a.mk_add(args.size(), args.c_ptr()), m); + } - void display_evidence(std::ostream& out, vector> const& evidence) { - for (auto const& ev : evidence) { - expr_ref e(m); - SASSERT(!ev.first.is_zero()); - if (ev.first.is_zero()) { - continue; - } - unsigned idx = ev.second; - switch (m_constraint_sources.get(idx, null_source)) { - case inequality_source: { - literal lit = m_inequalities[idx]; - ctx().literal2expr(lit, e); - out << e << " " << ctx().get_assignment(lit) << "\n"; - break; - } - case equality_source: - out << mk_pp(m_equalities[idx].first->get_owner(), m) << " = " - << mk_pp(m_equalities[idx].second->get_owner(), m) << "\n"; - break; - case definition_source: { - theory_var v = m_definitions[idx]; - out << "def: v" << v << " := " << mk_pp(th.get_enode(v)->get_owner(), m) << "\n"; - break; - } - case null_source: - default: - UNREACHABLE(); - break; - } + app_ref mk_obj(theory_var v) { + lp::var_index vi = m_theory_var2var_index[v]; + bool is_int = a.is_int(get_enode(v)->get_owner()); + if (m_solver->is_term(vi)) { + return mk_term(m_solver->get_term(vi), is_int); + } + else { + theory_var w = m_var_index2theory_var[vi]; + return app_ref(get_enode(w)->get_owner(), m); + } + } + + expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val) { + rational r = val.get_rational(); + bool is_strict = val.get_infinitesimal().is_pos(); + app_ref b(m); + bool is_int = a.is_int(get_enode(v)->get_owner()); + if (is_strict) { + b = a.mk_le(mk_obj(v), a.mk_numeral(r, is_int)); + } + else { + b = a.mk_ge(mk_obj(v), a.mk_numeral(r, is_int)); + } + if (!ctx().b_internalized(b)) { + fm.insert(b->get_decl()); + bool_var bv = ctx().mk_bool_var(b); + ctx().set_var_theory(bv, get_id()); + // ctx().set_enode_flag(bv, true); + lp_api::bound_kind bkind = lp_api::bound_kind::lower_t; + if (is_strict) bkind = lp_api::bound_kind::upper_t; + lp_api::bound* a = alloc(lp_api::bound, bv, v, is_int, r, bkind); + mk_bound_axioms(*a); + updt_unassigned_bounds(v, +1); + m_bounds[v].push_back(a); + m_bounds_trail.push_back(v); + m_bool_var2bound.insert(bv, a); + TRACE("arith", tout << mk_pp(b, m) << "\n";); + } + if (is_strict) { + b = m.mk_not(b); + } + TRACE("arith", tout << b << "\n";); + return expr_ref(b, m); + + } + + + void display(std::ostream & out) const { + if (m_solver) { + m_solver->print_constraints(out); + m_solver->print_terms(out); + } + unsigned nv = th.get_num_vars(); + for (unsigned v = 0; v < nv; ++v) { + out << "v" << v; + if (can_get_value(v)) out << ", value: " << get_value(v); + out << ", shared: " << ctx().is_shared(get_enode(v)) + << ", rel: " << ctx().is_relevant(get_enode(v)) + << ", def: "; th.display_var_flat_def(out, v) << "\n"; + } + } + + void display_evidence(std::ostream& out, vector> const& evidence) { + for (auto const& ev : evidence) { + expr_ref e(m); + SASSERT(!ev.first.is_zero()); + if (ev.first.is_zero()) { + continue; } - for (auto const& ev : evidence) { - m_solver->print_constraint(ev.second, out << ev.first << ": "); + unsigned idx = ev.second; + switch (m_constraint_sources.get(idx, null_source)) { + case inequality_source: { + literal lit = m_inequalities[idx]; + ctx().literal2expr(lit, e); + out << e << " " << ctx().get_assignment(lit) << "\n"; + break; + } + case equality_source: + out << mk_pp(m_equalities[idx].first->get_owner(), m) << " = " + << mk_pp(m_equalities[idx].second->get_owner(), m) << "\n"; + break; + case definition_source: { + theory_var v = m_definitions[idx]; + out << "def: v" << v << " := " << mk_pp(th.get_enode(v)->get_owner(), m) << "\n"; + break; + } + case null_source: + default: + UNREACHABLE(); + break; } } + for (auto const& ev : evidence) { + m_solver->print_constraint(ev.second, out << ev.first << ": "); + } + } - void collect_statistics(::statistics & st) const { - m_arith_eq_adapter.collect_statistics(st); - st.update("arith-lower", m_stats.m_assert_lower); - st.update("arith-upper", m_stats.m_assert_upper); - st.update("arith-rows", m_stats.m_add_rows); - st.update("arith-propagations", m_stats.m_bounds_propagations); - st.update("arith-iterations", m_stats.m_num_iterations); - st.update("arith-factorizations", m_solver->settings().st().m_num_factorizations); - st.update("arith-pivots", m_stats.m_need_to_solve_inf); - st.update("arith-plateau-iterations", m_stats.m_num_iterations_with_no_progress); - st.update("arith-fixed-eqs", m_stats.m_fixed_eqs); - st.update("arith-conflicts", m_stats.m_conflicts); - st.update("arith-bound-propagations-lp", m_stats.m_bound_propagations1); - st.update("arith-bound-propagations-cheap", m_stats.m_bound_propagations2); - st.update("arith-diseq", m_stats.m_assert_diseq); - st.update("arith-make-feasible", m_solver->settings().st().m_make_feasible); - st.update("arith-max-columns", m_solver->settings().st().m_max_cols); - st.update("arith-max-rows", m_solver->settings().st().m_max_rows); - } - }; - - theory_lra::theory_lra(ast_manager& m, theory_arith_params& ap): - theory(m.get_family_id("arith")) { - m_imp = alloc(imp, *this, m, ap); - } - theory_lra::~theory_lra() { - dealloc(m_imp); - } - theory* theory_lra::mk_fresh(context* new_ctx) { - return alloc(theory_lra, new_ctx->get_manager(), new_ctx->get_fparams()); - } - void theory_lra::init(context * ctx) { - theory::init(ctx); - m_imp->init(ctx); - } - bool theory_lra::internalize_atom(app * atom, bool gate_ctx) { - return m_imp->internalize_atom(atom, gate_ctx); - } - bool theory_lra::internalize_term(app * term) { - return m_imp->internalize_term(term); - } - void theory_lra::internalize_eq_eh(app * atom, bool_var v) { - m_imp->internalize_eq_eh(atom, v); - } - void theory_lra::assign_eh(bool_var v, bool is_true) { - m_imp->assign_eh(v, is_true); - } - void theory_lra::new_eq_eh(theory_var v1, theory_var v2) { - m_imp->new_eq_eh(v1, v2); - } - bool theory_lra::use_diseqs() const { - return m_imp->use_diseqs(); - } - void theory_lra::new_diseq_eh(theory_var v1, theory_var v2) { - m_imp->new_diseq_eh(v1, v2); - } - void theory_lra::push_scope_eh() { - theory::push_scope_eh(); - m_imp->push_scope_eh(); - } - void theory_lra::pop_scope_eh(unsigned num_scopes) { - m_imp->pop_scope_eh(num_scopes); - theory::pop_scope_eh(num_scopes); - } - void theory_lra::restart_eh() { - m_imp->restart_eh(); - } - void theory_lra::relevant_eh(app* e) { - m_imp->relevant_eh(e); - } - void theory_lra::init_search_eh() { - m_imp->init_search_eh(); - } - final_check_status theory_lra::final_check_eh() { - return m_imp->final_check_eh(); - } - bool theory_lra::is_shared(theory_var v) const { - return m_imp->is_shared(v); - } - bool theory_lra::can_propagate() { - return m_imp->can_propagate(); - } - void theory_lra::propagate() { - m_imp->propagate(); - } - justification * theory_lra::why_is_diseq(theory_var v1, theory_var v2) { - return m_imp->why_is_diseq(v1, v2); - } - void theory_lra::reset_eh() { - m_imp->reset_eh(); - } - void theory_lra::init_model(model_generator & m) { - m_imp->init_model(m); - } - model_value_proc * theory_lra::mk_value(enode * n, model_generator & mg) { - return m_imp->mk_value(n, mg); - } - bool theory_lra::get_value(enode* n, expr_ref& r) { - return m_imp->get_value(n, r); - } - bool theory_lra::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { - return m_imp->validate_eq_in_model(v1, v2, is_true); - } - void theory_lra::display(std::ostream & out) const { - m_imp->display(out); - } - void theory_lra::collect_statistics(::statistics & st) const { - m_imp->collect_statistics(st); - } - theory_lra::inf_eps theory_lra::value(theory_var v) { - return m_imp->value(v); - } - theory_lra::inf_eps theory_lra::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { - return m_imp->maximize(v, blocker, has_shared); - } - theory_var theory_lra::add_objective(app* term) { - return m_imp->add_objective(term); - } - expr_ref theory_lra::mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val) { - return m_imp->mk_ge(fm, v, val); - } + void collect_statistics(::statistics & st) const { + m_arith_eq_adapter.collect_statistics(st); + st.update("arith-lower", m_stats.m_assert_lower); + st.update("arith-upper", m_stats.m_assert_upper); + st.update("arith-rows", m_stats.m_add_rows); + st.update("arith-propagations", m_stats.m_bounds_propagations); + st.update("arith-iterations", m_stats.m_num_iterations); + st.update("arith-factorizations", m_solver->settings().st().m_num_factorizations); + st.update("arith-pivots", m_stats.m_need_to_solve_inf); + st.update("arith-plateau-iterations", m_stats.m_num_iterations_with_no_progress); + st.update("arith-fixed-eqs", m_stats.m_fixed_eqs); + st.update("arith-conflicts", m_stats.m_conflicts); + st.update("arith-bound-propagations-lp", m_stats.m_bound_propagations1); + st.update("arith-bound-propagations-cheap", m_stats.m_bound_propagations2); + st.update("arith-diseq", m_stats.m_assert_diseq); + st.update("arith-make-feasible", m_solver->settings().st().m_make_feasible); + st.update("arith-max-columns", m_solver->settings().st().m_max_cols); + st.update("arith-max-rows", m_solver->settings().st().m_max_rows); + st.update("cut_solver-calls", m_solver->settings().st().m_cut_solver_calls); + st.update("cut_solver-true", m_solver->settings().st().m_cut_solver_true); + st.update("cut_solver-false", m_solver->settings().st().m_cut_solver_false); + st.update("cut_solver-undef", m_solver->settings().st().m_cut_solver_undef); + } +}; + +theory_lra::theory_lra(ast_manager& m, theory_arith_params& ap): + theory(m.get_family_id("arith")) { + m_imp = alloc(imp, *this, m, ap); +} +theory_lra::~theory_lra() { + dealloc(m_imp); +} +theory* theory_lra::mk_fresh(context* new_ctx) { + return alloc(theory_lra, new_ctx->get_manager(), new_ctx->get_fparams()); +} +void theory_lra::init(context * ctx) { + theory::init(ctx); + m_imp->init(ctx); +} +bool theory_lra::internalize_atom(app * atom, bool gate_ctx) { + return m_imp->internalize_atom(atom, gate_ctx); +} +bool theory_lra::internalize_term(app * term) { + return m_imp->internalize_term(term); +} +void theory_lra::internalize_eq_eh(app * atom, bool_var v) { + m_imp->internalize_eq_eh(atom, v); +} +void theory_lra::assign_eh(bool_var v, bool is_true) { + m_imp->assign_eh(v, is_true); +} +void theory_lra::new_eq_eh(theory_var v1, theory_var v2) { + m_imp->new_eq_eh(v1, v2); +} +bool theory_lra::use_diseqs() const { + return m_imp->use_diseqs(); +} +void theory_lra::new_diseq_eh(theory_var v1, theory_var v2) { + m_imp->new_diseq_eh(v1, v2); +} +void theory_lra::push_scope_eh() { + theory::push_scope_eh(); + m_imp->push_scope_eh(); +} +void theory_lra::pop_scope_eh(unsigned num_scopes) { + m_imp->pop_scope_eh(num_scopes); + theory::pop_scope_eh(num_scopes); +} +void theory_lra::restart_eh() { + m_imp->restart_eh(); +} +void theory_lra::relevant_eh(app* e) { + m_imp->relevant_eh(e); +} +void theory_lra::init_search_eh() { + m_imp->init_search_eh(); +} +final_check_status theory_lra::final_check_eh() { + return m_imp->final_check_eh(); +} +bool theory_lra::is_shared(theory_var v) const { + return m_imp->is_shared(v); +} +bool theory_lra::can_propagate() { + return m_imp->can_propagate(); +} +void theory_lra::propagate() { + m_imp->propagate(); +} +justification * theory_lra::why_is_diseq(theory_var v1, theory_var v2) { + return m_imp->why_is_diseq(v1, v2); +} +void theory_lra::reset_eh() { + m_imp->reset_eh(); +} +void theory_lra::init_model(model_generator & m) { + m_imp->init_model(m); +} +model_value_proc * theory_lra::mk_value(enode * n, model_generator & mg) { + return m_imp->mk_value(n, mg); +} +bool theory_lra::get_value(enode* n, expr_ref& r) { + return m_imp->get_value(n, r); +} +bool theory_lra::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { + return m_imp->validate_eq_in_model(v1, v2, is_true); +} +void theory_lra::display(std::ostream & out) const { + m_imp->display(out); +} +void theory_lra::collect_statistics(::statistics & st) const { + m_imp->collect_statistics(st); +} +theory_lra::inf_eps theory_lra::value(theory_var v) { + return m_imp->value(v); +} +theory_lra::inf_eps theory_lra::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { + return m_imp->maximize(v, blocker, has_shared); +} +theory_var theory_lra::add_objective(app* term) { + return m_imp->add_objective(term); +} +expr_ref theory_lra::mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val) { + return m_imp->mk_ge(fm, v, val); +} diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 4e9b85367..f2432b4fb 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -11074,8 +11074,8 @@ namespace smt { ctx.force_phase(lit); } - zstring aStr = gen_val_string(len, options[static_cast(i) - static_cast(l)]); - expr * strAst; + zstring aStr = gen_val_string(len, options[i - l]); + expr * strAst; if (m_params.m_UseFastValueTesterCache) { if (!valueTesterCache.find(aStr, strAst)) { strAst = mk_string(aStr); diff --git a/src/tactic/smtlogics/qflia_tactic.cpp b/src/tactic/smtlogics/qflia_tactic.cpp index 541a81682..b6b0cd6bc 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -211,17 +211,35 @@ tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p) { params_ref no_cut_p; no_cut_p.set_uint("arith.branch_cut_ratio", 10000000); - - + + + tactic * st = using_params(and_then(preamble_st, +#if 0 + mk_smt_tactic()), +#else or_else(mk_ilp_model_finder_tactic(m), mk_pb_tactic(m), and_then(fail_if_not(mk_is_quasi_pb_probe()), using_params(mk_lia2sat_tactic(m), quasi_pb_p), mk_fail_if_undecided_tactic()), mk_bounded_tactic(m), - mk_psmt_tactic(m, p))), + mk_smt_tactic())), +#endif main_p); + + // + + + // tactic * st = using_params(and_then(preamble_st, + // or_else(mk_ilp_model_finder_tactic(m), + // mk_pb_tactic(m), + // and_then(fail_if_not(mk_is_quasi_pb_probe()), + // using_params(mk_lia2sat_tactic(m), quasi_pb_p), + // mk_fail_if_undecided_tactic()), + // mk_bounded_tactic(m), + // mk_smt_tactic())), + // main_p); st->updt_params(p); return st; diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index f95dba98a..4cc36b46a 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -35,10 +35,10 @@ Revision History: #include "util/lp/lp_utils.h" #include "util/lp/lp_primal_simplex.h" #include "util/lp/mps_reader.h" -#include "test/smt_reader.h" +#include "test/lp/smt_reader.h" #include "util/lp/binary_heap_priority_queue.h" -#include "test/argument_parser.h" -#include "test/test_file_reader.h" +#include "test/lp/argument_parser.h" +#include "test/lp/test_file_reader.h" #include "util/lp/indexed_value.h" #include "util/lp/lar_solver.h" #include "util/lp/numeric_pair.h" @@ -550,7 +550,7 @@ void test_lp_0() { costs[5] = 0; costs[6] = 0; - vector column_types(7, column_type::low_bound); + vector column_types(7, column_type::lower_bound); vector upper_bound_values; lp_settings settings; simple_column_namer cn; @@ -596,7 +596,7 @@ void test_lp_1() { - vector column_types(7, column_type::low_bound); + vector column_types(7, column_type::lower_bound); vector upper_bound_values; std::cout << "calling lp\n"; @@ -1750,7 +1750,7 @@ void solve_rational() { int bounds[] = {8, 6, 4, 15, 2, 10, 10, 3}; for (unsigned i = 0; i < 8; i++) { - solver.set_low_bound(i, lp::mpq(0)); + solver.set_lower_bound(i, lp::mpq(0)); solver.set_upper_bound(i, lp::mpq(bounds[i])); } @@ -1883,8 +1883,7 @@ void test_replace_column() { void setup_args_parser(argument_parser & parser) { - parser.add_option_with_help_string("-dji", "test integer_domain"); - parser.add_option_with_help_string("-cs", "test cut_solver"); + parser.add_option_with_help_string("-intd", "test integer_domain"); parser.add_option_with_help_string("-xyz_sample", "run a small interactive scenario"); parser.add_option_with_after_string_with_help("--density", "the percentage of non-zeroes in the matrix below which it is not dense"); parser.add_option_with_after_string_with_help("--harris_toler", "harris tolerance"); @@ -2782,7 +2781,7 @@ void test_bound_propagation_one_small_sample1() { got to get a <= c */ std::function bound_is_relevant = - [&](unsigned j, bool is_low_bound, bool strict, const rational& bound_val) { + [&](unsigned j, bool is_lower_bound, bool strict, const rational& bound_val) { return true; }; lar_solver ls; @@ -2807,7 +2806,7 @@ void test_bound_propagation_one_small_sample1() { vector ev; ls.add_var_bound(a, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); std::cout << " bound ev from test_bound_propagation_one_small_sample1" << std::endl; for (auto & be : bp.m_ibounds) { @@ -2860,7 +2859,7 @@ void test_bound_propagation_one_row() { vector ev; ls.add_var_bound(x0, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_one_row_with_bounded_vars() { @@ -2876,7 +2875,7 @@ void test_bound_propagation_one_row_with_bounded_vars() { ls.add_var_bound(x0, LE, mpq(3)); ls.add_var_bound(x0, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_one_row_mixed() { @@ -2890,7 +2889,7 @@ void test_bound_propagation_one_row_mixed() { vector ev; ls.add_var_bound(x1, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } @@ -2913,7 +2912,7 @@ void test_bound_propagation_two_rows() { vector ev; ls.add_var_bound(y, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } @@ -2933,7 +2932,7 @@ void test_total_case_u() { vector ev; ls.add_var_bound(z, GE, zero_of_type()); ls.solve(); - lp_bound_propagator bp(ls); + bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } bool contains_j_kind(unsigned j, lconstraint_kind kind, const mpq & rs, const vector & ev) { @@ -2960,7 +2959,7 @@ void test_total_case_l(){ vector ev; ls.add_var_bound(z, LE, zero_of_type()); ls.solve(); - lp_bound_propagator bp(ls); + bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); lp_assert(ev.size() == 4); lp_assert(contains_j_kind(x, GE, - one_of_type(), ev)); @@ -3113,17 +3112,17 @@ void get_random_interval(bool& neg_inf, bool& pos_inf, int& x, int &y) { } void test_integer_domain_intersection(integer_domain & d) { - int x, y; bool neg_inf, pos_inf; - get_random_interval(neg_inf, pos_inf, x, y); - if (neg_inf) { - if (!pos_inf) { - d.intersect_with_upper_bound(y); - } - } - else if (pos_inf) - d.intersect_with_lower_bound(x); - else - d.intersect_with_interval(x, y); + // int x, y; bool neg_inf, pos_inf; + // get_random_interval(neg_inf, pos_inf, x, y); + // if (neg_inf) { + // if (!pos_inf) { + // d.intersect_with_upper_bound(y); + // } + // } + // else if (pos_inf) + // d.intersect_with_lower_bound(x); + // else + // d.intersect_with_interval(x, y); } void test_integer_domain_union(integer_domain & d) { @@ -3154,66 +3153,108 @@ void test_integer_domain_randomly(integer_domain & d) { } void test_integer_domain() { - integer_domain d; - vector> stack; - for (int i = 0; i < 10000; i++) { - test_integer_domain_randomly(d); - stack.push_back(d); - d.push(); - if (i > 0 && i%100 == 0) { - if (stack.size() == 0) continue; - unsigned k = my_random() % stack.size(); - if (k == 0) - k = 1; - d.pop(k); - d.restore_domain(); - for (unsigned j = 0; j + 1 < k; j++) { - stack.pop_back(); - } - std::cout<<"comparing i = " << i << std::endl; - lp_assert(d == *stack.rbegin()); - stack.pop_back(); - } - //d.print(std::cout); - } + std::cout << "test_integer_domain\n"; + unsigned e0 = 0; + unsigned e1 = 1; + unsigned e2 = 2; + unsigned e3 = 3; // these are explanations + unsigned e4 = 4; + unsigned e5 = 5; + integer_domain d; + unsigned l0 = 0, l1 = 1, l2 = 3; + unsigned u0 = 10, u1 = 9, u2 = 8; + d.push(); + d.intersect_with_lower_bound(l0, e0); + unsigned b; + unsigned e; + bool r = d.get_lower_bound_with_expl(b, e); + lp_assert(r && b == l0 && e == e0); + d.push(); + d.intersect_with_upper_bound(u0, e1); + r = d.get_upper_bound_with_expl(b, e); + lp_assert(r && b == u0 && e == e1); + r = d.get_lower_bound_with_expl(b, e); + lp_assert(r && b == l0 && e == e0); + d.pop(); + r = d.get_upper_bound_with_expl(b, e); + lp_assert(!r); + d.intersect_with_upper_bound(u0, e1); + d.push(); + d.intersect_with_lower_bound(l1, e2); + d.intersect_with_upper_bound(u1, e3); + d.push(); + d.intersect_with_lower_bound(l2, e4); + d.intersect_with_upper_bound(u2, e5); + lp_assert(d.is_empty() == false); + d.print(std::cout); + d.pop(); + r = d.get_lower_bound_with_expl(b, e); + lp_assert(r && b == l1 && e == e2); + d.print(std::cout); + d.pop(2); + d.print(std::cout); + lp_assert(d.has_neg_inf() && d.has_pos_inf()); + // integer_domain d; + // std::vector> stack; + // for (int i = 0; i < 10000; i++) { + // test_integer_domain_randomly(d); + // stack.push_back(d); + // d.push(); + // if (i > 0 && i%100 == 0) { + // if (stack.size() == 0) continue; + // unsigned k = my_random() % stack.size(); + // if (k == 0) + // k = 1; + // d.pop(k); + // d.restore_domain(); + // for (unsigned j = 0; j + 1 < k; j++) { + // stack.pop_back(); + // } + // std::cout<<"comparing i = " << i << std::endl; + // lp_assert(d == *stack.rbegin()); + // stack.pop_back(); + // } + // //d.print(std::cout); + // } } -void test_cut_solver() { - cut_solver cs([](unsigned i) - { - if (i == 0) return std::string("x"); - if (i == 1) return std::string("y"); - return std::to_string(i); - }); - vector> term; - unsigned x = 0; - unsigned y = 1; - term.push_back(std::make_pair(2, x)); - term.push_back(std::make_pair(-3, y)); - unsigned ineq_index = cs.add_ineq(term, mpq(3)); - cs.print_ineq(ineq_index, std::cout); +void test_resolve_with_tight_constraint(cut_solver& cs, + lp::cut_solver::polynomial&i , + unsigned int j, + cut_solver::polynomial& ti) { - mpq l; - auto ineq = cs.m_ineqs[ineq_index]; - cs.add_lower_bound_for_user_var(x, 1); - cs.add_lower_bound_for_user_var(y, 1); - bool has_lower = cs.lower(ineq.m_poly, l); - if (has_lower) { - std::cout << "lower = " << l << std::endl; - } else { - std::cout << "no lower" << std::endl; - } - cs.add_upper_bound_for_user_var(y, 1); - has_lower = cs.lower(ineq.m_poly, l); - if (has_lower) { - std::cout << "lower = " << l << std::endl; - } else { - std::cout << "no lower" << std::endl; - } + // std::cout << "resolve constraint "; + // cs.print_polynomial(std::cout, i); + // std::cout << " for " << cs.get_column_name(j) << " by using poly "; + // cs.print_polynomial(std::cout, ti); + // std::cout << std::endl; + // bool j_coeff_is_one = ti.coeff(j) == 1; + // cut_solver::polynomial result; + // cs.resolve(i, j, j_coeff_is_one, ti); + // std::cout << "resolve result is "; + // cs.print_polynomial(std::cout, i); + // std::cout << std::endl; } +typedef cut_solver::monomial mono; + +void test_resolve(cut_solver& cs, unsigned constraint_index, unsigned i0) { + var_index x = 0; + var_index y = 1; + var_index z = 2; + std::cout << "test_resolve\n"; + + cut_solver::polynomial i; i += mono(2, x);i += mono(-3,y); + i+= mono(4, z); + i.m_a = 5; + cut_solver::polynomial ti; ti += mono(1, x); ti+= mono(1,y);ti.m_a = 3; + test_resolve_with_tight_constraint(cs, i, x, ti); + test_resolve_with_tight_constraint(cs, i, y ,ti); + } + + void test_lp_local(int argn, char**argv) { // initialize_util_module(); @@ -3230,14 +3271,11 @@ void test_lp_local(int argn, char**argv) { args_parser.print(); - if (args_parser.option_is_used("-dji")) { + if (args_parser.option_is_used("-intd")) { test_integer_domain(); return finalize(0); } - if (args_parser.option_is_used("-cs")) { - test_cut_solver(); - return finalize(0); - } + if (args_parser.option_is_used("--test_mpq")) { test_rationals(); return finalize(0); diff --git a/src/util/lp/CMakeLists.txt b/src/util/lp/CMakeLists.txt index 681c18463..0a821f9b7 100644 --- a/src/util/lp/CMakeLists.txt +++ b/src/util/lp/CMakeLists.txt @@ -1,35 +1,35 @@ z3_add_component(lp SOURCES lp_utils.cpp - binary_heap_priority_queue_instances.cpp - binary_heap_upair_queue_instances.cpp + binary_heap_priority_queue.cpp + binary_heap_upair_queue.cpp bound_propagator.cpp cut_solver.cpp - core_solver_pretty_printer_instances.cpp - dense_matrix_instances.cpp - eta_matrix_instances.cpp - indexed_vector_instances.cpp + core_solver_pretty_printer.cpp + dense_matrix.cpp + eta_matrix.cpp + indexed_vector.cpp int_solver.cpp - lar_solver_instances.cpp - lar_core_solver_instances.cpp - lp_core_solver_base_instances.cpp - lp_dual_core_solver_instances.cpp - lp_dual_simplex_instances.cpp - lp_primal_core_solver_instances.cpp - lp_primal_simplex_instances.cpp - lp_settings_instances.cpp - lp_solver_instances.cpp - lu_instances.cpp - matrix_instances.cpp + lar_solver.cpp + lar_core_solver.cpp + lp_core_solver_base.cpp + lp_dual_core_solver.cpp + lp_dual_simplex.cpp + lp_primal_core_solver.cpp + lp_primal_simplex.cpp + lp_settings.cpp + lp_solver.cpp + lu.cpp + matrix.cpp nra_solver.cpp - permutation_matrix_instances.cpp + permutation_matrix.cpp quick_xplain.cpp - row_eta_matrix_instances.cpp - scaler_instances.cpp - sparse_matrix_instances.cpp - square_dense_submatrix_instances.cpp - static_matrix_instances.cpp - random_updater_instances.cpp + row_eta_matrix.cpp + scaler.cpp + sparse_matrix.cpp + square_dense_submatrix.cpp + static_matrix.cpp + random_updater.cpp COMPONENT_DEPENDENCIES util polynomial diff --git a/src/util/lp/binary_heap_priority_queue_instances.cpp b/src/util/lp/binary_heap_priority_queue.cpp similarity index 96% rename from src/util/lp/binary_heap_priority_queue_instances.cpp rename to src/util/lp/binary_heap_priority_queue.cpp index fca826a6b..89b9dba6f 100644 --- a/src/util/lp/binary_heap_priority_queue_instances.cpp +++ b/src/util/lp/binary_heap_priority_queue.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "util/lp/numeric_pair.h" -#include "util/lp/binary_heap_priority_queue.hpp" +#include "util/lp/binary_heap_priority_queue_def.h" namespace lp { template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); template unsigned binary_heap_priority_queue::dequeue(); diff --git a/src/util/lp/binary_heap_priority_queue.hpp b/src/util/lp/binary_heap_priority_queue_def.h similarity index 100% rename from src/util/lp/binary_heap_priority_queue.hpp rename to src/util/lp/binary_heap_priority_queue_def.h diff --git a/src/util/lp/binary_heap_upair_queue_instances.cpp b/src/util/lp/binary_heap_upair_queue.cpp similarity index 95% rename from src/util/lp/binary_heap_upair_queue_instances.cpp rename to src/util/lp/binary_heap_upair_queue.cpp index 6d093b175..a521f7058 100644 --- a/src/util/lp/binary_heap_upair_queue_instances.cpp +++ b/src/util/lp/binary_heap_upair_queue.cpp @@ -17,7 +17,7 @@ Revision History: --*/ -#include "util/lp/binary_heap_upair_queue.hpp" +#include "util/lp/binary_heap_upair_queue_def.h" namespace lp { template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); diff --git a/src/util/lp/binary_heap_upair_queue.hpp b/src/util/lp/binary_heap_upair_queue_def.h similarity index 100% rename from src/util/lp/binary_heap_upair_queue.hpp rename to src/util/lp/binary_heap_upair_queue_def.h diff --git a/src/util/lp/bound_analyzer_on_row.h b/src/util/lp/bound_analyzer_on_row.h index 2bacd7ac4..2c421da72 100644 --- a/src/util/lp/bound_analyzer_on_row.h +++ b/src/util/lp/bound_analyzer_on_row.h @@ -20,10 +20,9 @@ Revision History: #pragma once #include "util/vector.h" #include "util/lp/linear_combination_iterator.h" -#include "util/lp/implied_bound.h" -#include "util/lp/test_bound_analyzer.h" -#include -#include "util/lp/lp_bound_propagator.h" +#include "implied_bound.h" +#include "test_bound_analyzer.h" +#include "util/lp/bound_propagator.h" // We have an equality : sum by j of row[j]*x[j] = rs // We try to pin a var by pushing the total by using the variable bounds // In a loop we drive the partial sum down, denoting the variables of this process by _u. @@ -31,22 +30,22 @@ Revision History: namespace lp { class bound_analyzer_on_row { - + linear_combination_iterator & m_it; - lp_bound_propagator & m_bp; + bound_propagator & m_bp; unsigned m_row_or_term_index; int m_column_of_u; // index of an unlimited from above monoid - // -1 means that such a value is not found, -2 means that at least two of such monoids were found + // -1 means that such a value is not found, -2 means that at least two of such monoids were found int m_column_of_l; // index of an unlimited from below monoid impq m_rs; public : // constructor bound_analyzer_on_row( - linear_combination_iterator &it, - const numeric_pair& rs, - unsigned row_or_term_index, - lp_bound_propagator & bp + linear_combination_iterator &it, + const numeric_pair& rs, + unsigned row_or_term_index, + bound_propagator & bp ) : m_it(it), @@ -60,7 +59,7 @@ public : unsigned j; void analyze() { - + mpq a; unsigned j; while (((m_column_of_l != -2) || (m_column_of_u != -2)) && m_it.next(a, j)) analyze_bound_on_var_on_coeff(j, a); @@ -76,33 +75,33 @@ public : limit_all_monoids_from_above(); } - bool bound_is_available(unsigned j, bool low_bound) { - return (low_bound && low_bound_is_available(j)) || - (!low_bound && upper_bound_is_available(j)); + bool bound_is_available(unsigned j, bool lower_bound) { + return (lower_bound && lower_bound_is_available(j)) || + (!lower_bound && upper_bound_is_available(j)); } bool upper_bound_is_available(unsigned j) const { switch (m_bp.get_column_type(j)) - { - case column_type::fixed: - case column_type::boxed: - case column_type::upper_bound: - return true; - default: - return false; - } + { + case column_type::fixed: + case column_type::boxed: + case column_type::upper_bound: + return true; + default: + return false; + } } - bool low_bound_is_available(unsigned j) const { + bool lower_bound_is_available(unsigned j) const { switch (m_bp.get_column_type(j)) - { - case column_type::fixed: - case column_type::boxed: - case column_type::low_bound: - return true; - default: - return false; - } + { + case column_type::fixed: + case column_type::boxed: + case column_type::lower_bound: + return true; + default: + return false; + } } const impq & ub(unsigned j) const { @@ -110,8 +109,8 @@ public : return m_bp.get_upper_bound(j); } const impq & lb(unsigned j) const { - lp_assert(low_bound_is_available(j)); - return m_bp.get_low_bound(j); + lp_assert(lower_bound_is_available(j)); + return m_bp.get_lower_bound(j); } @@ -151,7 +150,7 @@ public : strict = !is_zero(ub(j).y); return a * ub(j).x; } - + strict = !is_zero(lb(j).y); return a * lb(j).x; } @@ -160,10 +159,10 @@ public : if (is_neg(a)) { return a * ub(j).x; } - + return a * lb(j).x; } - + void limit_all_monoids_from_above() { int strict = 0; @@ -209,7 +208,7 @@ public : bool str; bool a_is_pos = is_pos(a); mpq bound = total / a + monoid_max_no_mult(a_is_pos, j, str); - bool astrict = strict - static_cast(str) > 0; + bool astrict = strict - static_cast(str) > 0; if (a_is_pos) { limit_j(j, bound, true, true, astrict); } @@ -219,7 +218,7 @@ public : } } - + void limit_monoid_u_from_below() { // we are going to limit from below the monoid m_column_of_u, // every other monoid is impossible to limit from below @@ -240,7 +239,7 @@ public : } bound /= u_coeff; - + if (numeric_traits::is_pos(u_coeff)) { limit_j(m_column_of_u, bound, true, true, strict); } else { @@ -275,51 +274,51 @@ public : limit_j(m_column_of_l, bound, false, true, strict); } } - + // // it is the coefficent before the bounded column // void provide_evidence(bool coeff_is_pos) { // /* // auto & be = m_ibounds.back(); - // bool low_bound = be.m_low_bound; + // bool lower_bound = be.m_lower_bound; // if (!coeff_is_pos) - // low_bound = !low_bound; + // lower_bound = !lower_bound; // auto it = m_it.clone(); // mpq a; unsigned j; // while (it->next(a, j)) { // if (be.m_j == j) continue; - // lp_assert(bound_is_available(j, is_neg(a) ? low_bound : !low_bound)); + // lp_assert(bound_is_available(j, is_neg(a) ? lower_bound : !lower_bound)); // be.m_vector_of_bound_signatures.emplace_back(a, j, numeric_traits:: - // is_neg(a)? low_bound: !low_bound); + // is_neg(a)? lower_bound: !lower_bound); // } // delete it; // */ // } - void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_low_bound, bool strict){ - m_bp.try_add_bound(u, j, is_low_bound, coeff_before_j_is_pos, m_row_or_term_index, strict); + void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){ + m_bp.try_add_bound(u, j, is_lower_bound, coeff_before_j_is_pos, m_row_or_term_index, strict); } - + void advance_u(unsigned j) { if (m_column_of_u == -1) m_column_of_u = j; else m_column_of_u = -2; } - + void advance_l(unsigned j) { if (m_column_of_l == -1) m_column_of_l = j; else m_column_of_l = -2; } - + void analyze_bound_on_var_on_coeff(int j, const mpq &a) { switch (m_bp.get_column_type(j)) { - case column_type::low_bound: + case column_type::lower_bound: if (numeric_traits::is_pos(a)) advance_u(j); - else + else advance_l(j); break; case column_type::upper_bound: @@ -340,7 +339,7 @@ public : static void analyze_row(linear_combination_iterator &it, const numeric_pair& rs, unsigned row_or_term_index, - lp_bound_propagator & bp + bound_propagator & bp ) { bound_analyzer_on_row a(it, rs, row_or_term_index, bp); a.analyze(); diff --git a/src/util/lp/bound_propagator.cpp b/src/util/lp/bound_propagator.cpp index 8b106c521..c4fa2aefa 100644 --- a/src/util/lp/bound_propagator.cpp +++ b/src/util/lp/bound_propagator.cpp @@ -9,8 +9,8 @@ bound_propagator::bound_propagator(lar_solver & ls): column_type bound_propagator::get_column_type(unsigned j) const { return m_lar_solver.m_mpq_lar_core_solver.m_column_types()[j]; } -const impq & bound_propagator::get_low_bound(unsigned j) const { - return m_lar_solver.m_mpq_lar_core_solver.m_r_low_bounds()[j]; +const impq & bound_propagator::get_lower_bound(unsigned j) const { + return m_lar_solver.m_mpq_lar_core_solver.m_r_lower_bounds()[j]; } const impq & bound_propagator::get_upper_bound(unsigned j) const { return m_lar_solver.m_mpq_lar_core_solver.m_r_upper_bounds()[j]; @@ -30,14 +30,14 @@ void bound_propagator::try_add_bound(mpq v, unsigned j, bool is_low, bool coeff return; unsigned k; // index to ibounds if (is_low) { - if (try_get_value(m_improved_low_bounds, j, k)) { + if (try_get_value(m_improved_lower_bounds, j, k)) { auto & found_bound = m_ibounds[k]; if (v > found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict)) { found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); TRACE("try_add_bound", m_lar_solver.print_implied_bound(found_bound, tout);); } } else { - m_improved_low_bounds[j] = m_ibounds.size(); + m_improved_lower_bounds[j] = m_ibounds.size(); m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); TRACE("try_add_bound", m_lar_solver.print_implied_bound(m_ibounds.back(), tout);); } diff --git a/src/util/lp/bound_propagator.h b/src/util/lp/bound_propagator.h new file mode 100644 index 000000000..61e3045f3 --- /dev/null +++ b/src/util/lp/bound_propagator.h @@ -0,0 +1,27 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Lev Nachmanson +*/ +#pragma once +#include "util/lp/lp_settings.h" +namespace lp { +class lar_solver; +class bound_propagator { + std::unordered_map m_improved_lower_bounds; // these maps map a column index to the corresponding index in ibounds + std::unordered_map m_improved_upper_bounds; + lar_solver & m_lar_solver; +public: + vector m_ibounds; +public: + bound_propagator(lar_solver & ls); + column_type get_column_type(unsigned) const; + const impq & get_lower_bound(unsigned) const; + const impq & get_upper_bound(unsigned) const; + void try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict); + virtual bool bound_is_interesting(unsigned vi, + lp::lconstraint_kind kind, + const rational & bval) {return true;} + unsigned number_of_found_bounds() const { return m_ibounds.size(); } + virtual void consume(mpq const& v, unsigned j) { std::cout << "doh\n"; } +}; +} diff --git a/src/util/lp/column_info.h b/src/util/lp/column_info.h index 793cc1de9..407f40dfc 100644 --- a/src/util/lp/column_info.h +++ b/src/util/lp/column_info.h @@ -30,11 +30,11 @@ inline bool is_valid(unsigned j) { return static_cast(j) >= 0;} template class column_info { std::string m_name; - bool m_low_bound_is_set; - bool m_low_bound_is_strict; + bool m_lower_bound_is_set; + bool m_lower_bound_is_strict; bool m_upper_bound_is_set; bool m_upper_bound_is_strict; - T m_low_bound; + T m_lower_bound; T m_upper_bound; T m_fixed_value; bool m_is_fixed; @@ -43,11 +43,11 @@ class column_info { public: bool operator==(const column_info & c) const { return m_name == c.m_name && - m_low_bound_is_set == c.m_low_bound_is_set && - m_low_bound_is_strict == c.m_low_bound_is_strict && + m_lower_bound_is_set == c.m_lower_bound_is_set && + m_lower_bound_is_strict == c.m_lower_bound_is_strict && m_upper_bound_is_set == c.m_upper_bound_is_set&& m_upper_bound_is_strict == c.m_upper_bound_is_strict&& - (!m_low_bound_is_set || m_low_bound == c.m_low_bound) && + (!m_lower_bound_is_set || m_lower_bound == c.m_low_bound) && (!m_upper_bound_is_set || m_upper_bound == c.m_upper_bound) && m_cost == c.m_cost && m_is_fixed == c.m_is_fixed && @@ -60,8 +60,8 @@ public: } // the default constructor column_info(): - m_low_bound_is_set(false), - m_low_bound_is_strict(false), + m_lower_bound_is_set(false), + m_lower_bound_is_strict(false), m_upper_bound_is_set (false), m_upper_bound_is_strict (false), m_is_fixed(false), @@ -70,8 +70,8 @@ public: {} column_info(unsigned column_index) : - m_low_bound_is_set(false), - m_low_bound_is_strict(false), + m_lower_bound_is_set(false), + m_lower_bound_is_strict(false), m_upper_bound_is_set (false), m_upper_bound_is_strict (false), m_is_fixed(false), @@ -81,11 +81,11 @@ public: column_info(const column_info & ci) { m_name = ci.m_name; - m_low_bound_is_set = ci.m_low_bound_is_set; - m_low_bound_is_strict = ci.m_low_bound_is_strict; + m_lower_bound_is_set = ci.m_lower_bound_is_set; + m_lower_bound_is_strict = ci.m_lower_bound_is_strict; m_upper_bound_is_set = ci.m_upper_bound_is_set; m_upper_bound_is_strict = ci.m_upper_bound_is_strict; - m_low_bound = ci.m_low_bound; + m_lower_bound = ci.m_lower_bound; m_upper_bound = ci.m_upper_bound; m_cost = ci.m_cost; m_fixed_value = ci.m_fixed_value; @@ -98,7 +98,7 @@ public: } column_type get_column_type() const { - return m_is_fixed? column_type::fixed : (m_low_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::low_bound) : (m_upper_bound_is_set? column_type::upper_bound: column_type::free_column)); + return m_is_fixed? column_type::fixed : (m_lower_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::lower_bound) : (m_upper_bound_is_set? column_type::upper_bound: column_type::free_column)); } column_type get_column_type_no_flipping() const { @@ -106,25 +106,25 @@ public: return column_type::fixed; } - if (m_low_bound_is_set) { - return m_upper_bound_is_set? column_type::boxed: column_type::low_bound; + if (m_lower_bound_is_set) { + return m_upper_bound_is_set? column_type::boxed: column_type::lower_bound; } // we are flipping the bounds! return m_upper_bound_is_set? column_type::upper_bound : column_type::free_column; } - T get_low_bound() const { - lp_assert(m_low_bound_is_set); - return m_low_bound; + T get_lower_bound() const { + lp_assert(m_lower_bound_is_set); + return m_lower_bound; } T get_upper_bound() const { lp_assert(m_upper_bound_is_set); return m_upper_bound; } - bool low_bound_is_set() const { - return m_low_bound_is_set; + bool lower_bound_is_set() const { + return m_lower_bound_is_set; } bool upper_bound_is_set() const { @@ -138,23 +138,23 @@ public: if (is_flipped()){ return m_upper_bound; } - return m_low_bound_is_set? m_low_bound : numeric_traits::zero(); + return m_lower_bound_is_set? m_lower_bound : numeric_traits::zero(); } bool is_flipped() { - return m_upper_bound_is_set && !m_low_bound_is_set; + return m_upper_bound_is_set && !m_lower_bound_is_set; } - bool adjusted_low_bound_is_set() { - return !is_flipped()? low_bound_is_set(): upper_bound_is_set(); + bool adjusted_lower_bound_is_set() { + return !is_flipped()? lower_bound_is_set(): upper_bound_is_set(); } bool adjusted_upper_bound_is_set() { - return !is_flipped()? upper_bound_is_set(): low_bound_is_set(); + return !is_flipped()? upper_bound_is_set(): lower_bound_is_set(); } T get_adjusted_upper_bound() { - return get_upper_bound() - get_low_bound(); + return get_upper_bound() - get_lower_bound(); } bool is_fixed() const { @@ -162,7 +162,7 @@ public: } bool is_free() { - return !m_low_bound_is_set && !m_upper_bound_is_set; + return !m_lower_bound_is_set && !m_upper_bound_is_set; } void set_fixed_value(T v) { @@ -191,9 +191,9 @@ public: return m_name; } - void set_low_bound(T const & l) { - m_low_bound = l; - m_low_bound_is_set = true; + void set_lower_bound(T const & l) { + m_lower_bound = l; + m_lower_bound_is_set = true; } void set_upper_bound(T const & l) { @@ -201,8 +201,8 @@ public: m_upper_bound_is_set = true; } - void unset_low_bound() { - m_low_bound_is_set = false; + void unset_lower_bound() { + m_lower_bound_is_set = false; } void unset_upper_bound() { @@ -213,8 +213,8 @@ public: m_is_fixed = false; } - bool low_bound_holds(T v) { - return !low_bound_is_set() || v >= m_low_bound -T(0.0000001); + bool lower_bound_holds(T v) { + return !lower_bound_is_set() || v >= m_lower_bound -T(0.0000001); } bool upper_bound_holds(T v) { @@ -222,36 +222,36 @@ public: } bool bounds_hold(T v) { - return low_bound_holds(v) && upper_bound_holds(v); + return lower_bound_holds(v) && upper_bound_holds(v); } bool adjusted_bounds_hold(T v) { - return adjusted_low_bound_holds(v) && adjusted_upper_bound_holds(v); + return adjusted_lower_bound_holds(v) && adjusted_upper_bound_holds(v); } - bool adjusted_low_bound_holds(T v) { - return !adjusted_low_bound_is_set() || v >= -T(0.0000001); + bool adjusted_lower_bound_holds(T v) { + return !adjusted_lower_bound_is_set() || v >= -T(0.0000001); } bool adjusted_upper_bound_holds(T v) { return !adjusted_upper_bound_is_set() || v <= get_adjusted_upper_bound() + T(0.000001); } bool is_infeasible() { - if ((!upper_bound_is_set()) || (!low_bound_is_set())) + if ((!upper_bound_is_set()) || (!lower_bound_is_set())) return false; // ok, both bounds are set - bool at_least_one_is_strict = upper_bound_is_strict() || low_bound_is_strict(); + bool at_least_one_is_strict = upper_bound_is_strict() || lower_bound_is_strict(); if (!at_least_one_is_strict) - return get_upper_bound() < get_low_bound(); + return get_upper_bound() < get_lower_bound(); // at least on bound is strict - return get_upper_bound() <= get_low_bound(); // the equality is impossible + return get_upper_bound() <= get_lower_bound(); // the equality is impossible } - bool low_bound_is_strict() const { - return m_low_bound_is_strict; + bool lower_bound_is_strict() const { + return m_lower_bound_is_strict; } - void set_low_bound_strict(bool val) { - m_low_bound_is_strict = val; + void set_lower_bound_strict(bool val) { + m_lower_bound_is_strict = val; } bool upper_bound_is_strict() const { diff --git a/src/util/lp/column_namer.h b/src/util/lp/column_namer.h index deb393796..7daf6676b 100644 --- a/src/util/lp/column_namer.h +++ b/src/util/lp/column_namer.h @@ -68,7 +68,31 @@ public: out << "v" << it.second; } } + + template + void print_linear_combination_of_column_indices_std(const vector> & coeffs, std::ostream & out) const { + bool first = true; + for (const auto & it : coeffs) { + auto val = it.first; + if (first) { + first = false; + } else { + if (numeric_traits::is_pos(val)) { + out << " + "; + } else { + out << " - "; + val = -val; + } + } + if (val == -numeric_traits::one()) + out << " - "; + else if (val != numeric_traits::one()) + out << val; + + out << get_column_name(it.second); + } + } template void print_linear_combination_of_column_indices(const vector> & coeffs, std::ostream & out) const { bool first = true; diff --git a/src/util/lp/conversion_helper.h b/src/util/lp/conversion_helper.h index f80b1c2c6..feb999743 100644 --- a/src/util/lp/conversion_helper.h +++ b/src/util/lp/conversion_helper.h @@ -22,8 +22,8 @@ Revision History: namespace lp { template struct conversion_helper { - static V get_low_bound(const column_info & ci) { - return V(ci.get_low_bound(), ci.low_bound_is_strict()? 1 : 0); + static V get_lower_bound(const column_info & ci) { + return V(ci.get_lower_bound(), ci.lower_bound_is_strict()? 1 : 0); } static V get_upper_bound(const column_info & ci) { @@ -37,20 +37,20 @@ struct conversion_helper { if (!ci.upper_bound_is_strict()) return ci.get_upper_bound().get_double(); double eps = 0.00001; - if (!ci.low_bound_is_set()) + if (!ci.lower_bound_is_set()) return ci.get_upper_bound().get_double() - eps; - eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double() / 1000, eps); + eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); return ci.get_upper_bound().get_double() - eps; } - static double get_low_bound(const column_info & ci) { - if (!ci.low_bound_is_strict()) - return ci.get_low_bound().get_double(); + static double get_lower_bound(const column_info & ci) { + if (!ci.lower_bound_is_strict()) + return ci.get_lower_bound().get_double(); double eps = 0.00001; if (!ci.upper_bound_is_set()) - return ci.get_low_bound().get_double() + eps; - eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double() / 1000, eps); - return ci.get_low_bound().get_double() + eps; + return ci.get_lower_bound().get_double() + eps; + eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); + return ci.get_lower_bound().get_double() + eps; } }; diff --git a/src/util/lp/core_solver_pretty_printer_instances.cpp b/src/util/lp/core_solver_pretty_printer.cpp similarity index 95% rename from src/util/lp/core_solver_pretty_printer_instances.cpp rename to src/util/lp/core_solver_pretty_printer.cpp index 0bd7f5559..3cba2240d 100644 --- a/src/util/lp/core_solver_pretty_printer_instances.cpp +++ b/src/util/lp/core_solver_pretty_printer.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "util/lp/numeric_pair.h" -#include "util/lp/core_solver_pretty_printer.hpp" +#include "util/lp/core_solver_pretty_printer_def.h" template lp::core_solver_pretty_printer::core_solver_pretty_printer(lp::lp_core_solver_base &, std::ostream & out); template void lp::core_solver_pretty_printer::print(); template lp::core_solver_pretty_printer::~core_solver_pretty_printer(); diff --git a/src/util/lp/core_solver_pretty_printer.h b/src/util/lp/core_solver_pretty_printer.h index 87c528792..2f2c61f7b 100644 --- a/src/util/lp/core_solver_pretty_printer.h +++ b/src/util/lp/core_solver_pretty_printer.h @@ -48,7 +48,7 @@ class core_solver_pretty_printer { std::string m_cost_title; std::string m_basis_heading_title; std::string m_x_title; - std::string m_low_bounds_title; + std::string m_lower_bounds_title; std::string m_upp_bounds_title; std::string m_exact_norm_title; std::string m_approx_norm_title; @@ -75,7 +75,7 @@ public: void init_column_widths(); - void adjust_width_with_low_bound(unsigned column, unsigned & w); + void adjust_width_with_lower_bound(unsigned column, unsigned & w); void adjust_width_with_upper_bound(unsigned column, unsigned & w); void adjust_width_with_bounds(unsigned column, unsigned & w); @@ -97,7 +97,7 @@ public: void print_x(); - std::string get_low_bound_string(unsigned j); + std::string get_lower_bound_string(unsigned j); std::string get_upp_bound_string(unsigned j); diff --git a/src/util/lp/core_solver_pretty_printer.hpp b/src/util/lp/core_solver_pretty_printer_def.h similarity index 95% rename from src/util/lp/core_solver_pretty_printer.hpp rename to src/util/lp/core_solver_pretty_printer_def.h index 8fc22cd2b..c8dac1b03 100644 --- a/src/util/lp/core_solver_pretty_printer.hpp +++ b/src/util/lp/core_solver_pretty_printer_def.h @@ -38,7 +38,7 @@ core_solver_pretty_printer::core_solver_pretty_printer(lp_core_solver_base m_rs(ncols(), zero_of_type()), m_w_buff(core_solver.m_w), m_ed_buff(core_solver.m_ed) { - m_low_bounds_title = "low"; + m_lower_bounds_title = "low"; m_upp_bounds_title = "upp"; m_exact_norm_title = "exact cn"; m_approx_norm_title = "approx cn"; @@ -139,9 +139,9 @@ template void core_solver_pretty_printer::init_co } } -template void core_solver_pretty_printer::adjust_width_with_low_bound(unsigned column, unsigned & w) { - if (!m_core_solver.low_bounds_are_set()) return; - w = std::max(w, (unsigned)T_to_string(m_core_solver.low_bound_value(column)).size()); +template void core_solver_pretty_printer::adjust_width_with_lower_bound(unsigned column, unsigned & w) { + if (!m_core_solver.lower_bounds_are_set()) return; + w = std::max(w, (unsigned)T_to_string(m_core_solver.lower_bound_value(column)).size()); } template void core_solver_pretty_printer::adjust_width_with_upper_bound(unsigned column, unsigned & w) { w = std::max(w, (unsigned)T_to_string(m_core_solver.upper_bound_value(column)).size()); @@ -151,11 +151,11 @@ template void core_solver_pretty_printer::adjust_ switch (m_core_solver.get_column_type(column)) { case column_type::fixed: case column_type::boxed: - adjust_width_with_low_bound(column, w); + adjust_width_with_lower_bound(column, w); adjust_width_with_upper_bound(column, w); break; - case column_type::low_bound: - adjust_width_with_low_bound(column, w); + case column_type::lower_bound: + adjust_width_with_lower_bound(column, w); break; case column_type::upper_bound: adjust_width_with_upper_bound(column, w); @@ -236,13 +236,13 @@ template void core_solver_pretty_printer::print_x m_out << std::endl; } -template std::string core_solver_pretty_printer::get_low_bound_string(unsigned j) { +template std::string core_solver_pretty_printer::get_lower_bound_string(unsigned j) { switch (m_core_solver.get_column_type(j)){ case column_type::boxed: - case column_type::low_bound: + case column_type::lower_bound: case column_type::fixed: - if (m_core_solver.low_bounds_are_set()) - return T_to_string(m_core_solver.low_bound_value(j)); + if (m_core_solver.lower_bounds_are_set()) + return T_to_string(m_core_solver.lower_bound_value(j)); else return std::string("0"); break; @@ -268,12 +268,12 @@ template void core_solver_pretty_printer::print_l if (ncols() == 0) { return; } - int blanks = m_title_width + 1 - static_cast(m_low_bounds_title.size()); - m_out << m_low_bounds_title; + int blanks = m_title_width + 1 - static_cast(m_lower_bounds_title.size()); + m_out << m_lower_bounds_title; print_blanks(blanks, m_out); for (unsigned i = 0; i < ncols(); i++) { - string s = get_low_bound_string(i); + string s = get_lower_bound_string(i); int blanks = m_column_widths[i] - static_cast(s.size()); print_blanks(blanks, m_out); m_out << s << " "; // the column interval diff --git a/src/util/lp/cut_solver.cpp b/src/util/lp/cut_solver.cpp index 2b527aa01..241759cb3 100644 --- a/src/util/lp/cut_solver.cpp +++ b/src/util/lp/cut_solver.cpp @@ -4,5 +4,21 @@ */ #include "util/lp/cut_solver.h" namespace lp { + mpq polynomial::m_local_zero = zero_of_type(); + + size_t constraint_hash::operator() (const constraint* c) const { return c->id(); } + + bool constraint_equal::operator() (const constraint* a, const constraint * b) const { return a->id() == b->id(); } + + std::ostream& operator<<(std::ostream& out, pp_poly const& p) { + p.s.print_polynomial(out, p.p); + return out; + } + + std::ostream& operator<<(std::ostream& out, pp_constraint const& c) { + c.s.print_constraint(out, c.c); + return out; + } + } diff --git a/src/util/lp/cut_solver.h b/src/util/lp/cut_solver.h index 6fcf041b6..6a11da117 100644 --- a/src/util/lp/cut_solver.h +++ b/src/util/lp/cut_solver.h @@ -8,192 +8,624 @@ #include "util/lp/lp_settings.h" #include "util/lp/column_namer.h" #include "util/lp/integer_domain.h" +#include "util/lp/lp_utils.h" #include +#include "util/lp/int_set.h" +#include "util/lp/linear_combination_iterator_on_std_vector.h" +#include "util/lp/stacked_vector.h" + namespace lp { -template +enum class lbool { l_false, l_true, l_undef }; +inline std::string lbool_to_string(lbool l) { + switch(l) { + case lbool::l_true: return std::string("true"); + case lbool::l_false: return std::string("false"); + case lbool::l_undef: return std::string("undef"); + default: + return std::string("what is it?"); + } +} + +class cut_solver; + +struct monomial { + mpq m_coeff; // the coefficient of the monomial + var_index m_var; // the variable index +public: + monomial(const mpq& coeff, var_index var) : m_coeff(coeff), m_var(var) {} + // copy constructor + monomial(const monomial& m) : monomial(m.coeff(), m.var()) {} + monomial(var_index var) : monomial(one_of_type(), var) {} + const mpq & coeff() const { return m_coeff; } + mpq & coeff() { return m_coeff; } + var_index var() const { return m_var; } + std::pair to_pair() const { return std::make_pair(coeff(), var());} +}; + +struct polynomial { + static mpq m_local_zero; + // the polynomial evaluates to m_coeffs + m_a + vector m_coeffs; + mpq m_a; // the free coefficient + polynomial(const vector& p, const mpq & a) : m_coeffs(p), m_a(a) {} + polynomial(const vector& p) : polynomial(p, zero_of_type()) {} + polynomial(): m_a(zero_of_type()) {} + polynomial(const polynomial & p) : m_coeffs(p.m_coeffs), m_a(p.m_a) {} + + const mpq & coeff(var_index j) const { + for (const auto & t : m_coeffs) { + if (j == t.var()) { + return t.coeff(); + } + } + return m_local_zero; + } + + polynomial & operator+=(const polynomial & p) { + m_a += p.m_a; + for (const auto & t: p.m_coeffs) + *this += monomial(t.coeff(), t.var()); + return *this; + } + + void add(const mpq & c, const polynomial &p) { + m_a += p.m_a * c; + + for (const auto & t: p.m_coeffs) + *this += monomial(c * t.coeff(), t.var()); + } + + void clear() { + m_coeffs.clear(); + m_a = zero_of_type(); + } + + bool is_empty() const { return m_coeffs.size() == 0 && numeric_traits::is_zero(m_a); } + + unsigned number_of_monomials() const { return m_coeffs.size();} + + void add(const monomial &m ){ + if (is_zero(m.coeff())) return; + for (unsigned k = 0; k < m_coeffs.size(); k++) { + auto & l = m_coeffs[k]; + if (m.var() == l.var()) { + l.coeff() += m.coeff(); + if (l.coeff() == 0) + m_coeffs.erase(m_coeffs.begin() + k); + return; + } + } + m_coeffs.push_back(m); + lp_assert(is_correct()); + } + + polynomial & operator+=(const monomial &m ){ + add(m); + return *this; + } + + polynomial & operator+=(const mpq &c ){ + m_a += c; + return *this; + } + + + bool is_correct() const { + std::unordered_set s; + for (auto & l : m_coeffs) { + if (l.coeff() == 0) + return false; + s.insert(l.var()); + } + return m_coeffs.size() == s.size(); + } + + bool var_coeff_is_unit(unsigned j) const { + const mpq & a = coeff(j); + return a == 1 || a == -1; + } + const vector & coeffs() const { return m_coeffs; } +}; + +class constraint; // forward definition +struct constraint_hash { + size_t operator() (const constraint* c) const; +}; + +struct constraint_equal { + bool operator() (const constraint * a, const constraint * b) const; +}; + +class constraint { // we only have less or equal for the inequality sign, which is enough for integral variables + int m_id; + bool m_is_ineq; + polynomial m_poly; + mpq m_d; // the divider for the case of a divisibility constraint + const svector m_assert_origins; // these indices come from the client + bool m_active; + std::unordered_set m_predecessors; // used in tight_constraints and lemmas +public : + void set_active_flag() {m_active = true;} + void remove_active_flag() { m_active = false; } + bool is_active() const { return m_active; } + unsigned id() const { return m_id; } + const polynomial & poly() const { return m_poly; } + polynomial & poly() { return m_poly; } + const svector & assert_origins() const { return m_assert_origins;} + bool is_lemma() const { return !is_assert(); } + bool is_assert() const { return m_predecessors.size() == 0; } + bool is_ineq() const { return m_is_ineq; } + const mpq & divider() const { return m_d; } +public : + static constraint * make_ineq_assert( + int id, + const vector& term, + const mpq& a, + const svector & origins) { + return new constraint(id, origins, polynomial(term, a), true); + } + + static constraint * make_ineq_constraint( + int id, + const polynomial & p, + std::unordered_set predecessors_set) { + auto c = new constraint(id, p, true); + c->predecessors() = predecessors_set; + return c; + } + + static constraint * make_ineq_lemma(unsigned id, const polynomial &p) { + return new constraint(id, p, true); + } + + // static constraint make_div_lemma(unsigned id, const polynomial &p, const mpq & div) { + // lp_assert(false); // not implemented + // // constraint * c = new constraint(id, p, true); + // // c->m_d = div; + // return nullptr; + // } +private: + constraint( + unsigned id, + const svector & assert_origins, + const polynomial & p, + bool is_ineq): + m_id(id), + m_is_ineq(is_ineq), + m_poly(p), + m_assert_origins(assert_origins), + m_active(false) { // creates an assert + } + + + + constraint( + unsigned id, + const polynomial & p, + bool is_ineq): + m_id(id), + m_is_ineq(is_ineq), + m_poly(p), + m_active(false) { // creates a lemma + } + +public: + constraint() {} + + const mpq & coeff(var_index j) const { + return m_poly.coeff(j); + } + const vector& coeffs() const { return m_poly.m_coeffs;} + + bool is_tight(unsigned j) const { + const mpq & a = m_poly.coeff(j); + return a == 1 || a == -1; + } + const std::unordered_set& predecessors() const { return m_predecessors; } + std::unordered_set& predecessors() { return m_predecessors; } + void add_predecessor(const constraint* p) { + lp_assert(p != nullptr); + m_predecessors.insert(p); } +}; + + +struct pp_poly { + cut_solver const& s; + polynomial const& p; + pp_poly(cut_solver const& s, polynomial const& p): s(s), p(p) {} +}; + +struct pp_constraint { + cut_solver const& s; + constraint const& c; + pp_constraint(cut_solver const& s, constraint const& c): s(s), c(c) {} +}; + +std::ostream& operator<<(std::ostream& out, pp_poly const& p); +std::ostream& operator<<(std::ostream& out, pp_constraint const& p); + class cut_solver : public column_namer { public: // for debugging - struct polynomial { - // the polynom evaluates to m_term + m_a - vector> m_term; - mpq m_a; // the free coefficient - polynomial(vector>& p, const mpq & a) : m_term(p), m_a(a) { - } - polynomial(vector>& p) : polynomial(p, 0) { - } - - }; + + typedef lp::polynomial polynomial; + typedef lp::monomial monomial; + + vector> to_pairs(const vector& ms) const { + vector> ret; + for (const auto p : ms) + ret.push_back(p.to_pair()); + return ret; + } + + + typedef const constraint const_constr; - struct ineq { // we only have less or equal, which is enough for integral variables - polynomial m_poly; - ineq(vector>& term, const mpq& a): m_poly(term, a) { + class literal { + int m_decision_context_index; // points to the trail element if a decision has been made + unsigned m_var; + bool m_is_lower; + mpq m_bound; + constraint* m_constraint; // nullptr if it is a decided literal + constraint* m_tight_constr; // nullptr if it is not calculated + unsigned m_prev_var_level; + public: + private: + literal( // creates an implied bound + unsigned var_index, + bool is_lower, + const mpq & bound, + constraint * constr, + unsigned prev_var_level) : + m_decision_context_index(-1), + m_var(var_index), + m_is_lower(is_lower), + m_bound(bound), + m_constraint(constr), + m_tight_constr(nullptr), + m_prev_var_level(prev_var_level) { } - }; + literal( // creates a decided bound + int trail_index, + unsigned var_index, + bool is_lower, + const mpq & bound, + unsigned prev_var_level): + m_decision_context_index(trail_index), + m_var(var_index), + m_is_lower(is_lower), + m_bound(bound), + m_constraint(nullptr), + m_tight_constr(nullptr), + m_prev_var_level(prev_var_level) { + } + public: + const constraint * tight_constr() const { return m_tight_constr; } + constraint*& tight_constr () { return m_tight_constr; } + const mpq & bound() const { return m_bound; } + bool is_lower() const { return m_is_lower; } + bool is_upper() const { return !m_is_lower; } + int decision_context_index() const { return m_decision_context_index; } + const_constr * cnstr() const { return m_constraint; } + constraint * cnstr() { return m_constraint; } + literal() {} - vector m_ineqs; + bool tight_constraint_is_calculated() const { + return m_tight_constr != nullptr; + } - enum class lbool { - l_false, // false - l_true, // true - l_undef // undef - }; - - enum class literal_type { - BOOL, - INEQ, - BOUND - }; + bool is_decided() const { return m_decision_context_index != -1; } - struct literal { - literal_type m_tag; - bool m_sign; // true means the pointed inequality is negated, or bound is negated, or boolean value is negated - bool m_is_lower; - unsigned m_id; - unsigned m_index_of_ineq; // index into m_ineqs - bool m_bool_val; // used if m_tag is equal to BOOL - mpq m_bound; // used if m_tag is BOUND - literal(bool sign, bool val): m_tag(literal_type::BOOL), - m_bool_val(val){ - } - literal(bool sign, unsigned index_of_ineq) : m_tag(literal_type::INEQ), m_index_of_ineq(index_of_ineq) {} + bool is_implied() const { return !is_decided();} + + // TBD: would be nice with a designated type for variables? + + unsigned var() const { return m_var; } + + static literal make_implied_literal(unsigned var_index, bool is_lower, const mpq & bound, constraint * c, unsigned var_level) { + return literal(var_index, is_lower, bound, c, var_level); + } + static literal make_decided_literal(unsigned var_index, bool is_lower, + const mpq & bound, int decision_context_index, unsigned var_level) { + return literal(decision_context_index, var_index, is_lower, bound, var_level); + } + + unsigned prev_var_level() const { return m_prev_var_level; } }; - struct var_info { - unsigned m_user_var_index; - var_info(unsigned user_var_index) : m_user_var_index(user_var_index) {} - vector m_literals; // point to m_trail - integer_domain m_domain; + enum class bound_type { + LOWER, UPPER, UNDEF + }; + + struct bound_result { + mpq m_bound; + bound_type m_type; + + bound_result(const mpq & b, bound_type bt): m_bound(b), m_type(bt) {} + bound_result() : m_type(bound_type::UNDEF) { + } + void print( std::ostream & out) const { + if (m_type == bound_type::LOWER) { + out << "lower bound = "; + } + else if (m_type == bound_type::UPPER) { + out << "upper bound = "; + } + else { + out << "undef"; + return; + } + out << m_bound; + } + const mpq & bound() const { return m_bound; } }; - vector m_var_infos; - - bool lhs_is_int(const vector> & lhs) const { + class var_info { + unsigned m_internal_j; // it is just the index into m_var_infos of this var_info, if the var_info is not active then this value is set to (unsigned)-1 + integer_domain m_domain; + // the map of constraints using the var: bound_type = UNDEF stands for a div constraint + std::unordered_map m_dependent_constraints; + unsigned m_external_stack_level; + unsigned m_number_of_bound_propagations; + unsigned m_number_of_asserts; + + public: + unsigned number_of_asserts() const { return m_number_of_asserts; } + + var_info(unsigned user_var_index) : + m_internal_j(user_var_index), + m_external_stack_level(static_cast(-1)), + m_number_of_bound_propagations(0), + m_number_of_asserts(0) + {} + var_info() : m_internal_j(static_cast(-1)), + m_external_stack_level(static_cast(-1)), + m_number_of_bound_propagations(0), + m_number_of_asserts(0) {} + + bool is_active() const { return m_internal_j != static_cast(-1); } + + const integer_domain & domain() const { return m_domain; } + unsigned internal_j() const { + return m_internal_j; + } + void activate(unsigned internal_j) { + m_internal_j = internal_j; + } + void add_dependent_constraint(constraint* i, bound_type bt) { + lp_assert(m_dependent_constraints.find(i) == m_dependent_constraints.end()); + if (i->is_assert()) + m_number_of_asserts++; + m_dependent_constraints[i] = bt; + } + void remove_depended_constraint(constraint* i) { + lp_assert(m_dependent_constraints.find(i) != m_dependent_constraints.end()); + if (i->is_assert()) + m_number_of_asserts--; + m_dependent_constraints.erase(i); + } + + void conditional_push(unsigned external_level) { + if (external_level != m_external_stack_level) { + m_domain.push(); + m_external_stack_level = external_level; + } + } + + bool intersect_with_lower_bound(const mpq & b, unsigned explanation, unsigned stack_level) { + conditional_push(stack_level); + return m_domain.intersect_with_bound(b, true, explanation); + } + + bool intersect_with_upper_bound(const mpq & b, unsigned explanation, unsigned external_level) { + conditional_push(external_level); + return m_domain.intersect_with_bound(b, false, explanation); + } + + bool is_fixed() const { return m_domain.is_fixed();} + bool get_upper_bound(mpq & b) const { return m_domain.get_upper_bound(b); } + bool get_lower_bound(mpq & b) const { return m_domain.get_lower_bound(b); } + bool get_upper_bound_with_expl(mpq & b, unsigned& expl) const { return m_domain.get_upper_bound_with_expl(b, expl); } + bool get_lower_bound_with_expl(mpq & b, unsigned& expl) const { return m_domain.get_lower_bound_with_expl(b, expl); } + void print_var_domain(std::ostream &out) const { m_domain.print(out); } + std::unordered_map & dependent_constraints() { return m_dependent_constraints; } + const std::unordered_map & dependent_constraints() const { return m_dependent_constraints; } + int get_lower_bound_expl() const { return m_domain.get_lower_bound_expl();} + int get_upper_bound_expl() const { return m_domain.get_upper_bound_expl();} + public: + void pop(unsigned k, unsigned external_level) { + m_domain.pop(k); + m_external_stack_level = external_level; + } + unsigned external_stack_level() const { return m_external_stack_level; } + unsigned &external_stack_level() { return m_external_stack_level; } + + unsigned number_of_bound_propagations() const { return m_number_of_bound_propagations; } + unsigned & number_of_bound_propagations() { return m_number_of_bound_propagations; } + + }; // end of var_info + + bool lhs_is_int(const vector & lhs) const { for (auto & p : lhs) { - if (numeric_traits::is_int(p.first) == false) return false; + if (numeric_traits::is_int(p.coeff()) == false) return false; } return true; } + +public: + + bool all_fixed_except_j(const polynomial & p, var_index j) const { + for (auto &m : p.coeffs()) + if (m.var() != j && m_var_infos[m.var()].is_fixed() == false) + return false; + return true; + } + + bool lower_bound_exists(const var_info & v) const { + return v.get_lower_bound_expl() != -1; + } + + bool upper_bound_exists(const var_info & v) const { + return v.get_upper_bound_expl() != -1; + } + + bool lower_bound_exists(const integer_domain & v) const { + return v.get_lower_bound_expl() != -1; + } + + bool upper_bound_exists(const integer_domain & v) const { + return v.get_upper_bound_expl() != -1; + } + - public: + bool is_cc(var_index j, const constraint*&lower, const constraint*&upper) const { + const var_info & vj = m_var_infos[j]; + if (lower_bound_exists(vj) && upper_bound_exists(vj)) + return false; + if (vj.domain().is_empty()) + return false; + + unsigned upper_bounds = 0; + unsigned lower_bounds = 0; + for (auto p : vj.dependent_constraints()) { + constraint* c = p.first; + const mpq& coeff = c->poly().coeff(j); + if (coeff == one_of_type() || coeff == - one_of_type()) + continue; + if (!all_fixed_except_j(c->poly(), j)) continue; + if (p.second == bound_type::UPPER) { + upper_bounds++; + upper = c; + if (lower_bounds) return true; + } else if (p.second == bound_type::LOWER) { + lower_bounds++; + lower = c; + if (upper_bounds) + return true; + } + } + return false; + } + std::string get_column_name(unsigned j) const { - return m_var_name_function(m_var_infos[j].m_user_var_index); + return m_var_name_function(m_var_infos[j].internal_j()); } - unsigned add_ineq(vector> & lhs, const mpq& free_coeff) { - lp_assert(lhs_is_int(lhs)); - lp_assert(free_coeff.is_int()); - vector> local_lhs; - for (auto & p : lhs) - local_lhs.push_back(std::make_pair(p.first, add_var(p.second))); - m_ineqs.push_back(ineq(local_lhs, free_coeff)); - return m_ineqs.size() - 1; + + ~cut_solver() { + for (constraint * c : m_asserts) + delete c; + for (constraint * c : m_lemmas) + delete c; } + + class active_set { + std::unordered_set m_cs; + public: + + std::unordered_set cs() const { return m_cs;} - std::function m_var_name_function; - bool m_inconsistent; // tracks if state is consistent - unsigned m_scope_lvl; // tracks the number of case splits + bool is_empty() const { return m_cs.size() == 0; } - svector m_trail; - // backtracking state from the SAT solver: - struct scope { - unsigned m_trail_lim; // pointer into assignment stack - unsigned m_clauses_to_reinit_lim; // ignore for now - bool m_inconsistent; // really needed? + void add_constraint(constraint* c) { + if (c->is_active()) return; + m_cs.insert(c); + c->set_active_flag(); + } + + void clear() { + for (constraint * c: m_cs) { + c->remove_active_flag(); + } + m_cs.clear(); + } + + + constraint* remove_random_constraint(unsigned rand) { + if (m_cs.size() == 0) + return nullptr; + unsigned j = rand % m_cs.size(); + auto it = std::next(m_cs.begin(), j); + constraint * c = *it; + c->remove_active_flag(); + m_cs.erase(it); + return c; + } + + unsigned size() const { + return static_cast(m_cs.size()); + } + + void remove_constraint(constraint * c) { + m_cs.erase(c); + c->remove_active_flag(); + } }; - svector m_scopes; - std::unordered_map m_user_vars_to_cut_solver_vars; - unsigned add_var(unsigned user_var_index) { - unsigned ret; - if (try_get_value(m_user_vars_to_cut_solver_vars, user_var_index, ret)) - return ret; - unsigned j = m_var_infos.size(); - m_var_infos.push_back(var_info(user_var_index)); - return m_user_vars_to_cut_solver_vars[user_var_index] = j; - } + struct scope { + unsigned m_asserts_size; + unsigned m_lemmas_size; + unsigned m_trail_size; + scope() {} + scope(unsigned asserts_size, + unsigned lemmas_size, + unsigned trail_size) : m_asserts_size(asserts_size), + m_lemmas_size(lemmas_size), + m_trail_size(trail_size) + {} + }; + + // fields + vector m_var_infos; + svector m_asserts; + svector m_lemmas; + vector m_v; // the values of the variables + std::function m_var_name_function; + std::function m_print_constraint_function; + std::function m_number_of_variables_function; + std::function m_var_value_function; + active_set m_active_set; + vector m_trail; + lp_settings & m_settings; + unsigned m_max_constraint_id; + std::set m_U; // the set of conflicting cores + unsigned m_bounded_search_calls; + unsigned m_number_of_conflicts; + vector m_scopes; + std::unordered_map m_user_vars_to_cut_solver_vars; + std::unordered_set m_explanation; // if this collection is not empty we have a conflict + // the number of decisions in the current trail + unsigned m_decision_level; + bool m_stuck_state; + bool m_cancelled; + // debug fields + unsigned m_number_of_constraints_tried_for_propagaton; + unsigned m_number_of_pops; + bool is_lower_bound(literal & l) const { - if (l.m_tag != literal_type::BOUND || l.m_is_lower) - return false; - l = l.m_bound; - return true; + return l.is_lower(); } - bool lower_for_var(unsigned j, T & lower) const { - bool ret = false; - for (unsigned i : m_var_infos[j].m_literals) - if (is_lower_bound(m_trail[i])) { - if (ret == false) { - ret = true; - lower = get_bound(m_trail[i]); - } else { - lower = std::max(lower, get_bound(m_trail[i])); - } - } - return ret; - } + // bool lower_for_var(unsigned j, mpq & lower) const { + // bool ret = false; + // for (unsigned i : m_var_infos[j].m_literals) + // if (is_lower_bound(m_trail[i])) { + // if (ret == false) { + // ret = true; + // lower = get_bound(m_trail[i]); + // } else { + // lower = std::max(lower, get_bound(m_trail[i])); + // } + // } + // return ret; + // } bool is_upper_bound(literal & l) const { - if (l.m_tag != literal_type::BOUND || l.m_is_upper) - return false; - l = l.m_bound; - return true; + return !l.is_lower(); } - bool upper_for_var(unsigned j, T & upper) const { - bool ret = false; - for (unsigned i : m_var_infos[j].m_literals) - if (is_upper_bound(m_trail[i])) { - if (ret == false) { - ret = true; - upper = get_bound(m_trail[i]); - } else { - upper = std::min(upper, get_bound(m_trail[i])); - } - } - return ret; - } - - // used for testing only - void add_lower_bound_for_user_var(unsigned user_var_index, const T& bound) { - unsigned j = m_user_vars_to_cut_solver_vars[user_var_index]; - auto & vi = m_var_infos[j]; - vi.m_domain.intersect_with_lower_bound(bound); - } - - // used for testing only - void add_upper_bound_for_user_var(unsigned user_var_index, const T& bound) { - unsigned j = m_user_vars_to_cut_solver_vars[user_var_index]; - auto & vi = m_var_infos[j]; - vi.m_domain.intersect_with_upper_bound(bound); - } - - - bool at_base_lvl() const { return m_scope_lvl == 0; } - - lbool check() { - init_search(); - propagate(); - while (true) { - lbool r = bounded_search(); - if (r != lbool::l_undef) - return r; - - restart(); - simplify_problem(); - if (check_inconsistent()) return lbool::l_false; - gc(); - } - } - - cut_solver(std::function var_name_function) : m_var_name_function(var_name_function) { - } - - void init_search() { - // TBD - // initialize data-structures - } + bool at_base_lvl() const { return m_decision_level == 0; } void simplify_problem() { // no-op @@ -208,145 +640,943 @@ public: // for debugging } bool check_inconsistent() { - // TBD - return false; - } - - lbool bounded_search() { - while (true) { - checkpoint(); - bool done = false; - while (!done) { - lbool is_sat = propagate_and_backjump_step(done); - if (is_sat != lbool::l_true) return is_sat; + if (at_base_lvl()) { + // the last added lemmas can give the contradiction + for (unsigned j = m_lemmas.size(); j--; ) { + if (lower_is_pos(m_lemmas[j])) { + TRACE("check_inconsistent_int", tout << pp_poly(*this, m_lemmas[j]->poly()) << "\n";); + lp_assert(false); // not implemented + return true; + } } - - gc(); - - if (!decide()) { - lbool is_sat = final_check(); - if (is_sat != lbool::l_undef) { - return is_sat; + for (unsigned j = m_asserts.size(); j--; ) { + if (lower_is_pos(m_asserts[j])) { + TRACE("check_inconsistent_int", tout << pp_poly(*this, m_asserts[j]->poly()) << "\n";); + lp_assert(false); // not implemented + return true; } } } + return false; } - void checkpoint() { - // check for cancelation - } + void cleanup() { } - void cleanup() { - } - - lbool propagate_and_backjump_step(bool& done) { - done = true; - propagate(); - if (!inconsistent()) - return lbool::l_true; - if (!resolve_conflict()) - return lbool::l_false; - if (at_base_lvl()) { - cleanup(); // cleaner may propagate frozen clauses - if (inconsistent()) { - TRACE("sat", tout << "conflict at level 0\n";); - return lbool::l_false; + bool all_constraints_hold() const { + for (auto c : m_asserts) { + if (is_pos(value(c->poly()))) { + TRACE("all_constraints_hold_int", tout << "constraint does not hold\n"; + tout << pp_constraint(*this, *c) << "value = " << value(c->poly()) << std::endl;); + + return false; } - gc(); } - done = false; - return lbool::l_true; + for (auto c : m_lemmas) { + if (is_pos(value(c->poly()))) { + TRACE("all_constraints_hold_int", tout << "constraint does not hold\n"; + print_constraint(tout, *c);); + return false; + } + } + return true; } - + lbool final_check() { + if (!all_vars_are_fixed()) { + TRACE("final_check_int", tout << "return undef" << std::endl;); + m_stuck_state = true; + return lbool::l_undef; // we are stuck + } + lp_assert(all_constraints_hold()); + lp_assert(at_base_lvl()); // there are no more case splits, and all clauses are satisfied. // prepare the model for external consumption. return lbool::l_true; } - - bool resolve_conflict() { - while (true) { - bool r = resolve_conflict_core(); - // after pop, clauses are reinitialized, - // this may trigger another conflict. - if (!r) - return false; - if (!inconsistent()) - return true; - } - } - bool resolve_conflict_core() { // this is where the main action is. return true; } - void propagate() { - // this is where the main action is. + void find_new_conflicting_cores_under_constraint(var_index j, const_constr* c) { + // lp_assert(false); } - bool decide() { - // this is where the main action is. - // pick the next variable and bound or value on the variable. - // return false if all variables have been assigned. - return false; + void find_new_conflicting_cores(var_index j) { + for (auto p: m_var_infos[j].dependent_constraints()) + find_new_conflicting_cores_under_constraint(j, p.first); } - bool inconsistent() const { return m_inconsistent; } - - bool get_var_low_bound(var_index i, T & bound) const { - const var_info & v = m_var_infos[i]; - return v.m_domain.get_lower_bound(bound); + void set_value_for_fixed_var_and_check_for_conf_cores(unsigned j) { + TRACE("set_value_for_fixed_var_and_check_for_conf_cores", tout << "j = " << j << std::endl;); + if (m_v.size() <= j) + m_v.resize(j + 1); + lp_assert(m_var_infos[j].is_fixed()); + lp_assert(m_var_infos[j].is_active()); + m_var_infos[j].domain().get_upper_bound(m_v[j]); // sets the value of the variable + find_new_conflicting_cores(j); + } + + void restrict_var_domain_with_bound_result(var_index j, const bound_result & br, unsigned trail_index) { + TRACE("restrict_var_domain_with_bound_result", tout << "j = " << j << std::endl; + br.print(tout);); + auto & vi = m_var_infos[j]; + lp_assert(!vi.is_fixed()); + lp_assert(m_trail.back().var() == j); + if (br.m_type == bound_type::UPPER) { + vi.intersect_with_upper_bound(br.m_bound, trail_index, m_scopes.size()); + } else { + vi.intersect_with_lower_bound(br.m_bound, trail_index, m_scopes.size()); + } + if (vi.is_fixed()) { + TRACE("d.is_fixed", tout << "j = " << j << std::endl;); + set_value_for_fixed_var_and_check_for_conf_cores(j); + } } - bool get_var_upper_bound(var_index i, T & bound) const { - const var_info & v = m_var_infos[i]; - return v.m_domain.get_upper_bound(bound); + // 'j' is a variable that changed + void add_changed_var(unsigned j) { + TRACE("add_changed_var_int", tout << "j = " << j << "\n";); + for (auto & p: m_var_infos[j].dependent_constraints()) { + TRACE("add_changed_var_int", tout << pp_constraint(*this, *p.first) << "\n";); + m_active_set.add_constraint(p.first); + } + } + + unsigned number_of_vars() const { return static_cast( m_var_infos.size()); } + + const mpq & var_value(unsigned j) const { + return m_v[j]; + } + + bool var_is_active(unsigned j) const { + return m_var_infos[j].is_active(); + } + + struct test_bound_struct { + mpq m_lower_bound; + mpq m_upper_bound; + int m_expl_lower; + int m_expl_upper; + test_bound_struct() :m_expl_lower(-1), m_expl_upper(-1) {} + }; + + + bool var_infos_are_correct() const { + vector bounds = get_bounds_from_trail(); + for (unsigned j = 0; j < m_var_infos.size(); j++) + if (!var_info_is_correct(j, bounds[j])) { + TRACE("var_infos_are_correct", print_var_info(tout, j); tout << " var_info is incorrect j = " << j;); + return false; + } + return true; + } + + bound_result bound_test_on_poly(const polynomial &p, const mpq& a, unsigned j, vector& bs) const { + lp_assert(!is_zero(a)); + if (numeric_traits::is_pos(a)) { + TRACE("bound_test_on_poly", tout << var_name(j) << " a is pos, p = "; print_polynomial(tout, p);); + bound_result r = lower_without_test(p, j, bs); + if (r.m_type == bound_type::UNDEF) + return r; + lp_assert(is_int(r.m_bound)); + r.m_bound = - ceil_ratio(r.m_bound , a); + r.m_type = bound_type::UPPER; + return r; + } + else { + TRACE("bound_test_on_poly", tout << var_name(j) << " a is neg, p = "; print_polynomial(tout, p);); + bound_result r = lower_without_test(p, j, bs); + if (r.m_type == bound_type::UNDEF) + return r; + r.m_bound = -floor_ratio(r.m_bound, a); + r.m_type = bound_type::LOWER; + return r; + } + + } + + bound_result bound_test(unsigned j, const polynomial &p, vector& bs) const { + const mpq &a = p.coeff(j); + bound_result br = bound_test_on_poly(p, a, j, bs); + TRACE("bound_test", tout << var_name(j) << ", p = "; print_polynomial(tout, p); + tout << ", br = "; br.print(tout); ); + return br; + } + + void bound_test_literal(const literal & l, vector & bs) const { + bound_result br = l.cnstr() != nullptr? + bound_test(l.var(), l.cnstr()->poly(), bs) : + bound_result(l.bound(), l.is_lower() ? bound_type::LOWER: bound_type::UPPER); + lp_assert(br.m_type != bound_type::UNDEF); + if (l.is_lower()) { + lp_assert(br.m_type == bound_type::LOWER); + lp_assert(br.m_bound >= l.bound()); + } else { + lp_assert(br.m_type == bound_type::UPPER); + lp_assert(br.m_bound <= l.bound()); + } + + if (l.tight_constr() != nullptr) { + br = bound_test(l.var(), l.tight_constr()->poly(), bs); + lp_assert(br.m_type != bound_type::UNDEF); + if (l.is_lower()) { + lp_assert(br.m_type == bound_type::LOWER); + lp_assert(br.m_bound >= l.bound()); + } else { + lp_assert(br.m_type == bound_type::UPPER); + lp_assert(br.m_bound <= l.bound()); + } + } + + } + + void get_bounds_from_trail_elem(unsigned j, vector& bs) const { + const literal & l = m_trail[j]; + auto & t = bs[l.var()]; + TRACE("get_bounds_from_trail_elem", tout << "j = " << j << ", literal = "; print_literal(tout, l);); + bound_test_literal(l, bs); + if (l.is_lower()) { + if (t.m_expl_lower == -1) { + t.m_expl_lower = j; + t.m_lower_bound = l.bound(); + TRACE("get_bounds_from_trail_elem", tout << var_name(l.var()) << " m_lower_bound = " << t.m_lower_bound << std::endl;); + } else { + lp_assert(t.m_lower_bound < l.bound()); + t.m_lower_bound = l.bound(); + TRACE("get_bounds_from_trail_elem", tout << var_name(l.var()) << " m_lower_bound = " << t.m_lower_bound << std::endl;); + t.m_expl_lower = j; + } + } else { + if (t.m_expl_upper == -1) { + t.m_expl_upper = j; + t.m_upper_bound = l.bound(); + TRACE("get_bounds_from_trail_elem", tout << var_name(l.var()) << " m_u = " << t.m_upper_bound << std::endl;); + } else { + lp_assert(t.m_upper_bound > l.bound()); + t.m_upper_bound = l.bound(); + t.m_expl_upper = j; + TRACE("get_bounds_from_trail_elem", tout << var_name(l.var()) << " m_u = " << t.m_upper_bound << std::endl;); + } + } + } + + vector get_bounds_from_trail() const { + vector ret(m_var_infos.size()); + for (unsigned j = 0; j < m_trail.size(); j++) { + get_bounds_from_trail_elem(j, ret); + } + return ret; + } + + bool var_info_is_correct(unsigned j, const test_bound_struct& t) const { + const var_info & v = m_var_infos[j]; + if (v.external_stack_level() != static_cast(-1) && v.external_stack_level() > m_scopes.size()) { + + TRACE("var_info_is_correct", tout << "incorrect: the level is too high:"; + print_var_info(tout, j); + tout << "external_level = " << v.external_stack_level(); + tout << "\nm_scopes.size() = " << m_scopes.size(); ); + return false; + } + std::unordered_set deps; + for (const auto c: m_asserts) { + if (!is_zero(c->coeff(j))) + deps.insert(c); + } + for (const auto c: m_lemmas) { + if (!is_zero(c->coeff(j))) + deps.insert(c); + } + + for (auto p : v.dependent_constraints()) { + if (deps.find(p.first) == deps.end()) { + TRACE("var_info_is_correct", tout << "deps.find(p.first) == deps.end()";); + return false; + } + } + if (v.is_fixed() && m_v.size() <= j) { + TRACE("var_info_is_correct", tout << "m_v is too short, j="<< j <= m_trail.size()) { + TRACE("var_bound_is_correct_by_trail", tout<< "expl =" << expl << " of " << var_name(j) << ", j = "<< j << " points out of the trail, trail.size() = " << m_trail.size(); tout << "return false";); + return false; + } + const literal & l = m_trail[expl]; + + if (! (b == l.bound() && !l.is_lower() && j == l.var())) { + TRACE("var_bound_is_correct_by_trail", tout<< "expl=" << expl << std::endl; print_var_info(tout, j); tout << "b=" << b<<", expl = " << expl << std::endl; print_literal(tout, l); tout << "return false";); + return false; + } + return (t.m_expl_upper == static_cast(expl) && t.m_upper_bound == b); + } + CTRACE("var_bound_is_correct_by_trail", t.m_expl_upper != -1 , + tout << "literal index = " << t.m_expl_upper << "\n"; + print_literal(tout, m_trail[t.m_expl_upper]); + tout << "will return false"; + ); + + return t.m_expl_upper == -1; + } + + // bool run_over_trail_with_upper_bound(unsigned j, const mpq & b) const { + // bool limited = false; + // mpq found_b; + // run_over_trail_with_upper_bound_on_literal(j, b, + // } + + bool var_lower_bound_is_correct_by_trail(unsigned j, const test_bound_struct& t) const { + const var_info & v = m_var_infos[j]; + mpq b; + unsigned expl; + if (v.get_lower_bound_with_expl(b, expl)) { + if (expl >= m_trail.size()) { + TRACE("var_bound_is_correct_by_trail", tout<< "expl =" << expl << " of " << var_name(j) << ", j = "<< j << " points out of the trail, trail.size() = " << m_trail.size(); + tout << ", "; print_var_info(tout, j);); + return false; + } + const literal & l = m_trail[expl]; + + + if (! (t.m_expl_lower == static_cast(expl) && t.m_lower_bound == b)) { + TRACE("var_bound_is_correct_by_trail", print_var_info(tout, j); tout << "b=" << b<<", expl = " << expl << std::endl; print_literal(tout, l); tout << "return " << (b == l.bound() && l.is_lower());); + return false; + } + return b == l.bound() && l.is_lower(); + } + + CTRACE("var_bound_is_correct_by_trail", !(t.m_expl_lower == -1), tout << "return " << (t.m_expl_lower == -1) << " t.m_expl_lower = " << t.m_expl_lower << ", "; print_var_info(tout, v); print_trail(tout);); + return t.m_expl_lower == -1; + } + + void push_literal_to_trail(literal & l) { + m_trail.push_back(l); + add_changed_var(l.var()); + } + + unsigned find_large_enough_j(unsigned i) { + unsigned r = 0; + for (const auto & p : m_asserts[i]->poly().m_coeffs) { + r = std::max(r, p.var() + 1); + } + return r; + } + + std::string var_name(unsigned j) const { + return get_column_name(j); + } + + void trace_print_domain_change(std::ostream& out, unsigned j, const mpq& v, const monomial & p, const_constr* c) const { + out << "trail.size() = " << m_trail.size() << "\n"; + out << "change in domain of " << var_name(j) << ", v = " << v << ", domain becomes "; + print_var_domain(out, j); + mpq lb; + bool r = lower(c, lb); + if (r) + out << "lower_of_constraint = " << lb << "\n"; + else + out << "no lower bound for constraint\n"; } - // finds the lower bound of the monomial, - // otherwise returns false - bool lower_monomial(const std::pair & p, mpq & lb) const { - lp_assert(p.first != 0); - T var_bound; - if (p.first > 0) { - if (!get_var_low_bound(p.second, var_bound)) + + bool new_lower_bound_is_relevant(unsigned j, const mpq & v) const { + mpq lb; + const var_info & vi = m_var_infos[j]; + auto & d = vi.domain(); + bool has_bound = d.get_lower_bound(lb); + if (!has_bound) + return true; + if (v <= lb) + return false; + if (upper_bound_exists(d)) + return true; + + if (too_many_propagations_for_var(vi)) + return false; + + // int delta = 2; + return v >= lb + 2 * abs(lb); + + } + + bool cycle_on_var_info(const var_info & vi) const { + return vi.number_of_bound_propagations() > m_settings.m_cut_solver_bound_propagation_factor * vi.number_of_asserts(); + } + + bool too_many_propagations_for_var(const var_info& vi) const { + return vi.number_of_bound_propagations() > m_settings.m_cut_solver_bound_propagation_factor * vi.dependent_constraints().size(); + } + + bool new_upper_bound_is_relevant(unsigned j, const mpq & v) const { + auto & vi = m_var_infos[j]; + auto & d = vi.domain(); + mpq b; + bool has_bound = d.get_upper_bound(b); + if (!has_bound) + return true; + if (v >= b) + return false; + if (lower_bound_exists(d)) + return true; + + if (too_many_propagations_for_var(vi)) + return false; + + // delta = 2 + return v <= b - 2 * abs(b); // returns false if the improvement is small + } + + void intersect_var_info_with_upper_bound(unsigned j, const mpq & v) { + lp_assert(m_trail.back().var() == j && m_trail.back().is_upper()); + m_var_infos[j].intersect_with_upper_bound(v, m_trail.size() - 1, m_scopes.size()); // m_trail.size() - 1 is the explanation + if (m_var_infos[j].is_fixed()) + set_value_for_fixed_var_and_check_for_conf_cores(j); + } + + void intersect_var_info_with_lower_bound(unsigned j, const mpq& v) { + lp_assert(m_trail.back().var() == j && m_trail.back().is_lower()); + m_var_infos[j].intersect_with_lower_bound(v, m_trail.size() - 1, m_scopes.size()); // m_trail.size() - 1 is the explanation + if (m_var_infos[j].is_fixed()) + set_value_for_fixed_var_and_check_for_conf_cores(j); + } + + void propagate_monomial_on_lower(const monomial & p, + const mpq& lower_val, + constraint* c) { + unsigned j = p.var(); + if (cycle_on_var_info(m_var_infos[j])) { + return; + } + + if (is_pos(p.coeff())) { + mpq m; + get_var_lower_bound(p.var(), m); + mpq v = floor(- lower_val / p.coeff()) + m; + if (new_upper_bound_is_relevant(j, v)) { + add_bound(v, j, false, c); + intersect_var_info_with_upper_bound(j, v); + } + } else { + mpq m; + get_var_upper_bound(p.var(), m); + mpq v = ceil( - lower_val / p.coeff()) + m; + if (new_lower_bound_is_relevant(j, v)) { + add_bound(v, j, true, c); + intersect_var_info_with_lower_bound(j, v); + } + } + } + + void propagate_monomial_on_right_side(const monomial & p, + const mpq& rs, + constraint *c) { + unsigned j = p.var(); + if (cycle_on_var_info(m_var_infos[j])) { + return; + } + if (is_pos(p.coeff())) { + mpq v = floor(rs / p.coeff()); + if (new_upper_bound_is_relevant(j, v)) { + add_bound(v, j, false, c); + intersect_var_info_with_upper_bound(j, v); + TRACE("ba_int_change", trace_print_domain_change(tout, j, v, p, c);); + } + } else { + mpq v = ceil(rs / p.coeff()); + if (new_lower_bound_is_relevant(j, v)) { + TRACE("ba_int_change", print_var_info(tout, j);); + add_bound(v, j, true , c); + intersect_var_info_with_lower_bound(j, v); + TRACE("ba_int_change", trace_print_domain_change(tout, j, v, p, c);); + } + } + } + + void print_var_info(std::ostream & out, const var_info & vi) const { + out << m_var_name_function(vi.internal_j()) << " "; + if (vi.internal_j() < m_v.size()) { + out << "m_v[" << vi.internal_j() << "] = " << m_v[vi.internal_j()] << std::endl; + } + if (vi.is_active()) out << ", active var "; + print_var_domain(out, vi); + out << ", propagaions = " << vi.number_of_bound_propagations() << ", deps = " << vi.dependent_constraints().size(); + out << ", asserts = " << vi.number_of_asserts() << std::endl; + // out << "external levels: "; + // for (auto j : vi.external_stack_level()) + // out << j << " "; + } + + void print_var_info(std::ostream & out, unsigned j) const { + print_var_info(out, m_var_infos[j]); + } + + void print_var_domain(std::ostream & out, unsigned j) const { + m_var_infos[j].print_var_domain(out); + } + + void print_var_domain(std::ostream & out, const var_info & vi) const { + vi.print_var_domain(out); + } + + // b is the value of lower + void propagate_constraint_on_lower(constraint* c, const mpq & b) { + for (const auto & p: c->coeffs()) + propagate_monomial_on_lower(p, b, c); + } + + + void explain_literal(unsigned trail_index, + std::unordered_set & visited_literals, + std::unordered_set & visited_constraints) { + if (visited_literals.find(trail_index) != visited_literals.end()) + return; + visited_literals.insert(trail_index); + const literal & l = m_trail[trail_index]; + const_constr* c = l.tight_constr(); + if (c == nullptr) + c = l.cnstr(); + lp_assert(c != nullptr); + add_premises(c, visited_constraints); + explain_bound(c, trail_index, visited_literals, visited_constraints); + } + + void explain_bound(const_constr * c, + unsigned trail_lim, + std::unordered_set & visited_literals, + std::unordered_set visited_constraints) { + add_premises(c, visited_constraints); + TRACE("fill_conflict_explanation", tout << "visited_constraints\n"; + for (auto cc: visited_constraints) + trace_print_constraint(tout, *cc);); + for (const monomial & m : c->poly().coeffs()) { + int trail_index = find_literal_index_after(m.var(), is_pos(m.coeff()), trail_lim); + if (trail_index == -1) + continue; + explain_literal(trail_index, visited_literals, visited_constraints); + } + + } + + void dumb_exlplanations() { + m_explanation.clear(); + for (auto c: m_asserts) + for (auto j : c->assert_origins()) + m_explanation.insert(j); + } + + void add_premises(const_constr *c, std::unordered_set & visited_constraints) { + if (visited_constraints.find(c) != visited_constraints.end()) + return; + + visited_constraints.insert(c); + + for (auto j : c->assert_origins()) + m_explanation.insert(j); + + for (const_constr* p : c->predecessors()) + add_premises(p, visited_constraints); + } + + void fill_conflict_explanation(const constraint *confl) { + //dumb_exlplanations(); + TRACE("fill_conflict_explanation", + trace_print_constraint(tout, *confl); + print_trail(tout); + ); + m_explanation.clear(); + std::unordered_set visited_literals; + std::unordered_set visited_constraints; + explain_bound(confl, m_trail.size(), visited_literals, visited_constraints); + TRACE("fill_conflict_explanation", tout << "m_explanation = "; + for (unsigned j : m_explanation) { + tout << j << " "; + }); + } + + void trace_print_constraint(std::ostream& out, const_constr& i) const { + trace_print_constraint(out, &i); + } + + void trace_print_constraint(std::ostream& out, const_constr* i) const { + print_constraint(out, *i); + out << "id = " << i->id() << ", "; + unsigned j; + auto pairs = to_pairs(i->poly().m_coeffs); + auto it = linear_combination_iterator_on_vector(pairs); + while (it.next(j)) { + out << "domain of " << var_name(j) << " = "; + print_var_domain(out, j); + } + if (i->assert_origins().size()) { + out << (i->assert_origins().size() > 1?"origins: ":"origin: ") ; + for (auto o : i->assert_origins()) + out << o << ", "; + out << "\n"; + } + } + + + void propagate_constraint_only_one_unlim(constraint* i, unsigned the_only_unlim) { + mpq rs = - i->poly().m_a; + for (unsigned j = 0; j < i->poly().m_coeffs.size(); j++) { + if (j == the_only_unlim) continue; + mpq m; + lower_monomial(i->poly().m_coeffs[j], m); + rs -= m; + } + + // we cannot get a conflict here because the monomial i.poly().m_coeffs[the_only_unlim] + // is unlimited from below and we are adding an upper bound for it + propagate_monomial_on_right_side(i->poly().m_coeffs[the_only_unlim], rs, i); + } + + bool conflict() const { return m_explanation.size() > 0; } + + bool get_var_lower_bound(var_index i, mpq & bound) const { + return m_var_infos[i].get_lower_bound(bound); + } + + bool get_var_lower_bound_test(var_index i, mpq & bound, const vector& bs) const { + const auto & t = bs[i]; + if (t.m_expl_lower == -1) + return false; + bound = t.m_lower_bound; + return true; + } + bool get_var_upper_bound_test(var_index i, mpq & bound, const vector& bs) const { + const auto & t = bs[i]; + if (t.m_expl_upper == -1) + return false; + bound = t.m_upper_bound; + return true; + } + + + + bool get_var_upper_bound(var_index i, mpq & bound) const { + const var_info & v = m_var_infos[i]; + return v.get_upper_bound(bound); + } + + // returns the reason for the lower bound, which is the index of the literal, + // or -1 if the bound does not exist + int get_lower_for_monomial_expl(const monomial & p) const { + lp_assert(p.coeff() != 0); + + const auto & v = m_var_infos[p.var()]; + return p.coeff() > 0? v.get_lower_bound_expl():v.get_upper_bound_expl(); + } + + bool lower_for_monomial_exists(const monomial &p ) const { + return get_lower_for_monomial_expl(p) != -1; + } + + bool lower_for_monomial_exists_test(const monomial &p, const vector& bs ) const { + return is_pos(p.coeff())? bs[p.var()].m_expl_lower != -1: bs[p.var()].m_expl_upper != -1; + } + + bool upper_for_monomial_exists(const monomial &p ) const { + return get_upper_for_monomial_expl(p) != -1; + } + bool upper_for_monomial_exists_test(const monomial &p, const vector & bs ) const { + const auto &t = bs[p.var()]; + TRACE("upper_for_monomial_exists_test", tout << p.coeff() << ", " << var_name(p.var()); + tout << "t.m_expl_lower = " << t.m_expl_lower << ", t.m_expl_upper = " << t.m_expl_upper;); + return is_neg(p.coeff())? t.m_expl_lower != -1: t.m_expl_upper != -1; + } + + int get_upper_for_monomial_expl(const monomial & p) const { + lp_assert(p.coeff() != 0); + const auto &v = m_var_infos[p.var()]; + return p.coeff() > 0? v.get_upper_bound_expl() : v.get_lower_bound_expl(); + } + + bool lower_monomial_test(const monomial & p, mpq & lb, vector& bs) const { + lp_assert(p.coeff() != 0); + mpq var_bound; + if (p.coeff() > 0) { + if (!get_var_lower_bound_test(p.var(), var_bound, bs)) return false; - lb = p.first * var_bound; + lb = p.coeff() * var_bound; } else { - if (!get_var_upper_bound(p.second, var_bound)) + if (!get_var_upper_bound_test(p.var(), var_bound, bs)) return false; - lb = p.first * var_bound; + lb = p.coeff() * var_bound; } return true; } + // finds the lower bound of the monomial, + // otherwise returns false + bool lower_monomial(const monomial & p, mpq & lb) const { + lp_assert(p.coeff() != 0); + mpq var_bound; + if (p.coeff() > 0) { + if (!get_var_lower_bound(p.var(), var_bound)) + return false; + lb = p.coeff() * var_bound; + } + else { + if (!get_var_upper_bound(p.var(), var_bound)) + return false; + lb = p.coeff() * var_bound; + } + return true; + } + + bool upper_monomial(const monomial & p, mpq & lb) const { + lp_assert(p.coeff() != 0); + mpq var_bound; + if (p.coeff() > 0) { + if (!get_var_upper_bound(p.var(), var_bound)) + return false; + } + else { + if (!get_var_lower_bound(p.var(), var_bound)) + return false; + } + lb = p.coeff() * var_bound; + return true; + } + + bool upper_monomial_test(const monomial & p, mpq & lb, const vector & bs) const { + lp_assert(p.coeff() != 0); + mpq var_bound; + if (p.coeff() > 0) { + if (!get_var_upper_bound_test(p.var(), var_bound, bs)) + return false; + } + else { + if (!get_var_lower_bound_test(p.var(), var_bound, bs)) + return false; + } + lb = p.coeff() * var_bound; + return true; + } + + // returns false if not limited from below - // otherwise the answer is put into lp + // otherwise the answer is put into lb + bool lower(const_constr* c, mpq & lb) const { + return lower(c->poly(), lb); + } + + // returns false if not limited from below + // otherwise the answer is put into lb + mpq lower_val(const_constr * c) const { + return lower_val(*c); + } + + mpq lower_val(const_constr & i) const { + mpq lb; +#if Z3DEBUG + bool r = +#endif + lower(i.poly(), lb); + lp_assert(r); + return lb; + } + + + // returns false if not limited from below + // otherwise the answer is put into lb bool lower(const polynomial & f, mpq & lb) const { - lb = 0; + lb = f.m_a; mpq lm; - for (const auto & p : f.m_term) { + for (const auto & p : f.m_coeffs) { if (lower_monomial(p, lm)) { lb += lm; } else { return false; } } - lb += f.m_a; return true; } - void print_ineq(unsigned i, std::ostream & out) { - print_polynomial(m_ineqs[i].m_poly, out); - out << " <= 0" << std::endl; + + // returns the number of lower unlimited monomials - 0, 1, >=2 + // if there is only one lower unlimited then the index is put into the_only_unlimited + int lower_analize(const_constr * f, unsigned & the_only_unlimited) const { + int ret = 0; + for (unsigned j = 0; j < f->poly().m_coeffs.size(); j++) { + int k; + if ((k = get_lower_for_monomial_expl(f->poly().m_coeffs[j])) == -1) { + if (ret == 1) + return 2; + ret ++; + the_only_unlimited = j; + } + } + return ret; } - void print_polynomial(const polynomial & p, std::ostream & out) { - this->print_linear_combination_of_column_indices(p.m_term, out); + + + bound_result lower_without(const polynomial & p, var_index j) const { + for (const auto & t: p.m_coeffs) { + if (t.var() == j) + continue; + if (!lower_for_monomial_exists(t)) { + return bound_result(); + } + } + // if we are here then there is a lower bound for p + mpq bound = p.m_a; + for (const auto & t: p.m_coeffs) { + if (t.var() == j) + continue; + + mpq l; + lower_monomial(t, l); + bound += l; + } + return bound_result(bound,bound_type::LOWER); + } + + bound_result lower_without_test(const polynomial & p, var_index j, vector& bs ) const { + for (const auto & t: p.m_coeffs) { + if (t.var() == j) + continue; + if (!lower_for_monomial_exists_test(t, bs)) { + return bound_result(); + } + } + // if we are here then there is a lower bound for p + mpq bound = p.m_a; + for (const auto & t: p.m_coeffs) { + if (t.var() == j) + continue; + + mpq l; + lower_monomial_test(t, l, bs); + bound += l; + } + return bound_result(bound,bound_type::LOWER); + } + + // a is the coefficient before j + bound_result bound_on_polynomial(const polynomial & p, const mpq& a, var_index j) const { + lp_assert(!is_zero(a)); + bound_result r = lower_without(p, j); + if (r.m_type == bound_type::UNDEF) + return r; + if (numeric_traits::is_pos(a)) { + r.m_bound = - ceil_ratio(r.m_bound , a); + r.m_type = bound_type::UPPER; + return r; + } + else { + r.m_bound = -floor_ratio(r.m_bound, a); + r.m_type = bound_type::LOWER; + return r; + } + } + + + + bound_result bound(const_constr * q, var_index j) const { + const mpq& a = q->poly().coeff(j); + return bound_on_polynomial(q->poly(), a, j); + } + + bound_result bound(const polynomial& q, var_index j) const { + const mpq& a = q.coeff(j); + return bound_on_polynomial(q, a, j); + } + + + bound_result bound(unsigned constraint_index, var_index j) const { + return bound(m_asserts[constraint_index], j); + } + + + void print_constraint(std::ostream & out, const_constr & i ) const { + out << (i.is_lemma()? "lemma ": "assert "); + out << "id = " << i.id() << ", "; + if (!i.is_ineq()) { + out << i.divider() << " | "; + } + out << pp_poly(*this, i.poly()); + if (i.is_ineq()) { + out << " <= 0"; + } + out << " active = " << i.is_active() << " "; + mpq b; + if (lower(&i, b)) { + out << ", lower = " << b; + if (is_pos(b)) + out << " INF"; + } else { + out << ", no lower"; + } + + bool all_vars_are_fixed = true; + for (const auto & p : i.poly().coeffs()) { + if (!m_var_infos[p.var()].is_fixed()) { + all_vars_are_fixed = false; + break; + } + } + + if (all_vars_are_fixed) { + auto v = value(i.poly()); + out << ", value = " << v; + if (is_pos(v)) + out << " INF"; + } + out << std::endl; + } + + void print_literal(std::ostream & out, const literal & t) const { + out << (t.is_decided()? "decided ": "implied "); + out << var_name(t.var()) << " "; + if (t.is_lower()) + out << ">= "; + else + out << "<= "; + out << t.bound(); + + if (t.is_decided() == false) { + out << " by constraint " << pp_constraint(*this, *(t.cnstr())); + } else { + out << " decided on trail element " << t.decision_context_index(); + if (m_trail[t.decision_context_index()].tight_constr() != nullptr) { + out << " with tight ineq " << pp_constraint(*this, *m_trail[t.decision_context_index()].tight_constr()); + } + out << "\n"; + } + if (t.tight_constr() != nullptr) { + out << "tight_constr() = " << pp_constraint(*this, *t.tight_constr()) << "\n"; + } + } + + void print_polynomial(std::ostream & out, const polynomial & p) const { + vector> pairs = to_pairs(p.m_coeffs); + this->print_linear_combination_of_column_indices_std(pairs, out); if (!is_zero(p.m_a)) { if (p.m_a < 0) { out << " - " << -p.m_a; @@ -355,5 +1585,1101 @@ public: // for debugging } } } + + void trace_print_polynomial(std::ostream & out, const polynomial & p) const { + vector> pairs = to_pairs(p.m_coeffs); + this->print_linear_combination_of_column_indices_std(pairs, out); + if (!is_zero(p.m_a)) { + if (p.m_a < 0) { + out << " - " << -p.m_a; + } else { + out << " + " << p.m_a; + } + } + out << "\n"; + for (auto m : p.coeffs()) { + unsigned j = m.var(); + out << "domain of " << var_name(j) << " = "; + print_var_domain(out, j); + } + mpq b; + if (lower(p, b)) { + out << ", lower = " << b; + if (is_pos(b)) + out << " INF"; + } else { + out << ", no lower"; + } + + bool all_vars_are_fixed = true; + for (const auto & pp : p.coeffs()) { + if (!m_var_infos[pp.var()].is_fixed()) { + all_vars_are_fixed = false; + break; + } + } + + if (all_vars_are_fixed) { + auto v = value(p); + out << ", value = " << v; + if (is_pos(v)) + out << " INF"; + } + } + + // trying to improve constraint "ie" by eliminating var j by using the tight inequality + // for j. the left side of the inequality is passed as a parameter. + bool resolve(polynomial & ie, unsigned j, bool sign_j_in_ti_is_pos, const polynomial & ti) const { + TRACE("resolve_int", tout << "ie = " << pp_poly(*this, ie); + tout << ", j = " << j << "(" << var_name(j) << ")" << ", sign_j_in_ti_is_pos = " << sign_j_in_ti_is_pos << ", ti = " << pp_poly(*this, ti) << "\n";); + lp_assert(ti.var_coeff_is_unit(j)); + lp_assert(is_pos(ti.coeff(j)) == sign_j_in_ti_is_pos); + auto &coeffs = ie.m_coeffs; + // todo: implement a more efficient version + bool found = false; + mpq a; + for (const auto & c : coeffs) { + if (c.var() == j) { + a = c.coeff(); + found = true; + break; + } + } + + if (!found) { + TRACE("resolve_int", tout << " no change\n";); + return false; + } + + if (is_neg(a)) { + if (!sign_j_in_ti_is_pos) + return false; + a = -a; + } else { + if (sign_j_in_ti_is_pos) + return false; + } + + for (auto & c : ti.m_coeffs) { + ie += monomial(a * c.coeff(), c.var()); + } + + ie.m_a += a * ti.m_a; + TRACE("resolve_int", tout << "ie = " << pp_poly(*this, ie) << "\n";); + return true; + } + + // returns true iff p imposes a better bound on j + bool improves(var_index j, const_constr * p) const { + auto a = p->coeff(j); + if (is_zero(a)) + return false; + const auto& dom = m_var_infos[j].domain(); + if (dom.is_empty()) + return false; + if (is_pos(a)) { + bound_result new_upper = bound(p, j); + if (new_upper.m_type == bound_type::UNDEF) + return false; + return dom.improves_with_upper_bound(new_upper.bound()); + } + + lp_assert(is_neg(a)); + bound_result new_lower = bound(p, j); + if (new_lower.m_type == bound_type::UNDEF) + return false; + return dom.improves_with_lower_bound(new_lower.bound()); + } + + + // returns true iff br imposes a better bound on j + bool improves(var_index j, const bound_result & br) const { + if (br.m_type == bound_type::UNDEF) + return false; + const auto& dom = m_var_infos[j].domain(); + if (dom.is_empty()) + return false; + if (br.m_type == bound_type::UPPER) { + mpq b; + bool j_has_upper_bound = get_var_upper_bound(j, b); + return (!j_has_upper_bound || br.bound() < b) && + !dom.intersection_with_upper_bound_is_empty(br.bound()); + } + + if (br.m_type == bound_type::UNDEF) + return false; + mpq b; + bool lower_bound_exists = get_var_lower_bound(j, b); + return (!lower_bound_exists || br.bound() > b) && + !dom.intersection_with_lower_bound_is_empty(br.bound()); + } + + + void add_bound(mpq v, unsigned j, bool is_lower, constraint * c) { + literal l = literal::make_implied_literal(j, is_lower, v, c, m_var_infos[j].external_stack_level()); + push_literal_to_trail(l); + m_var_infos[j].number_of_bound_propagations()++; + } + + mpq value(const polynomial & p) const { + mpq ret= p.m_a; + for (const auto & t:p.m_coeffs) + ret += t.coeff() * m_v[t.var()]; + return ret; + } + + bool flip_coin() { + return m_settings.random_next() % 2 == 0; + } + + void pop_to_external_level() { + while (m_decision_level > 0) { + pop(); + } + } + + void print_state_stats(std::ostream & out) const { + static bool one_time_print = true; + if (m_bounded_search_calls % 10 ) + return; + out << "search_calls = " << m_bounded_search_calls << ", "; + out << "vars = " << m_var_infos.size() << ","; + out << "asserts = "<< m_asserts.size() << ", "; + out << "lemmas = " << m_lemmas.size() << ", "; + out << "trail = " << m_trail.size() << ", props = " << m_number_of_constraints_tried_for_propagaton << ", "; + out << "cnfls = " << m_number_of_conflicts << ", "; + out << "pops = " << m_number_of_pops << std::endl; + if (one_time_print && m_lemmas.size() >= 500) { + print_state(out); + one_time_print = false; + } + } + + + lbool check() { + init_search(); + TRACE("check_int", tout << "starting check" << "\n";); + while (!m_stuck_state && !cancel()) { + TRACE("cs_ch", tout << "inside loop\n";); + lbool r = bounded_search(); + if (cancel()) { + break; + } + if (r != lbool::l_undef) { + TRACE("check_int", tout << "return " << (r == lbool::l_true ? "true" : "false") << "\n"; ); + pop_to_external_level(); + return r; + } + restart(); + simplify_problem(); + if (check_inconsistent()) { + TRACE("check_int", tout << "return " << (r == lbool::l_true ? "true" : "false") << "\n"; ); + pop_to_external_level(); + return lbool::l_false; + } + gc(); + TRACE("check_int", tout << "continue\n"; print_state_stats(tout); ); + + } + TRACE("check_int", tout << "return undef\n";); + pop_to_external_level(); + return lbool::l_undef; + } + + unsigned find_unused_index() const { + for (unsigned j = m_var_infos.size(); ; j++) + if (m_user_vars_to_cut_solver_vars.find(j) == m_user_vars_to_cut_solver_vars.end()) + return j; + + } + + + void init_search() { + lp_assert(m_explanation.size() == 0); + m_number_of_conflicts = 0; + m_bounded_search_calls = 0; + m_stuck_state = false; + m_cancelled = false; + for (constraint * c : m_asserts) + m_active_set.add_constraint(c); + for (auto & vi : m_var_infos) + vi.number_of_bound_propagations() = 0; + m_number_of_constraints_tried_for_propagaton = 0; + m_number_of_pops = 0; + } + + constraint* propagate_constraint(constraint* c) { + m_number_of_constraints_tried_for_propagaton ++; + lp_assert(c->is_ineq()); + TRACE("ba_int", trace_print_constraint(tout, c);); + // consider a special case for a constraint with just two variables + unsigned the_only_unlim; + int r = lower_analize(c, the_only_unlim); + if (r == 0) { + mpq b; + lower(c->poly(), b); + if (is_pos(b)) { + TRACE("cs_inconsistent", tout << "incostistent constraint "; + trace_print_constraint(tout, c); + tout << "\nlevel = " << m_decision_level << std::endl;); + return c; + } + propagate_constraint_on_lower(c, b); + } else if (r == 1) { + lp_assert(!lower_is_pos(c->poly())); + propagate_constraint_only_one_unlim(c, the_only_unlim); + } + lp_assert(!lower_is_pos(c->poly())); + return nullptr; + } + + void print_trail(std::ostream & out) const { print_trail(out, m_trail.size()); } + + void print_trail(std::ostream & out, unsigned last_index) const { + out << "trail\n"; + for (unsigned j = 0; j <= last_index && j < m_trail.size(); j++ ) { + out << "offset = " << j << ", "; + print_literal(out, m_trail[j]); + } + out << "end of trail\n"; + } + void print_state(std::ostream & out) const { + out << "asserts total " << m_asserts.size() << "\n"; + for (const auto i: m_asserts) { + print_constraint(out, *i); + } + out << "end of constraints\n"; + out << "lemmas total " << m_lemmas.size() << "\n"; + for (const auto i: m_lemmas) { + print_constraint(out, *i); + } + out << "end of constraints\n"; + print_trail(out); + out << "var_infos\n"; + for (const auto & v: m_var_infos) { + if (v.is_active()) + print_var_info(out, v); + } + out << "end of var_infos\n"; + out << "level = " << m_decision_level << "\n"; + out << "end of state dump, bounded_search_calls = " << m_bounded_search_calls << ", number_of_conflicts = " << m_number_of_conflicts << std::endl; + } + lbool bounded_search() { + m_bounded_search_calls++; + lbool is_sat = propagate_and_backjump_step(); + if (is_sat != lbool::l_undef) { + TRACE("decide_int", tout << "returning " << (int)is_sat << "\n";); + return is_sat; + } + gc(); + if (cancel()) + return lbool::l_undef; + if (!decide()) { + TRACE("decide_int", tout << "going to final_check()\n";); + lbool is_sat = final_check(); + if (is_sat != lbool::l_undef) { + return is_sat; + } + } + TRACE("decide_int", tout << "returning undef\n";); + return lbool::l_undef; + } + + bool pick_bound_kind(unsigned j) { + var_info & vi = m_var_infos[j]; + if (lower_bound_exists(vi) && upper_bound_exists(vi)) + return flip_coin(); + if (lower_bound_exists(vi)) + return true; + lp_assert(upper_bound_exists(vi)); + return false; + } + + bool decide() { + int j = find_var_for_deciding(); + if (j < 0) + return false; + TRACE("decide_int", tout << "going to decide " << var_name(j) << " var index = " << j << "\n"; + tout << "domain = "; print_var_domain(tout, j); tout << ", m_decision_level="< bound || m_number_of_conflicts > bound) { + m_cancelled = true; + return true; + } + return false; + } + + + + lbool propagate_and_backjump_step() { + do { + constraint* c = propagate(); + if (cancel()) + return lbool::l_undef; + TRACE("cs_dec", tout << "trail = \n"; print_trail(tout); tout << "end of trail\n";); + + if (c != nullptr) { + m_number_of_conflicts++; + TRACE("propagate_and_backjump_step_int", tout << "incostistent_constraint "; trace_print_constraint(tout, c); tout << "m_number_of_conflicts = " << m_number_of_conflicts << std::endl; tout << "cut_solver_calls " << m_settings.st().m_cut_solver_calls << std::endl;); + if (at_base_lvl()) { + fill_conflict_explanation(c); + return lbool::l_false; + } + handle_conflicting_cores(); // testing only + resolve_conflict(c); + } + } + while (!m_active_set.is_empty()); + + return !all_vars_are_fixed()? lbool::l_undef :lbool::l_true; + } + + bool decision_is_redundant_for_constraint(const polynomial& i, literal & l) const { + const mpq & coeff = i.coeff(l.var()); + if (is_zero(coeff)) + return true; + return is_pos(coeff)? ! l.is_lower(): l.is_lower(); + } + + bool is_divizible(const mpq & a, const mpq & b) const { + lp_assert(!is_zero(b)); + return is_zero(a % b); + } + + void create_div_ndiv_parts_for_tightening(const polynomial & p, const mpq & coeff, polynomial & div_part, polynomial & ndiv_part) { + for (const auto &m : p.m_coeffs) { + if (is_divizible(m.coeff(), coeff)){ + div_part.m_coeffs.push_back(m); + } else { + ndiv_part.m_coeffs.push_back(m); + } + } + + TRACE("tight", + tout << "div_part = " << pp_poly(*this, div_part) << "\n"; + tout << "ndiv_part = " << pp_poly(*this, ndiv_part) << "\n";); + } + + + void add_tight_constr_of_literal(polynomial &ndiv_part, + const mpq & c, + literal &l) { + lp_assert(is_pos(c)); + ndiv_part.add(c, m_trail[l.decision_context_index()].tight_constr()->poly()); + lp_assert(is_zero(ndiv_part.coeff(l.var()))); + TRACE("tight", tout << "ndiv_part = " << pp_poly(*this, ndiv_part) << "\n";); + } + + void decided_lower(const mpq & a, + const mpq & c, + polynomial &div_part, + polynomial &ndiv_part, + literal &l) { + mpq k = is_pos(a)?ceil( c / a): floor(c / a); + ndiv_part += monomial(-c, l.var()); // it will be moved to div_part + mpq a_k = a * k; + mpq m = a_k - c; + TRACE("tight", tout << "c = " << c << ", a = " << a << + ", c / a = " << c/a << ", k = " << + k << ", a * k = " << a * k << ", m = " << m << "\n"; ); + lp_assert(!is_neg(m)); + create_tight_constr_under_literal(l.decision_context_index()); + const literal & lex = m_trail[l.decision_context_index()]; + lp_assert(lex.var() == l.var()); + for (const monomial & n : lex.tight_constr()->poly().coeffs()) { + if (n.var() == l.var()) { + lp_assert(n.coeff() == one_of_type()); + div_part += monomial(a_k, l.var()); + } else { + ndiv_part += monomial(m * n.coeff(), n.var()); + } + } + ndiv_part += m * lex.tight_constr()->poly().m_a; + TRACE("tight", tout << "Decided-Lower "; + tout << "div_part = " << pp_poly(*this, div_part) << "\n"; + tout << "ndiv_part = " << pp_poly(*this, ndiv_part) << "\n";); + } + + void decided_upper(const mpq & a, + const mpq & c, + polynomial &div_part, + polynomial &r, + literal &l) { + // we would like to have c - ak > 0, or ak < c + mpq k = is_pos(a)? floor( c / a): ceil(c / a); + r += monomial(-c, l.var()); // it will be moved to div_part + + mpq a_k = a * k; + mpq m = c - a_k; + TRACE("tight", tout << "c = " << c << ", a = " << a << + ", c / a = " << c/a << ", k = " << + k << ", a * k = " << a * k << ", m = " << m << "\n"; ); + lp_assert(!is_neg(m)); + create_tight_constr_under_literal(l.decision_context_index()); + const literal & lex = m_trail[l.decision_context_index()]; + lp_assert(lex.var() == l.var()); + for (const monomial & n : lex.tight_constr()->poly().coeffs()) { + if (n.var() == l.var()) { + lp_assert(n.coeff() == -one_of_type()); + div_part += monomial(a_k, l.var()); + } else { + r += monomial(m * n.coeff(), n.var()); + } + } + r += m * lex.tight_constr()->poly().m_a; + TRACE("tight", tout << "Decided-Lower "; + tout << "div_part = " << pp_poly(*this, div_part) << "\n"; + tout << "r = " << pp_poly(*this, r) << "\n";); + } + + // returns true iff there was a change + bool tighten_on_literal(const mpq & a, + polynomial & div_part, + polynomial & ndiv_part, + int index_of_literal) { + bool change = false; + literal & l = m_trail[index_of_literal]; + if (l.tight_constr() == nullptr) { + create_tight_constr_under_literal(index_of_literal); + } + TRACE("tight", + tout << "index_of_literal = " << index_of_literal << ", "; + print_literal(tout, m_trail[index_of_literal]);); + if (l.is_implied()) { // Resolve-Implied + change = resolve(ndiv_part, l.var(), !l.is_lower(), l.tight_constr()->poly()); + TRACE("tight", tout << "after resolve ndiv_part = " << pp_poly(*this, ndiv_part) << "\n";); + } else { + create_tight_constr_under_literal(l.decision_context_index()); + TRACE("tight", + tout << "index_of_literal = " << index_of_literal << ", "; + print_literal(tout, m_trail[index_of_literal]); tout << "\n"; + tout << "div_part = " << pp_poly(*this, div_part) << "\n"; + tout << "ndiv_part = " << pp_poly(*this, ndiv_part) << "\n"; + tout << "a = " << a << "\n"; + ); + mpq c = ndiv_part.coeff(l.var()); + if (is_zero(c)) + return false; + if (l.is_lower()) { + if (is_neg(c)) + add_tight_constr_of_literal(ndiv_part, -c, l); + else + decided_lower(a, c, div_part, ndiv_part, l); + } else { + if (is_pos(c)) // Decided-Upper-Pos + add_tight_constr_of_literal(ndiv_part, c, l); + else + decided_upper(a, c, div_part, ndiv_part, l); + } + change = true; + } + return change; + } + + void eliminate_ndiv_part_monomials(const mpq& a, polynomial & div_part, polynomial& ndiv_part, unsigned index_of_literal) { + if (ndiv_part.number_of_monomials() == 0) + return; + int k = index_of_literal - 1; + lp_assert(k >= 0); + literal& l = m_trail[index_of_literal]; + while (ndiv_part.number_of_monomials() > 0) { + if (tighten_on_literal(a, div_part, ndiv_part, k)) { + literal & lk = m_trail[k]; + l.tight_constr()->add_predecessor( + lk.is_implied()? + lk.tight_constr() : + m_trail[lk.decision_context_index()].tight_constr() + ); + } + k--; + } + } + + // see page 88 of Cutting to Chase + void tighten(constraint * c, + unsigned j_of_var, + const mpq& a, + unsigned index_of_literal) { + polynomial div_part, ndiv_part; + ndiv_part.m_a = c->poly().m_a; + create_div_ndiv_parts_for_tightening(c->poly(), a, div_part, ndiv_part); + eliminate_ndiv_part_monomials(a, div_part, ndiv_part, index_of_literal); + mpq abs_a = abs(a); + polynomial & p = c->poly(); + p.clear(); + for (const auto & m : div_part.m_coeffs) { + p.m_coeffs.push_back(monomial(m.coeff() / abs_a, m.var())); + } + p.m_a = ceil(ndiv_part.m_a / abs_a); + lp_assert(p.m_a >= ndiv_part.m_a / abs_a); + TRACE("tight", tout << "index_of_literal = " << index_of_literal << ", got tight p = " << pp_poly(*this, p) << "\n";); + } + + void create_tight_constr_under_literal(unsigned index_of_literal) { + literal & l = m_trail[index_of_literal]; + if (l.tight_constr() != nullptr) + return; + if (l.is_decided()) { + create_tight_constr_under_literal(l.decision_context_index()); + return; + } + TRACE("tight", tout << "index_of_literal = " << index_of_literal << "\n";); + const_constr* c = l.cnstr(); + lp_assert(c->is_ineq()); + unsigned j = l.var(); + const mpq& a = c->poly().coeff(j); + lp_assert(!is_zero(a)); + if (a == one_of_type() || a == - one_of_type()) { + l.tight_constr() = l.cnstr(); + return; + } + l.tight_constr() = constraint::make_ineq_constraint( m_max_constraint_id++, + c->poly(), + c->predecessors()); + l.tight_constr()->add_predecessor(c); + tighten(l.tight_constr(), j, a, index_of_literal); + add_lemma_as_not_active(l.tight_constr()); + } + + void backjump(unsigned index_of_literal, + bool lemma_has_been_modified, + constraint* lemma, + constraint * orig_conflict) { + polynomial &p = lemma->poly(); + lp_assert(m_trail[index_of_literal].is_decided()); + unsigned j = m_trail[index_of_literal].var(); + while(m_trail.size() > index_of_literal) { pop(); } + lp_assert(m_trail.size() == index_of_literal); + bound_result br = bound_on_polynomial(p, p.coeff(j), j); + + TRACE("int_backjump", br.print(tout); + print_var_info(tout, j); + tout << "p = " << pp_poly(*this, p) << "\n";); + if (!improves(j, br)) { + CTRACE("int_backjump", lp_settings::ddd, br.print(tout); tout << "\nimproves is false\n"; + tout << "var info after pop = "; print_var_info(tout, j);); + if (lemma_has_been_modified) + add_lemma(lemma); + else { + m_active_set.add_constraint(orig_conflict); + } + } else { + constraint *c; + if (lemma_has_been_modified) { + c = lemma; + add_lemma_as_not_active(lemma); + } else { + c = orig_conflict; + } + add_bound(br.bound(), j, br.m_type == bound_type::LOWER, c); + restrict_var_domain_with_bound_result(j, br, m_trail.size() - 1); + lp_assert(!m_var_infos[j].domain().is_empty()); + TRACE("int_backjump", tout << "done resolving:\nvar info after restricton = "; + print_var_info(tout, j); + tout << "new literal = "; print_literal(tout, m_trail.back());); + lp_assert(!lower_is_pos(c->poly())); + } + } + + void print_resolvent(std::ostream& out, const polynomial& p, literal &l) const { + out << "p = "; trace_print_polynomial(out, p); + mpq rr; + bool bb = lower(p, rr); + if (!bb) { + out << "\nlower(p) is not defined\n"; + } else { + out << "\nlower(p) = " << rr << "\n"; + } + + out << "tight_constr = " << pp_constraint(*this, *l.tight_constr()) << "\n"; + out << "constraint = " << pp_constraint(*this, *l.cnstr()) << "\n"; + out << "var domains" << "\n"; + for (auto & m : l.tight_constr()->poly().coeffs()) { + out << "var = " << m.var() << " " << var_name(m.var()) << " "; + print_var_domain(out, m.var()); + out << " "; + } + out << "\n"; + } + + void trace_resolve_print(std::ostream& out, const polynomial & p, literal & l, unsigned index_of_literal) { + out << "index_of_literal = " << index_of_literal <<", p = " << pp_poly(*this, p) << "\n"; + out << "l = "; print_literal(out, l); + out << "lower(p) = " << lower_no_check(p) << "\n"; + for (auto & m : p.coeffs()) { + out << var_name(m.var()) << " "; + print_var_domain(out, m.var()); + out << " "; + } + out << "\nm_decision_level = " << m_decision_level << "\n"; + } + + + bool resolve_decided_literal(unsigned index_of_literal, literal& l, bool lemma_has_been_modified, constraint* lemma, constraint *orig_conflict) { + if (decision_is_redundant_for_constraint(lemma->poly(), l)) { + do { pop();} while(m_trail.size() > index_of_literal); + lp_assert(m_trail.size() == index_of_literal); + TRACE("resolve_decided_literal", tout << "n "; print_literal(tout, l); if (m_decision_level == 0) tout << ", done resolving";); + lp_assert(lower_is_pos(lemma->poly())); + return m_decision_level == 0; + } + handle_conflicting_cores(); + backjump(index_of_literal, lemma_has_been_modified, lemma, orig_conflict); + return true; // done + } + + // applying Resolve rule + void resolve_implied_literal(literal & l, + bool &lemma_has_been_modified, + constraint* lemma) { + polynomial & p = lemma->poly(); + lp_assert(lower_is_pos(p)); + if (!resolve(p, l.var(), !l.is_lower(), l.tight_constr()->poly())) + return; + lemma_has_been_modified = true; + lemma->add_predecessor(l.tight_constr()); + TRACE("resolve_implied_literal", tout << "resolved p: "; print_resolvent(tout, p, l);); + lp_assert(lower_is_pos(p)); + } + + // returns true iff resolved + bool resolve_conflict_for_inequality_on_trail_element(unsigned index_of_literal, bool & lemma_has_been_modified, constraint* lemma, constraint * orig_conflict) { + lp_assert(lower_is_pos(lemma->poly())); + literal & l = m_trail[index_of_literal]; + TRACE("resolve_conflict_for_inequality_on_trail_element", + tout << "index_of_literal = " << index_of_literal <<", p = " << pp_poly(*this, lemma->poly()) << "\n"; + tout << "\nm_decision_level = " << m_decision_level << "\n"; + print_literal(tout, l);); + if (l.is_decided()) { + return resolve_decided_literal(index_of_literal, l, lemma_has_been_modified, lemma, orig_conflict); + } else { + create_tight_constr_under_literal(index_of_literal); + resolve_implied_literal(l, lemma_has_been_modified, lemma); + return false; + } + } + + bool lower_is_pos(const_constr* c) const { return lower_is_pos(c->poly()); } + + bool lower_is_pos(const polynomial & p) const { + mpq b; + bool r = lower(p, b); + return r && is_pos(b); + } + + mpq lower_no_check(const polynomial & p) const { + mpq b; +#if Z3DEBUG + bool r = +#endif + lower(p, b); +#if Z3DEBUG + lp_assert(r); +#endif + return b; + } + + + void resolve_conflict_for_inequality(constraint * c) { + // Create a new constraint, almost copy of "c", that becomes a lemma and could be modified later + constraint *lemma = constraint::make_ineq_lemma(m_max_constraint_id++, c->poly()); + lemma->predecessors() = c->predecessors(); // copy predecessors + lemma->add_predecessor(c); // and add c + + TRACE("resolve_conflict_for_inequality", print_constraint(tout, *lemma);); + lp_assert(lower_is_pos(lemma->poly())); + bool done = false; + unsigned j = m_trail.size() - 1; + bool lemma_has_been_modified = false; + unsigned number_of_lemmas = m_lemmas.size(); + while (!done) { + if (cancel()) { + return; + } + done = resolve_conflict_for_inequality_on_trail_element(j--, lemma_has_been_modified, lemma, c); + if (j >= m_trail.size()) { + TRACE("resolve_conflict_for_inequality", tout << "adjust j";); + lp_assert(m_trail.size()); + j = m_trail.size() - 1; + } + } + + if (number_of_lemmas == m_lemmas.size()) { + if (lemma_has_been_modified) + add_lemma_as_not_active(lemma); + else { + delete lemma; + } + } + } + + void resolve_conflict(constraint * i) { + lp_assert(!at_base_lvl()); + TRACE("int_resolve_confl", tout << "inconstistent_constraint = "; + print_constraint(tout, *i); print_state(tout);); + if (i->is_ineq()) { + resolve_conflict_for_inequality(i); + } else { + lp_assert(false); // not implemented + } + } + + + void print_scope(std::ostream& out) const { + for (const scope & s : m_scopes) { + out << "asserts_size = " << s.m_asserts_size; + out << ", lemmas_size = " << s.m_lemmas_size << "\n"; + out << ", trail_size = " << s.m_trail_size << "\n"; + } + } + + void remove_active_flag_from_constraints_in_active_set() { + for (auto c : m_active_set.cs()) { + c->remove_active_flag(); + } + } + + void set_active_flag_for_constraints_in_active_set() { + for (auto c : m_active_set.cs()) { + c->set_active_flag(); + } + } + +public: + void push() { + m_scopes.push_back(scope(m_asserts.size(), m_lemmas.size(), m_trail.size())); + TRACE("pp_cs", tout << "level = " << m_scopes.size() << ", trail size = " << m_trail.size();); + } + + + void pop_constraints(unsigned n_asserts, unsigned n_lemmas) { + if (n_asserts >= m_asserts.size()) + return; // only shrink the lemmas if asserts are shrunk + while (m_asserts.size() > n_asserts) { + constraint * i = m_asserts.back();; + for (auto & p: i->poly().m_coeffs) { + m_var_infos[p.var()].remove_depended_constraint(i); + } + m_active_set.remove_constraint(i); + delete i; + m_asserts.pop_back(); + } + + while (m_lemmas.size() > n_lemmas) { + constraint * i = m_lemmas.back();; + for (auto & p: i->poly().m_coeffs) { + m_var_infos[p.var()].remove_depended_constraint(i); + } + m_active_set.remove_constraint(i); + delete i; + m_lemmas.pop_back(); + } + } + +public: + void pop(unsigned k) { + m_number_of_pops++; + unsigned new_scope_size = m_scopes.size() - k; + scope s = m_scopes[new_scope_size]; + m_scopes.shrink(new_scope_size); + for (unsigned j = m_trail.size(); j-- > s.m_trail_size; ) { + literal& lit = m_trail[j]; + if (lit.is_decided()) + m_decision_level--; + var_info & vi = m_var_infos[lit.var()]; + if (vi.external_stack_level() != lit.prev_var_level()) + vi.pop(1, lit.prev_var_level()); + m_trail.pop_back(); + } + pop_constraints(s.m_asserts_size, s.m_lemmas_size); + lp_assert(var_infos_are_correct()); + } +public: + void pop() { pop(1); } + + cut_solver(std::function var_name_function, + std::function print_constraint_function, + std::function number_of_variables_function, + std::function var_value_function, + lp_settings & settings + ) : m_var_name_function(var_name_function), + m_print_constraint_function(print_constraint_function), + m_number_of_variables_function(number_of_variables_function), + m_var_value_function(var_value_function), + m_settings(settings), + m_max_constraint_id(0), + m_decision_level(0) + {} + + + int find_conflicting_core(const constraint* &lower, const constraint* & upper) const { + for (unsigned j = 0; j < m_var_infos.size(); j++) { + if (is_cc(j, lower, upper)) + return j; + } + return -1; + } + + void list_confl_cores() { + const constraint* lower; const constraint* upper; + for (unsigned j = 0; j < m_var_infos.size(); j++) { + if (is_cc(j, lower, upper)) { + std::cout << "confl core = "; print_var_info(std::cout, j); + std::cout << "lower = "; print_constraint(std::cout, *lower); + std::cout << "upper = "; print_constraint(std::cout, *upper); + } + } + } + + void handle_conflicting_cores() { + return; + const constraint* lower; + const constraint* upper; + int j = find_conflicting_core(lower, upper); + + if (j >=0) { + std::cout << "confl core = "; print_var_info(std::cout, j); + std::cout << "lower = "; print_constraint(std::cout, *lower); + std::cout << "upper = "; print_constraint(std::cout, *upper); + lp_assert(false); // not implemented + } + } + + constraint* find_constraint_to_propagate(unsigned rand) { + handle_conflicting_cores(); + return m_active_set.remove_random_constraint(rand); + } + + // returns nullptr if there is no conflict, or a conflict constraint otherwise + constraint* propagate_constraints_on_active_set() { + constraint *c; + while ((c = find_constraint_to_propagate(m_settings.random_next())) != nullptr) { + c = propagate_constraint(c); + if (cancel()) { + return nullptr; + } + if (c != nullptr) { + return c; + } + } + return nullptr; + } + + + // returns -1 if there is no conflict and the index of the conflict constraint otherwise + constraint * propagate() { + constraint* cnf = propagate_constraints_on_active_set();; + if (cnf != nullptr){ + lp_assert(lower_is_pos(cnf)); + return cnf; + } + handle_conflicting_cores(); + return nullptr; + } + + + // walk the trail backward and find the last implied bound on j (of the right kind) + int find_literal_index_after(unsigned j, bool is_lower, unsigned trail_lim) const { + for (unsigned k = trail_lim; k-- > 0;) { + const auto & l = m_trail[k]; + lp_assert(!l.is_decided()); + if (l.var() == j && l.is_lower() == is_lower) + return k; + } + TRACE("find_literal", tout << "cannot find a literal for " << var_name(j)<< " j = " << j << " is_lower = " << is_lower << ", trail_lim = " << trail_lim;); + return -1; + } + + void decide_var_on_bound(unsigned j, bool decide_on_lower) { + mpq b; + vector lhs; + unsigned decision_context_index; + var_info & vi = m_var_infos[j]; + unsigned var_level = vi.external_stack_level(); + push(); + if (decide_on_lower) { + vi.domain().get_lower_bound_with_expl(b, decision_context_index); + vi.intersect_with_upper_bound(b, m_trail.size(), m_scopes.size()); + } + else { + vi.domain().get_upper_bound_with_expl(b, decision_context_index); + vi.intersect_with_lower_bound(b, m_trail.size(), m_scopes.size()); + } + if (j >= m_v.size()) + m_v.resize(j + 1); + m_v[j] = b; + TRACE("decide_var_on_bound", tout<< "j="<< j<<" ";print_var_info(tout, j);); + add_changed_var(j); + m_decision_level++; + literal nl = literal::make_decided_literal(j, !decide_on_lower, b, decision_context_index, var_level); + push_literal_to_trail(nl); + } + + bool consistent(const_constr * i) const { + // an option could be to check that upper(i.poly()) <= 0 + bool ret = value(i->poly()) <= zero_of_type(); + CTRACE("cut_solver_state_inconsistent", !ret, + tout << "inconsistent constraint " << pp_constraint(*this, *i) << "\n"; + tout << "value = " << value(i->poly()) << '\n';); + return ret; + } + + int find_var_for_deciding() const { + unsigned j = m_settings.random_next() % m_var_infos.size(); + + for (unsigned k = 0; k < m_var_infos.size(); k++, j++) { + if (j == m_var_infos.size()) + j = 0; + const auto & d = m_var_infos[j].domain(); + lp_assert(!d.is_empty()); + if (!d.is_fixed() && (lower_bound_exists(d) || upper_bound_exists(d))) + return j; + } + + // start using the rational solution for bounds and branches + + return -1; + } + + bool there_is_var_with_empty_domain() const { + for (unsigned j = 0; j < m_var_infos.size(); j++) { + const auto & d = m_var_infos[j].domain(); + if (d.is_empty()) + return true; + } + return false; + } + + bool all_vars_are_fixed() const { + for (unsigned j = 0; j < m_var_infos.size(); j++) { + if (m_var_infos[j].is_active() && ! m_var_infos[j].is_fixed()) + return false; + } + return true; + } + + bool consistent() const { + if (!all_vars_are_fixed()) { + // this check could be removed if we use upper bound to check if an constraint holds + return false; // ignore the variables values and only return true if every variable is fixed + } + + for (const_constr* i : m_asserts) { + if (!consistent(i)) + return false; + } + return true; + } + + + void simplify_ineq(polynomial & p) const { + TRACE("simplify_ineq_int", tout << "p = " << pp_poly(*this, p) << "\n";); + auto & ms = p.m_coeffs; + lp_assert(ms.size()); + mpq g; + if (ms.size() == 1) { + g = abs(ms[0].coeff()); + } else { + g = gcd(ms[0].coeff(), ms[1].coeff()); + for (unsigned j = 2; j < ms.size(); j++) { + g = gcd(g, ms[j].coeff()); + } + lp_assert(is_pos(g)); + } + if (g != one_of_type()) { + for (auto & m : ms) + m.coeff() /= g; + p.m_a = ceil(p.m_a /g); + } + TRACE("simplify_ineq_int", tout << "p = " << pp_poly(*this, p) << "\n";); + } + + void add_lemma_common(constraint* lemma) { + m_lemmas.push_back(lemma); + polynomial & p = lemma->poly(); + simplify_ineq(p); + for (const auto & m : p.coeffs()) { + m_var_infos[m.var()].add_dependent_constraint(lemma, is_pos(m.coeff())? bound_type::UPPER: bound_type::LOWER); + } + } + + void add_lemma_as_not_active(constraint * lemma) { + add_lemma_common(lemma); + TRACE("add_lemma_int", trace_print_constraint(tout, lemma);); + } + + void add_lemma(constraint * lemma) { + add_lemma_common(lemma); + m_active_set.add_constraint(lemma); + TRACE("add_lemma_int", trace_print_constraint(tout, lemma);); + } + + + unsigned add_ineq(const vector & lhs, + const mpq& free_coeff, + svector origins) { + lp_assert(lhs_is_int(lhs)); + lp_assert(is_int(free_coeff)); + for (auto & p : lhs) { + if (p.var() >= m_var_infos.size()) { + m_var_infos.resize(m_number_of_variables_function()); + } + + var_info & vi = m_var_infos[p.var()]; + + if (!vi.is_active()) { + vi.activate(p.var()); + } + } + + constraint * c = constraint::make_ineq_assert(m_max_constraint_id++, lhs, free_coeff,origins); + m_asserts.push_back(c); + add_constraint_to_dependend_for_its_vars(c); + m_active_set.add_constraint(c); + + TRACE("add_ineq_int", + tout << "explanation :"; + for (auto i: origins) { + m_print_constraint_function(i, tout); + tout << "\n"; + }); + + TRACE("add_ineq_int", tout << "m_asserts[" << m_asserts.size() - 1 << "] = "; + print_constraint(tout, *m_asserts.back()); tout << "\n";); + + return m_asserts.size() - 1; + } + + + void add_constraint_to_dependend_for_its_vars(constraint * c) { + for (auto & p : c->poly().coeffs()) { + m_var_infos[p.var()].add_dependent_constraint(c, is_pos(p.coeff())? bound_type::UPPER : bound_type::LOWER); + } + } + + bool var_has_no_bounds(const var_info& vi) const { + return !lower_bound_exists(vi) && !upper_bound_exists(vi); + } }; + +inline polynomial operator*(const mpq & a, polynomial & p) { + polynomial ret; + ret.m_a = p.m_a * a; + + for (const auto & t: p.m_coeffs) + ret.m_coeffs.push_back(monomial(a * t.coeff(), t.var())); + + return ret; +} } diff --git a/src/util/lp/dense_matrix_instances.cpp b/src/util/lp/dense_matrix.cpp similarity index 98% rename from src/util/lp/dense_matrix_instances.cpp rename to src/util/lp/dense_matrix.cpp index 54b0d15d6..50f2598c8 100644 --- a/src/util/lp/dense_matrix_instances.cpp +++ b/src/util/lp/dense_matrix.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "util/lp/lp_settings.h" -#include "util/lp/dense_matrix.hpp" +#include "util/lp/dense_matrix_def.h" #ifdef Z3DEBUG #include "util/vector.h" template lp::dense_matrix lp::operator*(lp::matrix&, lp::matrix&); diff --git a/src/util/lp/dense_matrix.h b/src/util/lp/dense_matrix.h index 42353b2cd..e1f34ecd9 100644 --- a/src/util/lp/dense_matrix.h +++ b/src/util/lp/dense_matrix.h @@ -100,7 +100,7 @@ public: void swap_rows(unsigned a, unsigned b); void multiply_row_by_constant(unsigned row, T & t); - + }; template dense_matrix operator* (matrix & a, matrix & b); diff --git a/src/util/lp/dense_matrix.hpp b/src/util/lp/dense_matrix_def.h similarity index 100% rename from src/util/lp/dense_matrix.hpp rename to src/util/lp/dense_matrix_def.h diff --git a/src/util/lp/eta_matrix_instances.cpp b/src/util/lp/eta_matrix.cpp similarity index 97% rename from src/util/lp/eta_matrix_instances.cpp rename to src/util/lp/eta_matrix.cpp index 65622adaa..4cb43d87f 100644 --- a/src/util/lp/eta_matrix_instances.cpp +++ b/src/util/lp/eta_matrix.cpp @@ -20,8 +20,8 @@ Revision History: #include #include "util/vector.h" #include "util/lp/numeric_pair.h" -#include "util/lp/eta_matrix.hpp" -#ifdef LEAN_DEBUG +#include "util/lp/eta_matrix_def.h" +#ifdef Z3DEBUG template double lp::eta_matrix::get_elem(unsigned int, unsigned int) const; template lp::mpq lp::eta_matrix::get_elem(unsigned int, unsigned int) const; template lp::mpq lp::eta_matrix >::get_elem(unsigned int, unsigned int) const; diff --git a/src/util/lp/eta_matrix.hpp b/src/util/lp/eta_matrix_def.h similarity index 100% rename from src/util/lp/eta_matrix.hpp rename to src/util/lp/eta_matrix_def.h diff --git a/src/util/lp/implied_bound.h b/src/util/lp/implied_bound.h index e28e9cf74..6c5f26cf2 100644 --- a/src/util/lp/implied_bound.h +++ b/src/util/lp/implied_bound.h @@ -24,31 +24,31 @@ namespace lp { struct implied_bound { mpq m_bound; unsigned m_j; // the column for which the bound has been found - bool m_is_low_bound; + bool m_is_lower_bound; bool m_coeff_before_j_is_pos; unsigned m_row_or_term_index; bool m_strict; lconstraint_kind kind() const { - lconstraint_kind k = m_is_low_bound? GE : LE; + lconstraint_kind k = m_is_lower_bound? GE : LE; if (m_strict) k = static_cast(k / 2); return k; } bool operator==(const implied_bound & o) const { - return m_j == o.m_j && m_is_low_bound == o.m_is_low_bound && m_bound == o.m_bound && + return m_j == o.m_j && m_is_lower_bound == o.m_is_lower_bound && m_bound == o.m_bound && m_coeff_before_j_is_pos == o.m_coeff_before_j_is_pos && m_row_or_term_index == o.m_row_or_term_index && m_strict == o.m_strict; } implied_bound(){} implied_bound(const mpq & a, unsigned j, - bool low_bound, + bool lower_bound, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict): m_bound(a), m_j(j), - m_is_low_bound(low_bound), + m_is_lower_bound(lower_bound), m_coeff_before_j_is_pos(coeff_before_j_is_pos), m_row_or_term_index(row_or_term_index), m_strict(strict) { diff --git a/src/util/lp/indexed_vector_instances.cpp b/src/util/lp/indexed_vector.cpp similarity index 98% rename from src/util/lp/indexed_vector_instances.cpp rename to src/util/lp/indexed_vector.cpp index 79c3ee1a1..d198c68bd 100644 --- a/src/util/lp/indexed_vector_instances.cpp +++ b/src/util/lp/indexed_vector.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "util/vector.h" -#include "util/lp/indexed_vector.hpp" +#include "util/lp/indexed_vector_def.h" namespace lp { template void indexed_vector::clear(); template void indexed_vector::clear_all(); diff --git a/src/util/lp/indexed_vector.h b/src/util/lp/indexed_vector.h index 4de827e10..aabc886f0 100644 --- a/src/util/lp/indexed_vector.h +++ b/src/util/lp/indexed_vector.h @@ -90,15 +90,6 @@ public: } void set_value(const T& value, unsigned index); - void set_value_as_in_dictionary(unsigned index) { - SASSERT(index < m_data.size()); - T & loc = m_data[index]; - if (is_zero(loc)) { - m_index.push_back(index); - loc = one_of_type(); // use as a characteristic function - } - } - void clear(); void clear_all(); diff --git a/src/util/lp/indexed_vector.hpp b/src/util/lp/indexed_vector_def.h similarity index 100% rename from src/util/lp/indexed_vector.hpp rename to src/util/lp/indexed_vector_def.h diff --git a/src/util/lp/int_solver.cpp b/src/util/lp/int_solver.cpp index e1beb73ec..acb468058 100644 --- a/src/util/lp/int_solver.cpp +++ b/src/util/lp/int_solver.cpp @@ -6,6 +6,7 @@ #include "util/lp/int_solver.h" #include "util/lp/lar_solver.h" #include "util/lp/cut_solver.h" +#include "util/lp/lp_utils.h" #include namespace lp { @@ -78,7 +79,7 @@ int int_solver::find_inf_int_boxed_base_column_with_smallest_range() { lp_assert(!is_fixed(j)); if (!is_boxed(j)) continue; - new_range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_low_bounds()[j].x; + new_range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x; if (new_range > small_range_thresold) continue; if (result == -1) { @@ -136,9 +137,9 @@ void int_solver::real_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, l new_a = a / f_0; new_a.neg(); } - k.addmul(new_a, low_bound(x_j).x); // is it a faster operation than + k.addmul(new_a, lower_bound(x_j).x); // is it a faster operation than // k += lower_bound(x_j).x * new_a; - expl.push_justification(column_low_bound_constraint(x_j), new_a); + expl.push_justification(column_lower_bound_constraint(x_j), new_a); } else { lp_assert(at_upper(x_j)); @@ -160,8 +161,8 @@ constraint_index int_solver::column_upper_bound_constraint(unsigned j) const { return m_lar_solver->get_column_upper_bound_witness(j); } -constraint_index int_solver::column_low_bound_constraint(unsigned j) const { - return m_lar_solver->get_column_low_bound_witness(j); +constraint_index int_solver::column_lower_bound_constraint(unsigned j) const { + return m_lar_solver->get_column_lower_bound_witness(j); } @@ -188,8 +189,8 @@ void int_solver::int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, la else { new_a = (1 - f_j) / f_0; } - k.addmul(new_a, low_bound(x_j).x); - expl.push_justification(column_low_bound_constraint(x_j), new_a); + k.addmul(new_a, lower_bound(x_j).x); + expl.push_justification(column_lower_bound_constraint(x_j), new_a); } else { lp_assert(at_upper(x_j)); @@ -400,34 +401,36 @@ unsigned int_solver::row_of_basic_column(unsigned j) const { return m_lar_solver->m_mpq_lar_core_solver.m_r_heading[j]; } -template -void int_solver::fill_cut_solver(cut_solver & cs) { - for (lar_base_constraint * c : m_lar_solver->constraints()) - fill_cut_solver_for_constraint(c, cs); -} +// template +// void int_solver::fill_cut_solver_for_constraint(constraint_index ci, cut_solver & cs) { +// const lar_base_constraint* c = m_lar_solver->constraints()[ci]; +// vector> coeffs; +// T rs; +// get_int_coeffs_from_constraint(c, coeffs, rs); +// vector explanation; +// explanation.push_back(ci); +// cs.add_ineq(coeffs, -rs, explanation); +// } -template -void int_solver::fill_cut_solver_for_constraint(const lar_base_constraint* c, cut_solver & cs) { - vector> coeffs; - T rs; - get_int_coeffs_from_constraint(c, coeffs, rs); - cs.add_ineq(coeffs, rs); -} +typedef cut_solver::monomial mono; // it produces an inequality coeff*x <= rs template -void int_solver::get_int_coeffs_from_constraint(const lar_base_constraint* c, vector>& coeffs, T & rs) { +void int_solver::get_int_coeffs_from_constraint(const lar_base_constraint* c, + vector& coeffs, T & rs) { lp_assert(c->m_kind != EQ); // it is not implemented, we need to create two inequalities in this case int sign = ((int)c->m_kind > 0) ? -1 : 1; vector> lhs = c->get_left_side_coefficients(); T den = denominator(c->m_right_side); for (auto & kv : lhs) { + lp_assert(!is_term(kv.second)); + lp_assert(is_int(kv.second)); // not implemented for real vars! den = lcm(den, denominator(kv.first)); } lp_assert(den > 0); for (auto& kv : lhs) { - coeffs.push_back(std::make_pair(den * kv.first * sign, kv.second)); + coeffs.push_back(mono(den * kv.first * sign, kv.second)); } rs = den * c->m_right_side * sign; if (kind_is_strict(c->m_kind)) @@ -452,12 +455,35 @@ struct pivoted_rows_tracking_control { } }; +void int_solver::copy_explanations_from_cut_solver(explanation &ex) { + TRACE("propagate_and_backjump_step_int", + for (unsigned j: m_cut_solver.m_explanation) + m_lar_solver->print_constraint(m_lar_solver->constraints()[j], tout);); + + for (unsigned j : m_cut_solver.m_explanation) { + ex.push_justification(j); + } + m_cut_solver.m_explanation.clear(); +} + +void int_solver::copy_values_from_cut_solver() { + for (unsigned j = 0; j < m_lar_solver->A_r().column_count() && j < m_cut_solver.number_of_vars(); j++) { + if (!m_cut_solver.var_is_active(j)) + continue; + if (!is_int(j)) { + continue; + } + m_lar_solver->m_mpq_lar_core_solver.m_r_x[j] = m_cut_solver.var_value(j); + lp_assert(m_lar_solver->column_value_is_int(j)); + } +} + lia_move int_solver::check(lar_term& t, mpq& k, explanation& ex) { init_check_data(); lp_assert(inf_int_set_is_correct()); - // it is mostly a reimplementation of + // it is a reimplementation of // final_check_status theory_arith::check_int_feasibility() - // from theory_arith_int.h + // from theory_arith_int.h with the addition of cut_solver if (!has_inf_int()) return lia_move::ok; if (settings().m_run_gcd_test) @@ -470,26 +496,43 @@ lia_move int_solver::check(lar_term& t, mpq& k, explanation& ex) { if (!has_inf_int()) return lia_move::ok; - // lp_assert(non_basic_columns_are_at_bounds()); - TRACE("gomory_cut", tout << m_branch_cut_counter+1 << ", " << settings().m_int_branch_cut_gomory_threshold << std::endl;); - if (++m_branch_cut_counter > 0) { // testing cut_solver - cut_solver cs([this](unsigned j) {return m_lar_solver->get_column_name(j);}); - fill_cut_solver(cs); - } else - if ((++m_branch_cut_counter) % settings().m_int_branch_cut_gomory_threshold == 0) { - if (move_non_basic_columns_to_bounds()) { - lp_status st = m_lar_solver->find_feasible_solution(); - lp_assert(non_basic_columns_are_at_bounds()); - if (st != lp_status::FEASIBLE && st != lp_status::OPTIMAL) { - TRACE("arith_int", tout << "give_up\n";); - return lia_move::give_up; - } - } - int j = find_inf_int_base_column(); - if (j == -1) return lia_move::ok; - TRACE("arith_int", tout << "j = " << j << " does not have an integer assignment: " << get_value(j) << "\n";); - return proceed_with_gomory_cut(t, k, ex, j); + if ((++m_branch_cut_counter) % settings().m_int_branch_cut_solver == 0) { + TRACE("check_main_int", tout<<"cut_solver";); + auto check_res = m_cut_solver.check(); + settings().st().m_cut_solver_calls++; + switch (check_res) { + case lbool::l_false: + copy_explanations_from_cut_solver(ex); + settings().st().m_cut_solver_false++; + return lia_move::conflict; + case lbool::l_true: + settings().st().m_cut_solver_true++; + copy_values_from_cut_solver(); + return lia_move::ok; + case lbool::l_undef: + settings().st().m_cut_solver_undef++; + settings().m_int_branch_cut_solver *= (settings().m_int_branch_cut_solver); // take a square + break; + default: + return lia_move::give_up; } + } + if ((m_branch_cut_counter) % settings().m_int_branch_cut_gomory_threshold == 0) { + TRACE("check_main_int", tout << "gomory";); + if (move_non_basic_columns_to_bounds()) { + lp_status st = m_lar_solver->find_feasible_solution(); + lp_assert(non_basic_columns_are_at_bounds()); + if (st != lp_status::FEASIBLE && st != lp_status::OPTIMAL) { + TRACE("arith_int", tout << "give_up\n";); + return lia_move::give_up; + } + } + int j = find_inf_int_base_column(); + if (j == -1) return lia_move::ok; + TRACE("arith_int", tout << "j = " << j << " does not have an integer assignment: " << get_value(j) << "\n";); + return proceed_with_gomory_cut(t, k, ex, j); + } + TRACE("check_main_int", tout << "branch"; ); return create_branch_on_column(find_inf_int_base_column(), t, k, false); } @@ -498,17 +541,17 @@ bool int_solver::move_non_basic_column_to_bounds(unsigned j) { auto & val = lcs.m_r_x[j]; switch (lcs.m_column_types()[j]) { case column_type::boxed: - if (val != lcs.m_r_low_bounds()[j] && val != lcs.m_r_upper_bounds()[j]) { + if (val != lcs.m_r_lower_bounds()[j] && val != lcs.m_r_upper_bounds()[j]) { if (random() % 2 == 0) - set_value_for_nbasic_column(j, lcs.m_r_low_bounds()[j]); + set_value_for_nbasic_column(j, lcs.m_r_lower_bounds()[j]); else set_value_for_nbasic_column(j, lcs.m_r_upper_bounds()[j]); return true; } break; - case column_type::low_bound: - if (val != lcs.m_r_low_bounds()[j]) { - set_value_for_nbasic_column(j, lcs.m_r_low_bounds()[j]); + case column_type::lower_bound: + if (val != lcs.m_r_lower_bounds()[j]) { + set_value_for_nbasic_column(j, lcs.m_r_lower_bounds()[j]); return true; } break; @@ -647,7 +690,7 @@ bool int_solver::gcd_test_for_row(static_matrix> & A, uns while (it.next(a, j)) { if (m_lar_solver->column_is_fixed(j)) { mpq aux = lcm_den * a; - consts += aux * m_lar_solver->column_low_bound(j).x; + consts += aux * m_lar_solver->column_lower_bound(j).x; } else if (m_lar_solver->column_is_real(j)) { return true; @@ -744,16 +787,16 @@ bool int_solver::ext_gcd_test(iterator_on_row & it, if (abs_ncoeff == least_coeff) { SASSERT(m_lar_solver->column_is_bounded(j)); if (ncoeff.is_pos()) { - // l += ncoeff * m_lar_solver->column_low_bound(j).x; - l.addmul(ncoeff, m_lar_solver->column_low_bound(j).x); + // l += ncoeff * m_lar_solver->column_lower_bound(j).x; + l.addmul(ncoeff, m_lar_solver->column_lower_bound(j).x); // u += ncoeff * m_lar_solver->column_upper_bound(j).x; u.addmul(ncoeff, m_lar_solver->column_upper_bound(j).x); } else { // l += ncoeff * upper_bound(j).get_rational(); l.addmul(ncoeff, m_lar_solver->column_upper_bound(j).x); - // u += ncoeff * low_bound(j).get_rational(); - u.addmul(ncoeff, m_lar_solver->column_low_bound(j).x); + // u += ncoeff * lower_bound(j).get_rational(); + u.addmul(ncoeff, m_lar_solver->column_lower_bound(j).x); } add_to_explanation_from_fixed_or_boxed_column(j, ex); } @@ -791,7 +834,12 @@ linear_combination_iterator * int_solver::get_column_iterator(unsigned j) { int_solver::int_solver(lar_solver* lar_slv) : m_lar_solver(lar_slv), - m_branch_cut_counter(0) { + m_branch_cut_counter(0), + m_cut_solver([this](unsigned j) {return m_lar_solver->get_column_name(j);}, + [this](unsigned j, std::ostream &o) {m_lar_solver->print_constraint(j, o);}, + [this]() {return m_lar_solver->A_r().column_count();}, + [this](unsigned j) {return get_value(j);}, + settings()) { lp_assert(m_old_values_set.size() == 0); m_old_values_set.resize(lar_slv->A_r().column_count()); m_old_values_data.resize(lar_slv->A_r().column_count(), zero_of_type()); @@ -802,7 +850,7 @@ bool int_solver::has_low(unsigned j) const { switch (m_lar_solver->m_mpq_lar_core_solver.m_column_types()[j]) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: + case column_type::lower_bound: return true; default: return false; @@ -853,7 +901,7 @@ bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq m = mpq(1); if (has_low(j)) { - set_lower(l, inf_l, low_bound(j)); + set_lower(l, inf_l, lower_bound(j)); } if (has_upper(j)) { set_upper(u, inf_u, upper_bound(j)); @@ -868,7 +916,7 @@ bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq m = lcm(m, denominator(a)); if (a.is_neg()) { if (has_low(i)) - set_lower(l, inf_l, xj + (xi - lcs.m_r_low_bounds()[i]) / a); + set_lower(l, inf_l, xj + (xi - lcs.m_r_lower_bounds()[i]) / a); if (has_upper(i)) set_upper(u, inf_u, xj + (xi - lcs.m_r_upper_bounds()[i]) / a); @@ -877,7 +925,7 @@ bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq if (has_upper(i)) set_lower(l, inf_l, xj + (xi - lcs.m_r_upper_bounds()[i]) / a); if (has_low(i)) - set_upper(u, inf_u, xj + (xi - lcs.m_r_low_bounds()[i]) / a); + set_upper(u, inf_u, xj + (xi - lcs.m_r_lower_bounds()[i]) / a); } if (!inf_l && !inf_u && l == u) break;; } @@ -971,10 +1019,10 @@ bool int_solver::at_bound(unsigned j) const { case column_type::fixed: case column_type::boxed: return - mpq_solver.m_low_bounds[j] == get_value(j) || + mpq_solver.m_lower_bounds[j] == get_value(j) || mpq_solver.m_upper_bounds[j] == get_value(j); - case column_type::low_bound: - return mpq_solver.m_low_bounds[j] == get_value(j); + case column_type::lower_bound: + return mpq_solver.m_lower_bounds[j] == get_value(j); case column_type::upper_bound: return mpq_solver.m_upper_bounds[j] == get_value(j); default: @@ -987,8 +1035,8 @@ bool int_solver::at_low(unsigned j) const { switch (mpq_solver.m_column_types[j] ) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: - return mpq_solver.m_low_bounds[j] == get_value(j); + case column_type::lower_bound: + return mpq_solver.m_lower_bounds[j] == get_value(j); default: return false; } @@ -1099,11 +1147,11 @@ bool int_solver::non_basic_columns_are_at_bounds() const { auto & val = lcs.m_r_x[j]; switch (lcs.m_column_types()[j]) { case column_type::boxed: - if (val != lcs.m_r_low_bounds()[j] && val != lcs.m_r_upper_bounds()[j]) + if (val != lcs.m_r_lower_bounds()[j] && val != lcs.m_r_upper_bounds()[j]) return false; break; - case column_type::low_bound: - if (val != lcs.m_r_low_bounds()[j]) + case column_type::lower_bound: + if (val != lcs.m_r_lower_bounds()[j]) return false; break; case column_type::upper_bound: @@ -1119,8 +1167,8 @@ bool int_solver::non_basic_columns_are_at_bounds() const { return true; } -const impq& int_solver::low_bound(unsigned j) const { - return m_lar_solver->column_low_bound(j); +const impq& int_solver::lower_bound(unsigned j) const { + return m_lar_solver->column_lower_bound(j); } lia_move int_solver::create_branch_on_column(int j, lar_term& t, mpq& k, bool free_column) const { @@ -1149,4 +1197,31 @@ void int_solver::display_inf_or_int_inf_columns(std::ostream & out) const { display_column(out, j); } } +bool int_solver::is_term(unsigned j) const { + return m_lar_solver->column_corresponds_to_term(j); +} + +void int_solver::add_constraint_to_cut_solver(unsigned ci, const lar_base_constraint * c) { + vector coeffs; + mpq rs; + get_int_coeffs_from_constraint(c, coeffs, rs); + svector explanation; + explanation.push_back(ci); + m_cut_solver.add_ineq(coeffs, -rs, explanation); +} + +void int_solver::notify_on_last_added_constraint() { + unsigned ci = m_lar_solver->constraints().size() - 1; + const lar_base_constraint* c = m_lar_solver->constraints()[ci]; + add_constraint_to_cut_solver(ci, c); +} + +void int_solver::pop(unsigned k) { + m_cut_solver.pop(k); +} + +void int_solver::push() { + m_cut_solver.push(); +} + } diff --git a/src/util/lp/int_solver.h b/src/util/lp/int_solver.h index 640ed9b3e..2c41220c9 100644 --- a/src/util/lp/int_solver.h +++ b/src/util/lp/int_solver.h @@ -16,18 +16,23 @@ class lar_solver; template struct lp_constraint; enum class lia_move { - ok, + ok, branch, cut, conflict, - give_up -}; + continue_with_check, + give_up, + unsat + }; struct explanation { vector> m_explanation; void push_justification(constraint_index j, const mpq& v) { m_explanation.push_back(std::make_pair(v, j)); } + void push_justification(constraint_index j) { + m_explanation.push_back(std::make_pair(one_of_type(), j)); + } }; class int_solver { @@ -37,15 +42,15 @@ public: int_set m_old_values_set; vector m_old_values_data; unsigned m_branch_cut_counter; - + cut_solver m_cut_solver; // methods int_solver(lar_solver* lp); int_set& inf_int_set(); const int_set& inf_int_set() const; - // main function to check that solution provided by lar_solver is valid for integral values, + // main function to check that the solution provided by lar_solver is valid for integral values, // or provide a way of how it can be adjusted. lia_move check(lar_term& t, mpq& k, explanation& ex); - bool move_non_basic_column_to_bounds(unsigned j); + bool move_non_basic_column_to_bounds(unsigned j); lia_move check_wrapper(lar_term& t, mpq& k, explanation& ex); private: @@ -78,11 +83,11 @@ private: explanation & ex); void fill_explanation_from_fixed_columns(iterator_on_row & it, explanation &); void add_to_explanation_from_fixed_or_boxed_column(unsigned j, explanation &); - void patch_int_infeasible_non_basic_column(unsigned j); - void patch_int_infeasible_nbasic_columns(); + void patch_int_infeasible_non_basic_column(unsigned j); + void patch_int_infeasible_nbasic_columns(); bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m); linear_combination_iterator * get_column_iterator(unsigned j); - const impq & low_bound(unsigned j) const; + const impq & lower_bound(unsigned j) const; const impq & upper_bound(unsigned j) const; bool is_int(unsigned j) const; bool is_real(unsigned j) const; @@ -110,7 +115,7 @@ private: lia_move mk_gomory_cut(lar_term& t, mpq& k,explanation & ex, unsigned inf_col, linear_combination_iterator& iter); lia_move report_conflict_from_gomory_cut(mpq & k); void adjust_term_and_k_for_some_ints_case_gomory(lar_term& t, mpq& k, mpq& lcm_den); - void init_check_data(); + void init_check_data(); bool constrain_free_vars(linear_combination_iterator * r); lia_move proceed_with_gomory_cut(lar_term& t, mpq& k, explanation& ex, unsigned j); int find_free_var_in_gomory_row(linear_combination_iterator& iter); @@ -133,7 +138,7 @@ private: void real_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, lar_term& t, explanation & ex, unsigned inf_column); void int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, lar_term& t, explanation& ex, mpq & lcm_den, unsigned inf_column); constraint_index column_upper_bound_constraint(unsigned j) const; - constraint_index column_low_bound_constraint(unsigned j) const; + constraint_index column_lower_bound_constraint(unsigned j) const; void display_row_info(std::ostream & out, unsigned row_index) const; void gomory_cut_adjust_t_and_k(vector> & pol, lar_term & t, mpq &k, bool num_ints, mpq &lcm_den); bool current_solution_is_inf_on_cut(const lar_term& t, const mpq& k) const; @@ -146,11 +151,15 @@ private: public: void display_inf_or_int_inf_columns(std::ostream & out) const; template - void fill_cut_solver(cut_solver & cs); + void fill_cut_solver_vars(); template - void fill_cut_solver_for_constraint(const lar_base_constraint*, cut_solver& ); - template - void get_int_coeffs_from_constraint(const lar_base_constraint* c, vector>& coeff, T & rs); - + void get_int_coeffs_from_constraint(const lar_base_constraint* c, vector& coeff, T & rs); + bool is_term(unsigned j) const; + void notify_on_last_added_constraint(); + void add_constraint_to_cut_solver(unsigned,const lar_base_constraint*); + void copy_explanations_from_cut_solver(explanation &); + void pop(unsigned); + void push(); + void copy_values_from_cut_solver(); }; } diff --git a/src/util/lp/integer_domain.h b/src/util/lp/integer_domain.h index 961a34da3..44f3c7f9e 100644 --- a/src/util/lp/integer_domain.h +++ b/src/util/lp/integer_domain.h @@ -8,17 +8,51 @@ #include "util/lp/stacked_value.h" #include "util/lp/stacked_map.h" namespace lp { +enum class endpoint_kind { START, END, STEND }; // represents the set of disjoint intervals of integer number template class integer_domain { #ifdef Z3DEBUG - std::set m_domain; -#endif - typedef typename std::map::iterator iter; - typedef typename std::map::const_iterator const_iter; - typedef typename std::map::reverse_iterator riter; - stacked_map m_endpoints; // 0 means start, 1 means end, 2 means both - for a point interval - stacked_value m_empty; + // std::set m_domain; +#endif + struct endpoint { int m_start_expl; int m_end_expl; + endpoint() : m_start_expl(-1), m_end_expl(-1) {} + endpoint(int s, int e) : m_start_expl(s), m_end_expl(e) {} + endpoint_kind kind() const { + lp_assert(m_start_expl != -1 || m_end_expl != -1); + if (m_end_expl == -1) { + return endpoint_kind::START; + } + if (m_start_expl == -1) { + return endpoint_kind::END; + } + + return endpoint_kind::STEND; + } + bool operator==(const endpoint& e) const { + return m_start_expl == e.m_start_expl && m_end_expl == e.m_end_expl; + } + bool operator!=(const endpoint & e) const { return !(*this == e); } + + void print(std::ostream & out) const { + if (m_start_expl != -1 && m_end_expl != -1) + out << "(" << m_start_expl << ", " << m_end_expl << ")"; + else { + if (m_start_expl != -1) { + out << "(" << m_start_expl << ")"; + } + else if (m_end_expl != -1) { + out << "(" << m_end_expl << ")"; + } + } + } + }; + stacked_map m_endpoints; + stacked_value m_empty; + typedef typename std::map::iterator iter; + typedef typename std::map::const_iterator const_iter; + typedef typename std::map::reverse_iterator riter; + public: // the default constructor creates a set containing all integer numbers integer_domain() : integer_domain(false) {} @@ -28,10 +62,10 @@ public: // otherwise it creates an empty set integer_domain(bool is_empty) : m_empty(is_empty) { #if Z3DEBUG - if (!is_empty) { - for (int i = 0; i <= 100; i++) - m_domain.insert(i); - } + // if (!is_empty) { + // for (int i = 0; i <= 100; i++) + // m_domain.insert(i); + // } #endif } @@ -39,15 +73,15 @@ public: m_empty = false; m_endpoints.clear(); #if Z3DEBUG - for (int i = 0; i <= 100; i++) - m_domain.insert(i); + // for (int i = 0; i <= 100; i++) + // m_domain.insert(i); #endif } // copy constructor integer_domain(const integer_domain & t) : #if Z3DEBUG - m_domain(t.m_domain), + // m_domain(t.m_domain), #endif m_endpoints(t.m_endpoints), m_empty(t.m_empty) @@ -57,11 +91,11 @@ public: // needed for debug only void restore_domain() { #if Z3DEBUG - for (int i = 0; i <= 100; i++) - if (contains(i)) - m_domain.insert(i); - else - m_domain.erase(i); + // for (int i = 0; i <= 100; i++) + // if (contains(i)) + // m_domain.insert(i); + // else + // m_domain.erase(i); #endif } @@ -86,6 +120,7 @@ public: return true; return is_proper_start(l); } + void handle_right_point_in_union(const_iter &r, const T &y) { if (pos(r) == y) { if (is_proper_start(r)) @@ -130,97 +165,98 @@ public: } void unite_with_interval(const T& x, const T& y) { - TRACE("disj_intervals", tout << "unite_with_interval(" << x << ", " << y << ")\n";); -#if Z3DEBUG - for (int i = std::max(x, 0); i <= std::min(100, y); i++) - m_domain.insert(i); -#endif + lp_assert(false); + // TRACE("disj_intervals", tout << "unite_with_interval(" << x << ", " << y << ")\n";); + // #if Z3DEBUG + // // for (int i = std::max(x, 0); i <= std::min(100, y); i++) + // // m_domain.insert(i); + // #endif - lp_assert(x <= y); - if (x == y) { - unite_with_one_point_interval(x); - return; - } + // lp_assert(x <= y); + // if (x == y) { + // unite_with_one_point_interval(x); + // return; + // } - const_iter l, r; - bool neg_inf, pos_inf; - bool found_left_point = get_left_point(x, neg_inf, l); - bool found_right_point = get_right_point(y, pos_inf, r); - m_empty = false; + // const_iter l, r; + // bool neg_inf, pos_inf; + // bool found_left_point = get_left_point(x, neg_inf, l); + // bool found_right_point = get_right_point(y, pos_inf, r); + // m_empty = false; - if (!found_left_point) { - if (!found_right_point) { - m_endpoints.clear(); - set_start(x); - set_end(y); - return; - } - // found_right_point is true - if (pos_inf) { - m_endpoints.clear(); - set_start(x); - return; - } - remove_from_the_left(y); + // if (!found_left_point) { + // if (!found_right_point) { + // m_endpoints.clear(); + // set_start(x); + // set_end(y); + // return; + // } + // // found_right_point is true + // if (pos_inf) { + // m_endpoints.clear(); + // set_start(x); + // return; + // } + // remove_from_the_left(y); - if (pos(m_endpoints.begin()) == y || pos(m_endpoints.begin()) == y + 1) { - if (is_proper_start(m_endpoints.begin())) - m_endpoints.erase(m_endpoints.begin()); - else - set_end(pos(m_endpoints.begin())); - set_start(x); - } - else { - lp_assert(pos(m_endpoints.begin()) > y + 1); - if (is_start(m_endpoints.begin())) - set_end(y); - set_start(x); - } - return; - } + // if (pos(m_endpoints.begin()) == y || pos(m_endpoints.begin()) == y + 1) { + // if (is_proper_start(m_endpoints.begin())) + // m_endpoints.erase(m_endpoints.begin()); + // else + // set_end(pos(m_endpoints.begin())); + // set_start(x); + // } + // else { + // lp_assert(pos(m_endpoints.begin()) > y + 1); + // if (is_start(m_endpoints.begin())) + // set_end(y); + // set_start(x); + // } + // return; + // } - lp_assert(found_left_point); - if (!found_right_point) { - bool neg_inf = has_neg_inf(); - remove_from_the_right(x); - if (m_endpoints.empty()) { - if (!neg_inf) - set_start_end(x, y); - else - set_end(y); - return; - } - if (pos(m_endpoints.rbegin()) == x || pos(m_endpoints.rbegin()) == x - 1) { - if (is_proper_end(m_endpoints.rbegin())) { - m_endpoints.erase(m_endpoints.rbegin()); - } - else if (is_one_point_interval(m_endpoints.rbegin())) { - set_start(pos(m_endpoints.rbegin())); - } - set_end(y); - } - else { - if (is_end(m_endpoints.rbegin())) { - set_start(x); - set_end(y); - } - else { - set_end(y); - } - } - return; - } + // lp_assert(found_left_point); + // if (!found_right_point) { + // bool neg_inf = has_neg_inf(); + // remove_from_the_right(x); + // if (m_endpoints.empty()) { + // if (!neg_inf) + // set_start_end(x, y); + // else + // set_end(y); + // return; + // } + // if (pos(m_endpoints.rbegin()) == x || pos(m_endpoints.rbegin()) == x - 1) { + // if (is_proper_end(m_endpoints.rbegin())) { + // m_endpoints.erase(m_endpoints.rbegin()); + // } + // else if (is_one_point_interval(m_endpoints.rbegin())) { + // set_start(pos(m_endpoints.rbegin())); + // } + // set_end(y); + // } + // else { + // if (is_end(m_endpoints.rbegin())) { + // set_start(x); + // set_end(y); + // } + // else { + // set_end(y); + // } + // } + // return; + // } - // found_right_point and found_left_point - if (!neg_inf) - handle_left_point_in_union(l, x, y); - else { - remove_from_the_left(y); - } - if (!pos_inf) - handle_right_point_in_union(r, y); - else - remove_from_the_right(x); + // // found_right_point and found_left_point + // if (!neg_inf) + // handle_left_point_in_union(l, x, y); + // else { + // remove_from_the_left(y); + // } + // if (!pos_inf) + // handle_right_point_in_union(r, y); + // else + // remove_from_the_right(x); } bool has_pos_inf() const { @@ -231,7 +267,7 @@ public: return true; lp_assert(m_endpoints.rbegin() != m_endpoints.rend()); - return m_endpoints.rbegin()->second == 0; + return m_endpoints.rbegin()->second.kind() == endpoint_kind::START; } bool has_neg_inf() const { @@ -241,7 +277,7 @@ public: if (m_endpoints.empty()) return true; auto it = m_endpoints.begin(); - return is_proper_end(it->second);//m_endpoints.begin()); + return is_proper_end(it->second.kind());//m_endpoints.begin()); } bool is_correct() const { @@ -272,7 +308,7 @@ public: return false; } } - if (t.second == 2) { + if (t.second.kind() == endpoint_kind::STEND) { expect_end = false; // swallow a point interval } else { @@ -285,12 +321,12 @@ public: prev_x = t.first; } #if Z3DEBUG - for (int i = 0; i <= 100; i++ ) { - if ( (m_domain.find(i) != m_domain.end()) != contains(i)) { - TRACE("disj_intervals", tout << "incorrect value of contains(" << i << ") is = " << contains(i) << std::endl;); - return false; - } - } + // for (int i = 0; i <= 100; i++ ) { + // if ( (m_domain.find(i) != m_domain.end()) != contains(i)) { + // TRACE("disj_intervals", tout << "incorrect value of contains(" << i << ") is = " << contains(i) << std::endl;); + // return false; + // } + // } #endif return true; } @@ -307,45 +343,54 @@ public: bool first = true; for (auto t : m_endpoints()) { if (first) { - if (t.second == 1) { - out << "[-oo," << t.first << "]"; + if (t.second.kind() == endpoint_kind::END) { + out << "[-oo," << t.first; t.second.print(out); out << "]"; + } + else if (t.second.kind() == endpoint_kind::START) { + out << "[" << t.first; t.second.print(out); out << ","; + } else if (t.second.kind() == endpoint_kind::STEND) { + out << "[" << t.first; t.second.print(out); out << "]"; } - else if (t.second == 0) - out << "[" << t.first << ","; - else if (t.second == 2) - out << "[" << t.first << "]"; first = false; } else { - if (t.second==0) - out << "[" << t.first << ","; - else if (t.second == 1) - out << t.first << "]"; - else if (t.second == 2) - out << "[" << t.first << "]"; + if (t.second.kind() == endpoint_kind::START) { + out << "[" << t.first; t.second.print(out); out << ","; + } + else if (t.second.kind() == endpoint_kind::END) { + out << t.first; t.second.print(out); out << "]"; + } + else if (t.second.kind() == endpoint_kind::STEND) { + out << "[" << t.first; t.second.print(out); out << "]";; + } } } if (has_pos_inf()) out << "oo]"; + out << "\n"; } void push() { m_endpoints.push(); m_empty.push(); } void pop() { m_endpoints.pop(); m_empty.pop(); } void pop(unsigned k) { while(k--) pop(); } - + + bool intersect_with_bound(const T & x, bool is_lower, unsigned explanation) { + return is_lower? intersect_with_lower_bound(x, explanation) : intersect_with_upper_bound(x, explanation); + } // we intersect the existing set with the half open to the right interval - void intersect_with_lower_bound(const T& x) { + // returns true if the domain changes + bool intersect_with_lower_bound(const T& x, unsigned explanation) { #ifdef Z3DEBUG - for (int i = 0; i < x; i++) - m_domain.erase(i); + // for (int i = 0; i < x; i++) + // m_domain.erase(i); #endif TRACE("disj_intervals", tout << "intersect_with_lower_bound(" << x << ")\n";); if (m_empty) - return; + return false; if (m_endpoints.empty()) { - set_start(x); - return; + set_start(x, explanation); + return true; } bool pos_inf = has_pos_inf(); auto it = m_endpoints.begin(); @@ -356,70 +401,104 @@ public: if (m_endpoints.empty()) { if (!pos_inf) { m_empty = true; - return; + return true; } - set_start(x); - return; + set_start(x, explanation); + return true; } lp_assert(pos(it) >= x); if (pos(it) == x) { - if (is_proper_end(it)) - set_one_point_interval(x); + if (is_proper_end(it)) { + set_start(x, explanation); + return true; + } } else { // x(it) > x if (is_proper_end(it)) { - set_start(x); + set_start(x, explanation); + return true; } } + return false; + } +public: + bool intersection_with_upper_bound_is_empty(const T& x) const { + if (has_neg_inf()) + return false; + if (m_empty) + return true; + T b; + lp_assert(get_lower_bound(b)); + get_lower_bound(b); + return x < b; + } + + bool intersection_with_lower_bound_is_empty(const T& x) const { + if (has_pos_inf()) + return false; + if (m_empty) + return true; + T b; + lp_assert(get_upper_bound(b)); + get_upper_bound(b); + return x > b; } // we intersect the existing set with the half open interval - void intersect_with_upper_bound(const T& x) { + // returns true if there is a change + bool intersect_with_upper_bound(const T& x, unsigned explanation) { #ifdef Z3DEBUG - for (int i = 100; i > x; i--) - m_domain.erase(i); + // for (int i = 100; i > x; i--) + // m_domain.erase(i); #endif TRACE("disj_intervals", tout << "intersect_with_upper_bound(" << x << ")\n";); if (m_empty) - return; + return false; if (m_endpoints.empty()) { - set_end(x); - return; + set_end(x, explanation); + return true; } bool neg_inf = has_neg_inf(); auto it = m_endpoints.rbegin(); + bool change = false; while (!m_endpoints.empty() && pos(it) > x) { m_endpoints.erase(std::prev(m_endpoints.end())); it = m_endpoints.rbegin(); + change = true; } if (m_endpoints.empty()) { if (!neg_inf) { m_empty = true; - return; + return true; } - set_end(x); + set_end(x, explanation); + change = true; } lp_assert(pos(it) <= x); if (pos(it) == x) { if (is_one_point_interval(it)) {} else if (is_proper_end(it)) {} else {// is_proper_start(it->second) - set_one_point_interval(x); + set_end(x, explanation); + change = true; } } else { // pos(it) < x} - if (is_proper_start(it)) - set_end(x); + if (is_proper_start(it)) { + set_end(x, explanation); + change = true; + } } lp_assert(is_correct()); + return change; } public: void intersect_with_interval(const T& x, const T & y) { #ifdef Z3DEBUG - for (int i = 0; i <= 100; i++) - if (i < x || i > y) - m_domain.erase(i); + // for (int i = 0; i <= 100; i++) + // if (i < x || i > y) + // m_domain.erase(i); #endif TRACE("disj_intervals", tout << "intersect_with_interval(" << x << ", " << y <<")\n";); @@ -432,110 +511,108 @@ public: // add an intervar [x, inf] void unite_with_interval_x_pos_inf(const T& x) { - if (contains_all()) - return; -#if Z3DEBUG - for (int i = x; i <= 100; i++) - m_domain.insert(i); -#endif - TRACE("disj_intervals", tout << "unite_with_interval_x_pos_inf(" << x << ")\n";); - if (m_empty) { - set_start(x); - m_empty = false; - return; - } - bool neg_inf = has_neg_inf(); - remove_from_the_right(x); - if (m_endpoints.empty()) { - if (!neg_inf) - set_start(x); - return; - } - auto it = m_endpoints.rbegin(); - lp_assert(pos(it) <= x); - if (pos(it) == x) { - if (is_proper_end(it)) { - m_endpoints.erase(x); - } else { - set_start(x); - } - } else if (pos(it) == x - 1 && is_end(it)) { - if (is_proper_start(it)) { - // do nothing - } - else if (is_proper_end(it)) { - m_endpoints.erase(it); - } - else { - lp_assert(is_one_point_interval(it)); - set_start(it); - } - } else { - if (!has_pos_inf()) - set_start(x); - } + lp_assert(false); + // if (contains_all()) + // return; + // #if Z3DEBUG + // // for (int i = x; i <= 100; i++) + // // m_domain.insert(i); + // #endif + // TRACE("disj_intervals", tout << "unite_with_interval_x_pos_inf(" << x << ")\n";); + // if (m_empty) { + // set_start(x); + // m_empty = false; + // return; + // } + // bool neg_inf = has_neg_inf(); + // remove_from_the_right(x); + // if (m_endpoints.empty()) { + // if (!neg_inf) + // set_start(x); + // return; + // } + // auto it = m_endpoints.rbegin(); + // lp_assert(pos(it) <= x); + // if (pos(it) == x) { + // if (is_proper_end(it)) { + // m_endpoints.erase(x); + // } else { + // set_start(x); + // } + // } else if (pos(it) == x - 1 && is_end(it)) { + // if (is_proper_start(it)) { + // // do nothing + // } + // else if (is_proper_end(it)) { + // m_endpoints.erase(it); + // } + // else { + // lp_assert(is_one_point_interval(it)); + // set_start(it); + // } + // } else { + // if (!has_pos_inf()) + // set_start(x); + // } } // add an interval [-inf, x] void unite_with_interval_neg_inf_x(const T& x) { -#if Z3DEBUG - for (int i = 0; i <= x; i++) - m_domain.insert(i); -#endif - TRACE("disj_intervals", tout << "unite_with_interval_neg_inf_x(" << x << ")\n";); - if (m_empty) { - set_end(x); - m_empty = false; - return; - } - bool pos_inf; - const_iter r; - bool found_right_point = get_right_point(x, pos_inf, r); - if (!found_right_point) { - m_endpoints.clear(); - set_end(x); - return; - } - if (pos_inf) { - m_endpoints.clear(); - return; - } - lp_assert(pos(r) >= x); - if (pos(r) == x || pos(r) == x + 1) { - if (is_proper_start(r)) - erase(r); - else if (is_one_point_interval(r)) { - set_end(pos(r)); - } // do nothing for the proper end - } else { - if (!is_proper_end(r)) - set_end(x); - } + lp_assert(false); // not implemented + // #if Z3DEBUG + // // for (int i = 0; i <= x; i++) + // // m_domain.insert(i); + // #endif + // TRACE("disj_intervals", tout << "unite_with_interval_neg_inf_x(" << x << ")\n";); + // if (m_empty) { + // set_end(x); + // m_empty = false; + // return; + // } + // bool pos_inf; + // const_iter r; + // bool found_right_point = get_right_point(x, pos_inf, r); + // if (!found_right_point) { + // m_endpoints.clear(); + // set_end(x); + // return; + // } + // if (pos_inf) { + // m_endpoints.clear(); + // return; + // } + // lp_assert(pos(r) >= x); + // if (pos(r) == x || pos(r) == x + 1) { + // if (is_proper_start(r)) + // erase(r); + // else if (is_one_point_interval(r)) { + // set_end(pos(r)); + // } // do nothing for the proper end + // } else { + // if (!is_proper_end(r)) + // set_end(x); + // } - while (!m_endpoints.empty() && m_endpoints.begin()->first < x) { - m_endpoints.erase(m_endpoints.begin()); - } - lp_assert(is_correct()); + // while (!m_endpoints.empty() && m_endpoints.begin()->first < x) { + // m_endpoints.erase(m_endpoints.begin()); + // } + // lp_assert(is_correct()); } private: - bool is_start(char x) const { return x == 0 || x == 2; } + bool is_start(endpoint_kind x) const { return x == endpoint_kind::START || x == endpoint_kind::STEND; } bool is_start(const iter & it) const { return is_start(it->second); } bool is_start(const const_iter & it) const { return is_start(it->second); } - bool is_start(const riter & it) const { - return is_start(it->second); - } - bool is_end(char x) const { return x == 1 || x == 2; } - bool is_end(const iter & it) const { - return is_end(it->second); - } - bool is_end(const const_iter & it) const { - return is_end(it->second); - } - bool is_end(const riter & it) const { - return is_end(it->second); - } + bool is_start(const riter & it) const { return is_start(it->second); } + bool is_start(const endpoint & e) const { return is_start(e.kind()); } + + bool is_end(endpoint_kind x) const { return x == endpoint_kind::END || x == endpoint_kind::STEND; } + bool is_end(const iter & it) const { return is_end(it->second); } + bool is_end(const const_iter & it) const { return is_end(it->second); } + bool is_end(const riter & it) const { return is_end(it->second); } + bool is_end(const endpoint& e) const { return is_end(e.kind()); } + T pos(const iter & it) const { return it->first; } @@ -554,22 +631,21 @@ private: return it->second; } - bool is_proper_start(char x) const { return x == 0; } + bool is_proper_start(endpoint_kind x) const { return x == endpoint_kind::START; } bool is_proper_start(const riter &x) const { return is_proper_start(x->second);} bool is_proper_start(const iter &x) const { return is_proper_start(x->second);} bool is_proper_start(const const_iter &x) const { return is_proper_start(x->second);} + bool is_proper_start(const endpoint &x) const { return is_proper_start(x.kind());} - bool is_proper_end(char x) const { return x == 1; } - bool is_proper_end(const iter & it) const { return is_proper_end(it->second); } + bool is_proper_end(endpoint_kind x) const { return x == endpoint_kind::END; } + bool is_proper_end(const iter & it) const { return is_proper_end(it->second.kind()); } bool is_proper_end(const const_iter & it) const { return is_proper_end(it->second); } - bool is_proper_end(const riter & it) const { - return is_proper_end(it->second); - } + bool is_proper_end(const riter & it) const { return is_proper_end(it->second); } + bool is_proper_end(const endpoint & x) const { return is_proper_end(x.kind()); } - bool is_one_point_interval(char x) const { return x == 2; } - bool is_one_point_interval(const iter & it) const { - return is_one_point_interval(it->second); - } + bool is_one_point_interval(const endpoint & x) const { return is_one_point_interval(x.kind()); } + bool is_one_point_interval(endpoint_kind x) const { return x == endpoint_kind::STEND; } + bool is_one_point_interval(const iter & it) const { return is_one_point_interval(it->second); } bool is_one_point_interval(const const_iter & it) const { return is_one_point_interval(it->second); } @@ -592,41 +668,54 @@ private: m_endpoints.erase(x); } - void set_one_point_interval(const T& x) { - m_endpoints[x] = 2; + /* void set_one_point_interval(const T& x, unsigned explanation) { + auto it = m_endpoints().find(x); + set_one_point_interval(it, explanation); + }*/ + + void set_start(const_iter &t, unsigned explanation) { + lp_assert(t != m_endpoints.end()); + endpoint e = t->second; + e.m_start_expl = explanation; + m_endpoints[t->first] = e; } - void set_start(const T& x) { - m_endpoints[x] = 0; + void set_start(const T& x, unsigned explanation) { + endpoint e = get_endpoint(x); + e.m_start_expl = explanation; + m_endpoints[x] = e; } - void set_start(const iter &t ) { - t->second = 0; + endpoint get_endpoint(const T& x) const { + auto it = m_endpoints().find(x); + if (it == m_endpoints().end()) + return endpoint(); + return it->second; + } + + void set_end(const T& x, unsigned expl) { + endpoint e = get_endpoint(x); + e.m_end_expl = expl; + m_endpoints[x] = e; } - - void set_start(riter &t ) { - t->second = 0; + void set_end(const_iter& t, unsigned expl) { + endpoint e = t->second; + e.m_end_expl = expl; + m_endpoints[t->first] = e; } - void set_end(const T& x) { - m_endpoints[x] = 1; + void set_end(riter& t, unsigned explanation) { + endpoint e = t->second; + e.m_end_expl = expl; + m_endpoints[t->first] = e; } - void set_end(const iter& t) { - t->second = 1; - } - - void set_end(riter& t) { - t->second = 1; - } - - private: - void set_start_end(const T& x, const T & y) { - set_start(x); - set_end(y); - } + /* void set_start_end(const T& x, const T & y, unsigned expl) { + set_start(x, expl); + set_end(y, expl); + }*/ void unite_with_one_point_interval(const T &x) { TRACE("disj_intervals", tout << "unite_with_one_point_interval(" << x << ")\n";); @@ -855,6 +944,18 @@ private: } } public: + bool get_lower_bound_with_expl(T& b, unsigned & expl) const { + if (m_empty) + return false; + if (has_neg_inf()) + return false; + expl = m_endpoints.begin()->second.m_start_expl; + if (expl == static_cast(-1)) + return false; + b = pos(m_endpoints.begin()); + return true; + } + bool get_lower_bound(T& b) const { if (m_empty) return false; @@ -864,6 +965,45 @@ public: return true; } + int get_lower_bound_expl() const { + if (m_empty) + return -1; + if (has_neg_inf()) + return -1; + return m_endpoints.begin()->second.m_start_expl; + } + + int get_upper_bound_expl() const { + if (m_empty) + return -1; + if (has_pos_inf()) + return -1; + return m_endpoints.rbegin()->second.m_end_expl; + } + + bool get_upper_bound_with_expl(T& b, unsigned & expl) const { + if (m_empty) + return false; + if (has_pos_inf()) + return false; + expl = m_endpoints.rbegin()->second.m_end_expl; + if (expl == static_cast(-1)) + return false; + b = m_endpoints.rbegin()->first; + return true; + } + + bool get_upper_bound_and_kind_with_expl(T& b, endpoint_kind & kind, unsigned & expl) const { + if (m_empty) + return false; + if (has_pos_inf()) + return false; + b = m_endpoints.rbegin()->first; + kind = m_endpoints.rbegin()->second.kind(); + expl = m_endpoints.rbegin()->second.m_explanation; + return true; + } + bool get_upper_bound(T& b) const { if (m_empty) return false; @@ -872,5 +1012,39 @@ public: b = m_endpoints.rbegin()->first; return true; } + + bool is_empty() const { return m_empty; } + + bool is_fixed() const { + if (has_pos_inf() || has_neg_inf()) + return false; + T l; + get_lower_bound(l); + T u; + get_upper_bound(u); + return l==u; + } + + + bool improves_with_lower_bound(const T & v) const { + T b; + bool lower_bound_exists = get_lower_bound(b); + return (!lower_bound_exists || v > b) && + !intersection_with_lower_bound_is_empty(v); + } + + bool improves_with_upper_bound(const T & v) const { + T b; + bool upper_bound_exists = get_upper_bound(b); + return (!upper_bound_exists || v < b) && + !intersection_with_upper_bound_is_empty(v); + } + + // returns true if adding the bound b narrows the domain, but does not make it empty + bool improves(const T & v, bool is_lower_bound) const { + if (is_lower_bound) + return improves_with_lower_bound(v); + return improves_with_upper_bound(v); + } }; } diff --git a/src/util/lp/lar_core_solver_instances.cpp b/src/util/lp/lar_core_solver.cpp similarity index 86% rename from src/util/lp/lar_core_solver_instances.cpp rename to src/util/lp/lar_core_solver.cpp index a6a4048e5..95e516135 100644 --- a/src/util/lp/lar_core_solver_instances.cpp +++ b/src/util/lp/lar_core_solver.cpp @@ -22,4 +22,4 @@ Revision History: #include #include "util/vector.h" #include -#include "util/lp/lar_core_solver.hpp" +#include "util/lp/lar_core_solver_def.h" diff --git a/src/util/lp/lar_core_solver.h b/src/util/lp/lar_core_solver.h index 136db9119..c5b281f51 100644 --- a/src/util/lp/lar_core_solver.h +++ b/src/util/lp/lar_core_solver.h @@ -50,7 +50,7 @@ public: stacked_vector m_column_types; // r - solver fields, for rational numbers vector> m_r_x; // the solution - stacked_vector> m_r_low_bounds; + stacked_vector> m_r_lower_bounds; stacked_vector> m_r_upper_bounds; static_matrix> m_r_A; stacked_vector m_r_pushed_basis; @@ -62,7 +62,7 @@ public: // d - solver fields, for doubles vector m_d_x; // the solution in doubles - vector m_d_low_bounds; + vector m_d_lower_bounds; vector m_d_upper_bounds; static_matrix m_d_A; stacked_vector m_d_pushed_basis; @@ -159,7 +159,7 @@ public: void solve(); - bool low_bounds_are_set() const { return true; } + bool lower_bounds_are_set() const { return true; } const indexed_vector & get_pivot_row() const { return m_r_solver.m_pivot_row; @@ -192,7 +192,7 @@ public: // rational if (!settings().use_tableau()) m_r_A.push(); - m_r_low_bounds.push(); + m_r_lower_bounds.push(); m_r_upper_bounds.push(); if (!settings().use_tableau()) { push_vector(m_r_pushed_basis, m_r_basis); @@ -234,7 +234,7 @@ public: // rationals if (!settings().use_tableau()) m_r_A.pop(k); - m_r_low_bounds.pop(k); + m_r_lower_bounds.pop(k); m_r_upper_bounds.pop(k); m_column_types.pop(k); @@ -276,11 +276,11 @@ public: bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair & delta) { auto & x = m_r_x[j]; switch (pos_type) { - case at_low_bound: - if (x == m_r_solver.m_low_bounds[j]) + case at_lower_bound: + if (x == m_r_solver.m_lower_bounds[j]) return false; - delta = m_r_solver.m_low_bounds[j] - x; - m_r_solver.m_x[j] = m_r_solver.m_low_bounds[j]; + delta = m_r_solver.m_lower_bounds[j] - x; + m_r_solver.m_x[j] = m_r_solver.m_lower_bounds[j]; break; case at_fixed: case at_upper_bound: @@ -300,22 +300,22 @@ public: delta = m_r_solver.m_upper_bounds[j] - x; x = m_r_solver.m_upper_bounds[j]; break; - case column_type::low_bound: - delta = m_r_solver.m_low_bounds[j] - x; - x = m_r_solver.m_low_bounds[j]; + case column_type::lower_bound: + delta = m_r_solver.m_lower_bounds[j] - x; + x = m_r_solver.m_lower_bounds[j]; break; case column_type::boxed: if (x > m_r_solver.m_upper_bounds[j]) { delta = m_r_solver.m_upper_bounds[j] - x; x += m_r_solver.m_upper_bounds[j]; } else { - delta = m_r_solver.m_low_bounds[j] - x; - x = m_r_solver.m_low_bounds[j]; + delta = m_r_solver.m_lower_bounds[j] - x; + x = m_r_solver.m_lower_bounds[j]; } break; case column_type::fixed: - delta = m_r_solver.m_low_bounds[j] - x; - x = m_r_solver.m_low_bounds[j]; + delta = m_r_solver.m_lower_bounds[j] - x; + x = m_r_solver.m_lower_bounds[j]; break; default: @@ -359,8 +359,8 @@ public: lp_assert(m_r_heading[j] < 0); auto pos_type = t.second; switch (pos_type) { - case at_low_bound: - s.m_x[j] = s.m_low_bounds[j]; + case at_lower_bound: + s.m_x[j] = s.m_lower_bounds[j]; break; case at_fixed: case at_upper_bound: @@ -377,18 +377,18 @@ public: case column_type::upper_bound: s.m_x[j] = s.m_upper_bounds[j]; break; - case column_type::low_bound: - s.m_x[j] = s.m_low_bounds[j]; + case column_type::lower_bound: + s.m_x[j] = s.m_lower_bounds[j]; break; case column_type::boxed: if (settings().random_next() % 2) { - s.m_x[j] = s.m_low_bounds[j]; + s.m_x[j] = s.m_lower_bounds[j]; } else { s.m_x[j] = s.m_upper_bounds[j]; } break; case column_type::fixed: - s.m_x[j] = s.m_low_bounds[j]; + s.m_x[j] = s.m_lower_bounds[j]; break; default: lp_assert(false); @@ -665,27 +665,27 @@ public: void get_bounds_for_double_solver() { unsigned n = m_n(); - m_d_low_bounds.resize(n); + m_d_lower_bounds.resize(n); m_d_upper_bounds.resize(n); double delta = find_delta_for_strict_boxed_bounds().get_double(); if (delta > 0.000001) delta = 0.000001; for (unsigned j = 0; j < n; j++) { - if (low_bound_is_set(j)) { - const auto & lb = m_r_solver.m_low_bounds[j]; - m_d_low_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); + if (lower_bound_is_set(j)) { + const auto & lb = m_r_solver.m_lower_bounds[j]; + m_d_lower_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); } if (upper_bound_is_set(j)) { const auto & ub = m_r_solver.m_upper_bounds[j]; m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double(); - lp_assert(!low_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_low_bounds[j])); + lp_assert(!lower_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_lower_bounds[j])); } } } void scale_problem_for_doubles( static_matrix& A, - vector & low_bounds, + vector & lower_bounds, vector & upper_bounds) { vector column_scale_vector; vector right_side_vector(A.column_count()); @@ -705,8 +705,8 @@ public: if (m_r_solver.column_has_upper_bound(j)) { upper_bounds[j] /= column_scale_vector[j]; } - if (m_r_solver.column_has_low_bound(j)) { - low_bounds[j] /= column_scale_vector[j]; + if (m_r_solver.column_has_lower_bound(j)) { + lower_bounds[j] /= column_scale_vector[j]; } } } @@ -733,12 +733,12 @@ public: } - bool low_bound_is_set(unsigned j) const { + bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: case column_type::upper_bound: return false; - case column_type::low_bound: + case column_type::lower_bound: case column_type::boxed: case column_type::fixed: return true; @@ -751,7 +751,7 @@ public: bool upper_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: - case column_type::low_bound: + case column_type::lower_bound: return false; case column_type::upper_bound: case column_type::boxed: @@ -780,7 +780,7 @@ public: for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { if (m_column_types()[j] != column_type::boxed) continue; - update_delta(delta, m_r_low_bounds[j], m_r_upper_bounds[j]); + update_delta(delta, m_r_lower_bounds[j], m_r_upper_bounds[j]); } return delta; } @@ -789,8 +789,8 @@ public: mpq find_delta_for_strict_bounds(const mpq & initial_delta) const{ mpq delta = initial_delta; for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { - if (low_bound_is_set(j)) - update_delta(delta, m_r_low_bounds[j], m_r_x[j]); + if (lower_bound_is_set(j)) + update_delta(delta, m_r_lower_bounds[j], m_r_x[j]); if (upper_bound_is_set(j)) update_delta(delta, m_r_x[j], m_r_upper_bounds[j]); } @@ -813,14 +813,14 @@ public: bool column_is_fixed(unsigned j) const { return m_column_types()[j] == column_type::fixed || ( m_column_types()[j] == column_type::boxed && - m_r_solver.m_low_bounds[j] == m_r_solver.m_upper_bounds[j]); + m_r_solver.m_lower_bounds[j] == m_r_solver.m_upper_bounds[j]); } - const impq & low_bound(unsigned j) const { + const impq & lower_bound(unsigned j) const { lp_assert(m_column_types()[j] == column_type::fixed || m_column_types()[j] == column_type::boxed || - m_column_types()[j] == column_type::low_bound); - return m_r_low_bounds[j]; + m_column_types()[j] == column_type::lower_bound); + return m_r_lower_bounds[j]; } const impq & upper_bound(unsigned j) const { diff --git a/src/util/lp/lar_core_solver.hpp b/src/util/lp/lar_core_solver_def.h similarity index 96% rename from src/util/lp/lar_core_solver.hpp rename to src/util/lp/lar_core_solver_def.h index 0943d6977..f9937e77a 100644 --- a/src/util/lp/lar_core_solver.hpp +++ b/src/util/lp/lar_core_solver_def.h @@ -54,7 +54,7 @@ lar_core_solver::lar_core_solver( m_r_heading, m_costs_dummy, m_column_types(), - m_r_low_bounds(), + m_r_lower_bounds(), m_r_upper_bounds(), settings, column_names), @@ -66,7 +66,7 @@ lar_core_solver::lar_core_solver( m_d_heading, m_d_costs_dummy, m_column_types(), - m_d_low_bounds, + m_d_lower_bounds, m_d_upper_bounds, settings, column_names){} @@ -108,17 +108,17 @@ void lar_core_solver::init_cost_for_column(unsigned j) { if (x > this->m_upper_bounds[j]) { this->m_costs[j] = 1; this->m_infeasibility += x - this->m_upper_bounds[j]; - } else if (x < this->m_low_bounds[j]) { - this->m_infeasibility += this->m_low_bounds[j] - x; + } else if (x < this->m_lower_bounds[j]) { + this->m_infeasibility += this->m_lower_bounds[j] - x; this->m_costs[j] = -1; } else { this->m_costs[j] = numeric_traits::zero(); } break; - case low_bound: - if (x < this->m_low_bounds[j]) { + case lower_bound: + if (x < this->m_lower_bounds[j]) { this->m_costs[j] = -1; - this->m_infeasibility += this->m_low_bounds[j] - x; + this->m_infeasibility += this->m_lower_bounds[j] - x; } else { this->m_costs[j] = numeric_traits::zero(); } @@ -154,7 +154,7 @@ int lar_core_solver::column_is_out_of_bounds(unsigned j) { return 1; } return 0; - case low_bound: + case lower_bound: if (this->x_below_low_bound(j)) { return -1; } diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 70c322c4a..fb975186c 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -219,13 +219,13 @@ void lar_solver::propagate_bounds_on_a_term(const lar_term& t, bound_propagator void lar_solver::explain_implied_bound(implied_bound & ib, bound_propagator & bp) { unsigned i = ib.m_row_or_term_index; - int bound_sign = ib.m_is_low_bound? 1: -1; + int bound_sign = ib.m_is_lower_bound? 1: -1; int j_sign = (ib.m_coeff_before_j_is_pos ? 1 :-1) * bound_sign; unsigned m_j = ib.m_j; if (is_term(m_j)) { auto it = m_ext_vars_to_columns.find(m_j); lp_assert(it != m_ext_vars_to_columns.end()); - m_j = it->second.ext_j(); + m_j = it->second.internal_j(); } for (auto const& r : A_r().m_rows[i]) { unsigned j = r.m_j; @@ -234,12 +234,12 @@ void lar_solver::explain_implied_bound(implied_bound & ib, bound_propagator & bp if (is_term(j)) { auto it = m_ext_vars_to_columns.find(j); lp_assert(it != m_ext_vars_to_columns.end()); - j = it->second.ext_j(); + j = it->second.internal_j(); } int a_sign = is_pos(a)? 1: -1; int sign = j_sign * a_sign; const ul_pair & ul = m_columns_to_ul_pairs[j]; - auto witness = sign > 0? ul.upper_bound_witness(): ul.low_bound_witness(); + auto witness = sign > 0? ul.upper_bound_witness(): ul.lower_bound_witness(); lp_assert(is_valid(witness)); bp.consume(a, witness); } @@ -269,6 +269,8 @@ void lar_solver::propagate_bounds_for_touched_rows(bound_propagator & bp) { for (unsigned i : m_rows_with_changed_bounds.m_index) { calculate_implied_bounds_for_row(i, bp); + if (settings().get_cancel_flag()) + return; } m_rows_with_changed_bounds.clear(); if (!use_tableau()) { @@ -326,7 +328,7 @@ void lar_solver::fill_explanation_from_infeasible_column(vector::one(), ul.upper_bound_witness())); - evidence.push_back(std::make_pair(-numeric_traits::one(), ul.low_bound_witness())); + evidence.push_back(std::make_pair(-numeric_traits::one(), ul.lower_bound_witness())); } @@ -347,6 +349,7 @@ void lar_solver::push() { m_term_count.push(); m_constraint_count = m_constraints.size(); m_constraint_count.push(); + m_int_solver->push(); } void lar_solver::clean_popped_elements(unsigned n, int_set& set) { @@ -411,6 +414,8 @@ void lar_solver::pop(unsigned k) { m_settings.simplex_strategy() = m_simplex_strategy; lp_assert(sizes_are_correct()); lp_assert((!m_settings.use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + m_status = m_mpq_lar_core_solver.m_r_solver.current_x_is_feasible()? lp_status::OPTIMAL: lp_status::UNKNOWN; + m_int_solver->pop(k); } vector lar_solver::get_all_constraint_indices() const { @@ -557,9 +562,9 @@ void lar_solver::set_upper_bound_witness(var_index j, constraint_index ci) { m_columns_to_ul_pairs[j] = ul; } -void lar_solver::set_low_bound_witness(var_index j, constraint_index ci) { +void lar_solver::set_lower_bound_witness(var_index j, constraint_index ci) { ul_pair ul = m_columns_to_ul_pairs[j]; - ul.low_bound_witness() = ci; + ul.lower_bound_witness() = ci; m_columns_to_ul_pairs[j] = ul; } @@ -884,7 +889,7 @@ void lar_solver::copy_from_mpq_matrix(static_matrix & matr) { bool lar_solver::try_to_set_fixed(column_info & ci) { - if (ci.upper_bound_is_set() && ci.low_bound_is_set() && ci.get_upper_bound() == ci.get_low_bound() && !ci.is_fixed()) { + if (ci.upper_bound_is_set() && ci.lower_bound_is_set() && ci.get_upper_bound() == ci.get_lower_bound() && !ci.is_fixed()) { ci.set_fixed_value(ci.get_upper_bound()); return true; } @@ -894,7 +899,7 @@ bool lar_solver::try_to_set_fixed(column_info & ci) { column_type lar_solver::get_column_type(const column_info & ci) { auto ret = ci.get_column_type_no_flipping(); if (ret == column_type::boxed) { // changing boxed to fixed because of the no span - if (ci.get_low_bound() == ci.get_upper_bound()) + if (ci.get_lower_bound() == ci.get_upper_bound()) ret = column_type::fixed; } return ret; @@ -1069,9 +1074,9 @@ bool lar_solver::has_lower_bound(var_index var, constraint_index& ci, mpq& value return false; } const ul_pair & ul = m_columns_to_ul_pairs[var]; - ci = ul.low_bound_witness(); + ci = ul.lower_bound_witness(); if (ci != static_cast(-1)) { - auto& p = m_mpq_lar_core_solver.m_r_low_bounds()[var]; + auto& p = m_mpq_lar_core_solver.m_r_lower_bounds()[var]; value = p.x; is_strict = p.y.is_pos(); return true; @@ -1130,7 +1135,7 @@ void lar_solver::get_infeasibility_explanation_for_inf_sign( int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign; const ul_pair & ul = m_columns_to_ul_pairs[j]; - constraint_index bound_constr_i = adj_sign < 0 ? ul.upper_bound_witness() : ul.low_bound_witness(); + constraint_index bound_constr_i = adj_sign < 0 ? ul.upper_bound_witness() : ul.lower_bound_witness(); lp_assert(bound_constr_i < m_constraints.size()); explanation.push_back(std::make_pair(coeff, bound_constr_i)); } @@ -1481,7 +1486,16 @@ bool lar_solver::strategy_is_undecided() const { return m_settings.simplex_strategy() == simplex_strategy_enum::undecided; } +void lar_solver::catch_up_in_updating_int_solver() { + for (unsigned i = 0; i < constraints().size(); i++) { + m_int_solver->add_constraint_to_cut_solver(i, constraints()[i]); + } +} + var_index lar_solver::add_var(unsigned ext_j, bool is_int) { + if (is_int && !has_int_var()) + catch_up_in_updating_int_solver(); + TRACE("add_var", tout << "adding var " << ext_j << (is_int? " int" : " nonint") << std::endl;); var_index i; lp_assert(ext_j < m_terms_start_index); @@ -1490,7 +1504,7 @@ var_index lar_solver::add_var(unsigned ext_j, bool is_int) { throw 0; // todo : what is the right way to exit? auto it = m_ext_vars_to_columns.find(ext_j); if (it != m_ext_vars_to_columns.end()) { - return it->second.ext_j(); + return it->second.internal_j(); } lp_assert(m_columns_to_ul_pairs.size() == A_r().column_count()); i = A_r().column_count(); @@ -1526,9 +1540,9 @@ void lar_solver::add_new_var_to_core_fields_for_doubles(bool register_in_basis) unsigned j = A_d().column_count(); A_d().add_column(); lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); - // lp_assert(m_mpq_lar_core_solver.m_d_low_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later + // lp_assert(m_mpq_lar_core_solver.m_d_lower_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later m_mpq_lar_core_solver.m_d_x.resize(j + 1); - m_mpq_lar_core_solver.m_d_low_bounds.resize(j + 1); + m_mpq_lar_core_solver.m_d_lower_bounds.resize(j + 1); m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method if (register_in_basis) { @@ -1546,9 +1560,9 @@ void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { unsigned j = A_r().column_count(); A_r().add_column(); lp_assert(m_mpq_lar_core_solver.m_r_x.size() == j); - // lp_assert(m_mpq_lar_core_solver.m_r_low_bounds.size() == j && m_mpq_lar_core_solver.m_r_upper_bounds.size() == j); // restore later + // lp_assert(m_mpq_lar_core_solver.m_r_lower_bounds.size() == j && m_mpq_lar_core_solver.m_r_upper_bounds.size() == j); // restore later m_mpq_lar_core_solver.m_r_x.resize(j + 1); - m_mpq_lar_core_solver.m_r_low_bounds.increase_size_by_one(); + m_mpq_lar_core_solver.m_r_lower_bounds.increase_size_by_one(); m_mpq_lar_core_solver.m_r_upper_bounds.increase_size_by_one(); m_mpq_lar_core_solver.m_r_solver.m_inf_set.increase_size_by_one(); m_mpq_lar_core_solver.m_r_solver.m_costs.resize(j + 1); @@ -1626,7 +1640,7 @@ void lar_solver::add_basic_var_to_core_fields() { add_new_var_to_core_fields_for_doubles(true); } -bool lar_solver::bound_is_integer_if_needed(unsigned j, const mpq & right_side) const { +bool lar_solver::bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const { if (!column_is_int(j)) return true; return right_side.is_int(); @@ -1636,9 +1650,12 @@ constraint_index lar_solver::add_var_bound(var_index j, lconstraint_kind kind, c TRACE("lar_solver", tout << "j = " << j << std::endl;); constraint_index ci = m_constraints.size(); if (!is_term(j)) { // j is a var - lp_assert(bound_is_integer_if_needed(j, right_side)); + lp_assert(bound_is_integer_for_integer_column(j, right_side)); auto vc = new lar_var_constraint(j, kind, right_side); m_constraints.push_back(vc); + if (has_int_var()) { + m_int_solver->notify_on_last_added_constraint(); + } update_column_type_and_bound(j, kind, right_side, ci); } else { @@ -1657,8 +1674,8 @@ void lar_solver::update_column_type_and_bound(var_index j, lconstraint_kind kind case column_type::boxed: update_boxed_column_type_and_bound(j, kind, right_side, constr_index); break; - case column_type::low_bound: - update_low_bound_column_type_and_bound(j, kind, right_side, constr_index); + case column_type::lower_bound: + update_lower_bound_column_type_and_bound(j, kind, right_side, constr_index); break; case column_type::upper_bound: update_upper_bound_column_type_and_bound(j, kind, right_side, constr_index); @@ -1677,9 +1694,11 @@ void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_k lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int()); auto it = m_ext_vars_to_columns.find(j); if (it != m_ext_vars_to_columns.end()) { - unsigned term_j = it->second.ext_j(); + unsigned term_j = it->second.internal_j(); mpq rs = right_side - m_terms[adjusted_term_index]->m_v; m_constraints.push_back(new lar_term_constraint(m_terms[adjusted_term_index], kind, right_side)); + if (has_int_var()) + m_int_solver->notify_on_last_added_constraint(); update_column_type_and_bound(term_j, kind, rs, ci); } else { @@ -1738,7 +1757,7 @@ void lar_solver::adjust_initial_state_for_lu() { copy_from_mpq_matrix(A_d()); unsigned n = A_d().column_count(); m_mpq_lar_core_solver.m_d_x.resize(n); - m_mpq_lar_core_solver.m_d_low_bounds.resize(n); + m_mpq_lar_core_solver.m_d_lower_bounds.resize(n); m_mpq_lar_core_solver.m_d_upper_bounds.resize(n); m_mpq_lar_core_solver.m_d_heading = m_mpq_lar_core_solver.m_r_heading; m_mpq_lar_core_solver.m_d_basis = m_mpq_lar_core_solver.m_r_basis; @@ -1747,9 +1766,9 @@ void lar_solver::adjust_initial_state_for_lu() { unsigned j = A_d().column_count(); A_d().add_column(); lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); - // lp_assert(m_mpq_lar_core_solver.m_d_low_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later + // lp_assert(m_mpq_lar_core_solver.m_d_lower_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later m_mpq_lar_core_solver.m_d_x.resize(j + 1 ); - m_mpq_lar_core_solver.m_d_low_bounds.resize(j + 1); + m_mpq_lar_core_solver.m_d_lower_bounds.resize(j + 1); m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method if (register_in_basis) { @@ -1805,19 +1824,19 @@ void lar_solver::update_free_column_type_and_bound(var_index j, lconstraint_kind case GT: y_of_bound = 1; case GE: - m_mpq_lar_core_solver.m_column_types[j] = column_type::low_bound; + m_mpq_lar_core_solver.m_column_types[j] = column_type::lower_bound; lp_assert(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j); { auto low = numeric_pair(right_side, y_of_bound); - m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; } - set_low_bound_witness(j, constr_ind); + set_lower_bound_witness(j, constr_ind); break; case EQ: m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; - m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = numeric_pair(right_side, zero_of_type()); + m_mpq_lar_core_solver.m_r_lower_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = numeric_pair(right_side, zero_of_type()); set_upper_bound_witness(j, constr_ind); - set_low_bound_witness(j, constr_ind); + set_lower_bound_witness(j, constr_ind); break; default: @@ -1849,15 +1868,15 @@ void lar_solver::update_upper_bound_column_type_and_bound(var_index j, lconstrai m_mpq_lar_core_solver.m_column_types[j] = column_type::boxed; { auto low = numeric_pair(right_side, y_of_bound); - m_mpq_lar_core_solver.m_r_low_bounds[j] = low; - set_low_bound_witness(j, ci); + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; + set_lower_bound_witness(j, ci); m_columns_with_changed_bound.insert(j); if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; } else { - m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; + m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; } } break; @@ -1866,13 +1885,13 @@ void lar_solver::update_upper_bound_column_type_and_bound(var_index j, lconstrai auto v = numeric_pair(right_side, zero_of_type()); if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; - set_low_bound_witness(j, ci); + set_lower_bound_witness(j, ci); m_infeasible_column_index = j; } else { - m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + m_mpq_lar_core_solver.m_r_lower_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; m_columns_with_changed_bound.insert(j); - set_low_bound_witness(j, ci); + set_lower_bound_witness(j, ci); set_upper_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; } @@ -1887,7 +1906,7 @@ void lar_solver::update_upper_bound_column_type_and_bound(var_index j, lconstrai } void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::boxed && m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j])); + lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::boxed && m_mpq_lar_core_solver.m_r_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j])); mpq y_of_bound(0); switch (kind) { case LT: @@ -1901,13 +1920,13 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin m_columns_with_changed_bound.insert(j); } - if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; lp_assert(false); m_infeasible_column_index = j; } else { - if (m_mpq_lar_core_solver.m_r_low_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j]) + if (m_mpq_lar_core_solver.m_r_lower_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j]) m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; } } @@ -1917,10 +1936,10 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin case GE: { auto low = numeric_pair(right_side, y_of_bound); - if (low > m_mpq_lar_core_solver.m_r_low_bounds[j]) { - m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + if (low > m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; m_columns_with_changed_bound.insert(j); - set_low_bound_witness(j, ci); + set_lower_bound_witness(j, ci); } if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; @@ -1934,7 +1953,7 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin case EQ: { auto v = numeric_pair(right_side, zero_of_type()); - if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); @@ -1942,11 +1961,11 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; - set_low_bound_witness(j, ci); + set_lower_bound_witness(j, ci); } else { - m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; - set_low_bound_witness(j, ci); + m_mpq_lar_core_solver.m_r_lower_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + set_lower_bound_witness(j, ci); set_upper_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; m_columns_with_changed_bound.insert(j); @@ -1960,8 +1979,8 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin } } -void lar_solver::update_low_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::low_bound); +void lar_solver::update_lower_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::lower_bound); mpq y_of_bound(0); switch (kind) { case LT: @@ -1973,12 +1992,12 @@ void lar_solver::update_low_bound_column_type_and_bound(var_index j, lconstraint set_upper_bound_witness(j, ci); m_columns_with_changed_bound.insert(j); - if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; } else { - m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; + m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; } } break; @@ -1987,24 +2006,24 @@ void lar_solver::update_low_bound_column_type_and_bound(var_index j, lconstraint case GE: { auto low = numeric_pair(right_side, y_of_bound); - if (low > m_mpq_lar_core_solver.m_r_low_bounds[j]) { - m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + if (low > m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; m_columns_with_changed_bound.insert(j); - set_low_bound_witness(j, ci); + set_lower_bound_witness(j, ci); } } break; case EQ: { auto v = numeric_pair(right_side, zero_of_type()); - if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); } else { - m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; - set_low_bound_witness(j, ci); + m_mpq_lar_core_solver.m_r_lower_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + set_lower_bound_witness(j, ci); set_upper_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; } @@ -2019,14 +2038,14 @@ void lar_solver::update_low_bound_column_type_and_bound(var_index j, lconstraint } void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::fixed && m_mpq_lar_core_solver.m_r_low_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j])); - lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_r_low_bounds()[j].y.is_zero() && m_mpq_lar_core_solver.m_r_upper_bounds()[j].y.is_zero())); + lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::fixed && m_mpq_lar_core_solver.m_r_lower_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j])); + lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_r_lower_bounds()[j].y.is_zero() && m_mpq_lar_core_solver.m_r_upper_bounds()[j].y.is_zero())); auto v = numeric_pair(right_side, mpq(0)); mpq y_of_bound(0); switch (kind) { case LT: - if (v <= m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (v <= m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); @@ -2034,7 +2053,7 @@ void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kin break; case LE: { - if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); @@ -2046,7 +2065,7 @@ void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kin if (v >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; - set_low_bound_witness(j, ci); + set_lower_bound_witness(j, ci); } } break; @@ -2055,13 +2074,13 @@ void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kin if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; - set_low_bound_witness(j, ci); + set_lower_bound_witness(j, ci); } } break; case EQ: { - if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); @@ -2069,7 +2088,7 @@ void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kin else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; - set_low_bound_witness(j, ci); + set_lower_bound_witness(j, ci); } break; } @@ -2080,7 +2099,15 @@ void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kin } } +bool lar_solver::column_corresponds_to_term(unsigned j) const { + return m_columns_to_ext_vars_or_term_indices[j] >= m_terms_start_index; +} +var_index lar_solver:: to_var_index(unsigned ext_j) const { + auto it = m_ext_vars_to_columns.find(ext_j); + lp_assert(it != m_ext_vars_to_columns.end()); + return it->second.internal_j(); +} } // namespace lp diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index aecc337cf..b2585dea8 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -44,18 +44,20 @@ Revision History: #include "util/lp/iterator_on_row.h" #include "util/lp/quick_xplain.h" #include "util/lp/conversion_helper.h" +#include "util/lp/int_solver.h" +#include "util/lp/nra_solver.h" namespace lp { class lar_solver : public column_namer { class ext_var_info { - unsigned m_ext_j; // the external index + unsigned m_internal_j; // the internal index bool m_is_integer; public: - ext_var_info(unsigned j): ext_var_info(j, false) {} - ext_var_info(unsigned j , bool is_int) : m_ext_j(j), m_is_integer(is_int) {} - unsigned ext_j() const { return m_ext_j;} + ext_var_info(unsigned j, var_index internal_j): ext_var_info(j, false) {} + ext_var_info(unsigned j , bool is_int) : m_internal_j(j), m_is_integer(is_int) {} + unsigned internal_j() const { return m_internal_j;} bool is_integer() const {return m_is_integer;} }; //////////////////// fields ////////////////////////// @@ -73,16 +75,14 @@ public : private: stacked_value m_constraint_count; // the set of column indices j such that bounds have changed for j - - int_set m_columns_with_changed_bound; - int_set m_rows_with_changed_bounds; - int_set m_basic_columns_with_changed_cost; - stacked_value m_infeasible_column_index; // such can be found at the initialization step - stacked_value m_term_count; - vector m_terms; - vector m_orig_terms; - const var_index m_terms_start_index; - indexed_vector m_column_buffer; + int_set m_columns_with_changed_bound; + int_set m_rows_with_changed_bounds; + int_set m_basic_columns_with_changed_cost; + stacked_value m_infeasible_column_index; // such can be found at the initialization step + stacked_value m_term_count; + vector m_terms; + const var_index m_terms_start_index; + indexed_vector m_column_buffer; public: lar_core_solver m_mpq_lar_core_solver; private: @@ -99,11 +99,11 @@ public: const lar_base_constraint& get_constraint(unsigned ci) const; int_set m_inf_int_set; ////////////////// methods //////////////////////////////// - static_matrix> & A_r() { return m_mpq_lar_core_solver.m_r_A;} - static_matrix> const & A_r() const { return m_mpq_lar_core_solver.m_r_A;} - static_matrix & A_d() { return m_mpq_lar_core_solver.m_d_A;} - static_matrix const & A_d() const { return m_mpq_lar_core_solver.m_d_A;} - + static_matrix> & A_r(); + static_matrix> const & A_r() const; + static_matrix & A_d(); + static_matrix const & A_d() const; + static bool valid_index(unsigned j){ return static_cast(j) >= 0;} bool column_is_int(unsigned j) const; @@ -121,37 +121,69 @@ public: // init region bool strategy_is_undecided() const; - void clear() {SASSERT(false); // not implemented - } + var_index add_var(unsigned ext_j, bool is_integer); void register_new_ext_var_index(unsigned ext_v, bool is_int); - lar_solver() : m_status(OPTIMAL), - m_infeasible_column_index(-1), - m_terms_start_index(1000000), - m_mpq_lar_core_solver(m_settings, *this) - {} - - void set_propagate_bounds_on_pivoted_rows_mode(bool v) { - m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows = v? (& m_rows_with_changed_bounds) : nullptr; - } + bool term_is_int(const lar_term * t) const; bool var_is_int(var_index v) const; -#include "util/lp/init_lar_solver.h" - - numeric_pair const& get_value(var_index vi) const { return m_mpq_lar_core_solver.m_r_x[vi]; } + bool ext_var_is_int(var_index ext_var) const; + + void add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int); void add_new_var_to_core_fields_for_doubles(bool register_in_basis); - unsigned adjust_term_index(unsigned j) const { - SASSERT(is_term(j)); - return j - m_terms_start_index; - } + void add_new_var_to_core_fields_for_mpq(bool register_in_basis); + + + var_index add_term_undecided(const vector> & coeffs, + const mpq &m_v); + + // terms + var_index add_term(const vector> & coeffs, + const mpq &m_v); + + void add_row_for_term(const lar_term * term, unsigned term_ext_index); + + void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index); + + void add_basic_var_to_core_fields(); + + constraint_index add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) ; + + void update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index); + + void add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); + + + void add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, + lconstraint_kind kind, const mpq & right_side); void decide_on_strategy_and_adjust_initial_state(); - bool use_lu() const { return m_settings.simplex_strategy() == simplex_strategy_enum::lu; } + void adjust_initial_state(); + + void adjust_initial_state_for_lu(); + + void adjust_initial_state_for_tableau_rows(); + + // this fills the last row of A_d and sets the basis column: -1 in the last column of the row + void fill_last_row_of_A_d(static_matrix & A, const lar_term* ls); + + void update_free_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_ind); + + void update_upper_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); + + void update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); + void update_lower_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); + + void update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); + //end of init region + lp_settings & settings(); + + lp_settings const & settings() const; void clear(); lar_solver(); @@ -163,223 +195,60 @@ public: unsigned adjust_term_index(unsigned j) const; + + bool use_lu() const; + + bool sizes_are_correct() const; + + void print_implied_bound(const implied_bound& be, std::ostream & out) const; + + bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; + void analyze_new_bounds_on_row( unsigned row_index, bound_propagator & bp); void analyze_new_bounds_on_row_tableau( unsigned row_index, - bound_propagator & bp); + bound_propagator & bp + ); - void substitute_basis_var_in_terms_for_row(unsigned i) { - // todo : create a map from term basic vars to the rows where they are used - unsigned basis_j = m_mpq_lar_core_solver.m_r_solver.m_basis[i]; - for (unsigned k = 0; k < m_terms.size(); k++) { - if (term_is_used_as_row(k)) - continue; - if (!m_terms[k]->contains(basis_j)) - continue; - m_terms[k]->subst(basis_j, m_mpq_lar_core_solver.m_r_solver.m_pivot_row); - } - } + + void substitute_basis_var_in_terms_for_row(unsigned i); + + void calculate_implied_bounds_for_row(unsigned i, bound_propagator & bp); - void calculate_implied_bounds_for_row(unsigned i, lp_bound_propagator & bp) { - if(use_tableau()) { - analyze_new_bounds_on_row_tableau(i, bp); - } else { - m_mpq_lar_core_solver.calculate_pivot_row(i); - substitute_basis_var_in_terms_for_row(i); - analyze_new_bounds_on_row(i, bp); - } - } + + linear_combination_iterator * create_new_iter_from_term(unsigned term_index) const; - /* - void process_new_implied_evidence_for_low_bound( - implied_bound_explanation& implied_evidence, // not pushed yet - vector & bound_evidences, - std::unordered_map & improved_low_bounds) { - - unsigned existing_index; - if (try_get_val(improved_low_bounds, implied_evidence.m_j, existing_index)) { - // we are improving the existent bound - bound_evidences[existing_index] = fill_bound_evidence(implied_evidence); - } else { - improved_low_bounds[implied_evidence.m_j] = bound_evidences.size(); - bound_evidences.push_back(fill_bound_evidence(implied_evidence)); - } - } - - void fill_bound_evidence_on_term(implied_bound & ie, implied_bound& be) { - SASSERT(false); - } - void fill_implied_bound_on_row(implied_bound & ie, implied_bound& be) { - iterator_on_row it(A_r().m_rows[ie.m_row_or_term_index]); - mpq a; unsigned j; - bool toggle = ie.m_coeff_before_j_is_pos; - if (!ie.m_is_low_bound) - toggle = !toggle; - while (it.next(a, j)) { - if (j == ie.m_j) continue; - const ul_pair & ul = m_vars_to_ul_pairs[j]; - - if (is_neg(a)) { // so the monoid has a positive coeff on the right side - constraint_index witness = toggle ? ul.m_low_bound_witness : ul.m_upper_bound_witness; - SASSERT(is_valid(witness)); - be.m_explanation.emplace_back(a, witness); - } - } - } - */ - /* - implied_bound fill_implied_bound_for_low_bound(implied_bound& ie) { - implied_bound be(ie.m_j, ie.m_bound.y.is_zero() ? GE : GT, ie.m_bound.x); - if (is_term(ie.m_row_or_term_index)) { - fill_implied_bound_for_low_bound_on_term(ie, be); - } - else { - fill_implied_bound_for_low_bound_on_row(ie, be); - } - return be; - } - - implied_bound fill_implied_bound_for_upper_bound(implied_bound& implied_evidence) { - SASSERT(false); - - be.m_j = implied_evidence.m_j; - be.m_bound = implied_evidence.m_bound.x; - be.m_kind = implied_evidence.m_bound.y.is_zero() ? LE : LT; - for (auto t : implied_evidence.m_vector_of_bound_signatures) { - const ul_pair & ul = m_vars_to_ul_pairs[t.m_column_index]; - constraint_index witness = t.m_low_bound ? ul.m_low_bound_witness : ul.m_upper_bound_witness; - SASSERT(is_valid(witness)); - be.m_explanation.emplace_back(t.m_coeff, witness); - } - - } - */ - /* - void process_new_implied_evidence_for_upper_bound( - implied_bound& implied_evidence, - vector & implied_bounds, - std::unordered_map & improved_upper_bounds) { - unsigned existing_index; - if (try_get_val(improved_upper_bounds, implied_evidence.m_j, existing_index)) { - implied_bound & be = implied_bounds[existing_index]; - be.m_explanation.clear(); - // we are improving the existent bound improve the existing bound - be = fill_implied_bound_for_upper_bound(implied_evidence); - } else { - improved_upper_bounds[implied_evidence.m_j] = implied_bounds.size(); - implied_bounds.push_back(fill_implied_bound_for_upper_bound(implied_evidence)); - } - } - */ - // implied_bound * get_existing_ - - linear_combination_iterator * create_new_iter_from_term(unsigned term_index) const { - SASSERT(false); // not implemented - return nullptr; - // new linear_combination_iterator_on_vector(m_terms[adjust_term_index(term_index)]->coeffs_as_vector()); - } - - unsigned adjust_column_index_to_term_index(unsigned j) const { - unsigned ext_var_or_term = m_columns_to_ext_vars_or_term_indices[j]; - return ext_var_or_term < m_terms_start_index ? j : ext_var_or_term; - } - - void propagate_bounds_on_a_term(const lar_term& t, lp_bound_propagator & bp, unsigned term_offset) { - SASSERT(false); // not implemented - } + unsigned adjust_column_index_to_term_index(unsigned j) const; + + void propagate_bounds_on_a_term(const lar_term& t, bound_propagator & bp, unsigned term_offset); - void explain_implied_bound(implied_bound & ib, lp_bound_propagator & bp) { - unsigned i = ib.m_row_or_term_index; - int bound_sign = ib.m_is_low_bound? 1: -1; - int j_sign = (ib.m_coeff_before_j_is_pos ? 1 :-1) * bound_sign; - unsigned m_j = ib.m_j; - if (is_term(m_j)) { - m_j = m_ext_vars_to_columns[m_j]; - } - for (auto const& r : A_r().m_rows[i]) { - unsigned j = r.m_j; - mpq const& a = r.get_val(); - if (j == m_j) continue; - if (is_term(j)) { - j = m_ext_vars_to_columns[j]; - } - int a_sign = is_pos(a)? 1: -1; - int sign = j_sign * a_sign; - const ul_pair & ul = m_vars_to_ul_pairs[j]; - auto witness = sign > 0? ul.upper_bound_witness(): ul.low_bound_witness(); - SASSERT(is_valid(witness)); - bp.consume(a, witness); - } - // SASSERT(implied_bound_is_correctly_explained(ib, explanation)); - } + void explain_implied_bound(implied_bound & ib, bound_propagator & bp); - bool term_is_used_as_row(unsigned term) const { - SASSERT(is_term(term)); - return contains(m_ext_vars_to_columns, term); - } - - void propagate_bounds_on_terms(lp_bound_propagator & bp) { - for (unsigned i = 0; i < m_terms.size(); i++) { - if (term_is_used_as_row(i + m_terms_start_index)) - continue; // this term is used a left side of a constraint, - // it was processed as a touched row if needed - propagate_bounds_on_a_term(*m_terms[i], bp, i); - } - } + bool term_is_used_as_row(unsigned term) const; + + void propagate_bounds_on_terms(bound_propagator & bp); // goes over touched rows and tries to induce bounds - void propagate_bounds_for_touched_rows(lp_bound_propagator & bp) { - if (!use_tableau()) - return; // ! todo : enable bound propagaion here. The current bug is that after the pop - // the changed terms become incorrect! - - for (unsigned i : m_rows_with_changed_bounds.m_index) { - calculate_implied_bounds_for_row(i, bp); - if (settings().get_cancel_flag()) - return; - } - m_rows_with_changed_bounds.clear(); - if (!use_tableau()) { - propagate_bounds_on_terms(bp); - } - } - - lp_status get_status() const { return m_status;} + void propagate_bounds_for_touched_rows(bound_propagator & bp); lp_status get_status() const; void set_status(lp_status s); - m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; - return solve(); - } - - lp_status solve() { - if (m_status == INFEASIBLE) { - return m_status; - } - solve_with_core_solver(); - if (m_status != INFEASIBLE) { - if (m_settings.bound_propagation()) - detect_rows_with_changed_bounds(); - } - - m_columns_with_changed_bound.clear(); - return m_status; - } - - void fill_explanation_from_infeasible_column(vector> & evidence) const{ + lp_status find_feasible_solution(); + + lp_status solve(); void fill_explanation_from_infeasible_column(explanation_t & evidence) const; - unsigned get_total_iterations() const { return m_mpq_lar_core_solver.m_r_solver.total_iterations(); } + unsigned get_total_iterations() const; // see http://research.microsoft.com/projects/z3/smt07.pdf // This method searches for a feasible solution with as many different values of variables, reverenced in vars, as it can find // Attention, after a call to this method the non-basic variables don't necesserarly stick to their bounds anymore @@ -390,175 +259,31 @@ public: static void shrink_inf_set_after_pop(unsigned n, int_set & set); - void pop(unsigned k) { - int n_was = static_cast(m_ext_vars_to_columns.size()); - m_status.pop(k); - m_infeasible_column_index.pop(k); - unsigned n = m_vars_to_ul_pairs.peek_size(k); - for (unsigned j = n_was; j-- > n;) - m_ext_vars_to_columns.erase(m_columns_to_ext_vars_or_term_indices[j]); - m_columns_to_ext_vars_or_term_indices.resize(n); - if (m_settings.use_tableau()) { - pop_tableau(); - } - m_vars_to_ul_pairs.pop(k); - - m_mpq_lar_core_solver.pop(k); - clean_large_elements_after_pop(n, m_columns_with_changed_bound); - unsigned m = A_r().row_count(); - clean_large_elements_after_pop(m, m_rows_with_changed_bounds); - clean_inf_set_of_r_solver_after_pop(); - SASSERT(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || - (!use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - - - SASSERT(ax_is_correct()); - SASSERT(m_mpq_lar_core_solver.m_r_solver.inf_set_is_correct()); - m_constraint_count.pop(k); - for (unsigned i = m_constraint_count; i < m_constraints.size(); i++) - delete m_constraints[i]; - - m_constraints.resize(m_constraint_count); - m_term_count.pop(k); - for (unsigned i = m_term_count; i < m_terms.size(); i++) { - delete m_terms[i]; - delete m_orig_terms[i]; - } - m_terms.resize(m_term_count); - m_orig_terms.resize(m_term_count); - m_simplex_strategy.pop(k); - m_settings.simplex_strategy() = m_simplex_strategy; - SASSERT(sizes_are_correct()); - SASSERT((!m_settings.use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } - - vector get_all_constraint_indices() const { - vector ret; - constraint_index i = 0; - while ( i < m_constraints.size()) - ret.push_back(i++); - return ret; - } + + void pop(unsigned k); + + vector get_all_constraint_indices() const; bool maximize_term_on_tableau(const vector> & term, - impq &term_max) { - if (settings().simplex_strategy() == simplex_strategy_enum::undecided) - decide_on_strategy_and_adjust_initial_state(); + impq &term_max); - m_mpq_lar_core_solver.solve(); - if (m_mpq_lar_core_solver.m_r_solver.get_status() == UNBOUNDED) - return false; - - term_max = 0; - for (auto & p : term) - term_max += p.first * m_mpq_lar_core_solver.m_r_x[p.second]; - - return true; - } - - bool costs_are_zeros_for_r_solver() const { - for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_costs.size(); j++) { - SASSERT(is_zero(m_mpq_lar_core_solver.m_r_solver.m_costs[j])); - } - return true; - } - bool reduced_costs_are_zeroes_for_r_solver() const { - for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_d.size(); j++) { - SASSERT(is_zero(m_mpq_lar_core_solver.m_r_solver.m_d[j])); - } - return true; - } - - void set_costs_to_zero(const vector> & term) { - auto & rslv = m_mpq_lar_core_solver.m_r_solver; - auto & jset = m_mpq_lar_core_solver.m_r_solver.m_inf_set; // hijack this set that should be empty right now - SASSERT(jset.m_index.size()==0); - - for (auto & p : term) { - unsigned j = p.second; - rslv.m_costs[j] = zero_of_type(); - int i = rslv.m_basis_heading[j]; - if (i < 0) - jset.insert(j); - else { - for (auto & rc : A_r().m_rows[i]) - jset.insert(rc.m_j); - } - } - - for (unsigned j : jset.m_index) - rslv.m_d[j] = zero_of_type(); - - jset.clear(); - - SASSERT(reduced_costs_are_zeroes_for_r_solver()); - SASSERT(costs_are_zeros_for_r_solver()); - } - - void prepare_costs_for_r_solver(const vector> & term) { - - auto & rslv = m_mpq_lar_core_solver.m_r_solver; - rslv.m_using_infeas_costs = false; - SASSERT(costs_are_zeros_for_r_solver()); - SASSERT(reduced_costs_are_zeroes_for_r_solver()); - rslv.m_costs.resize(A_r().column_count(), zero_of_type()); - for (auto & p : term) { - unsigned j = p.second; - rslv.m_costs[j] = p.first; - if (rslv.m_basis_heading[j] < 0) - rslv.m_d[j] += p.first; - else - rslv.update_reduced_cost_for_basic_column_cost_change(- p.first, j); - } - SASSERT(rslv.reduced_costs_are_correct_tableau()); - } + bool costs_are_zeros_for_r_solver() const; + bool reduced_costs_are_zeroes_for_r_solver() const; + + void set_costs_to_zero(const vector> & term); + void prepare_costs_for_r_solver(const vector> & term); + bool maximize_term_on_corrected_r_solver(const vector> & term, - impq &term_max) { - settings().backup_costs = false; - switch (settings().simplex_strategy()) { - case simplex_strategy_enum::tableau_rows: - prepare_costs_for_r_solver(term); - settings().simplex_strategy() = simplex_strategy_enum::tableau_costs; - { - bool ret = maximize_term_on_tableau(term, term_max); - settings().simplex_strategy() = simplex_strategy_enum::tableau_rows; - set_costs_to_zero(term); - m_mpq_lar_core_solver.m_r_solver.set_status(OPTIMAL); - return ret; - } - case simplex_strategy_enum::tableau_costs: - prepare_costs_for_r_solver(term); - { - bool ret = maximize_term_on_tableau(term, term_max); - set_costs_to_zero(term); - m_mpq_lar_core_solver.m_r_solver.set_status(OPTIMAL); - return ret; - } - - case simplex_strategy_enum::lu: - SASSERT(false); // not implemented - return false; - default: - SASSERT(false); // wrong mode - } - return false; - } + impq &term_max); // starting from a given feasible state look for the maximum of the term // return true if found and false if unbounded bool maximize_term(const vector> & term, - impq &term_max) { - SASSERT(m_mpq_lar_core_solver.m_r_solver.current_x_is_feasible()); - m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = false; - return maximize_term_on_corrected_r_solver(term, term_max); - } + impq &term_max); + - - - const lar_term & get_term(unsigned j) const { - SASSERT(j >= m_terms_start_index); - return *m_terms[j - m_terms_start_index]; - } + + const lar_term & get_term(unsigned j) const; void pop_core_solver_params(); @@ -567,334 +292,76 @@ public: void set_upper_bound_witness(var_index j, constraint_index ci); - void set_low_bound_witness(var_index j, constraint_index ci); + void set_lower_bound_witness(var_index j, constraint_index ci); void substitute_terms_in_linear_expression( const vector>& left_side_with_terms, vector> &left_side, mpq & free_coeff) const; - void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { - if (A_r().row_count() != m_column_buffer.data_size()) - m_column_buffer.resize(A_r().row_count()); - else - m_column_buffer.clear(); - SASSERT(m_column_buffer.size() == 0 && m_column_buffer.is_OK()); - - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - for (unsigned i : m_column_buffer.m_index) - m_rows_with_changed_bounds.insert(i); - } + void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j); - - void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) { - for (auto & rc : m_mpq_lar_core_solver.m_r_A.m_columns[j]) - m_rows_with_changed_bounds.insert(rc.m_i); - } + + void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j); bool use_tableau() const; - bool use_tableau_costs() const { - return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; - } + bool use_tableau_costs() const; + + void detect_rows_of_column_with_bound_change(unsigned j); - void detect_rows_of_column_with_bound_change(unsigned j) { - if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { // it is a basic column - // just mark the row at touched and exit - m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]); - return; - } + void adjust_x_of_column(unsigned j); - if (use_tableau()) - detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); - else - detect_rows_of_bound_change_column_for_nbasic_column(j); - } - - void adjust_x_of_column(unsigned j) { - SASSERT(false); - } - - bool row_is_correct(unsigned i) const { - numeric_pair r = zero_of_type>(); - for (const auto & c : A_r().m_rows[i]) - r += c.m_value * m_mpq_lar_core_solver.m_r_x[c.m_j]; - return is_zero(r); - } - - bool ax_is_correct() const { - for (unsigned i = 0; i < A_r().row_count(); i++) { - if (!row_is_correct(i)) - return false; - } - return true; - } + bool row_is_correct(unsigned i) const; + + bool ax_is_correct() const; bool tableau_with_costs() const; bool costs_are_used() const; void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta); + void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j); - void detect_rows_with_changed_bounds_for_column(unsigned j) { - if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { - m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]); - return; - } + + void detect_rows_with_changed_bounds_for_column(unsigned j); + + void detect_rows_with_changed_bounds(); - if (use_tableau()) - detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); - else - detect_rows_of_bound_change_column_for_nbasic_column(j); - } + void update_x_and_inf_costs_for_columns_with_changed_bounds(); - void detect_rows_with_changed_bounds() { - for (auto j : m_columns_with_changed_bound.m_index) - detect_rows_with_changed_bounds_for_column(j); - } + void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); - void update_x_and_inf_costs_for_columns_with_changed_bounds() { - for (auto j : m_columns_with_changed_bound.m_index) - update_x_and_inf_costs_for_column_with_changed_bounds(j); - } + + void solve_with_core_solver(); - void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { - SASSERT(ax_is_correct()); - for (auto j : m_columns_with_changed_bound.m_index) - update_x_and_inf_costs_for_column_with_changed_bounds(j); - - if (tableau_with_costs()) { - for (unsigned j : m_basic_columns_with_changed_cost.m_index) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - SASSERT(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } - } - - - void solve_with_core_solver() { - if (!use_tableau()) - add_last_rows_to_lu(m_mpq_lar_core_solver.m_r_solver); - if (m_mpq_lar_core_solver.need_to_presolve_with_double_solver()) { - add_last_rows_to_lu(m_mpq_lar_core_solver.m_d_solver); - } - m_mpq_lar_core_solver.prefix_r(); - if (costs_are_used()) { - m_basic_columns_with_changed_cost.clear(); - m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); - } - if (use_tableau()) - update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); - else - update_x_and_inf_costs_for_columns_with_changed_bounds(); - m_mpq_lar_core_solver.solve(); - set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); - SASSERT(m_status != OPTIMAL || all_constraints_hold()); - } - - - numeric_pair get_basic_var_value_from_row_directly(unsigned i) { - numeric_pair r = zero_of_type>(); - - unsigned bj = m_mpq_lar_core_solver.m_r_solver.m_basis[i]; - for (const auto & c: A_r().m_rows[i]) { - if (c.m_j == bj) continue; - const auto & x = m_mpq_lar_core_solver.m_r_x[c.m_j]; - if (!is_zero(x)) - r -= c.m_value * x; - } - return r; - } - - - - numeric_pair get_basic_var_value_from_row(unsigned i) { - if (settings().use_tableau()) { - return get_basic_var_value_from_row_directly(i); - } - - numeric_pair r = zero_of_type>(); - m_mpq_lar_core_solver.calculate_pivot_row(i); - for (unsigned j : m_mpq_lar_core_solver.m_r_solver.m_pivot_row.m_index) { - SASSERT(m_mpq_lar_core_solver.m_r_solver.m_basis_heading[j] < 0); - r -= m_mpq_lar_core_solver.m_r_solver.m_pivot_row.m_data[j] * m_mpq_lar_core_solver.m_r_x[j]; - } - return r; - } + + numeric_pair get_basic_var_value_from_row_directly(unsigned i); + + + + numeric_pair get_basic_var_value_from_row(unsigned i); template - void add_last_rows_to_lu(lp_primal_core_solver & s) { - auto & f = s.m_factorization; - if (f != nullptr) { - auto columns_to_replace = f->get_set_of_columns_to_replace_for_add_last_rows(s.m_basis_heading); - if (f->m_refactor_counter + columns_to_replace.size() >= 200 || f->has_dense_submatrix()) { - delete f; - f = nullptr; - } else { - f->add_last_rows_to_B(s.m_basis_heading, columns_to_replace); - } - } - if (f == nullptr) { - init_factorization(f, s.m_A, s.m_basis, m_settings); - if (f->get_status() != LU_status::OK) { - delete f; - f = nullptr; - } - } - - } - - bool x_is_correct() const { - if (m_mpq_lar_core_solver.m_r_x.size() != A_r().column_count()) { - // std::cout << "the size is off " << m_r_solver.m_x.size() << ", " << A().column_count() << std::endl; - return false; - } - for (unsigned i = 0; i < A_r().row_count(); i++) { - numeric_pair delta = A_r().dot_product_with_row(i, m_mpq_lar_core_solver.m_r_x); - if (!delta.is_zero()) { - // std::cout << "x is off ("; - // std::cout << "m_b[" << i << "] = " << m_b[i] << " "; - // std::cout << "left side = " << A().dot_product_with_row(i, m_r_solver.m_x) << ' '; - // std::cout << "delta = " << delta << ' '; - // std::cout << "iters = " << total_iterations() << ")" << std::endl; - // std::cout << "row " << i << " is off" << std::endl; - return false; - } - } - return true;; - - } + void add_last_rows_to_lu(lp_primal_core_solver & s); + + bool x_is_correct() const; bool var_is_registered(var_index vj) const; unsigned constraint_stack_size() const; - void fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls) { - SASSERT(A.row_count() > 0); - SASSERT(A.column_count() > 0); - unsigned last_row = A.row_count() - 1; - SASSERT(A.m_rows[last_row].size() == 0); - for (auto & t : ls->m_coeffs) { - SASSERT(!is_zero(t.second)); - var_index j = t.first; - A.set(last_row, j, - t.second); - } - unsigned basis_j = A.column_count() - 1; - A.set(last_row, basis_j, mpq(1)); - } + void fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls); template - void create_matrix_A(static_matrix & matr) { - SASSERT(false); // not implemented - /* - unsigned m = number_or_nontrivial_left_sides(); - unsigned n = m_vec_of_canonic_left_sides.size(); - if (matr.row_count() == m && matr.column_count() == n) - return; - matr.init_empty_matrix(m, n); - copy_from_mpq_matrix(matr); - */ - } + void create_matrix_A(static_matrix & matr); template - void copy_from_mpq_matrix(static_matrix & matr) { - matr.m_rows.resize(A_r().row_count()); - matr.m_columns.resize(A_r().column_count()); - for (unsigned i = 0; i < matr.row_count(); i++) { - for (auto & it : A_r().m_rows[i]) { - matr.set(i, it.m_j, convert_struct::convert(it.get_val())); - } - } - } + void copy_from_mpq_matrix(static_matrix & matr); - bool try_to_set_fixed(column_info & ci) { - if (ci.upper_bound_is_set() && ci.low_bound_is_set() && ci.get_upper_bound() == ci.get_low_bound() && !ci.is_fixed()) { - ci.set_fixed_value(ci.get_upper_bound()); - return true; - } - return false; - } - - column_type get_column_type(const column_info & ci) { - auto ret = ci.get_column_type_no_flipping(); - if (ret == column_type::boxed) { // changing boxed to fixed because of the no span - if (ci.get_low_bound() == ci.get_upper_bound()) - ret = column_type::fixed; - } - return ret; - } - - std::string get_column_name(unsigned j) const override { - if (j >= m_terms_start_index) - return std::string("_t") + T_to_string(j); - if (j >= m_columns_to_ext_vars_or_term_indices.size()) - return std::string("_s") + T_to_string(j); - - return std::string("v") + T_to_string(m_columns_to_ext_vars_or_term_indices[j]); - } - - bool all_constrained_variables_are_registered(const vector>& left_side) { - for (auto it : left_side) { - if (! var_is_registered(it.second)) - return false; - } - return true; - } - - constraint_index add_constraint(const vector>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm) { - /* - mpq rs = right_side_parm; - vector> left_side; - substitute_terms(one_of_type(), left_side_with_terms, left_side, rs); - SASSERT(left_side.size() > 0); - SASSERT(all_constrained_variables_are_registered(left_side)); - lar_constraint original_constr(left_side, kind_par, rs); - unsigned j; // j is the index of the basic variables corresponding to the left side - canonic_left_side ls = create_or_fetch_canonic_left_side(left_side, j); - mpq ratio = find_ratio_of_original_constraint_to_normalized(ls, original_constr); - auto kind = ratio.is_neg() ? flip_kind(kind_par) : kind_par; - rs/= ratio; - lar_normalized_constraint normalized_constraint(ls, ratio, kind, rs, original_constr); - m_constraints.push_back(normalized_constraint); - constraint_index constr_ind = m_constraints.size() - 1; - update_column_type_and_bound(j, kind, rs, constr_ind); - return constr_ind; - */ - SASSERT(false); // not implemented - return 0; - } - - bool all_constraints_hold() const { - if (m_settings.get_cancel_flag()) - return true; - std::unordered_map var_map; - get_model(var_map); - - for (unsigned i = 0; i < m_constraints.size(); i++) { - if (!constraint_holds(*m_constraints[i], var_map)) { - print_constraint(i, std::cout); - return false; - } - } - return true; - } - - bool constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const { - mpq left_side_val = get_left_side_val(constr, var_map); - switch (constr.m_kind) { - case LE: return left_side_val <= constr.m_right_side; - case LT: return left_side_val < constr.m_right_side; - case GE: return left_side_val >= constr.m_right_side; - case GT: return left_side_val > constr.m_right_side; - case EQ: return left_side_val == constr.m_right_side; - default: - SASSERT(false); - } - return false; // it is unreachable - } - bool try_to_set_fixed(column_info & ci); column_type get_column_type(const column_info & ci); @@ -914,153 +381,20 @@ public: bool the_left_sides_sum_to_zero(const vector> & evidence) const; - static void register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a) { - for (auto & it : cn.get_left_side_coefficients()) { - unsigned j = it.second; - auto p = coeffs.find(j); - if (p == coeffs.end()) - coeffs[j] = it.first * a; - else { - p->second += it.first * a; - if (p->second.is_zero()) - coeffs.erase(p); - } - } - } - bool the_left_sides_sum_to_zero(const vector> & evidence) const { - std::unordered_map coeff_map; - for (auto & it : evidence) { - mpq coeff = it.first; - constraint_index con_ind = it.second; - SASSERT(con_ind < m_constraints.size()); - register_in_map(coeff_map, *m_constraints[con_ind], coeff); - } + bool the_right_sides_do_not_sum_to_zero(const vector> & evidence); - if (!coeff_map.empty()) { - std::cout << "left side = "; - vector> t; - for (auto & it : coeff_map) { - t.push_back(std::make_pair(it.second, it.first)); - } - print_linear_combination_of_column_indices(t, std::cout); - std::cout << std::endl; - return false; - } + bool explanation_is_correct(const vector>& explanation) const; - return true; - } + bool inf_explanation_is_correct() const; - bool the_right_sides_do_not_sum_to_zero(const vector> & evidence) { - mpq ret = numeric_traits::zero(); - for (auto & it : evidence) { - mpq coeff = it.first; - constraint_index con_ind = it.second; - SASSERT(con_ind < m_constraints.size()); - const lar_constraint & constr = *m_constraints[con_ind]; - ret += constr.m_right_side * coeff; - } - return !numeric_traits::is_zero(ret); - } + mpq sum_of_right_sides_of_explanation(const vector> & explanation) const; - bool explanation_is_correct(const vector>& explanation) const { -#ifdef Z3DEBUG - lconstraint_kind kind = EQ; // initialize it just to avoid a warning - SASSERT(the_relations_are_of_same_type(explanation, kind)); - SASSERT(the_left_sides_sum_to_zero(explanation)); - mpq rs = sum_of_right_sides_of_explanation(explanation); - switch (kind) { - case LE: SASSERT(rs < zero_of_type()); - break; - case LT: SASSERT(rs <= zero_of_type()); - break; - case GE: SASSERT(rs > zero_of_type()); - break; - case GT: SASSERT(rs >= zero_of_type()); - break; - case EQ: SASSERT(rs != zero_of_type()); - break; - default: - SASSERT(false); - return false; - } -#endif - return true; - } - - bool inf_explanation_is_correct() const { -#ifdef Z3DEBUG - vector> explanation; - get_infeasibility_explanation(explanation); - return explanation_is_correct(explanation); -#endif - return true; - } - - mpq sum_of_right_sides_of_explanation(const vector> & explanation) const { - mpq ret = numeric_traits::zero(); - for (auto & it : explanation) { - mpq coeff = it.first; - constraint_index con_ind = it.second; - SASSERT(con_ind < m_constraints.size()); - ret += (m_constraints[con_ind]->m_right_side - m_constraints[con_ind]->get_free_coeff_of_left_side()) * coeff; - } - return ret; - } - - bool has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) { - - if (var >= m_vars_to_ul_pairs.size()) { - // TBD: bounds on terms could also be used, caller may have to track these. - return false; - } - const ul_pair & ul = m_vars_to_ul_pairs[var]; - ci = ul.low_bound_witness(); - if (ci != static_cast(-1)) { - auto& p = m_mpq_lar_core_solver.m_r_low_bounds()[var]; - value = p.x; - is_strict = p.y.is_pos(); - return true; - } - else { - return false; - } - } - - bool has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) { - - if (var >= m_vars_to_ul_pairs.size()) { - // TBD: bounds on terms could also be used, caller may have to track these. - return false; - } - const ul_pair & ul = m_vars_to_ul_pairs[var]; - ci = ul.upper_bound_witness(); - if (ci != static_cast(-1)) { - auto& p = m_mpq_lar_core_solver.m_r_upper_bounds()[var]; - value = p.x; - is_strict = p.y.is_neg(); - return true; - } - else { - return false; - } - } + bool has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict); + + bool has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict); - void get_infeasibility_explanation(vector> & explanation) const { - explanation.clear(); - if (m_infeasible_column_index != -1) { - fill_explanation_from_infeasible_column(explanation); - return; - } - if (m_mpq_lar_core_solver.get_infeasible_sum_sign() == 0) { - return; - } - // the infeasibility sign - int inf_sign; - auto inf_row = m_mpq_lar_core_solver.get_infeasibility_info(inf_sign); - get_infeasibility_explanation_for_inf_sign(explanation, inf_row, inf_sign); - SASSERT(explanation_is_correct(explanation)); - } + void get_infeasibility_explanation(vector> & explanation) const; void get_infeasibility_explanation_for_inf_sign( vector> & explanation, @@ -1068,48 +402,12 @@ public: int inf_sign) const; - int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign; - const ul_pair & ul = m_vars_to_ul_pairs[j]; - - constraint_index bound_constr_i = adj_sign < 0 ? ul.upper_bound_witness() : ul.low_bound_witness(); - SASSERT(bound_constr_i < m_constraints.size()); - explanation.push_back(std::make_pair(coeff, bound_constr_i)); - } - } void get_model(std::unordered_map & variable_values) const; void get_model_do_not_care_about_diff_vars(std::unordered_map & variable_values) const; - void get_model(std::unordered_map & variable_values) const { - mpq delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(mpq(1, 2)); // start from 0.5 to have less clashes - SASSERT(m_status == OPTIMAL); - unsigned i; - do { - - // different pairs have to produce different singleton values - std::unordered_set set_of_different_pairs; - std::unordered_set set_of_different_singles; - for (i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) { - const numeric_pair & rp = m_mpq_lar_core_solver.m_r_x[i]; - set_of_different_pairs.insert(rp); - mpq x = rp.x + delta * rp.y; - set_of_different_singles.insert(x); - if (set_of_different_pairs.size() - != set_of_different_singles.size()) { - delta /= mpq(2); - break; - } - - variable_values[i] = x; - } - } while (i != m_mpq_lar_core_solver.m_r_x.size()); - } - - - std::string get_variable_name(var_index vi) const { - return get_column_name(vi); - } + std::string get_variable_name(var_index vi) const; // ********** print region start void print_constraint(constraint_index ci, std::ostream & out) const; @@ -1120,35 +418,15 @@ public: void print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const; - mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const { - mpq ret = cns.get_free_coeff_of_left_side(); - for (auto & it : cns.get_left_side_coefficients()) { - var_index j = it.second; - auto vi = var_map.find(j); - SASSERT(vi != var_map.end()); - ret += it.first * vi->second; - } - return ret; - } + void print_term(lar_term const& term, std::ostream & out) const; void print_term_as_indices(lar_term const& term, std::ostream & out) const; mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const; - void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list) { - for (unsigned i = 0; i < sz; i++) { - var_index var = vars[i]; - if (var >= m_terms_start_index) { // handle the term - for (auto & it : m_terms[var - m_terms_start_index]->m_coeffs) { - column_list.push_back(it.first); - } - } else { - column_list.push_back(var); - } - } - } + void print_constraint(const lar_base_constraint * c, std::ostream & out) const; - void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list); + void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list); void random_update(unsigned sz, var_index const * vars); void pivot_fixed_vars_from_basis(); @@ -1164,6 +442,8 @@ public: void clean_inf_set_of_r_solver_after_pop(); void shrink_explanation_to_minimum(vector> & explanation) const; + + bool column_value_is_integer(unsigned j) const { return get_column_value(j).is_int(); } @@ -1174,148 +454,31 @@ public: bool model_is_int_feasible() const; - - void remove_last_row_and_column_from_tableau(unsigned j) { - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); - auto & slv = m_mpq_lar_core_solver.m_r_solver; - unsigned i = A_r().row_count() - 1; //last row index - make_sure_that_the_bottom_right_elem_not_zero_in_tableau(i, j); - if (slv.m_basis_heading[j] < 0) { - slv.pivot_column_tableau(j, i); - } - - auto & last_row = A_r().m_rows[i]; - mpq &cost_j = m_mpq_lar_core_solver.m_r_solver.m_costs[j]; - bool cost_is_nz = !is_zero(cost_j); - for (unsigned k = last_row.size(); k-- > 0;) { - auto &rc = last_row[k]; - if (cost_is_nz) { - m_mpq_lar_core_solver.m_r_solver.m_d[rc.m_j] += cost_j*rc.get_val(); - } - - A_r().remove_element(last_row, rc); - } - SASSERT(last_row.size() == 0); - SASSERT(A_r().m_columns[j].size() == 0); - A_r().m_rows.pop_back(); - A_r().m_columns.pop_back(); - slv.m_b.pop_back(); + const impq & column_lower_bound(unsigned j) const { + return m_mpq_lar_core_solver.lower_bound(j); } - void remove_last_column_from_tableau(unsigned j) { - SASSERT(j == A_r().column_count() - 1); - // the last column has to be empty - SASSERT(A_r().m_columns[j].size() == 0); - A_r().m_columns.pop_back(); + const impq & column_upper_bound(unsigned j) const { + return m_mpq_lar_core_solver.upper_bound(j); } - void remove_last_column_from_basis_tableau(unsigned j) { - auto& rslv = m_mpq_lar_core_solver.m_r_solver; - int i = rslv.m_basis_heading[j]; - if (i >= 0) { // j is a basic var - int last_pos = static_cast(rslv.m_basis.size()) - 1; - SASSERT(last_pos >= 0); - if (i != last_pos) { - unsigned j_at_last_pos = rslv.m_basis[last_pos]; - rslv.m_basis[i] = j_at_last_pos; - rslv.m_basis_heading[j_at_last_pos] = i; - } - rslv.m_basis.pop_back(); // remove j from the basis - } else { - int last_pos = static_cast(rslv.m_nbasis.size()) - 1; - SASSERT(last_pos >= 0); - i = - 1 - i; - if (i != last_pos) { - unsigned j_at_last_pos = rslv.m_nbasis[last_pos]; - rslv.m_nbasis[i] = j_at_last_pos; - rslv.m_basis_heading[j_at_last_pos] = - i - 1; - } - rslv.m_nbasis.pop_back(); // remove j from the basis - } - rslv.m_basis_heading.pop_back(); - SASSERT(rslv.m_basis.size() == A_r().row_count()); - SASSERT(rslv.basis_heading_is_correct()); - } - - void remove_column_from_tableau(unsigned j) { - auto& rslv = m_mpq_lar_core_solver.m_r_solver; - SASSERT(j == A_r().column_count() - 1); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); - if (column_represents_row_in_tableau(j)) { - remove_last_row_and_column_from_tableau(j); - if (rslv.m_basis_heading[j] < 0) - rslv.change_basis_unconditionally(j, rslv.m_basis[A_r().row_count()]); // A_r().row_count() is the index of the last row in the basis still - } - else { - remove_last_column_from_tableau(j); - } - rslv.m_x.pop_back(); - rslv.m_d.pop_back(); - rslv.m_costs.pop_back(); - - remove_last_column_from_basis_tableau(j); - SASSERT(m_mpq_lar_core_solver.r_basis_is_OK()); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); + bool column_is_bounded(unsigned j) const { + return m_mpq_lar_core_solver.column_is_bounded(j); } void get_bound_constraint_witnesses_for_column(unsigned j, constraint_index & lc, constraint_index & uc) const { const ul_pair & ul = m_columns_to_ul_pairs[j]; - lc = ul.low_bound_witness(); + lc = ul.lower_bound_witness(); uc = ul.upper_bound_witness(); } - - - - - void clean_inf_set_of_r_solver_after_pop() { - vector became_feas; - clean_large_elements_after_pop(A_r().column_count(), m_mpq_lar_core_solver.m_r_solver.m_inf_set); - std::unordered_set basic_columns_with_changed_cost; - auto inf_index_copy = m_mpq_lar_core_solver.m_r_solver.m_inf_set.m_index; - for (auto j: inf_index_copy) { - if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { - continue; - } - // some basic columns might become non-basic - these columns need to be made feasible - numeric_pair delta; - if (m_mpq_lar_core_solver.m_r_solver.make_column_feasible(j, delta)) - change_basic_x_by_delta_on_column(j, delta); - became_feas.push_back(j); - } - - for (unsigned j : became_feas) { - SASSERT(m_mpq_lar_core_solver.m_r_solver.m_basis_heading[j] < 0); - m_mpq_lar_core_solver.m_r_solver.m_d[j] -= m_mpq_lar_core_solver.m_r_solver.m_costs[j]; - m_mpq_lar_core_solver.m_r_solver.m_costs[j] = zero_of_type(); - m_mpq_lar_core_solver.m_r_solver.m_inf_set.erase(j); - } - became_feas.clear(); - for (unsigned j : m_mpq_lar_core_solver.m_r_solver.m_inf_set.m_index) { - SASSERT(m_mpq_lar_core_solver.m_r_heading[j] >= 0); - if (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(j)) - became_feas.push_back(j); - } - for (unsigned j : became_feas) - m_mpq_lar_core_solver.m_r_solver.m_inf_set.erase(j); - - - if (use_tableau_costs()) { - for (unsigned j : became_feas) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - for (unsigned j : basic_columns_with_changed_cost) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - SASSERT(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } - } - - - void shrink_explanation_to_minimum(vector> & explanation) const { - // implementing quickXplain - quick_xplain::run(explanation, *this); - SASSERT(this->explanation_is_correct(explanation)); + indexed_vector & get_column_in_lu_mode(unsigned j) { + m_column_buffer.clear(); + m_column_buffer.resize(A_r().row_count()); + m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); + return m_column_buffer; } - bool bound_is_integer_if_needed(unsigned j, const mpq & right_side) const; + bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const; linear_combination_iterator * get_iterator_on_row(unsigned i) { return m_mpq_lar_core_solver.m_r_solver.get_iterator_on_row(i); } @@ -1328,8 +491,8 @@ public: return m_columns_to_ul_pairs()[j].upper_bound_witness(); } - constraint_index get_column_low_bound_witness(unsigned j) const { - return m_columns_to_ul_pairs()[j].low_bound_witness(); + constraint_index get_column_lower_bound_witness(unsigned j) const { + return m_columns_to_ul_pairs()[j].lower_bound_witness(); } void subs_terms_for_debugging(lar_term& t) { @@ -1380,6 +543,8 @@ public: } lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; } - + bool column_corresponds_to_term(unsigned) const; + void catch_up_in_updating_int_solver(); + var_index to_var_index(unsigned ext_j) const; }; } diff --git a/src/util/lp/linear_combination_iterator.h b/src/util/lp/linear_combination_iterator.h index 739cefdc0..3ba30bdd8 100644 --- a/src/util/lp/linear_combination_iterator.h +++ b/src/util/lp/linear_combination_iterator.h @@ -34,7 +34,7 @@ struct linear_combination_iterator_on_vector : linear_combination_iterator { vector> & m_vector; int m_offset; bool next(T & a, unsigned & i) { - if(m_offset >= m_vector.size()) + if(static_cast(m_offset) >= m_vector.size()) return false; auto & p = m_vector[m_offset]; a = p.first; @@ -44,7 +44,7 @@ struct linear_combination_iterator_on_vector : linear_combination_iterator { } bool next(unsigned & i) { - if(m_offset >= m_vector.size()) + if(static_cast(m_offset) >= m_vector.size()) return false; auto & p = m_vector[m_offset]; i = p.second; diff --git a/src/util/lp/linear_combination_iterator_on_std_vector.h b/src/util/lp/linear_combination_iterator_on_std_vector.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/util/lp/lp_bound_propagator.h b/src/util/lp/lp_bound_propagator.h index 357d9c381..f3deaac5e 100644 --- a/src/util/lp/lp_bound_propagator.h +++ b/src/util/lp/lp_bound_propagator.h @@ -21,8 +21,8 @@ Revision History: #include "util/lp/lp_settings.h" namespace lp { class lar_solver; -class lp_bound_propagator { - std::unordered_map m_improved_low_bounds; // these maps map a column index to the corresponding index in ibounds +class bound_propagator { + std::unordered_map m_improved_lower_bounds; // these maps map a column index to the corresponding index in ibounds std::unordered_map m_improved_upper_bounds; lar_solver & m_lar_solver; public: @@ -30,7 +30,7 @@ public: public: lp_bound_propagator(lar_solver & ls); column_type get_column_type(unsigned) const; - const impq & get_low_bound(unsigned) const; + const impq & get_lower_bound(unsigned) const; const impq & get_upper_bound(unsigned) const; void try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict); virtual bool bound_is_interesting(unsigned vi, diff --git a/src/util/lp/lp_core_solver_base_instances.cpp b/src/util/lp/lp_core_solver_base.cpp similarity index 99% rename from src/util/lp/lp_core_solver_base_instances.cpp rename to src/util/lp/lp_core_solver_base.cpp index a72fa2b0e..613404e68 100644 --- a/src/util/lp/lp_core_solver_base_instances.cpp +++ b/src/util/lp/lp_core_solver_base.cpp @@ -22,7 +22,7 @@ Revision History: #include #include "util/vector.h" #include -#include "util/lp/lp_core_solver_base.hpp" +#include "util/lp/lp_core_solver_base_def.h" template bool lp::lp_core_solver_base::A_mult_x_is_off() const; template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const; diff --git a/src/util/lp/lp_core_solver_base.h b/src/util/lp/lp_core_solver_base.h index 1ed6e9a3e..72e15e362 100644 --- a/src/util/lp/lp_core_solver_base.h +++ b/src/util/lp/lp_core_solver_base.h @@ -77,7 +77,7 @@ public: vector m_d; // the vector of reduced costs indexed_vector m_ed; // the solution of B*m_ed = a const vector & m_column_types; - const vector & m_low_bounds; + const vector & m_lower_bounds; const vector & m_upper_bounds; vector m_column_norms; // the approximate squares of column norms that help choosing a profitable column vector m_copy_of_xB; @@ -127,7 +127,7 @@ public: lp_settings & settings, const column_namer& column_names, const vector & column_types, - const vector & low_bound_values, + const vector & lower_bound_values, const vector & upper_bound_values); void allocate_basis_heading(); @@ -272,14 +272,14 @@ public: } bool x_below_low_bound(unsigned p) const { - return below_bound(m_x[p], m_low_bounds[p]); + return below_bound(m_x[p], m_lower_bounds[p]); } bool infeasibility_costs_are_correct() const; bool infeasibility_cost_is_correct_for_column(unsigned j) const; - bool x_above_low_bound(unsigned p) const { - return above_bound(m_x[p], m_low_bounds[p]); + bool x_above_lower_bound(unsigned p) const { + return above_bound(m_x[p], m_lower_bounds[p]); } bool x_below_upper_bound(unsigned p) const { @@ -290,15 +290,15 @@ public: bool x_above_upper_bound(unsigned p) const { return above_bound(m_x[p], m_upper_bounds[p]); } - bool x_is_at_low_bound(unsigned j) const { - return at_bound(m_x[j], m_low_bounds[j]); + bool x_is_at_lower_bound(unsigned j) const { + return at_bound(m_x[j], m_lower_bounds[j]); } bool x_is_at_upper_bound(unsigned j) const { return at_bound(m_x[j], m_upper_bounds[j]); } bool x_is_at_bound(unsigned j) const { - return x_is_at_low_bound(j) || x_is_at_upper_bound(j); + return x_is_at_lower_bound(j) || x_is_at_upper_bound(j); } bool column_is_feasible(unsigned j) const; @@ -337,8 +337,8 @@ public: void fill_reduced_costs_from_m_y_by_rows(); void copy_rs_to_xB(vector & rs); - virtual bool low_bounds_are_set() const { return false; } - X low_bound_value(unsigned j) const { return m_low_bounds[j]; } + virtual bool lower_bounds_are_set() const { return false; } + X lower_bound_value(unsigned j) const { return m_lower_bounds[j]; } X upper_bound_value(unsigned j) const { return m_upper_bounds[j]; } column_type get_column_type(unsigned j) const {return m_column_types[j]; } @@ -348,7 +348,7 @@ public: } X bound_span(unsigned j) const { - return m_upper_bounds[j] - m_low_bounds[j]; + return m_upper_bounds[j] - m_lower_bounds[j]; } std::string column_name(unsigned column) const; @@ -376,21 +376,21 @@ public: case column_type::fixed: if (x_is_at_bound(j)) break; - m_x[j] = m_low_bounds[j]; + m_x[j] = m_lower_bounds[j]; return true; case column_type::boxed: if (x_is_at_bound(j)) break; // we should preserve x if possible // snap randomly if (m_settings.random_next() % 2 == 1) - m_x[j] = m_low_bounds[j]; + m_x[j] = m_lower_bounds[j]; else m_x[j] = m_upper_bounds[j]; return true; - case column_type::low_bound: - if (x_is_at_low_bound(j)) + case column_type::lower_bound: + if (x_is_at_lower_bound(j)) break; - m_x[j] = m_low_bounds[j]; + m_x[j] = m_lower_bounds[j]; return true; case column_type::upper_bound: if (x_is_at_upper_bound(j)) @@ -409,15 +409,15 @@ public: auto & x = m_x[j]; switch (m_column_types[j]) { case column_type::fixed: - lp_assert(m_low_bounds[j] == m_upper_bounds[j]); - if (x != m_low_bounds[j]) { - delta = m_low_bounds[j] - x; + lp_assert(m_lower_bounds[j] == m_upper_bounds[j]); + if (x != m_lower_bounds[j]) { + delta = m_lower_bounds[j] - x; ret = true;; } break; case column_type::boxed: - if (x < m_low_bounds[j]) { - delta = m_low_bounds[j] - x; + if (x < m_lower_bounds[j]) { + delta = m_lower_bounds[j] - x; ret = true;; } if (x > m_upper_bounds[j]) { @@ -425,9 +425,9 @@ public: ret = true; } break; - case column_type::low_bound: - if (x < m_low_bounds[j]) { - delta = m_low_bounds[j] - x; + case column_type::lower_bound: + if (x < m_lower_bounds[j]) { + delta = m_lower_bounds[j] - x; ret = true; } break; @@ -540,8 +540,8 @@ public: if (!this->x_is_at_bound(j)) return false; break; - case column_type::low_bound: - if (!this->x_is_at_low_bound(j)) + case column_type::lower_bound: + if (!this->x_is_at_lower_bound(j)) return false; break; case column_type::upper_bound: @@ -570,10 +570,10 @@ public: switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: - out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl; + out << "(" << m_lower_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl; break; - case column_type::low_bound: - out << m_low_bounds[j] << std::endl; + case column_type::lower_bound: + out << m_lower_bounds[j] << std::endl; break; case column_type::upper_bound: out << m_upper_bounds[j] << std::endl; @@ -588,10 +588,10 @@ public: switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: - out << " [" << m_low_bounds[j] << ", " << m_upper_bounds[j] << "]"; + out << " [" << m_lower_bounds[j] << ", " << m_upper_bounds[j] << "]"; break; - case column_type::low_bound: - out << " [" << m_low_bounds[j] << "," << "oo" << "]"; + case column_type::lower_bound: + out << " [" << m_lower_bounds[j] << "," << "oo" << "]"; break; case column_type::upper_bound: out << " [-oo, " << m_upper_bounds[j] << ']'; @@ -619,7 +619,7 @@ public: bool column_has_upper_bound(unsigned j) { switch(m_column_types[j]) { case column_type::free_column: - case column_type::low_bound: + case column_type::lower_bound: return false; default: return true; @@ -629,13 +629,13 @@ public: bool bounds_for_boxed_are_set_correctly() const { for (unsigned j = 0; j < m_column_types.size(); j++) { if (m_column_types[j] != column_type::boxed) continue; - if (m_low_bounds[j] > m_upper_bounds[j]) + if (m_lower_bounds[j] > m_upper_bounds[j]) return false; } return true; } - bool column_has_low_bound(unsigned j) { + bool column_has_lower_bound(unsigned j) { switch(m_column_types[j]) { case column_type::free_column: case column_type::upper_bound: diff --git a/src/util/lp/lp_core_solver_base.hpp b/src/util/lp/lp_core_solver_base_def.h similarity index 97% rename from src/util/lp/lp_core_solver_base.hpp rename to src/util/lp/lp_core_solver_base_def.h index d54b7d363..44a0a0c74 100644 --- a/src/util/lp/lp_core_solver_base.hpp +++ b/src/util/lp/lp_core_solver_base_def.h @@ -35,7 +35,7 @@ lp_core_solver_base(static_matrix & A, lp_settings & settings, const column_namer& column_names, const vector & column_types, - const vector & low_bound_values, + const vector & lower_bound_values, const vector & upper_bound_values): m_total_iterations(0), m_iters_with_no_cost_growing(0), @@ -59,7 +59,7 @@ lp_core_solver_base(static_matrix & A, m_d(m_n()), m_ed(m_m()), m_column_types(column_types), - m_low_bounds(low_bound_values), + m_lower_bounds(lower_bound_values), m_upper_bounds(upper_bound_values), m_column_norms(m_n()), m_copy_of_xB(m_m()), @@ -392,10 +392,10 @@ set_non_basic_x_to_correct_bounds() { for (unsigned j : non_basis()) { switch (m_column_types[j]) { case column_type::boxed: - m_x[j] = m_d[j] < 0? m_upper_bounds[j]: m_low_bounds[j]; + m_x[j] = m_d[j] < 0? m_upper_bounds[j]: m_lower_bounds[j]; break; - case column_type::low_bound: - m_x[j] = m_low_bounds[j]; + case column_type::lower_bound: + m_x[j] = m_lower_bounds[j]; lp_assert(column_is_dual_feasible(j)); break; case column_type::upper_bound: @@ -412,12 +412,12 @@ column_is_dual_feasible(unsigned j) const { switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: - return (x_is_at_low_bound(j) && d_is_not_negative(j)) || + return (x_is_at_lower_bound(j) && d_is_not_negative(j)) || (x_is_at_upper_bound(j) && d_is_not_positive(j)); - case column_type::low_bound: - return x_is_at_low_bound(j) && d_is_not_negative(j); + case column_type::lower_bound: + return x_is_at_lower_bound(j) && d_is_not_negative(j); case column_type::upper_bound: - LP_OUT(m_settings, "upper_bound type should be switched to low_bound" << std::endl); + LP_OUT(m_settings, "upper_bound type should be switched to lower_bound" << std::endl); lp_assert(false); // impossible case case column_type::free_column: return numeric_traits::is_zero(m_d[j]); @@ -485,14 +485,14 @@ template bool lp_core_solver_base::column_is_feas case column_type::boxed: if (this->above_bound(x, this->m_upper_bounds[j])) { return false; - } else if (this->below_bound(x, this->m_low_bounds[j])) { + } else if (this->below_bound(x, this->m_lower_bounds[j])) { return false; } else { return true; } break; - case column_type::low_bound: - if (this->below_bound(x, this->m_low_bounds[j])) { + case column_type::lower_bound: + if (this->below_bound(x, this->m_lower_bounds[j])) { return false; } else { return true; @@ -861,8 +861,8 @@ snap_non_basic_x_to_bound_and_free_to_zeroes() { switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: - m_x[j] = m_low_bounds[j]; + case column_type::lower_bound: + m_x[j] = m_lower_bounds[j]; break; case column_type::upper_bound: m_x[j] = m_upper_bounds[j]; @@ -895,23 +895,23 @@ template non_basic_column_value_position lp_core_solver get_non_basic_column_value_position(unsigned j) const { switch (m_column_types[j]) { case column_type::fixed: - return x_is_at_low_bound(j)? at_fixed : not_at_bound; + return x_is_at_lower_bound(j)? at_fixed : not_at_bound; case column_type::free_column: return free_of_bounds; case column_type::boxed: - return x_is_at_low_bound(j)? at_low_bound :( + return x_is_at_lower_bound(j)? at_lower_bound :( x_is_at_upper_bound(j)? at_upper_bound: not_at_bound ); - case column_type::low_bound: - return x_is_at_low_bound(j)? at_low_bound : not_at_bound; + case column_type::lower_bound: + return x_is_at_lower_bound(j)? at_lower_bound : not_at_bound; case column_type::upper_bound: return x_is_at_upper_bound(j)? at_upper_bound : not_at_bound; default: lp_unreachable(); } lp_unreachable(); - return at_low_bound; + return at_lower_bound; } template void lp_core_solver_base::init_lu() { @@ -1026,7 +1026,7 @@ lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) } return is_zero(this->m_costs[j]); - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(j)) { return this->m_costs[j] == -r; } diff --git a/src/util/lp/lp_dual_core_solver_instances.cpp b/src/util/lp/lp_dual_core_solver.cpp similarity index 97% rename from src/util/lp/lp_dual_core_solver_instances.cpp rename to src/util/lp/lp_dual_core_solver.cpp index db68bda65..bd8f605a7 100644 --- a/src/util/lp/lp_dual_core_solver_instances.cpp +++ b/src/util/lp/lp_dual_core_solver.cpp @@ -22,7 +22,7 @@ Revision History: #include #include "util/vector.h" #include -#include "util/lp/lp_dual_core_solver.hpp" +#include "util/lp/lp_dual_core_solver_def.h" template void lp::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); template void lp::lp_dual_core_solver::solve(); template lp::lp_dual_core_solver::lp_dual_core_solver(lp::static_matrix&, vector&, diff --git a/src/util/lp/lp_dual_core_solver.h b/src/util/lp/lp_dual_core_solver.h index 7aa572171..c620aa096 100644 --- a/src/util/lp/lp_dual_core_solver.h +++ b/src/util/lp/lp_dual_core_solver.h @@ -56,7 +56,7 @@ public: vector & heading, vector & costs, vector & column_type_array, - vector & low_bound_values, + vector & lower_bound_values, vector & upper_bound_values, lp_settings & settings, const column_namer & column_names): @@ -70,7 +70,7 @@ public: settings, column_names, column_type_array, - low_bound_values, + lower_bound_values, upper_bound_values), m_can_enter_basis(can_enter_basis), m_a_wave(this->m_m()), @@ -110,7 +110,7 @@ public: bool done(); - T get_edge_steepness_for_low_bound(unsigned p); + T get_edge_steepness_for_lower_bound(unsigned p); T get_edge_steepness_for_upper_bound(unsigned p); @@ -174,7 +174,7 @@ public: // it is positive if going from low bound to upper bound and negative if going from upper bound to low bound T signed_span_of_boxed(unsigned j) { - return this->x_is_at_low_bound(j)? this->bound_span(j): - this->bound_span(j); + return this->x_is_at_lower_bound(j)? this->bound_span(j): - this->bound_span(j); } void add_tight_breakpoints_and_q_to_flipped_set(); @@ -207,6 +207,6 @@ public: void solve(); - bool low_bounds_are_set() const override { return true; } + bool lower_bounds_are_set() const override { return true; } }; } diff --git a/src/util/lp/lp_dual_core_solver.hpp b/src/util/lp/lp_dual_core_solver_def.h similarity index 93% rename from src/util/lp/lp_dual_core_solver.hpp rename to src/util/lp/lp_dual_core_solver_def.h index 765b80db8..0b47e7178 100644 --- a/src/util/lp/lp_dual_core_solver.hpp +++ b/src/util/lp/lp_dual_core_solver_def.h @@ -107,9 +107,9 @@ template bool lp_dual_core_solver::done() { return false; // todo, need to be more cases } -template T lp_dual_core_solver::get_edge_steepness_for_low_bound(unsigned p) { +template T lp_dual_core_solver::get_edge_steepness_for_lower_bound(unsigned p) { lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); - T del = this->m_x[p] - this->m_low_bounds[p]; + T del = this->m_x[p] - this->m_lower_bounds[p]; del *= del; return del / this->m_betas[this->m_basis_heading[p]]; } @@ -127,7 +127,7 @@ template T lp_dual_core_solver::pricing_for_row(u case column_type::fixed: case column_type::boxed: if (this->x_below_low_bound(p)) { - T del = get_edge_steepness_for_low_bound(p); + T del = get_edge_steepness_for_lower_bound(p); return del; } if (this->x_above_upper_bound(p)) { @@ -135,9 +135,9 @@ template T lp_dual_core_solver::pricing_for_row(u return del; } return numeric_traits::zero(); - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(p)) { - T del = get_edge_steepness_for_low_bound(p); + T del = get_edge_steepness_for_lower_bound(p); return del; } return numeric_traits::zero(); @@ -244,7 +244,7 @@ template int lp_dual_core_solver::define_sign_of_ return 1; } lp_unreachable(); - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(m_p)) { return -1; } @@ -264,17 +264,17 @@ template int lp_dual_core_solver::define_sign_of_ template bool lp_dual_core_solver::can_be_breakpoint(unsigned j) { if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false; switch (this->m_column_types[j]) { - case column_type::low_bound: - lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bounds[j])); + case column_type::lower_bound: + lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_lower_bounds[j])); return m_sign_of_alpha_r * this->m_pivot_row[j] > 0; case column_type::upper_bound: lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j])); return m_sign_of_alpha_r * this->m_pivot_row[j] < 0; case column_type::boxed: { - bool low_bound = this->x_is_at_low_bound(j); + bool lower_bound = this->x_is_at_lower_bound(j); bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0; - return low_bound == grawing; + return lower_bound == grawing; } case column_type::fixed: // is always dual feasible so we ingore it return false; @@ -302,15 +302,15 @@ template T lp_dual_core_solver::get_delta() { switch (this->m_column_types[m_p]) { case column_type::boxed: if (this->x_below_low_bound(m_p)) { - return this->m_x[m_p] - this->m_low_bounds[m_p]; + return this->m_x[m_p] - this->m_lower_bounds[m_p]; } if (this->x_above_upper_bound(m_p)) { return this->m_x[m_p] - this->m_upper_bounds[m_p]; } lp_unreachable(); - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(m_p)) { - return this->m_x[m_p] - this->m_low_bounds[m_p]; + return this->m_x[m_p] - this->m_lower_bounds[m_p]; } lp_unreachable(); case column_type::upper_bound: @@ -371,10 +371,10 @@ template void lp_dual_core_solver::update_betas() template void lp_dual_core_solver::apply_flips() { for (unsigned j : m_flipped_boxed) { lp_assert(this->x_is_at_bound(j)); - if (this->x_is_at_low_bound(j)) { + if (this->x_is_at_lower_bound(j)) { this->m_x[j] = this->m_upper_bounds[j]; } else { - this->m_x[j] = this->m_low_bounds[j]; + this->m_x[j] = this->m_lower_bounds[j]; } } } @@ -382,17 +382,17 @@ template void lp_dual_core_solver::apply_flips() template void lp_dual_core_solver::snap_xN_column_to_bounds(unsigned j) { switch (this->m_column_type[j]) { case column_type::fixed: - this->m_x[j] = this->m_low_bounds[j]; + this->m_x[j] = this->m_lower_bounds[j]; break; case column_type::boxed: - if (this->x_is_at_low_bound(j)) { - this->m_x[j] = this->m_low_bounds[j]; + if (this->x_is_at_lower_bound(j)) { + this->m_x[j] = this->m_lower_bounds[j]; } else { this->m_x[j] = this->m_upper_bounds[j]; } break; - case column_type::low_bound: - this->m_x[j] = this->m_low_bounds[j]; + case column_type::lower_bound: + this->m_x[j] = this->m_lower_bounds[j]; break; case column_type::upper_bound: this->m_x[j] = this->m_upper_bounds[j]; @@ -462,9 +462,9 @@ template bool lp_dual_core_solver::basis_change_a template void lp_dual_core_solver::recover_leaving() { switch (m_entering_boundary_position) { - case at_low_bound: + case at_lower_bound: case at_fixed: - this->m_x[m_q] = this->m_low_bounds[m_q]; + this->m_x[m_q] = this->m_lower_bounds[m_q]; break; case at_upper_bound: this->m_x[m_q] = this->m_upper_bounds[m_q]; @@ -497,23 +497,23 @@ template void lp_dual_core_solver::revert_to_prev template bool lp_dual_core_solver::snap_runaway_nonbasic_column(unsigned j) { switch (this->m_column_types[j]) { case column_type::fixed: - case column_type::low_bound: - if (!this->x_is_at_low_bound(j)) { - this->m_x[j] = this->m_low_bounds[j]; + case column_type::lower_bound: + if (!this->x_is_at_lower_bound(j)) { + this->m_x[j] = this->m_lower_bounds[j]; return true; } break; case column_type::boxed: { - bool closer_to_low_bound = abs(this->m_low_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]); - if (closer_to_low_bound) { - if (!this->x_is_at_low_bound(j)) { - this->m_x[j] = this->m_low_bounds[j]; + bool closer_to_lower_bound = abs(this->m_lower_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]); + if (closer_to_lower_bound) { + if (!this->x_is_at_lower_bound(j)) { + this->m_x[j] = this->m_lower_bounds[j]; return true; } } else { if (!this->x_is_at_upper_bound(j)) { - this->m_x[j] = this->m_low_bounds[j]; + this->m_x[j] = this->m_lower_bounds[j]; return true; } } @@ -539,7 +539,7 @@ template bool lp_dual_core_solver::problem_is_dua // std::cout << "m_d[" << j << "] = " << this->m_d[j] << std::endl; // std::cout << "x[" << j << "] = " << this->m_x[j] << std::endl; // std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl; - // std::cout << "bounds = " << this->m_low_bounds[j] << "," << this->m_upper_bounds[j] << std::endl; + // std::cout << "bounds = " << this->m_lower_bounds[j] << "," << this->m_upper_bounds[j] << std::endl; // std::cout << "total_iterations = " << this->total_iterations() << std::endl; return false; } @@ -602,7 +602,7 @@ template T lp_dual_core_solver::calculate_harris_ lp_assert(m_breakpoint_set.size() > 0); for (auto j : m_breakpoint_set) { T t; - if (this->x_is_at_low_bound(j)) { + if (this->x_is_at_lower_bound(j)) { t = abs((std::max(this->m_d[j], numeric_traits::zero()) + m_harris_tolerance) / this->m_pivot_row[j]); } else { t = abs((std::min(this->m_d[j], numeric_traits::zero()) - m_harris_tolerance) / this->m_pivot_row[j]); @@ -620,7 +620,7 @@ template T lp_dual_core_solver::calculate_harris_ template void lp_dual_core_solver::fill_tight_set_on_harris_delta(const T & harris_delta ){ m_tight_set.clear(); for (auto j : m_breakpoint_set) { - if (this->x_is_at_low_bound(j)) { + if (this->x_is_at_lower_bound(j)) { if (abs(std::max(this->m_d[j], numeric_traits::zero()) / this->m_pivot_row[j]) <= harris_delta){ m_tight_set.insert(j); } diff --git a/src/util/lp/lp_dual_simplex_instances.cpp b/src/util/lp/lp_dual_simplex.cpp similarity index 92% rename from src/util/lp/lp_dual_simplex_instances.cpp rename to src/util/lp/lp_dual_simplex.cpp index 9e45849de..ff71b7b4f 100644 --- a/src/util/lp/lp_dual_simplex_instances.cpp +++ b/src/util/lp/lp_dual_simplex.cpp @@ -17,7 +17,7 @@ Revision History: --*/ -#include "util/lp/lp_dual_simplex.hpp" +#include "util/lp/lp_dual_simplex_def.h" template lp::mpq lp::lp_dual_simplex::get_current_cost() const; template void lp::lp_dual_simplex::find_maximal_solution(); template double lp::lp_dual_simplex::get_current_cost() const; diff --git a/src/util/lp/lp_dual_simplex.h b/src/util/lp/lp_dual_simplex.h index 15463fc34..59b1bc24c 100644 --- a/src/util/lp/lp_dual_simplex.h +++ b/src/util/lp/lp_dual_simplex.h @@ -28,7 +28,7 @@ template class lp_dual_simplex: public lp_solver { lp_dual_core_solver * m_core_solver; vector m_b_copy; - vector m_low_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver + vector m_lower_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver vector m_column_types_of_core_solver; vector m_column_types_of_logicals; vector m_can_enter_basis; diff --git a/src/util/lp/lp_dual_simplex.hpp b/src/util/lp/lp_dual_simplex_def.h similarity index 92% rename from src/util/lp/lp_dual_simplex.hpp rename to src/util/lp/lp_dual_simplex_def.h index 96fb65399..e32a5e97f 100644 --- a/src/util/lp/lp_dual_simplex.hpp +++ b/src/util/lp/lp_dual_simplex_def.h @@ -48,13 +48,13 @@ template void lp_dual_simplex::decide_on_status_a template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { lp_assert(j >= this->number_of_core_structurals()); switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { - case column_type::low_bound: - m_low_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::low_bound; + case column_type::lower_bound: + m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::lower_bound; m_can_enter_basis[j] = true; break; case column_type::fixed: - this->m_upper_bounds[j] = m_low_bounds[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits::zero(); m_column_types_of_core_solver[j] = column_type::fixed; m_can_enter_basis[j] = false; break; @@ -66,9 +66,9 @@ template void lp_dual_simplex::fix_logical_for_st template void lp_dual_simplex::fix_structural_for_stage2(unsigned j) { column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; switch (ci->get_column_type()) { - case column_type::low_bound: - m_low_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::low_bound; + case column_type::lower_bound: + m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::lower_bound; m_can_enter_basis[j] = true; break; case column_type::fixed: @@ -76,7 +76,7 @@ template void lp_dual_simplex::fix_structural_for lp_unreachable(); case column_type::boxed: this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - m_low_bounds[j] = numeric_traits::zero(); + m_lower_bounds[j] = numeric_traits::zero(); m_column_types_of_core_solver[j] = column_type::boxed; m_can_enter_basis[j] = true; break; @@ -158,7 +158,7 @@ template void lp_dual_simplex::stage1() { this->m_heading, this->m_costs, this->m_column_types_of_core_solver, - this->m_low_bounds, + this->m_lower_bounds, this->m_upper_bounds, this->m_settings, *this); @@ -216,10 +216,10 @@ template void lp_dual_simplex::fill_costs_bounds_ throw_exception(s.str()); break; } - case column_type::low_bound: { + case column_type::lower_bound: { m_can_enter_basis[j] = true; this->set_scaled_cost(j); - this->m_low_bounds[j] = numeric_traits::zero(); + this->m_lower_bounds[j] = numeric_traits::zero(); this->m_upper_bounds[j] =numeric_traits::one(); break; } @@ -227,13 +227,13 @@ template void lp_dual_simplex::fill_costs_bounds_ m_can_enter_basis[j] = true; this->set_scaled_cost(j); this->m_upper_bounds[j] = free_bound; - this->m_low_bounds[j] = -free_bound; + this->m_lower_bounds[j] = -free_bound; break; } case column_type::boxed: m_can_enter_basis[j] = false; this->m_costs[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = this->m_low_bounds[j] = numeric_traits::zero(); // is it needed? + this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits::zero(); // is it needed? break; default: lp_unreachable(); @@ -244,13 +244,13 @@ template void lp_dual_simplex::fill_costs_bounds_ template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { this->m_costs[j] = 0; lp_assert(get_column_type(j) != column_type::upper_bound); - if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::low_bound))) { + if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) { m_column_types_of_core_solver[j] = column_type::boxed; - this->m_low_bounds[j] = numeric_traits::zero(); + this->m_lower_bounds[j] = numeric_traits::zero(); this->m_upper_bounds[j] = numeric_traits::one(); } else { m_column_types_of_core_solver[j] = column_type::fixed; - this->m_low_bounds[j] = numeric_traits::zero(); + this->m_lower_bounds[j] = numeric_traits::zero(); this->m_upper_bounds[j] = numeric_traits::zero(); } } @@ -283,7 +283,7 @@ template void lp_dual_simplex::fill_first_stage_s break; case Greater_or_equal: - set_type_for_logical(slack_var, column_type::low_bound); + set_type_for_logical(slack_var, column_type::lower_bound); (*this->m_A)(row, slack_var) = - numeric_traits::one(); if (rs > 0) { // adding one artificial @@ -301,7 +301,7 @@ template void lp_dual_simplex::fill_first_stage_s break; case Less_or_equal: // introduce a non-negative slack variable - set_type_for_logical(slack_var, column_type::low_bound); + set_type_for_logical(slack_var, column_type::lower_bound); (*this->m_A)(row, slack_var) = numeric_traits::one(); if (rs < 0) { // adding one artificial @@ -328,7 +328,7 @@ template void lp_dual_simplex::augment_matrix_A_a m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); this->m_costs.resize(n); this->m_upper_bounds.resize(n); - this->m_low_bounds.resize(n); + this->m_lower_bounds.resize(n); m_can_enter_basis.resize(n); this->m_basis.resize(this->m_A->row_count()); } diff --git a/src/util/lp/lp_primal_core_solver_instances.cpp b/src/util/lp/lp_primal_core_solver.cpp similarity index 92% rename from src/util/lp/lp_primal_core_solver_instances.cpp rename to src/util/lp/lp_primal_core_solver.cpp index dc2e3fda4..f3eefa2b9 100644 --- a/src/util/lp/lp_primal_core_solver_instances.cpp +++ b/src/util/lp/lp_primal_core_solver.cpp @@ -23,8 +23,8 @@ Revision History: #include "util/vector.h" #include #include "util/lp/lar_solver.h" -#include "util/lp/lp_primal_core_solver.hpp" -#include "util/lp/lp_primal_core_solver_tableau.hpp" +#include "util/lp/lp_primal_core_solver_def.h" +#include "util/lp/lp_primal_core_solver_tableau_def.h" namespace lp { template void lp_primal_core_solver::find_feasible_solution(); diff --git a/src/util/lp/lp_primal_core_solver.h b/src/util/lp/lp_primal_core_solver.h index cee5b1ef2..3c95e8ee0 100644 --- a/src/util/lp/lp_primal_core_solver.h +++ b/src/util/lp/lp_primal_core_solver.h @@ -40,7 +40,7 @@ Revision History: #include "util/lp/iterator_on_row.h" namespace lp { -// This core solver solves (Ax=b, low_bound_values \leq x \leq upper_bound_values, maximize costs*x ) +// This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) // The right side b is given implicitly by x and the basis template class lp_primal_core_solver:public lp_core_solver_base { @@ -118,17 +118,17 @@ public: switch (this->m_column_types[j]) { case column_type::free_column: return true; case column_type::fixed: return false; - case column_type::low_bound: + case column_type::lower_bound: if (sign < 0) return true; - return !this->x_is_at_low_bound(j); + return !this->x_is_at_lower_bound(j); case column_type::upper_bound: if (sign > 0) return true; return !this->x_is_at_upper_bound(j); case column_type::boxed: if (sign < 0) - return !this->x_is_at_low_bound(j); + return !this->x_is_at_lower_bound(j); return !this->x_is_at_upper_bound(j); } @@ -143,7 +143,7 @@ public: case column_type::free_column: return false; case column_type::fixed: - case column_type::low_bound: + case column_type::lower_bound: case column_type::boxed: return this-> x_below_low_bound(bj); default: @@ -158,7 +158,7 @@ public: switch(this->m_column_types[bj]) { case column_type::free_column: return 0; - case column_type::low_bound: + case column_type::lower_bound: return 1; case column_type::fixed: case column_type::boxed: @@ -180,9 +180,9 @@ public: return true; case column_type::fixed: return false; - case column_type::low_bound: + case column_type::lower_bound: if (is_pos(rc.get_val())) { - return this->x_above_low_bound(j); + return this->x_above_lower_bound(j); } return true; @@ -194,7 +194,7 @@ public: return this->x_below_upper_bound(j); case column_type::boxed: if (is_pos(rc.get_val())) { - return this->x_above_low_bound(j); + return this->x_above_lower_bound(j); } return this->x_below_upper_bound(j); @@ -213,9 +213,9 @@ public: return true; case column_type::fixed: return false; - case column_type::low_bound: + case column_type::lower_bound: if (is_neg(rc.get_val())) { - return this->x_above_low_bound(j); + return this->x_above_lower_bound(j); } return true; @@ -227,7 +227,7 @@ public: return this->x_below_upper_bound(j); case column_type::boxed: if (is_neg(rc.get_val())) { - return this->x_above_low_bound(j); + return this->x_above_lower_bound(j); } return this->x_below_upper_bound(j); @@ -349,15 +349,15 @@ public: } - void limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0 && this->m_column_types[j] == column_type::low_bound); - limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited); + void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { + lp_assert(m < 0 && this->m_column_types[j] == column_type::lower_bound); + limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); } - void limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0 && this->m_column_types[j] == column_type::low_bound); - limit_inf_on_low_bound_m_pos(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited); + void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { + lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound); + limit_inf_on_lower_bound_m_pos(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); } void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { @@ -370,7 +370,7 @@ public: void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); - vector m_low_bounds_dummy; // needed for the base class only + vector m_lower_bounds_dummy; // needed for the base class only X get_max_bound(vector & b); @@ -464,14 +464,14 @@ public: case column_type::upper_bound: new_val_for_leaving = this->m_upper_bounds[j]; break; - case column_type::low_bound: - new_val_for_leaving = this->m_low_bounds[j]; + case column_type::lower_bound: + new_val_for_leaving = this->m_lower_bounds[j]; break; case column_type::boxed: if (this->x_above_upper_bound(j)) new_val_for_leaving = this->m_upper_bounds[j]; else - new_val_for_leaving = this->m_low_bounds[j]; + new_val_for_leaving = this->m_lower_bounds[j]; break; default: lp_assert(false); @@ -528,18 +528,18 @@ public: // void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) { // lp_assert(m < 0); - // lp_assert(this->m_column_type[j] == low_bound || this->m_column_type[j] == boxed); - // const X & eps = harris_eps_for_bound(this->m_low_bounds[j]); - // if (this->above_bound(this->m_x[j], this->m_low_bounds[j])) { - // theta = std::min((this->m_low_bounds[j] -this->m_x[j] - eps) / m, theta); + // lp_assert(this->m_column_type[j] == lower_bound || this->m_column_type[j] == boxed); + // const X & eps = harris_eps_for_bound(this->m_lower_bounds[j]); + // if (this->above_bound(this->m_x[j], this->m_lower_bounds[j])) { + // theta = std::min((this->m_lower_bounds[j] -this->m_x[j] - eps) / m, theta); // if (theta < zero_of_type()) theta = zero_of_type(); // } // } void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m < 0); - const X& eps = harris_eps_for_bound(this->m_low_bounds[j]); - limit_theta((this->m_low_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); + const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]); + limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); if (theta < zero_of_type()) theta = zero_of_type(); } @@ -591,7 +591,7 @@ public: return true; } - void limit_inf_on_low_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { + void limit_inf_on_lower_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { if (numeric_traits::precise()) { // x gets larger lp_assert(m > 0); @@ -621,7 +621,7 @@ public: void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { // lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed); const X & x = this->m_x[j]; - const X & lbound = this->m_low_bounds[j]; + const X & lbound = this->m_lower_bounds[j]; if (this->below_bound(x, lbound)) { const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); @@ -646,7 +646,7 @@ public: const X& eps = harris_eps_for_bound(ubound); limit_theta((ubound - x - eps) / m, theta, unlimited); } else { - const X & lbound = this->m_low_bounds[j]; + const X & lbound = this->m_lower_bounds[j]; if (this->above_bound(x, lbound)){ const X& eps = harris_eps_for_bound(lbound); limit_theta((lbound - x - eps) / m, theta, unlimited); @@ -679,7 +679,7 @@ public: // j is a basic column or the entering, in any case x[j] has to stay feasible. // m is the multiplier. updating t in a way that holds the following - // x[j] + t * m >= this->m_low_bounds[j]- harris_feasibility_tolerance ( if m < 0 ) + // x[j] + t * m >= this->m_lower_bounds[j]- harris_feasibility_tolerance ( if m < 0 ) // or // x[j] + t * m <= this->m_upper_bounds[j] + harris_feasibility_tolerance ( if m > 0) void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) { @@ -696,15 +696,15 @@ public: limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(j, m, theta, unlimited); } break; - case column_type::low_bound: + case column_type::lower_bound: if (this->current_x_is_feasible()) { if (m < 0) limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); } else { if (m < 0) - limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(j, m, theta, unlimited); + limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(j, m, theta, unlimited); else - limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(j, m, theta, unlimited); + limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(j, m, theta, unlimited); } break; // case fixed: @@ -770,7 +770,7 @@ public: void init_reduced_costs(); - bool low_bounds_are_set() const override { return true; } + bool lower_bounds_are_set() const override { return true; } int advance_on_sorted_breakpoints(unsigned entering, X & t); @@ -807,7 +807,7 @@ public: if (this->x_above_upper_bound(j)) return 1; break; - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(j)) return -1; break; @@ -842,11 +842,11 @@ public: case column_type::fixed: return 0; case column_type::boxed: - if (this->x_is_at_low_bound(j)) + if (this->x_is_at_lower_bound(j)) return 1; return -1; break; - case column_type::low_bound: + case column_type::lower_bound: return 1; break; case column_type::upper_bound: @@ -906,7 +906,7 @@ public: vector & heading, vector & costs, const vector & column_type_array, - const vector & low_bound_values, + const vector & lower_bound_values, const vector & upper_bound_values, lp_settings & settings, const column_namer& column_names): @@ -919,7 +919,7 @@ public: settings, column_names, column_type_array, - low_bound_values, + lower_bound_values, upper_bound_values), m_beta(A.row_count()), m_epsilon_of_reduced_cost(T(1)/T(10000000)), @@ -954,12 +954,12 @@ public: settings, column_names, column_type_array, - m_low_bounds_dummy, + m_lower_bounds_dummy, upper_bound_values), m_beta(A.row_count()), m_converted_harris_eps(convert_struct::convert(this->m_settings.harris_feasibility_tolerance)) { lp_assert(initial_x_is_correct()); - m_low_bounds_dummy.resize(A.column_count(), zero_of_type()); + m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); #ifdef Z3DEBUG // check_correctness(); @@ -972,7 +972,7 @@ public: basis_set.insert(this->m_basis[i]); } for (unsigned j = 0; j < this->m_n(); j++) { - if (this->column_has_low_bound(j) && this->m_x[j] < numeric_traits::zero()) { + if (this->column_has_lower_bound(j) && this->m_x[j] < numeric_traits::zero()) { LP_OUT(this->m_settings, "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl); return false; } @@ -983,7 +983,7 @@ public: } if (basis_set.find(j) != basis_set.end()) continue; - if (this->m_column_types[j] == column_type::low_bound) { + if (this->m_column_types[j] == column_type::lower_bound) { if (numeric_traits::zero() != this->m_x[j]) { LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits::zero() << " is not equal to " << this->m_x[j] << std::endl); return false; diff --git a/src/util/lp/lp_primal_core_solver.hpp b/src/util/lp/lp_primal_core_solver_def.h similarity index 96% rename from src/util/lp/lp_primal_core_solver.hpp rename to src/util/lp/lp_primal_core_solver_def.h index 436a13c5b..1e9edbd31 100644 --- a/src/util/lp/lp_primal_core_solver.hpp +++ b/src/util/lp/lp_primal_core_solver_def.h @@ -25,7 +25,7 @@ Revision History: #include #include "util/lp/lp_primal_core_solver.h" namespace lp { -// This core solver solves (Ax=b, low_bound_values \leq x \leq upper_bound_values, maximize costs*x ) +// This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) // The right side b is given implicitly by x and the basis template @@ -84,8 +84,8 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_on_breakpoin bool ret; const T & d = this->m_d[j]; switch (this->m_column_types[j]) { - case column_type::low_bound: - lp_assert(this->x_is_at_low_bound(j)); + case column_type::lower_bound: + lp_assert(this->x_is_at_lower_bound(j)); ret = d < -m_epsilon_of_reduced_cost; break; case column_type::upper_bound: @@ -97,9 +97,9 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_on_breakpoin break; case column_type::boxed: { - bool low_bound = this->x_is_at_low_bound(j); - lp_assert(low_bound || this->x_is_at_upper_bound(j)); - ret = (low_bound && d < -m_epsilon_of_reduced_cost) || ((!low_bound) && d > m_epsilon_of_reduced_cost); + bool lower_bound = this->x_is_at_lower_bound(j); + lp_assert(lower_bound || this->x_is_at_upper_bound(j)); + ret = (lower_bound && d < -m_epsilon_of_reduced_cost) || ((!lower_bound) && d > m_epsilon_of_reduced_cost); } break; case column_type::free_column: @@ -125,7 +125,7 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsign if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost) return true; break; - case column_type::low_bound: + case column_type::lower_bound: if (dj > m_epsilon_of_reduced_cost) return true;; break; case column_type::upper_bound: @@ -137,7 +137,7 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsign return true; break; } else if (dj < - m_epsilon_of_reduced_cost) { - if (this->m_x[j] > this->m_low_bounds[j] + this->bound_span(j)/2) + if (this->m_x[j] > this->m_lower_bounds[j] + this->bound_span(j)/2) return true; } break; @@ -159,9 +159,9 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis if (!is_zero(dj)) return true; break; - case column_type::low_bound: + case column_type::lower_bound: if (dj > zero_of_type()) return true; - if (dj < 0 && this->m_x[j] > this->m_low_bounds[j]){ + if (dj < 0 && this->m_x[j] > this->m_lower_bounds[j]){ return true; } break; @@ -177,7 +177,7 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis return true; break; } else if (dj < zero_of_type()) { - if (this->m_x[j] > this->m_low_bounds[j]) + if (this->m_x[j] > this->m_lower_bounds[j]) return true; } break; @@ -364,7 +364,7 @@ template bool lp_primal_core_solver::try_jump_to_ return true; } } else { // m_sign_of_entering_delta == -1 - t = this->m_x[entering] - this->m_low_bounds[entering]; + t = this->m_x[entering] - this->m_lower_bounds[entering]; if (unlimited || t <= theta) { lp_assert(t >= zero_of_type()); return true; @@ -380,9 +380,9 @@ template bool lp_primal_core_solver::try_jump_to_ } } return false; - case column_type::low_bound: + case column_type::lower_bound: if (m_sign_of_entering_delta < 0) { - t = this->m_x[entering] - this->m_low_bounds[entering]; + t = this->m_x[entering] - this->m_lower_bounds[entering]; if (unlimited || t <= theta) { lp_assert(t >= zero_of_type()); return true; @@ -404,7 +404,7 @@ try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t ) { return true; } // m_sign_of_entering_delta == -1 - t = this->m_x[entering] - this->m_low_bounds[entering]; + t = this->m_x[entering] - this->m_lower_bounds[entering]; return true; } @@ -489,7 +489,7 @@ template int lp_primal_core_solver::find_leavi // m is the multiplier. updating t in a way that holds the following -// x[j] + t * m >= m_low_bounds[j] ( if m < 0 ) +// x[j] + t * m >= m_lower_bounds[j] ( if m < 0 ) // or // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) template void @@ -501,7 +501,7 @@ lp_primal_core_solver::get_bound_on_variable_and_update_leaving_precisely( return; default:break; } - X tt = - (this->m_low_bounds[j] - this->m_x[j]) / m; + X tt = - (this->m_lower_bounds[j] - this->m_x[j]) / m; if (numeric_traits::is_neg(tt)) tt = zero_of_type(); if (leavings.size() == 0 || tt < t || (tt == t && m > abs_of_d_of_leaving)) { @@ -516,7 +516,7 @@ lp_primal_core_solver::get_bound_on_variable_and_update_leaving_precisely( } else if (m < 0){ switch (this->m_column_types[j]) { // check that j has an upper bound case column_type::free_column: - case column_type::low_bound: + case column_type::lower_bound: return; default:break; } @@ -558,7 +558,7 @@ template void lp_primal_core_solver::check_the } template void lp_primal_core_solver::check_bound(unsigned i) { - lp_assert (!(this->column_has_low_bound(i) && (numeric_traits::zero() > this->m_x[i]))); + lp_assert (!(this->column_has_lower_bound(i) && (numeric_traits::zero() > this->m_x[i]))); lp_assert (!(this->column_has_upper_bound(i) && (this->m_upper_bounds[i] < this->m_x[i]))); } @@ -1136,7 +1136,7 @@ lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const ret = numeric_traits::zero(); } break; - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(j)) { ret = -1; } else { @@ -1190,7 +1190,7 @@ lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { this->m_costs[j] = numeric_traits::zero(); } break; - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(j)) { this->m_costs[j] = -1; } else { @@ -1228,13 +1228,13 @@ template void lp_primal_core_solver::print_column switch (this->m_column_type[j]) { case column_type::fixed: case column_type::boxed: - out << "( " << this->m_low_bounds[j] << " " << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl; + out << "( " << this->m_lower_bounds[j] << " " << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl; break; case column_type::upper_bound: out << "( _" << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl; break; - case column_type::low_bound: - out << "( " << this->m_low_bounds[j] << " " << this->m_x[j] << " " << "_ )" << std::endl; + case column_type::lower_bound: + out << "( " << this->m_lower_bounds[j] << " " << this->m_x[j] << " " << "_ )" << std::endl; break; case column_type::free_column: out << "( _" << this->m_x[j] << "_)" << std::endl; @@ -1343,14 +1343,14 @@ template void lp_primal_core_solver::try_add_b const X & x = this->m_x[j]; switch (this->m_column_types[j]) { case column_type::fixed: - try_add_breakpoint(j, x, d, fixed_break, this->m_low_bounds[j]); + try_add_breakpoint(j, x, d, fixed_break, this->m_lower_bounds[j]); break; case column_type::boxed: - try_add_breakpoint(j, x, d, low_break, this->m_low_bounds[j]); + try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); break; - case column_type::low_bound: - try_add_breakpoint(j, x, d, low_break, this->m_low_bounds[j]); + case column_type::lower_bound: + try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); break; case column_type::upper_bound: try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); @@ -1370,10 +1370,10 @@ template void lp_primal_core_solver::print_bound_ switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: - out << "[" << this->m_low_bounds[j] << "," << this->m_upper_bounds[j] << "]" << std::endl; + out << "[" << this->m_lower_bounds[j] << "," << this->m_upper_bounds[j] << "]" << std::endl; break; - case column_type::low_bound: - out << "[" << this->m_low_bounds[j] << ", inf" << std::endl; + case column_type::lower_bound: + out << "[" << this->m_lower_bounds[j] << ", inf" << std::endl; break; case column_type::upper_bound: out << "inf ," << this->m_upper_bounds[j] << "]" << std::endl; diff --git a/src/util/lp/lp_primal_core_solver_tableau.h b/src/util/lp/lp_primal_core_solver_tableau_def.h similarity index 96% rename from src/util/lp/lp_primal_core_solver_tableau.h rename to src/util/lp/lp_primal_core_solver_tableau_def.h index cd47abb47..b1d52c674 100644 --- a/src/util/lp/lp_primal_core_solver_tableau.h +++ b/src/util/lp/lp_primal_core_solver_tableau_def.h @@ -177,13 +177,13 @@ unsigned lp_primal_core_solver::solve_with_tableau() { default: break; // do nothing } - } while (this->get_status() != FLOATING_POINT_ERROR + } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && - this->get_status() != UNBOUNDED + this->get_status() != lp_status::UNBOUNDED && - this->get_status() != OPTIMAL + this->get_status() != lp_status::OPTIMAL && - this->get_status() != INFEASIBLE + this->get_status() != lp_status::INFEASIBLE && this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements && @@ -194,14 +194,17 @@ unsigned lp_primal_core_solver::solve_with_tableau() { this->m_settings.get_cancel_flag() == false); if (this->m_settings.get_cancel_flag()) { - this->set_status(CANCELLED); + this->set_status(lp_status::CANCELLED); } - lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR - || - this->current_x_is_feasible() == false - || - this->calc_current_x_is_feasible_include_non_basis()); + lp_assert( + this->get_status() == lp_status::FLOATING_POINT_ERROR + || + this->get_status() == lp_status::CANCELLED + || + this->current_x_is_feasible() == false + || + this->calc_current_x_is_feasible_include_non_basis()); return this->total_iterations(); } diff --git a/src/util/lp/lp_primal_simplex_instances.cpp b/src/util/lp/lp_primal_simplex.cpp similarity index 96% rename from src/util/lp/lp_primal_simplex_instances.cpp rename to src/util/lp/lp_primal_simplex.cpp index 92e3a77ff..56d4a6134 100644 --- a/src/util/lp/lp_primal_simplex_instances.cpp +++ b/src/util/lp/lp_primal_simplex.cpp @@ -22,7 +22,7 @@ Revision History: #include #include "util/vector.h" #include -#include "util/lp/lp_primal_simplex.hpp" +#include "util/lp/lp_primal_simplex_def.h" template bool lp::lp_primal_simplex::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); template bool lp::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); template double lp::lp_primal_simplex::get_current_cost() const; diff --git a/src/util/lp/lp_primal_simplex.h b/src/util/lp/lp_primal_simplex.h index 06ea26f8f..10efdcb79 100644 --- a/src/util/lp/lp_primal_simplex.h +++ b/src/util/lp/lp_primal_simplex.h @@ -31,7 +31,7 @@ namespace lp { template class lp_primal_simplex: public lp_solver { lp_primal_core_solver * m_core_solver; - vector m_low_bounds; + vector m_lower_bounds; private: unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } diff --git a/src/util/lp/lp_primal_simplex.hpp b/src/util/lp/lp_primal_simplex_def.h similarity index 94% rename from src/util/lp/lp_primal_simplex.hpp rename to src/util/lp/lp_primal_simplex_def.h index c179a9fd1..5366144d8 100644 --- a/src/util/lp/lp_primal_simplex.hpp +++ b/src/util/lp/lp_primal_simplex_def.h @@ -83,7 +83,7 @@ template void lp_primal_simplex::fill_costs_and_x T artificial_cost = - numeric_traits::one(); switch (constraint.m_relation) { case Equal: // no slack variable here - this->m_column_types[artificial] = column_type::low_bound; + this->m_column_types[artificial] = column_type::lower_bound; this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero this->m_basis[row] = artificial; if (rs >= 0) { @@ -97,13 +97,13 @@ template void lp_primal_simplex::fill_costs_and_x break; case Greater_or_equal: - this->m_column_types[slack_var] = column_type::low_bound; + this->m_column_types[slack_var] = column_type::lower_bound; (*this->m_A)(row, slack_var) = - numeric_traits::one(); if (rs > 0) { lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); // adding one artificial - this->m_column_types[artificial] = column_type::low_bound; + this->m_column_types[artificial] = column_type::lower_bound; (*this->m_A)(row, artificial) = numeric_traits::one(); this->m_costs[artificial] = artificial_cost; this->m_basis[row] = artificial; @@ -118,13 +118,13 @@ template void lp_primal_simplex::fill_costs_and_x break; case Less_or_equal: // introduce a non-negative slack variable - this->m_column_types[slack_var] = column_type::low_bound; + this->m_column_types[slack_var] = column_type::lower_bound; (*this->m_A)(row, slack_var) = numeric_traits::one(); if (rs < 0) { // adding one artificial lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - this->m_column_types[artificial] = column_type::low_bound; + this->m_column_types[artificial] = column_type::lower_bound; (*this->m_A)(row, artificial) = - numeric_traits::one(); this->m_costs[artificial] = artificial_cost; this->m_x[artificial] = - rs; @@ -208,20 +208,20 @@ template void lp_primal_simplex::fill_A_x_and_bas this->m_x[j] = this->m_b[row]; (*this->m_A)(row, j) = numeric_traits::one(); this->m_column_types[j] = column_type::fixed; - this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type(); + this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); break; case Greater_or_equal: this->m_x[j] = - this->m_b[row]; (*this->m_A)(row, j) = - numeric_traits::one(); - this->m_column_types[j] = column_type::low_bound; + this->m_column_types[j] = column_type::lower_bound; this->m_upper_bounds[j] = zero_of_type(); break; case Less_or_equal: this->m_x[j] = this->m_b[row]; (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::low_bound; - this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type(); + this->m_column_types[j] = column_type::lower_bound; + this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); break; default: lp_unreachable(); @@ -234,8 +234,8 @@ template void lp_primal_simplex::solve_with_total this->m_status = lp_status::OPTIMAL; return; } - m_low_bounds.clear(); - m_low_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero + m_lower_bounds.clear(); + m_lower_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero this->m_x.resize(total_vars, numeric_traits::zero()); this->m_basis.resize(this->row_count()); this->m_costs.clear(); @@ -253,7 +253,7 @@ template void lp_primal_simplex::solve_with_total this->m_heading, this->m_costs, this->m_column_types, - m_low_bounds, + m_lower_bounds, this->m_upper_bounds, this->m_settings, *this); m_core_solver->solve(); diff --git a/src/util/lp/lp_settings_instances.cpp b/src/util/lp/lp_settings.cpp similarity index 91% rename from src/util/lp/lp_settings_instances.cpp rename to src/util/lp/lp_settings.cpp index bd5a1515f..147319ed2 100644 --- a/src/util/lp/lp_settings_instances.cpp +++ b/src/util/lp/lp_settings.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include #include "util/vector.h" -#include "util/lp/lp_settings.hpp" +#include "util/lp/lp_settings_def.h" template bool lp::vectors_are_equal(vector const&, vector const&); template bool lp::vectors_are_equal(vector const&, vector const&); diff --git a/src/util/lp/lp_settings.h b/src/util/lp/lp_settings.h index 08dbb86af..0a7e3f6ea 100644 --- a/src/util/lp/lp_settings.h +++ b/src/util/lp/lp_settings.h @@ -36,7 +36,7 @@ typedef vector> explanation_t; enum class column_type { free_column = 0, - low_bound = 1, + lower_bound = 1, upper_bound = 2, boxed = 3, fixed = 4 @@ -84,7 +84,7 @@ inline std::ostream& operator<<(std::ostream& out, lp_status status) { lp_status lp_status_from_string(std::string status); -enum non_basic_column_value_position { at_low_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; +enum non_basic_column_value_position { at_lower_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; template bool is_epsilon_small(const X & v, const double& eps); // forward definition @@ -102,6 +102,10 @@ struct stats { unsigned m_need_to_solve_inf; unsigned m_max_cols; unsigned m_max_rows; + unsigned m_cut_solver_calls; + unsigned m_cut_solver_true; + unsigned m_cut_solver_false; + unsigned m_cut_solver_undef; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; @@ -222,7 +226,10 @@ public: backup_costs(true), column_number_threshold_for_using_lu_in_lar_solver(4000), m_int_branch_cut_gomory_threshold(4), - m_run_gcd_test(true) + m_int_branch_cut_solver(4), + m_run_gcd_test(true), + m_cut_solver_bound_propagation_factor(5), + m_cut_solver_cycle_on_var(10) {} void set_resource_limit(lp_resource_limit& lim) { m_resource_limit = &lim; } @@ -330,7 +337,10 @@ public: bool backup_costs; unsigned column_number_threshold_for_using_lu_in_lar_solver; unsigned m_int_branch_cut_gomory_threshold; + unsigned m_int_branch_cut_solver; bool m_run_gcd_test; + unsigned m_cut_solver_bound_propagation_factor; + unsigned m_cut_solver_cycle_on_var; }; // end of lp_settings class diff --git a/src/util/lp/lp_settings.hpp b/src/util/lp/lp_settings_def.h similarity index 98% rename from src/util/lp/lp_settings.hpp rename to src/util/lp/lp_settings_def.h index 68b08490b..a35d08114 100644 --- a/src/util/lp/lp_settings.hpp +++ b/src/util/lp/lp_settings_def.h @@ -26,7 +26,7 @@ std::string column_type_to_string(column_type t) { switch (t) { case column_type::fixed: return "fixed"; case column_type::boxed: return "boxed"; - case column_type::low_bound: return "low_bound"; + case column_type::lower_bound: return "lower_bound"; case column_type::upper_bound: return "upper_bound"; case column_type::free_column: return "free_column"; default: lp_unreachable(); diff --git a/src/util/lp/lp_solver_instances.cpp b/src/util/lp/lp_solver.cpp similarity index 98% rename from src/util/lp/lp_solver_instances.cpp rename to src/util/lp/lp_solver.cpp index 4fe04c05f..ea88dd14c 100644 --- a/src/util/lp/lp_solver_instances.cpp +++ b/src/util/lp/lp_solver.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include -#include "util/lp/lp_solver.hpp" +#include "util/lp/lp_solver_def.h" template void lp::lp_solver::add_constraint(lp::lp_relation, double, unsigned int); template void lp::lp_solver::cleanup(); template void lp::lp_solver::count_slacks_and_artificials(); diff --git a/src/util/lp/lp_solver.h b/src/util/lp/lp_solver.h index c447b1870..fb1f92c24 100644 --- a/src/util/lp/lp_solver.h +++ b/src/util/lp/lp_solver.h @@ -114,9 +114,9 @@ public: // returns -1 if not found virtual int get_column_index_by_name(std::string name) const; - void set_low_bound(unsigned i, T bound) { + void set_lower_bound(unsigned i, T bound) { column_info *ci = get_or_create_column_info(i); - ci->set_low_bound(bound); + ci->set_lower_bound(bound); } void set_upper_bound(unsigned i, T bound) { @@ -124,8 +124,8 @@ public: ci->set_upper_bound(bound); } - void unset_low_bound(unsigned i) { - get_or_create_column_info(i)->unset_low_bound(); + void unset_lower_bound(unsigned i) { + get_or_create_column_info(i)->unset_lower_bound(); } void unset_upper_bound(unsigned i) { @@ -194,9 +194,9 @@ protected: void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ); - bool get_minimal_row_value(std::unordered_map & row, T & low_bound); + bool get_minimal_row_value(std::unordered_map & row, T & lower_bound); - bool get_maximal_row_value(std::unordered_map & row, T & low_bound); + bool get_maximal_row_value(std::unordered_map & row, T & lower_bound); bool row_is_zero(std::unordered_map & row); @@ -244,7 +244,7 @@ protected: void count_slacks_and_artificials_for_row(unsigned i); - T low_bound_shift_for_row(unsigned i); + T lower_bound_shift_for_row(unsigned i); void fill_m_b(); diff --git a/src/util/lp/lp_solver.hpp b/src/util/lp/lp_solver_def.h similarity index 93% rename from src/util/lp/lp_solver.hpp rename to src/util/lp/lp_solver_def.h index 071e4232f..10c7a6feb 100644 --- a/src/util/lp/lp_solver.hpp +++ b/src/util/lp/lp_solver_def.h @@ -176,26 +176,26 @@ template void lp_solver::pin_vars_on_row_with_sig lp_assert(ci->upper_bound_is_set()); ci->set_fixed_value(ci->get_upper_bound()); } else { - lp_assert(ci->low_bound_is_set()); - ci->set_fixed_value(ci->get_low_bound()); + lp_assert(ci->lower_bound_is_set()); + ci->set_fixed_value(ci->get_lower_bound()); } } } -template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & low_bound) { - low_bound = numeric_traits::zero(); +template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & lower_bound) { + lower_bound = numeric_traits::zero(); for (auto & t : row) { T a = t.second; column_info * ci = m_map_from_var_index_to_column_info[t.first]; if (a > numeric_traits::zero()) { - if (ci->low_bound_is_set()) { - low_bound += ci->get_low_bound() * a; + if (ci->lower_bound_is_set()) { + lower_bound += ci->get_lower_bound() * a; } else { return false; } } else { if (ci->upper_bound_is_set()) { - low_bound += ci->get_upper_bound() * a; + lower_bound += ci->get_upper_bound() * a; } else { return false; } @@ -204,20 +204,20 @@ template bool lp_solver::get_minimal_row_value return true; } -template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & low_bound) { - low_bound = numeric_traits::zero(); +template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & lower_bound) { + lower_bound = numeric_traits::zero(); for (auto & t : row) { T a = t.second; column_info * ci = m_map_from_var_index_to_column_info[t.first]; if (a < numeric_traits::zero()) { - if (ci->low_bound_is_set()) { - low_bound += ci->get_low_bound() * a; + if (ci->lower_bound_is_set()) { + lower_bound += ci->get_lower_bound() * a; } else { return false; } } else { if (ci->upper_bound_is_set()) { - low_bound += ci->get_upper_bound() * a; + lower_bound += ci->get_upper_bound() * a; } else { return false; } @@ -242,12 +242,12 @@ template bool lp_solver::row_e_is_obsolete(std return true; } - T low_bound; - bool lb = get_minimal_row_value(row, low_bound); + T lower_bound; + bool lb = get_minimal_row_value(row, lower_bound); if (lb) { - T diff = low_bound - rs; + T diff = lower_bound - rs; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // low_bound > rs + m_settings.refactor_epsilon + // lower_bound > rs + m_settings.refactor_epsilon m_status = lp_status::INFEASIBLE; return true; } @@ -301,7 +301,7 @@ template bool lp_solver::row_ge_is_obsolete(std: } template bool lp_solver::row_le_is_obsolete(std::unordered_map & row, unsigned row_index) { - T low_bound; + T lower_bound; T rs = m_constraints[row_index].m_rs; if (row_is_zero(row)) { if (rs < zero_of_type()) @@ -309,10 +309,10 @@ template bool lp_solver::row_le_is_obsolete(std:: return true; } - if (get_minimal_row_value(row, low_bound)) { - T diff = low_bound - rs; + if (get_minimal_row_value(row, lower_bound)) { + T diff = lower_bound - rs; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // low_bound > rs + m_settings.refactor_tolerance + // lower_bound > rs + m_settings.refactor_tolerance m_status = lp_status::INFEASIBLE; return true; } @@ -504,7 +504,7 @@ template void lp_solver::count_slacks_and_artific } } -template T lp_solver::low_bound_shift_for_row(unsigned i) { +template T lp_solver::lower_bound_shift_for_row(unsigned i) { T ret = numeric_traits::zero(); auto row = this->m_A_values.find(i); @@ -522,7 +522,7 @@ template void lp_solver::fill_m_b() { lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; auto & constraint = this->m_constraints[external_i]; - this->m_b[i] = constraint.m_rs - low_bound_shift_for_row(external_i); + this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i); } } @@ -545,7 +545,7 @@ template T lp_solver::get_column_value_with_core_ return v; } if (!ci->is_flipped()) { - return v + ci->get_low_bound(); + return v + ci->get_lower_bound(); } // the flipped case when there is only upper bound diff --git a/src/util/lp/lp_utils.cpp b/src/util/lp/lp_utils.cpp index 71bac4623..98ab022d1 100644 --- a/src/util/lp/lp_utils.cpp +++ b/src/util/lp/lp_utils.cpp @@ -23,4 +23,5 @@ namespace lp { double numeric_traits::g_zero = 0.0; double numeric_traits::g_one = 1.0; } +#endif diff --git a/src/util/lp/lp_utils.h b/src/util/lp/lp_utils.h index f6c98645a..b7a896c11 100644 --- a/src/util/lp/lp_utils.h +++ b/src/util/lp/lp_utils.h @@ -42,20 +42,25 @@ bool contains(const std::unordered_map & map, const A& key) { #endif namespace lp { - inline void throw_exception(const std::string & str) { - throw default_exception(str); - } - typedef z3_exception exception; +inline void throw_exception(const std::string & str) { + throw default_exception(str); +} +typedef z3_exception exception; #define lp_assert(_x_) { SASSERT(_x_); } - inline void lp_unreachable() { lp_assert(false); } - template inline X zero_of_type() { return numeric_traits::zero(); } - template inline X one_of_type() { return numeric_traits::one(); } - template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } - template inline bool is_pos(const X & v) { return numeric_traits::is_pos(v); } - template inline bool is_neg(const X & v) { return numeric_traits::is_neg(v); } +inline void lp_unreachable() { lp_assert(false); } +template inline X zero_of_type() { return numeric_traits::zero(); } +template inline X one_of_type() { return numeric_traits::one(); } +template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } +template inline bool is_pos(const X & v) { return numeric_traits::is_pos(v); } +template inline bool is_neg(const X & v) { return numeric_traits::is_neg(v); } +template inline bool is_int(const X & v) { return numeric_traits::is_int(v); } - template inline bool precise() { return numeric_traits::precise(); } +template inline X ceil_ratio(const X & a, const X & b) { return numeric_traits::ceil_ratio(a, b); } +template inline X floor_ratio(const X & a, const X & b) { return numeric_traits::floor_ratio(a, b); } + + +template inline bool precise() { return numeric_traits::precise(); } } namespace std { template<> @@ -115,6 +120,12 @@ namespace lp { template inline bool precise() { return numeric_traits::precise();} template inline X one_of_type() { return numeric_traits::one(); } template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } +template inline bool is_pos(const X & v) { return numeric_traits::is_pos(v); } +template inline bool is_int(const X & v) { return numeric_traits::is_int(v); } +template inline X ceil_ratio(const X & a, const X & b) { return numeric_traits::ceil_ratio(a, b); } +template inline X floor_ratio(const X & a, const X & b) { return numeric_traits::floor_ratio(v); } + + template inline double get_double(const X & v) { return numeric_traits::get_double(v); } template inline T zero_of_type() {return numeric_traits::zero();} inline void throw_exception(std::string str) { throw exception(str); } diff --git a/src/util/lp/lu_instances.cpp b/src/util/lp/lu.cpp similarity index 99% rename from src/util/lp/lu_instances.cpp rename to src/util/lp/lu.cpp index 42b4119e4..58da60d81 100644 --- a/src/util/lp/lu_instances.cpp +++ b/src/util/lp/lu.cpp @@ -22,7 +22,7 @@ Revision History: #include #include "util/vector.h" #include "util/debug.h" -#include "util/lp/lu.hpp" +#include "util/lp/lu_def.h" template double lp::dot_product(vector const&, vector const&); template lp::lu::lu(lp::static_matrix const&, vector&, lp::lp_settings&); template void lp::lu::push_matrix_to_tail(lp::tail_matrix*); diff --git a/src/util/lp/lu.hpp b/src/util/lp/lu_def.h similarity index 100% rename from src/util/lp/lu.hpp rename to src/util/lp/lu_def.h diff --git a/src/util/lp/matrix_instances.cpp b/src/util/lp/matrix.cpp similarity index 96% rename from src/util/lp/matrix_instances.cpp rename to src/util/lp/matrix.cpp index 8271a4d8a..2fb064111 100644 --- a/src/util/lp/matrix_instances.cpp +++ b/src/util/lp/matrix.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include "util/lp/lp_settings.h" #ifdef Z3DEBUG -#include "util/lp/matrix.hpp" +#include "util/lp/matrix_def.h" #include "util/lp/static_matrix.h" #include template void lp::print_matrix(lp::matrix const*, std::ostream & out); diff --git a/src/util/lp/matrix.hpp b/src/util/lp/matrix_def.h similarity index 100% rename from src/util/lp/matrix.hpp rename to src/util/lp/matrix_def.h diff --git a/src/util/lp/mps_reader.h b/src/util/lp/mps_reader.h index 0e822ad9f..ca6188dd7 100644 --- a/src/util/lp/mps_reader.h +++ b/src/util/lp/mps_reader.h @@ -183,7 +183,7 @@ class mps_reader { void set_boundary_for_column(unsigned col, bound * b, lp_solver * solver){ if (b == nullptr) { - solver->set_low_bound(col, numeric_traits::zero()); + solver->set_lower_bound(col, numeric_traits::zero()); return; } @@ -191,7 +191,7 @@ class mps_reader { return; } if (b->m_low_is_set) { - solver->set_low_bound(col, b->m_low); + solver->set_lower_bound(col, b->m_low); } if (b->m_upper_is_set) { solver->set_upper_bound(col, b->m_upper); diff --git a/src/util/lp/nra_solver.cpp b/src/util/lp/nra_solver.cpp index 327d2b70f..42e971e12 100644 --- a/src/util/lp/nra_solver.cpp +++ b/src/util/lp/nra_solver.cpp @@ -89,7 +89,7 @@ namespace nra { */ lbool check(lp::explanation_t& ex) { SASSERT(need_check()); - m_nlsat = alloc(nlsat::solver, m_limit, m_params); + m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); m_lp2nl.reset(); vector core; diff --git a/src/util/lp/numeric_pair.h b/src/util/lp/numeric_pair.h index ce8dba3a0..baff91dac 100644 --- a/src/util/lp/numeric_pair.h +++ b/src/util/lp/numeric_pair.h @@ -18,10 +18,11 @@ Revision History: --*/ #pragma once - +#define lp_for_z3 #include #include #include +#ifdef lp_for_z3 #include "../rational.h" #include "../sstream.h" #include "../z3_exception.h" @@ -39,7 +40,7 @@ namespace lp { template std::string T_to_string(const T & t); // forward definition - +#ifdef lp_for_z3 template class numeric_traits {}; template <> class numeric_traits { @@ -50,6 +51,7 @@ public: static bool is_zero(unsigned v) { return v == 0; } static double get_double(unsigned const & d) { return d; } static bool is_int(unsigned) {return true;} + static bool is_pos(unsigned) {return true;} }; template <> class numeric_traits { @@ -61,6 +63,9 @@ public: static double const get_double(int const & d) { return d; } static bool is_int(int) {return true;} static bool is_pos(int j) {return j > 0;} + static bool is_neg(int j) {return j < 0;} + static int ceil_ratio(int a, int b) { return static_cast(ceil(mpq(a, b)).get_int32());} + static int floor_ratio(int a, int b) { return static_cast(floor(mpq(a, b)).get_int32());} }; @@ -92,7 +97,15 @@ template <> class numeric_traits { static bool is_pos(const rational & d) {return d.is_pos();} static bool is_neg(const rational & d) {return d.is_neg();} static bool is_int(const rational & d) {return d.is_int();} + static mpq ceil_ratio(const mpq & a, const mpq & b) { + return ceil(a / b); + } + static mpq floor_ratio(const mpq & a, const mpq & b) { + return floor(a / b); + } + }; +#endif template struct convert_struct { diff --git a/src/util/lp/permutation_matrix_instances.cpp b/src/util/lp/permutation_matrix.cpp similarity index 99% rename from src/util/lp/permutation_matrix_instances.cpp rename to src/util/lp/permutation_matrix.cpp index 692d32337..8b57cfe00 100644 --- a/src/util/lp/permutation_matrix_instances.cpp +++ b/src/util/lp/permutation_matrix.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include #include "util/vector.h" -#include "util/lp/permutation_matrix.hpp" +#include "util/lp/permutation_matrix_def.h" #include "util/lp/numeric_pair.h" template void lp::permutation_matrix::apply_from_right(vector&); template void lp::permutation_matrix::init(unsigned int); diff --git a/src/util/lp/permutation_matrix.hpp b/src/util/lp/permutation_matrix_def.h similarity index 100% rename from src/util/lp/permutation_matrix.hpp rename to src/util/lp/permutation_matrix_def.h diff --git a/src/util/lp/random_updater_instances.cpp b/src/util/lp/random_updater.cpp similarity index 80% rename from src/util/lp/random_updater_instances.cpp rename to src/util/lp/random_updater.cpp index 4f9b880c0..564885ea0 100644 --- a/src/util/lp/random_updater_instances.cpp +++ b/src/util/lp/random_updater.cpp @@ -17,4 +17,5 @@ Revision History: --*/ -#include "util/lp/random_updater.hpp" +#include "util/lp/random_updater_def.h" + diff --git a/src/util/lp/random_updater.hpp b/src/util/lp/random_updater_def.h similarity index 100% rename from src/util/lp/random_updater.hpp rename to src/util/lp/random_updater_def.h diff --git a/src/util/lp/row_eta_matrix_instances.cpp b/src/util/lp/row_eta_matrix.cpp similarity index 98% rename from src/util/lp/row_eta_matrix_instances.cpp rename to src/util/lp/row_eta_matrix.cpp index 3c4ab9bed..c74ce9186 100644 --- a/src/util/lp/row_eta_matrix_instances.cpp +++ b/src/util/lp/row_eta_matrix.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include #include "util/vector.h" -#include "util/lp/row_eta_matrix.hpp" +#include "util/lp/row_eta_matrix_def.h" #include "util/lp/lu.h" namespace lp { template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); diff --git a/src/util/lp/row_eta_matrix.hpp b/src/util/lp/row_eta_matrix_def.h similarity index 100% rename from src/util/lp/row_eta_matrix.hpp rename to src/util/lp/row_eta_matrix_def.h diff --git a/src/util/lp/scaler_instances.cpp b/src/util/lp/scaler.cpp similarity index 89% rename from src/util/lp/scaler_instances.cpp rename to src/util/lp/scaler.cpp index ba02321ea..dcb4cd308 100644 --- a/src/util/lp/scaler_instances.cpp +++ b/src/util/lp/scaler.cpp @@ -17,6 +17,6 @@ Revision History: --*/ -#include "util/lp/scaler.hpp" +#include "util/lp/scaler_def.h" template bool lp::scaler::scale(); template bool lp::scaler::scale(); diff --git a/src/util/lp/scaler.hpp b/src/util/lp/scaler_def.h similarity index 99% rename from src/util/lp/scaler.hpp rename to src/util/lp/scaler_def.h index 7863a3283..4a0e51da6 100644 --- a/src/util/lp/scaler.hpp +++ b/src/util/lp/scaler_def.h @@ -214,6 +214,7 @@ template void scaler::scale_rows() { } template void scaler::scale_row(unsigned i) { + std::cout << "t" << "\n"; T row_max = std::max(m_A.get_max_abs_in_row(i), abs(convert_struct::convert(m_b[i]))); T alpha = numeric_traits::one(); if (numeric_traits::is_zero(row_max)) { @@ -243,7 +244,7 @@ template void scaler::scale_column(unsigned i) if (numeric_traits::is_zero(column_max)){ return; // the column has zeros only } - + std::cout << "f"; if (numeric_traits::get_double(column_max) < m_scaling_minimum) { do { alpha *= 2; diff --git a/src/util/lp/signature_bound_evidence.h b/src/util/lp/signature_bound_evidence.h index e4eeb328d..7e09f720f 100644 --- a/src/util/lp/signature_bound_evidence.h +++ b/src/util/lp/signature_bound_evidence.h @@ -25,14 +25,14 @@ struct bound_signature { unsigned m_i; bool m_at_low; bound_signature(unsigned i, bool at_low) :m_i(i), m_at_low(m_at_low) {} - bool at_upper_bound() const { return !m_at_low_bound;} - bool at_low_bound() const { return m_at_low;} + bool at_upper_bound() const { return !m_at_lower_bound;} + bool at_lower_bound() const { return m_at_low;} }; template struct signature_bound_evidence { vector m_evidence; unsigned m_j; // found new bound - bool m_low_bound; + bool m_lower_bound; X m_bound; }; } diff --git a/src/util/lp/sparse_matrix_instances.cpp b/src/util/lp/sparse_matrix.cpp similarity index 99% rename from src/util/lp/sparse_matrix_instances.cpp rename to src/util/lp/sparse_matrix.cpp index a5b2fc90a..cdf255695 100644 --- a/src/util/lp/sparse_matrix_instances.cpp +++ b/src/util/lp/sparse_matrix.cpp @@ -21,7 +21,7 @@ Revision History: #include "util/vector.h" #include "util/lp/lp_settings.h" #include "util/lp/lu.h" -#include "util/lp/sparse_matrix.hpp" +#include "util/lp/sparse_matrix_def.h" #include "util/lp/dense_matrix.h" namespace lp { template double sparse_matrix::dot_product_with_row(unsigned int, vector const&) const; diff --git a/src/util/lp/sparse_matrix.hpp b/src/util/lp/sparse_matrix_def.h similarity index 100% rename from src/util/lp/sparse_matrix.hpp rename to src/util/lp/sparse_matrix_def.h diff --git a/src/util/lp/square_dense_submatrix_instances.cpp b/src/util/lp/square_dense_submatrix.cpp similarity index 98% rename from src/util/lp/square_dense_submatrix_instances.cpp rename to src/util/lp/square_dense_submatrix.cpp index e1df0036e..a4592c72d 100644 --- a/src/util/lp/square_dense_submatrix_instances.cpp +++ b/src/util/lp/square_dense_submatrix.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include #include "util/vector.h" -#include "util/lp/square_dense_submatrix.hpp" +#include "util/lp/square_dense_submatrix_def.h" template void lp::square_dense_submatrix::init(lp::sparse_matrix*, unsigned int); template lp::square_dense_submatrix::square_dense_submatrix(lp::sparse_matrix*, unsigned int); template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); diff --git a/src/util/lp/square_dense_submatrix.hpp b/src/util/lp/square_dense_submatrix_def.h similarity index 100% rename from src/util/lp/square_dense_submatrix.hpp rename to src/util/lp/square_dense_submatrix_def.h diff --git a/src/util/lp/stacked_map.h b/src/util/lp/stacked_map.h index 471aa41a0..caa80636c 100644 --- a/src/util/lp/stacked_map.h +++ b/src/util/lp/stacked_map.h @@ -147,7 +147,7 @@ public: } unsigned size() const { - return m_map.size(); + return static_cast(m_map.size()); } bool contains(const A & key) const { @@ -163,6 +163,19 @@ public: // d.m_deb_copy = m_map; m_stack.push(d); } + + void revert() { + if (m_stack.empty()) return; + + delta & d = m_stack.top(); + for (auto & t : d.m_new) { + m_map.erase(t); + } + for (auto & t: d.m_original_changed) { + m_map[t.first] = t.second; + } + d.clear(); + } void pop() { pop(1); @@ -232,7 +245,9 @@ public: m_map.clear(); } - bool empty() const { return m_map.empty(); } + unsigned stack_size() const { return m_stack.size(); } + + bool empty() const { return m_map.empty(); } const std::map& operator()() const { return m_map;} }; diff --git a/src/util/lp/stacked_value.h b/src/util/lp/stacked_value.h index 5ef7ea0c8..a80e0e7d7 100644 --- a/src/util/lp/stacked_value.h +++ b/src/util/lp/stacked_value.h @@ -30,6 +30,10 @@ public: m_stack.push(m_value); } + void clear() { + m_stack.clear(); + } + unsigned stack_size() const { return static_cast(m_stack.size()); } diff --git a/src/util/lp/stacked_vector.h b/src/util/lp/stacked_vector.h index 57d189963..511c7cf18 100644 --- a/src/util/lp/stacked_vector.h +++ b/src/util/lp/stacked_vector.h @@ -86,10 +86,10 @@ public: } /* - const B & operator[](unsigned a) const { - lp_assert(a < m_vector.size()); - return m_vector[a]; - } + const B & operator[](unsigned a) const { + lp_assert(a < m_vector.size()); + return m_vector[a]; + } */ unsigned size() const { return m_vector.size(); @@ -106,15 +106,21 @@ public: } template - void pop_tail(vector & v, unsigned k) { - lp_assert(v.size() >= k); - v.resize(v.size() - k); - } + void pop_tail(vector & v, unsigned k) { + lp_assert(v.size() >= k); + v.resize(v.size() - k); + } template void resize(vector & v, unsigned new_size) { v.resize(new_size); } + + void pop_back() { + unsigned last = m_vector.size() - 1; + m_changes.push_back(std::make_pair(last, m_vector[last])); + m_vector.pop_back(); + } void pop(unsigned k) { lp_assert(m_stack_of_vector_sizes.size() >= k); @@ -132,22 +138,22 @@ public: resize(m_changes, first_change); /* - while (k-- > 0) { + while (k-- > 0) { - if (m_stack.empty()) - return; + if (m_stack.empty()) + return; - delta & d = m_stack.back(); - lp_assert(m_vector.size() >= d.m_size); - while (m_vector.size() > d.m_size) - m_vector.pop_back(); + delta & d = m_stack.back(); + lp_assert(m_vector.size() >= d.m_size); + while (m_vector.size() > d.m_size) + m_vector.pop_back(); - for (auto & t : d.m_original_changed) { - lp_assert(t.first < m_vector.size()); - m_vector[t.first] = t.second; - } - // lp_assert(d.m_deb_copy == m_vector); - m_stack.pop_back();*/ + for (auto & t : d.m_original_changed) { + lp_assert(t.first < m_vector.size()); + m_vector[t.first] = t.second; + } + // lp_assert(d.m_deb_copy == m_vector); + m_stack.pop_back();*/ } @@ -175,10 +181,10 @@ public: m_vector.resize(m_vector.size() + 1); } - unsigned peek_size(unsigned k) const { - lp_assert(k > 0 && k <= m_stack_of_vector_sizes.size()); - return m_stack_of_vector_sizes[m_stack_of_vector_sizes.size() - k]; - } + unsigned peek_size(unsigned k) const { + lp_assert(k > 0 && k <= m_stack_of_vector_sizes.size()); + return m_stack_of_vector_sizes[m_stack_of_vector_sizes.size() - k]; + } const vector& operator()() const { return m_vector; } }; diff --git a/src/util/lp/static_matrix_instances.cpp b/src/util/lp/static_matrix.cpp similarity index 96% rename from src/util/lp/static_matrix_instances.cpp rename to src/util/lp/static_matrix.cpp index 8fa66691c..9d12087bb 100644 --- a/src/util/lp/static_matrix_instances.cpp +++ b/src/util/lp/static_matrix.cpp @@ -17,17 +17,11 @@ Revision History: --*/ -======= -/* - Copyright (c) 2017 Microsoft Corporation - Author: Lev Nachmanson -*/ #include #include "util/vector.h" #include #include -#include "util/vector.h" -#include "util/lp/static_matrix.hpp" +#include "util/lp/static_matrix_def.h" #include "util/lp/lp_core_solver_base.h" #include "util/lp/lp_dual_core_solver.h" #include "util/lp/lp_dual_simplex.h" diff --git a/src/util/lp/static_matrix.hpp b/src/util/lp/static_matrix_def.h similarity index 100% rename from src/util/lp/static_matrix.hpp rename to src/util/lp/static_matrix_def.h diff --git a/src/util/lp/test_bound_analyzer.h b/src/util/lp/test_bound_analyzer.h index b3e1ff7fb..b11fe6d94 100644 --- a/src/util/lp/test_bound_analyzer.h +++ b/src/util/lp/test_bound_analyzer.h @@ -35,7 +35,7 @@ namespace lp { class test_bound_analyzer { linear_combination_iterator & m_it; - std::function& m_low_bounds; + std::function& m_lower_bounds; std::function& m_upper_bounds; std::function m_column_types; vector & m_implied_bounds; @@ -47,14 +47,14 @@ class test_bound_analyzer { public : // constructor test_bound_analyzer(linear_combination_iterator &it, - std::function & low_bounds, + std::function & lower_bounds, std::function & upper_bounds, std::function column_types, vector & evidence_vector, unsigned row_or_term_index, std::function & try_get_found_bound) : m_it(it), - m_low_bounds(low_bounds), + m_lower_bounds(lower_bounds), m_upper_bounds(upper_bounds), m_column_types(column_types), m_implied_bounds(evidence_vector), @@ -121,16 +121,16 @@ public : } - bool low_bound_of_monoid(unsigned k, mpq & lb, bool &strict) const { + bool lower_bound_of_monoid(unsigned k, mpq & lb, bool &strict) const { int s = - m_coeff_sign * sign(m_coeffs[k]); unsigned j = m_index[k]; if (s > 0) { switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: - lb = -m_coeffs[k] * m_low_bounds(j).x; - strict = !is_zero(m_low_bounds(j).y); + case column_type::lower_bound: + lb = -m_coeffs[k] * m_lower_bounds(j).x; + strict = !is_zero(m_lower_bounds(j).y); return true; default: return false; @@ -169,9 +169,9 @@ public : switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: - lb = -m_coeffs[k] * m_low_bounds(j).x; - strict = !is_zero(m_low_bounds(j).y); + case column_type::lower_bound: + lb = -m_coeffs[k] * m_lower_bounds(j).x; + strict = !is_zero(m_lower_bounds(j).y); return true; default: return false; @@ -187,7 +187,7 @@ public : continue; mpq lb; bool str; - if (!low_bound_of_monoid(k, lb, str)) { + if (!lower_bound_of_monoid(k, lb, str)) { return; } if (str) @@ -199,9 +199,9 @@ public : switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: + case column_type::lower_bound: { - const auto & lb = m_low_bounds(j); + const auto & lb = m_lower_bounds(j); if (l < lb.x || (l == lb.x && !(is_zero(lb.y) && strict))) { break; // no improvement on the existing upper bound } @@ -212,11 +212,11 @@ public : } } - bool low_bound_is_available(unsigned j) const { + bool lower_bound_is_available(unsigned j) const { switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: + case column_type::lower_bound: return true; default: return false; @@ -239,15 +239,15 @@ public : return true; } if (is_lower_bound) { - if (low_bound_is_available(j)) { - best_bound = m_low_bounds(j).x; - strict_of_best_bound = !is_zero(m_low_bounds(j).y); + if (lower_bound_is_available(j)) { + best_bound = m_lower_bounds(j).x; + strict_of_best_bound = !is_zero(m_lower_bounds(j).y); return true; } } else { if (upper_bound_is_available(j)) { best_bound = m_upper_bounds(j).x; - strict_of_best_bound = !is_zero(m_low_bounds(j).y); + strict_of_best_bound = !is_zero(m_lower_bounds(j).y); return true; } } diff --git a/src/util/lp/ul_pair.h b/src/util/lp/ul_pair.h index e0edaadcc..7fac6b3ae 100644 --- a/src/util/lp/ul_pair.h +++ b/src/util/lp/ul_pair.h @@ -50,11 +50,11 @@ inline bool compare(const std::pair & a, const std::pair(-1)), + m_lower_bound_witness(static_cast(-1)), m_upper_bound_witness(static_cast(-1)), m_i(static_cast(-1)) {} ul_pair(row_index ri) : - m_low_bound_witness(static_cast(-1)), + m_lower_bound_witness(static_cast(-1)), m_upper_bound_witness(static_cast(-1)), m_i(ri) {} - ul_pair(const ul_pair & o): m_low_bound_witness(o.m_low_bound_witness), m_upper_bound_witness(o.m_upper_bound_witness), m_i(o.m_i) {} + ul_pair(const ul_pair & o): m_lower_bound_witness(o.m_lower_bound_witness), m_upper_bound_witness(o.m_upper_bound_witness), m_i(o.m_i) {} }; } diff --git a/src/util/rational.h b/src/util/rational.h index 930d79010..8047696be 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -421,6 +421,10 @@ inline bool operator>(rational const & r1, rational const & r2) { return operator<(r2, r1); } +inline bool operator<(int r1, rational const & r2) { + return rational(r1) < r2; +} + inline bool operator<(rational const & r1, int r2) { return r1 < rational(r2); } @@ -429,6 +433,11 @@ inline bool operator<=(rational const & r1, rational const & r2) { return !operator>(r1, r2); } +inline bool operator<=(rational const & r1, int r2) { + return r1 <= rational(r2); +} + + inline bool operator>=(rational const & r1, rational const & r2) { return !operator<(r1, r2); } @@ -437,6 +446,11 @@ inline bool operator>(rational const & a, int b) { return a > rational(b); } +inline bool operator>(int a, rational const & b) { + return rational(a) > b; +} + + inline bool operator!=(rational const & a, int b) { return !(a == rational(b)); }