diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 17b8fa1d0..f5f82b5fd 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -534,3 +534,4 @@ inline app_ref operator>(app_ref const& x, app_ref const& y) { } #endif /* ARITH_DECL_PLUGIN_H_ */ + diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 7451795f3..7a03d84d7 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -801,106 +801,104 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu return BR_DONE; } if (m_util.is_numeral(arg2, v2, is_int) && v2.is_one()) { - result = arg1; - return BR_DONE; - } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { - return BR_FAILED; - } - if (arg1 == arg2) { - expr_ref zero(m_util.mk_int(0), m()); - result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); - return BR_REWRITE3; - } - if (divides(arg1, arg2, result)) { - return BR_REWRITE_FULL; - } + result = arg1; + return BR_DONE; + } + if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { + return BR_FAILED; + } + if (arg1 == arg2) { + expr_ref zero(m_util.mk_int(0), m()); + result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); + return BR_REWRITE3; + } + if (divides(arg1, arg2, result)) { + return BR_REWRITE_FULL; + } return BR_FAILED; } - - -// -// implement div ab ac = floor( ab / ac) = floor (b / c) = div b c + +// +// implement div ab ac = floor( ab / ac) = floor (b / c) = div b c // -bool arith_rewriter::divides(expr* num, expr* den, expr_ref& result) { - expr_fast_mark1 mark; - rational num_r(1), den_r(1); - expr* num_e = nullptr, *den_e = nullptr; - ptr_buffer args1, args2; - flat_mul(num, args1); - flat_mul(den, args2); - for (expr * arg : args1) { - mark.mark(arg); - if (m_util.is_numeral(arg, num_r)) num_e = arg; - } - for (expr* arg : args2) { - if (mark.is_marked(arg)) { - result = remove_divisor(arg, num, den); - return true; - } - if (m_util.is_numeral(arg, den_r)) den_e = arg; - } - rational g = gcd(num_r, den_r); - if (!g.is_one()) { - SASSERT(g.is_pos()); - // replace num_e, den_e by their gcd reduction. - for (unsigned i = 0; i < args1.size(); ++i) { - if (args1[i] == num_e) { - args1[i] = m_util.mk_numeral(num_r / g, true); - break; - } - } - for (unsigned i = 0; i < args2.size(); ++i) { - if (args2[i] == den_e) { - args2[i] = m_util.mk_numeral(den_r / g, true); - break; - } - } - - num = m_util.mk_mul(args1.size(), args1.c_ptr()); - den = m_util.mk_mul(args2.size(), args2.c_ptr()); - result = m_util.mk_idiv(num, den); - return true; - } - return false; -} - -expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) { - ptr_buffer args1, args2; - flat_mul(num, args1); - flat_mul(den, args2); - remove_divisor(arg, args1); - remove_divisor(arg, args2); - expr_ref zero(m_util.mk_int(0), m()); - num = args1.empty() ? m_util.mk_int(1) : m_util.mk_mul(args1.size(), args1.c_ptr()); - den = args2.empty() ? m_util.mk_int(1) : m_util.mk_mul(args2.size(), args2.c_ptr()); - return expr_ref(m().mk_ite(m().mk_eq(zero, arg), m_util.mk_idiv(zero, zero), m_util.mk_idiv(num, den)), m()); -} - -void arith_rewriter::flat_mul(expr* e, ptr_buffer& args) { - args.push_back(e); - for (unsigned i = 0; i < args.size(); ++i) { - e = args[i]; - if (m_util.is_mul(e)) { - args.append(to_app(e)->get_num_args(), to_app(e)->get_args()); - args[i] = args.back(); - args.shrink(args.size()-1); - --i; - } - } -} - -void arith_rewriter::remove_divisor(expr* d, ptr_buffer& args) { - for (unsigned i = 0; i < args.size(); ++i) { - if (args[i] == d) { - args[i] = args.back(); - args.shrink(args.size()-1); - return; - } - } - UNREACHABLE(); -} +bool arith_rewriter::divides(expr* num, expr* den, expr_ref& result) { + expr_fast_mark1 mark; + rational num_r(1), den_r(1); + expr* num_e = nullptr, *den_e = nullptr; + ptr_buffer args1, args2; + flat_mul(num, args1); + flat_mul(den, args2); + for (expr * arg : args1) { + mark.mark(arg); + if (m_util.is_numeral(arg, num_r)) num_e = arg; + } + for (expr* arg : args2) { + if (mark.is_marked(arg)) { + result = remove_divisor(arg, num, den); + return true; + } + if (m_util.is_numeral(arg, den_r)) den_e = arg; + } + rational g = gcd(num_r, den_r); + if (!g.is_one()) { + SASSERT(g.is_pos()); + // replace num_e, den_e by their gcd reduction. + for (unsigned i = 0; i < args1.size(); ++i) { + if (args1[i] == num_e) { + args1[i] = m_util.mk_numeral(num_r / g, true); + break; + } + } + for (unsigned i = 0; i < args2.size(); ++i) { + if (args2[i] == den_e) { + args2[i] = m_util.mk_numeral(den_r / g, true); + break; + } + } + num = m_util.mk_mul(args1.size(), args1.c_ptr()); + den = m_util.mk_mul(args2.size(), args2.c_ptr()); + result = m_util.mk_idiv(num, den); + return true; + } + return false; +} +expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) { + ptr_buffer args1, args2; + flat_mul(num, args1); + flat_mul(den, args2); + remove_divisor(arg, args1); + remove_divisor(arg, args2); + expr_ref zero(m_util.mk_int(0), m()); + num = args1.empty() ? m_util.mk_int(1) : m_util.mk_mul(args1.size(), args1.c_ptr()); + den = args2.empty() ? m_util.mk_int(1) : m_util.mk_mul(args2.size(), args2.c_ptr()); + return expr_ref(m().mk_ite(m().mk_eq(zero, arg), m_util.mk_idiv(zero, zero), m_util.mk_idiv(num, den)), m()); +} + +void arith_rewriter::flat_mul(expr* e, ptr_buffer& args) { + args.push_back(e); + for (unsigned i = 0; i < args.size(); ++i) { + e = args[i]; + if (m_util.is_mul(e)) { + args.append(to_app(e)->get_num_args(), to_app(e)->get_args()); + args[i] = args.back(); + args.shrink(args.size()-1); + --i; + } + } +} + +void arith_rewriter::remove_divisor(expr* d, ptr_buffer& args) { + for (unsigned i = 0; i < args.size(); ++i) { + if (args[i] == d) { + args[i] = args.back(); + args.shrink(args.size()-1); + return; + } + } + UNREACHABLE(); +} + 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 fa8677941..93b6e5ad5 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -96,10 +96,9 @@ class arith_rewriter : public poly_rewriter { expr * mk_sin_value(rational const & k); app * mk_sqrt(rational const & k); bool divides(expr* d, expr* n, expr_ref& result); - expr_ref remove_divisor(expr* arg, expr* num, expr* den); - void flat_mul(expr* e, ptr_buffer& args); - void remove_divisor(expr* d, ptr_buffer& args); - + expr_ref remove_divisor(expr* arg, expr* num, expr* den); + void flat_mul(expr* e, ptr_buffer& args); + void remove_divisor(expr* d, ptr_buffer& args); public: arith_rewriter(ast_manager & m, params_ref const & p = params_ref()): poly_rewriter(m, p) { diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp index ad9bcbd54..718f59854 100644 --- a/src/shell/lp_frontend.cpp +++ b/src/shell/lp_frontend.cpp @@ -85,7 +85,7 @@ void run_solver(lp_params & params, char const * mps_file_name) { solver->find_maximal_solution(); *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp::OPTIMAL) { + if (solver->get_status() == lp::lp_status::OPTIMAL) { if (params.min()) { solver->flip_costs(); } diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 3f4105c34..c39b74722 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -40,7 +40,7 @@ def_module_params(module_name='smt', ('bv.reflect', BOOL, True, 'create enode for every bit-vector term'), ('bv.enable_int2bv', BOOL, True, 'enable support for int2bv and bv2int operators'), ('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'), - ('arith.solver', UINT, 2, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver'), + ('arith.solver', UINT, 6, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation'), ('arith.nl.gb', BOOL, True, 'groebner Basis computation, this option is ignored when arith.nl=false'), ('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters'), diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index eb1459058..9d09b4c38 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -25,11 +25,11 @@ Revision History: enum arith_solver_id { AS_NO_ARITH, // 0 AS_DIFF_LOGIC, // 1 - AS_ARITH, // 2 + AS_OLD_ARITH, // 2 AS_DENSE_DIFF_LOGIC, // 3 AS_UTVPI, // 4 AS_OPTINF, // 5 - AS_LRA // 6 + AS_NEW_ARITH // 6 }; enum bound_prop_mode { @@ -113,7 +113,7 @@ struct theory_arith_params { theory_arith_params(params_ref const & p = params_ref()): m_arith_eq2ineq(false), m_arith_process_all_eqs(false), - m_arith_mode(AS_ARITH), + m_arith_mode(AS_NEW_ARITH), m_arith_auto_config_simplex(false), m_arith_blands_rule_threshold(1000), m_arith_propagate_eqs(true), diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 73a3c27bd..ac6482b95 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3763,6 +3763,7 @@ namespace smt { } m_stats.m_num_final_checks++; + TRACE("final_check_stats", tout << "m_stats.m_num_final_checks = " << m_stats.m_num_final_checks << "\n";); final_check_status ok = m_qmanager->final_check_eh(false); if (ok != FC_DONE) diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index d377e5355..ae023078a 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -849,7 +849,7 @@ namespace smt { std::cerr << v << " ::=\n" << mk_ll_pp(n, m_manager) << "\n"; } #endif - TRACE("mk_bool_var", tout << "creating boolean variable: " << v << " for:\n" << mk_pp(n, m_manager) << "\n";); + TRACE("mk_bool_var", tout << "creating boolean variable: " << v << " for:\n" << mk_pp(n, m_manager) << " " << n->get_id() << "\n";); TRACE("mk_var_bug", tout << "mk_bool: " << v << "\n";); set_bool_var(id, v); m_bdata.reserve(v+1); diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 6e996e409..8977ddb2a 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -740,8 +740,12 @@ namespace smt { } void setup::setup_i_arith() { - // m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params)); - m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); + if (AS_OLD_ARITH == m_params.m_arith_mode) { + m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); + } + else { + setup_r_arith(); + } } void setup::setup_r_arith() { @@ -749,14 +753,21 @@ namespace smt { } void setup::setup_mi_arith() { - if (m_params.m_arith_mode == AS_OPTINF) { + switch (m_params.m_arith_mode) { + case AS_OPTINF: m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); - } - else { + break; + case AS_NEW_ARITH: + setup_r_arith(); + break; + default: m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); + break; } } + + void setup::setup_arith() { static_features st(m_manager); IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";); @@ -810,15 +821,15 @@ namespace smt { case AS_OPTINF: m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); break; - case AS_LRA: - setup_r_arith(); - break; - default: + case AS_OLD_ARITH: if (m_params.m_arith_int_only && int_only) m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); break; + default: + setup_i_arith(); + break; } } @@ -978,7 +989,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 && st.m_num_non_linear == 0) setup_QF_UFLRA(); else setup_unknown(); diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 992a87dab..4b3ee52f6 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -42,7 +42,7 @@ Revision History: namespace smt { struct theory_arith_stats { - unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests; + unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests, m_patches, m_patches_succ; unsigned m_assert_lower, m_assert_upper, m_assert_diseq, m_core2th_eqs, m_core2th_diseqs; unsigned m_th2core_eqs, m_th2core_diseqs, m_bound_props, m_offset_eqs, m_fixed_eqs, m_offline_eqs; unsigned m_max_min; diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index b5281f218..ee3bd5e2e 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -1335,7 +1335,7 @@ namespace smt { } } }); - + m_stats.m_patches++; patch_int_infeasible_vars(); fix_non_base_vars(); @@ -1368,6 +1368,7 @@ namespace smt { theory_var int_var = find_infeasible_int_base_var(); if (int_var == null_theory_var) { + m_stats.m_patches_succ++; TRACE("arith_int_incomp", tout << "FC_DONE 2...\n"; display(tout);); return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; } @@ -1385,6 +1386,7 @@ namespace smt { m_branch_cut_counter++; // TODO: add giveup code + TRACE("gomory_cut", tout << m_branch_cut_counter << ", " << m_params.m_arith_branch_cut_ratio << std::endl;); if (m_branch_cut_counter % m_params.m_arith_branch_cut_ratio == 0) { TRACE("opt_verbose", display(tout);); move_non_base_vars_to_bounds(); @@ -1399,7 +1401,7 @@ namespace smt { SASSERT(is_base(int_var)); row const & r = m_rows[get_var_row(int_var)]; if (!mk_gomory_cut(r)) { - // silent failure + TRACE("gomory_cut", tout << "silent failure\n";); } return FC_CONTINUE; } diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index a218445f5..6579856a4 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -38,6 +38,8 @@ namespace smt { st.update("arith gcd tests", m_stats.m_gcd_tests); st.update("arith ineq splits", m_stats.m_branches); st.update("arith gomory cuts", m_stats.m_gomory_cuts); + st.update("arith patches", m_stats.m_patches); + st.update("arith patches_succ", m_stats.m_patches_succ); st.update("arith max-min", m_stats.m_max_min); st.update("arith grobner", m_stats.m_gb_compute_basis); st.update("arith pseudo nonlinear", m_stats.m_nl_linear); @@ -389,8 +391,19 @@ namespace smt { void theory_arith::display_vars(std::ostream & out) const { out << "vars:\n"; int n = get_num_vars(); - for (theory_var v = 0; v < n; v++) - display_var(out, v); + int inf_vars = 0; + int int_inf_vars = 0; + for (theory_var v = 0; v < n; v++) { + if ((lower(v) && lower(v)->get_value() > get_value(v)) + || (upper(v) && upper(v)->get_value() < get_value(v))) + inf_vars++; + if (is_int(v) && !get_value(v).is_int()) + int_inf_vars++; + } + out << "infeasibles = " << inf_vars << " int_inf = " << int_inf_vars << std::endl; + for (theory_var v = 0; v < n; v++) { + display_var(out, v); + } } template diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index de9b41f64..88e73c10b 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" @@ -36,2594 +35,3028 @@ Revision History: #include "smt/smt_model_generator.h" #include "smt/arith_eq_adapter.h" #include "util/nat_set.h" +#include "util/lp/nra_solver.h" #include "tactic/generic_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 lra_lp { - enum bound_kind { lower_t, upper_t }; +namespace lp_api { +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; - 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, rational const & val, bound_kind k): - m_bv(bv), - m_var(v), - m_value(val), - m_bound_kind(k) { +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 } - 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; } - 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 + 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 << "v" << get_var() << " " << get_bound_kind() << " " << m_value; - } - }; - - std::ostream& operator<<(std::ostream& out, bound const& b) { - return b.display(out); + } + virtual std::ostream& display(std::ostream& out) const { + return out << m_value << " " << get_bound_kind() << " v" << get_var(); } +}; - 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_num_factorizations; - 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_make_feasible; - unsigned m_max_cols; - unsigned m_max_rows; - stats() { reset(); } - void reset() { - memset(this, 0, sizeof(*this)); - } - }; +std::ostream& operator<<(std::ostream& out, bound const& b) { + return b.display(out); +} - typedef optional opt_inf_rational; +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_delayed_terms_lim; - unsigned m_delayed_equalities_lim; - unsigned m_delayed_defs_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) {} - }; - - 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(); } - }; - - - 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; - struct delayed_def { - vector m_coeffs; - svector m_vars; - rational m_coeff; - theory_var m_var; - delayed_def(svector const& vars, vector const& coeffs, rational const& r, theory_var v): - m_coeffs(coeffs), m_vars(vars), m_coeff(r), m_var(v) {} - }; - - 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 - - bool m_delay_constraints; // configuration - svector m_asserted_atoms; - app_ref_vector m_delayed_terms; - svector> m_delayed_equalities; - vector m_delayed_defs; - 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; - - - struct var_value_eq { - imp & m_th; - var_value_eq(imp & th):m_th(th) {} - bool operator()(theory_var v1, theory_var v2) const { return m_th.get_ivalue(v1) == m_th.get_ivalue(v2) && m_th.is_int(v1) == m_th.is_int(v2); } - }; - struct var_value_hash { - imp & m_th; - var_value_hash(imp & th):m_th(th) {} - unsigned operator()(theory_var v) const { return (unsigned)std::hash()(m_th.get_ivalue(v)); } - }; - int_hashtable m_model_eqs; - - - svector m_scopes; - lra_lp::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_propagate_bounds_on_pivoted_rows_mode(lp.bprop_on_pivoted_rows()); - //m_solver->settings().set_ostream(0); - } - - void found_not_handled(expr* n) { - m_not_handled = n; - if (is_app(n) && is_underspecified(to_app(n))) { - m_underspecified.push_back(to_app(n)); - } - TRACE("arith", tout << "Unhandled: " << mk_pp(n, m) << "\n";); - } - - 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_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); - } - } - } - - 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); - } - - 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); // TBD: 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 (unsigned i = 0; i < v.size(); ++i) { - if (!v[i].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(delayed_def const& d) { - scoped_internalize_state st(*this); - st.vars().append(d.m_vars); - st.coeffs().append(d.m_coeffs); - init_left_side(st); - add_def_constraint(m_solver->add_constraint(m_left_side, lp::EQ, -d.m_coeff), d.m_var); - } - - 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]; - lra_lp::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", 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; - } - } + 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) {} + }; + class resource_limit : public lp::lp_resource_limit { + imp& m_imp; 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_delay_constraints(false), - m_delayed_terms(m), - m_not_handled(nullptr), - m_asserted_qhead(0), - m_assume_eq_head(0), - m_num_conflicts(0), - m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), - m_solver(nullptr), - m_resource_limit(*this) { - } + resource_limit(imp& i): m_imp(i) { } + virtual bool get_cancel_flag() { return m_imp.m.canceled(); } + }; - ~imp() { - del_bounds(0); - std::for_each(m_internalize_states.begin(), m_internalize_states.end(), delete_proc()); - } - void init(context* ctx) { - init_solver(); - } + 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; + - bool internalize_atom(app * atom, bool gate_ctx) { - if (m_delay_constraints) { - return internalize_atom_lazy(atom, gate_ctx); + // 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 internalize_atom_strict(atom, gate_ctx); + return (unsigned)std::hash()(m_th.get_ivalue(v)); } } + }; + int_hashtable m_model_eqs; - 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; - lra_lp::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 = lra_lp::upper_t; + + 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()); + + // todo : do not use m_arith_branch_cut_ratio for deciding on cheap cuts + unsigned branch_cut_ratio = ctx().get_fparams().m_arith_branch_cut_ratio; + m_solver->set_cut_strategy(branch_cut_ratio); + + m_solver->settings().m_int_run_gcd_test = ctx().get_fparams().m_arith_gcd_test; + m_solver->settings().set_random_seed(ctx().get_fparams().m_random_seed); + 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 (auto const& _s : m_scopes) { + (void)_s; + m_nra->push(); } - else if (a.is_ge(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { - v = internalize_def(to_app(n1)); - k = lra_lp::lower_t; - } - else { - TRACE("arith", tout << "Could not internalize " << mk_pp(atom, m) << "\n";); - found_not_handled(atom); + } + } + + + 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; } - lra_lp::bound* b = alloc(lra_lp::bound, bv, 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", tout << "Internalized " << mk_pp(atom, m) << "\n";); - mk_bound_axioms(*b); - //add_use_lists(b); - return true; - } - - bool internalize_atom_lazy(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; - lra_lp::bound_kind k; - theory_var v = null_theory_var; - scoped_internalize_state st(*this); - if (a.is_le(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { - v = internalize_def(to_app(n1), st); - k = lra_lp::upper_t; - } - else if (a.is_ge(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { - v = internalize_def(to_app(n1), st); - k = lra_lp::lower_t; - } - else { - TRACE("arith", tout << "Could not internalize " << mk_pp(atom, m) << "\n";); - found_not_handled(atom); - return true; - } - lra_lp::bound* b = alloc(lra_lp::bound, bv, 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", tout << "Internalized " << mk_pp(atom, m) << "\n";); - if (!is_unit_var(st) && m_bounds[v].size() == 1) { - m_delayed_defs.push_back(delayed_def(st.vars(), st.coeffs(), st.coeff(), v)); - } - return true; - } - - bool internalize_term(app * term) { - if (ctx().e_internalized(term) && th.is_attached_to_var(ctx().get_enode(term))) { - // skip - } - else if (m_delay_constraints) { - scoped_internalize_state st(*this); - linearize_term(term, st); // ensure that a theory_var was created. - SASSERT(ctx().e_internalized(term)); - if(!th.is_attached_to_var(ctx().get_enode(term))) { - mk_var(term); - } - m_delayed_terms.push_back(term); - } - 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", 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) { - if (m_delay_constraints) { - m_delayed_equalities.push_back(std::make_pair(v1, v2)); - } - else { - // 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_delayed_terms_lim = m_delayed_terms.size(); - s.m_delayed_equalities_lim = m_delayed_equalities.size(); - s.m_delayed_defs_lim = m_delayed_defs.size(); - s.m_not_handled = m_not_handled; - s.m_underspecified_lim = m_underspecified.size(); - s.m_var_trail_lim = m_var_trail.size(); - if (!m_delay_constraints) m_solver->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_delayed_terms.shrink(m_scopes[old_size].m_delayed_terms_lim); - m_delayed_defs.shrink(m_scopes[old_size].m_delayed_defs_lim); - m_delayed_equalities.shrink(m_scopes[old_size].m_delayed_equalities_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); - if (!m_delay_constraints) m_solver->pop(num_scopes); - // VERIFY(l_false != make_feasible()); - m_new_bounds.reset(); - m_to_check.reset(); - 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]); - } - - lp::impq get_ivalue(theory_var v) const { - SASSERT(can_get_ivalue(v)); - lp::var_index vi = m_theory_var2var_index[v]; - if (!m_solver->is_term(vi)) - return m_solver->get_value(vi); - - const lp::lar_term& term = m_solver->get_term(vi); - lp::impq result(term.m_v); - for (const auto & i: term.m_coeffs) { - result += m_solver->get_value(i.first) * i.second; - } - 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]; - } - if (m_solver->is_term(vi)) { - const lp::lar_term& term = m_solver->get_term(vi); - rational result = term.m_v; - for (auto i = term.m_coeffs.begin(); i != term.m_coeffs.end(); ++i) { - result += m_variable_values[i->first] * i->second; - } - m_variable_values[vi] = result; - return result; - } - UNREACHABLE(); - return m_variable_values[vi]; - } - - 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"; - ); - 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", - get_ivalue(v1) == get_ivalue(v2) && n1->get_root() != n2->get_root(), - tout << "assuming eq: v" << v1 << " = v" << v2 << "\n";); - if (get_ivalue(v1) == get_ivalue(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 has_delayed_constraints() const { - return !(m_asserted_atoms.empty() && m_delayed_terms.empty() && m_delayed_equalities.empty()); + 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)) { + for (expr* arg : *to_app(n)) { + st.push(arg, 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()) { + bool is_first = !ctx().e_internalized(n); + app* t = to_app(n); + internalize_args(t); + mk_enode(t); + + theory_var v = mk_var(n); + coeffs[vars.size()] = coeffs[index]; + vars.push_back(v); + ++index; + if (!is_first) { + // skip recursive internalization + } + else if (a.is_to_int(n, n1)) { + if (!ctx().relevancy()) + mk_to_int_axiom(t); + } + else if (a.is_to_real(n, n1)) { + theory_var v1 = mk_var(n1); + internalize_eq(v, v1); + } + else if (a.is_idiv(n, n1, n2)) { + if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n); + app * mod = a.mk_mod(n1, n2); + ctx().internalize(mod, false); + if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); + } + else if (a.is_mod(n, n1, n2)) { + bool is_num = a.is_numeral(n2, r) && !r.is_zero(); + if (!is_num) { + found_not_handled(n); + } + else { + app_ref div(a.mk_idiv(n1, n2), m); + mk_enode(div); + theory_var w = mk_var(div); + theory_var u = mk_var(n1); + // add axioms: + // u = v + r*w + // abs(r) > v >= 0 + assert_idiv_mod_axioms(u, v, w, r); + } + if (!ctx().relevancy() && !is_num) mk_idiv_mod_axioms(n1, n2); + } + else if (a.is_rem(n, n1, n2)) { + if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n); + if (!ctx().relevancy()) mk_rem_axiom(n1, n2); + } + else if (a.is_div(n, n1, n2)) { + if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n); + if (!ctx().relevancy()) mk_div_axiom(n1, n2); + } + else if (a.is_power(n)) { + found_not_handled(n); + } + else { + found_not_handled(n); + } + } + else { + 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; + } } + for (unsigned i = st.terms_to_internalize().size(); i-- > 0; ) { + 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() { - lbool is_sat = l_true; - if (m_delay_constraints) { - init_solver(); - for (unsigned i = 0; i < m_asserted_atoms.size(); ++i) { - bool_var bv = m_asserted_atoms[i].m_bv; - assert_bound(bv, m_asserted_atoms[i].m_is_true, *m_bool_var2bound.find(bv)); - } - for (unsigned i = 0; i < m_delayed_terms.size(); ++i) { - internalize_def(m_delayed_terms[i].get()); - } - for (unsigned i = 0; i < m_delayed_defs.size(); ++i) { - internalize_eq(m_delayed_defs[i]); - } - for (unsigned i = 0; i < m_delayed_equalities.size(); ++i) { - std::pair const& eq = m_delayed_equalities[i]; - internalize_eq(eq.first, eq.second); - } - 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); } - else if (m_solver->get_status() != lp::lp_status::OPTIMAL) { - is_sat = make_feasible(); + } + } + + 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(); + if (a.is_mul(n)) { + for (expr* arg : *to_app(n)) { + todo.push_back(arg); + } } - switch (is_sat) { - case l_true: - if (delayed_assume_eqs()) { - return FC_CONTINUE; + else if (a.is_numeral(n, r1)) { + r *= r1; + } + else { + if (!ctx().e_internalized(n)) { + ctx().internalize(n, false); } - if (assume_eqs()) { - return FC_CONTINUE; - } - if (m_not_handled != nullptr) { - return FC_GIVEUP; - } - return FC_DONE; - case l_false: - set_conflict(); - return FC_CONTINUE; - case l_undef: - return m.canceled() ? FC_CONTINUE : FC_GIVEUP; + vars.push_back(get_var_index(mk_var(n))); + } + } + TRACE("arith", tout << mk_pp(t, m) << " " << _has_var << "\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: - UNREACHABLE(); break; } - return FC_GIVEUP; } + return false; + } + bool reflect(app* n) const { + return m_arith_params.m_arith_reflect || is_underspecified(n); + } - /** - \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 has_var(expr* n) { + if (!ctx().e_internalized(n)) { + return false; + } + enode* e = get_enode(n); + return th.is_attached_to_var(e); + } - 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()) { + 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) { + m_constraint_sources.setx(index, definition_source, null_source); + m_definitions.setx(index, null_theory_var, null_theory_var); + ++m_stats.m_add_rows; + } + + 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); + expr* o1 = n1->get_owner(); + expr* o2 = n2->get_owner(); + app_ref term(m.mk_fresh_const("eq", a.mk_real()), m); + 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()); + theory_var z = internalize_linearized_def(term, st); + lp::var_index vi = get_var_index(z); + add_def_constraint(m_solver->add_var_bound(vi, lp::LE, rational::zero())); + add_def_constraint(m_solver->add_var_bound(vi, lp::GE, rational::zero())); + TRACE("arith", + tout << "v" << v1 << " = " << "v" << v2 << ": " + << mk_pp(o1, m) << " = " << mk_pp(o2, 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) { + if (ctx().e_internalized(term)) { + IF_VERBOSE(0, verbose_stream() << "repeated term\n";); + return mk_var(term, false); + } + 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); + return internalize_linearized_def(term, st); + } + + theory_var internalize_linearized_def(app* term, scoped_internalize_state& 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); + TRACE("arith", tout << mk_pp(term, m) << " " << v << " " << vi << "\n";); + 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(); + } + + void internalize_is_int(app * n) { + SASSERT(a.is_is_int(n)); + (void) mk_enode(n); + if (!ctx().relevancy()) + mk_is_int_axiom(n); + } + + bool internalize_atom(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 if (a.is_is_int(atom)) { + internalize_is_int(atom); + return true; + } + 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); + } + + // create axiom for + // u = v + r*w, + /// abs(r) > r >= 0 + void assert_idiv_mod_axioms(theory_var u, theory_var v, theory_var w, rational const& r) { + app_ref term(m); + term = a.mk_sub(get_enode(u)->get_owner(), + a.mk_add(get_enode(v)->get_owner(), + a.mk_mul(a.mk_numeral(r, true), + get_enode(w)->get_owner()))); + theory_var z = internalize_def(term); + lp::var_index vi = get_var_index(z); + add_def_constraint(m_solver->add_var_bound(vi, lp::GE, rational::zero())); + add_def_constraint(m_solver->add_var_bound(vi, lp::LE, rational::zero())); + add_def_constraint(m_solver->add_var_bound(get_var_index(v), lp::GE, rational::zero())); + add_def_constraint(m_solver->add_var_bound(get_var_index(v), lp::LT, abs(r))); + } + + 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 + // enable_trace("mk_bool_var"); + 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); + ctx().add_rel_watch(~l1, ctx().bool_var2expr(l2.var())); + } + } + + void mk_axiom(literal l1, literal l2, literal l3) { + ctx().mk_th_axiom(get_id(), l1, l2, l3); + if (ctx().relevancy()) { + ctx().mark_as_relevant(l1); + ctx().add_rel_watch(~l1, ctx().bool_var2expr(l2.var())); + ctx().add_rel_watch(~l2, ctx().bool_var2expr(l3.var())); + } + } + + literal mk_literal(expr* e) { + expr_ref pinned(e, m); + TRACE("mk_bool_var", tout << pinned << " " << pinned->get_id() << "\n";); + 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_bound(theory_var v) const { + return + (v != null_theory_var) && + (v < static_cast(m_theory_var2var_index.size())) && + (UINT_MAX != m_theory_var2var_index[v]); + } + + + 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.canceled() && 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_CONTINUE; + 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 is lower_bound is true, and term <= k if it is false + app_ref mk_bound(lp::lar_term const& term, rational const& k, bool lower_bound) { + app_ref t = mk_term(term, k.is_int()); + app_ref atom(m); + if (lower_bound) { + atom = a.mk_ge(t, a.mk_numeral(k, k.is_int())); + } + else { + atom = a.mk_le(t, a.mk_numeral(k, k.is_int())); + } + 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";); + 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 + bool upper; + switch(m_lia->check(term, k, ex, upper)) { + case lp::lia_move::sat: + return l_true; + case lp::lia_move::branch: { + app_ref b = mk_bound(term, k, !upper); + // branch on term >= k + 1 + // branch on term <= k + // TBD: ctx().force_phase(ctx().get_literal(b)); + // 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, !upper); + 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); + } + } + literal lit(ctx().get_bool_var(b), false); + TRACE("arith", + ctx().display_lemma_as_smt_problem(tout << "new cut:\n", m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); + display(tout);); + assign(lit); + 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::undef: + TRACE("arith", tout << "lia undef\n";); + return l_undef; + case lp::lia_move::continue_with_check: + 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. } +#endif + 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; - + void propagate() { + enable_trace("arith"); + 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); + m_to_check.push_back(bv); #else - propagate_bound(bv, is_true, b); + propagate_bound(bv, is_true, b); #endif - if (!m_delay_constraints) { - lra_lp::bound& b = *m_bool_var2bound.find(bv); - assert_bound(bv, is_true, b); - } - - ++m_asserted_qhead; - } - if (m_delay_constraints || 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; - lra_lp::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; - } + 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); + }*/ - 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) { - lra_lp::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)) { return false; } - - struct local_bound_propagator: public lp::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) override { - 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); + 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) {} - void propagate_lp_solver_bound(lp::implied_bound& be) { + bool bound_is_interesting(unsigned j, lp::lconstraint_kind kind, const rational & v) { + return m_imp.bound_is_interesting(j, kind, v); + } - 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); + void consume(rational const& v, lp::constraint_index j) override { + 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) { + + 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; } - else { - v = m_var_index2theory_var.get(vi, null_theory_var); + 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";); - 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); + 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; - if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { - TRACE("arith", tout << "return\n";); - return; + 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); } - lp_bounds const& bounds = m_bounds[v]; - bool first = true; - for (unsigned i = 0; i < bounds.size(); ++i) { - lra_lp::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; - } - - m_solver->settings().st().m_num_of_implied_bounds ++; - if (first) { - first = false; - m_core.reset(); - m_eqs.reset(); - m_params.reset(); - 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); + 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); } - literal_vector m_core2; + return null_literal; + } - void assign(literal lit) { - SASSERT(validate_assign(lit)); - if (m_core.size() < small_lemma_size() && m_eqs.empty()) { - m_core2.reset(); - for (unsigned i = 0; i < m_core.size(); ++i) { - m_core2.push_back(~m_core[i]); - } - 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()))); - } + 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]; - literal is_bound_implied(lp::lconstraint_kind k, rational const& value, lra_lp::bound const& b) const { - if ((k == lp::LE || k == lp::LT) && b.get_bound_kind() == lra_lp::upper_t && value <= b.get_value()) { - // v <= value <= b.get_value() => v <= b.get_value() - return literal(b.get_bv(), false); - } - if ((k == lp::GE || k == lp::GT) && b.get_bound_kind() == lra_lp::lower_t && b.get_value() <= value) { - // b.get_value() <= value <= v => b.get_value() <= v - return literal(b.get_bv(), false); - } - if (k == lp::LE && b.get_bound_kind() == lra_lp::lower_t && value < b.get_value()) { - // v <= value < b.get_value() => v < b.get_value() - return literal(b.get_bv(), true); - } - if (k == lp::LT && b.get_bound_kind() == lra_lp::lower_t && value <= b.get_value()) { - // v < value <= b.get_value() => v < b.get_value() - return literal(b.get_bv(), true); - } - if (k == lp::GE && b.get_bound_kind() == lra_lp::upper_t && b.get_value() < value) { - // b.get_value() < value <= v => b.get_value() < v - return literal(b.get_bv(), true); - } - if (k == lp::GT && b.get_bound_kind() == lra_lp::upper_t && b.get_value() <= value) { - // b.get_value() <= value < v => b.get_value() < v - return literal(b.get_bv(), true); + 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; } - return null_literal; - } - - void mk_bound_axioms(lra_lp::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(); - lra_lp::bound_kind kind1 = b.get_bound_kind(); - rational const& k1 = b.get_value(); - lp_bounds & bounds = m_bounds[v]; - - lra_lp::bound* end = nullptr; - lra_lp::bound* lo_inf = end, *lo_sup = end; - lra_lp::bound* hi_inf = end, *hi_sup = end; - - for (unsigned i = 0; i < bounds.size(); ++i) { - lra_lp::bound& other = *bounds[i]; - if (&other == &b) continue; - if (b.get_bv() == other.get_bv()) continue; - lra_lp::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 == lra_lp::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(lra_lp::bound& b1, lra_lp::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(); - lra_lp::bound_kind kind1 = b1.get_bound_kind(); - lra_lp::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 == lra_lp::lower_t) { - if (kind2 == lra_lp::lower_t) { - if (k2 <= k1) { - mk_clause(~l1, l2, 3, coeffs); - } - else { - mk_clause(l1, ~l2, 3, coeffs); + if (kind2 == lp_api::lower_t) { + if (k2 < k1) { + if (lo_inf == end || k2 > lo_inf->get_value()) { + lo_inf = &other; } } - 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 (lo_sup == end || k2 < lo_sup->get_value()) { + lo_sup = &other; } } - else if (kind2 == lra_lp::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 if (k2 < k1) { + if (hi_inf == end || k2 > hi_inf->get_value()) { + hi_inf = &other; } } - 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 + 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); } - } - } - - 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(lra_lp::lower_t, begin1, end); - begin2 = first(lra_lp::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) { - lra_lp::bound* a1 = atoms[i]; - lo_inf1 = next_inf(a1, lra_lp::lower_t, lo_inf, end, flo_inf); - hi_inf1 = next_inf(a1, lra_lp::upper_t, hi_inf, end, fhi_inf); - lo_sup1 = next_sup(a1, lra_lp::lower_t, lo_sup, end, flo_sup); - hi_sup1 = next_sup(a1, lra_lp::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()(lra_lp::bound* a1, lra_lp::bound* a2) const { return a1->get_value() < a2->get_value(); } - }; - - - lp_bounds::iterator first( - lra_lp::bound_kind kind, - iterator it, - iterator end) { - for (; it != end; ++it) { - lra_lp::bound* a = *it; - if (a->get_bound_kind() == kind) return it; - } - return end; - } - - lp_bounds::iterator next_inf( - lra_lp::bound* a1, - lra_lp::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) { - lra_lp::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; + mk_clause(l1, ~l2, 3, coeffs); } } - return result; - } - - lp_bounds::iterator next_sup( - lra_lp::bound* a1, - lra_lp::bound_kind kind, - iterator it, - iterator end, - bool& found_compatible) { - rational const & k1(a1->get_value()); - found_compatible = false; - for (; it != end; ++it) { - lra_lp::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) { - lra_lp::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, lra_lp::bound& b) { - if (BP_NONE == propagation_mode()) { - return; - } - lra_lp::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 == lra_lp::lower_t)); - if (find_glb) { - rational glb; - lra_lp::bound* lb = nullptr; - for (unsigned i = 0; i < bounds.size(); ++i) { - lra_lp::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() != lra_lp::lower_t; - lit2 = literal(lb->get_bv(), sign); + else if (k1 <= k2) { + // k1 <= k2, k1 <= x or x <= k2 + mk_clause(l1, l2, 3, coeffs); } else { - rational lub; - lra_lp::bound* ub = nullptr; - for (unsigned i = 0; i < bounds.size(); ++i) { - lra_lp::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; - } + // 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 (!ub) return; - bool sign = ub->get_bound_kind() != lra_lp::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; } + 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); + } + } + } - void add_use_lists(lra_lp::bound* b) { - theory_var v = b->get_var(); - lp::var_index vi = get_var_index(v); - if (m_solver->is_term(vi)) { - 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; + 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.reserve(w + 1, ptr_vector()); m_use_list[w].push_back(b); } } } + } - void del_use_lists(lra_lp::bound* b) { - theory_var v = b->get_var(); - lp::var_index vi = m_theory_var2var_index[v]; - if (m_solver->is_term(vi)) { - 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; + 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, lra_lp::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; + // + // 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; } - 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); } - inf_rational r; - // x + y - // x >= 0, y >= 1 -> x + y >= 1 - // x <= 0, y <= 2 -> x + y <= 2 - literal lit = null_literal; - if (lra_lp::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";); + else if (get_lub(*vb, r) && r < vb->get_value()) { // vb is assigned false + lit = literal(vb->get_bv(), true); } } - } - - bool get_lub(lra_lp::bound const& b, inf_rational& lub) { - return get_bound(b, lub, true); - } - - bool get_glb(lra_lp::bound const& b, inf_rational& glb) { - return get_bound(b, glb, false); - } - - std::ostream& display_bound(std::ostream& out, lra_lp::bound const& b) { - return out << mk_pp(ctx().bool_var2expr(b.get_bv()), m); - } - - bool get_bound(lra_lp::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 (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 (get_glb(*vb, r) && r > vb->get_value()) { // VB <= value < val(VB) + lit = literal(vb->get_bv(), true); } - 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()); - } + else if (get_lub(*vb, r) && r <= vb->get_value()) { // val(VB) <= value + lit = literal(vb->get_bv(), false); } - r += value * coeff.second; - set_evidence(ci); - } - TRACE("arith_verbose", tout << (is_lub?"lub":"glb") << " is " << r << "\n";); - return true; - } + } + + if (lit != null_literal) { + TRACE("arith", + ctx().display_literals_verbose(tout, m_core); + tout << "\n --> "; + ctx().display_literal_verbose(tout, lit); + tout << "\n"; + ); + - void assert_bound(bool_var bv, bool is_true, lra_lp::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; - switch (b.get_bound_kind()) { - case lra_lp::lower_t: - k = is_true ? lp::GE : lp::LT; - break; - case lra_lp::upper_t: - k = is_true ? lp::LE : lp::GT; - break; - } - if (k == lp::LT || k == lp::LE) { - ++m_stats.m_assert_lower; + assign(lit); } else { - ++m_stats.m_assert_upper; - } - auto vi = get_var_index(b.get_var()); - auto 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, lra_lp::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); - } - } + TRACE("arith_verbose", display_bound(tout << "skip ", *vb) << "\n";); } } + } - bool dump_lemmas() const { return m_arith_params.m_arith_dump_lemmas; } + bool get_lub(lp_api::bound const& b, inf_rational& lub) { + return get_bound(b, lub, true); + } - bool propagate_eqs() const { return m_arith_params.m_arith_propagate_eqs && m_num_conflicts < m_arith_params.m_arith_propagation_threshold; } + bool get_glb(lp_api::bound const& b, inf_rational& glb) { + return get_bound(b, glb, false); + } - 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; } + std::ostream& display_bound(std::ostream& out, lp_api::bound const& b) { + return out << mk_pp(ctx().bool_var2expr(b.get_bv()), m); + } - 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; + 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; } - 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; } - - lbool make_feasible() { - reset_variable_values(); - ++m_stats.m_make_feasible; - if (m_solver->A_r().column_count() > m_stats.m_max_cols) - m_stats.m_max_cols = m_solver->A_r().column_count(); - if (m_solver->A_r().row_count() > m_stats.m_max_rows) - m_stats.m_max_rows = m_solver->A_r().row_count(); - TRACE("arith_verbose", display(tout);); - lp::lp_status status = m_solver->find_feasible_solution(); - m_stats.m_num_iterations = m_solver->settings().st().m_total_iterations; - m_stats.m_num_factorizations = m_solver->settings().st().m_num_factorizations; - m_stats.m_need_to_solve_inf = m_solver->settings().st().m_need_to_solve_inf; - - 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; - } + 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())); } - - 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; - } + 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; } + } - void set_conflict() { - m_eqs.reset(); - m_core.reset(); - m_params.reset(); - m_explanation.clear(); - m_solver->get_infeasibility_explanation(m_explanation); - // 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); - } + 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; } - 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);); - } - - model_value_proc * mk_value(enode * n, model_generator & mg) { - theory_var v = n->get_th_var(get_id()); - return alloc(expr_wrapper_proc, m_factory->mk_value(get_value(v), m.get_sort(n->get_owner()))); - } - - 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. - - 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); - } - 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); - } - 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) { - 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; - if (vi == UINT_MAX) { - has_shared = false; - blocker = m.mk_false(); - return inf_eps(rational::one(), inf_rational()); - } - else if (m_solver->is_term(vi)) { - const lp::lar_term& term = m_solver->get_term(vi); - for (auto & ti : term.m_coeffs) { - 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(); + return m_solver->has_upper_bound(vi, ci, b, is_strict) && b == bound && !is_strict; } - 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); + } + } + + + 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 { - TRACE("arith", tout << "Unbounded " << v << "\n";); - has_shared = false; - blocker = m.mk_false(); - return inf_eps(rational::one(), inf_rational()); + // bounds on v2 were changed. + m_fixed_var_table.insert(key, v1); } } - - 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_gt(obj, e); - } - } - TRACE("opt", tout << "v" << v << " " << val << " " << r << " " << e << "\n";); - return e; + else { + m_fixed_var_table.insert(key, v1); } + } - theory_var add_objective(app* term) { - return internalize_def(term); + 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; - 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)) { - expr_ref_vector args(m); - const lp::lar_term& term = m_solver->get_term(vi); - for (auto & ti : term.m_coeffs) { - theory_var w = m_var_index2theory_var[ti.first]; - expr* o = get_enode(w)->get_owner(); - args.push_back(a.mk_mul(a.mk_numeral(ti.second, is_int), o)); - } - args.push_back(a.mk_numeral(term.m_v, is_int)); - return app_ref(a.mk_add(args.size(), args.c_ptr()), m); - } - else { - theory_var w = m_var_index2theory_var[vi]; - return app_ref(get_enode(w)->get_owner(), m); + // 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()))); + } - 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)); - } - 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); - lra_lp::bound_kind bkind = lra_lp::bound_kind::lower_t; - if (is_strict) bkind = lra_lp::bound_kind::upper_t; - lra_lp::bound* a = alloc(lra_lp::bound, bv, v, 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); + 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); + } + } + } + return r; } - - - 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"; - } + else { + return m_nra->value(vi); } + } - 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; - } - } - for (auto const& ev : evidence) { - m_solver->print_constraint(ev.second, out << ev.first << ": "); + 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))); + } + } - 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_stats.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_stats.m_make_feasible); - st.update("arith-max-columns", m_stats.m_max_cols); - st.update("arith-max-rows", m_stats.m_max_rows); + 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 get_lower(enode* n, expr_ref& r) { + theory_var v = n->get_th_var(get_id()); + if (!can_get_bound(v)) { + TRACE("arith", tout << "cannot get lower for " << v << "\n";); + return false; + } + lp::var_index vi = m_theory_var2var_index[v]; + lp::constraint_index ci; + rational val; + bool is_strict; + if (m_solver->has_lower_bound(vi, ci, val, is_strict)) { + r = a.mk_numeral(val, is_int(n)); + return true; + } + TRACE("arith", m_solver->print_constraints(tout << "does not have lower bound " << vi << "\n");); + return false; + } + + bool get_upper(enode* n, expr_ref& r) { + theory_var v = n->get_th_var(get_id()); + if (!can_get_bound(v)) + return false; + lp::var_index vi = m_theory_var2var_index[v]; + lp::constraint_index ci; + rational val; + bool is_strict; + if (m_solver->has_upper_bound(vi, ci, val, is_strict)) { + r = a.mk_numeral(val, is_int(n)); + return true; + } + TRACE("arith", m_solver->print_constraints(tout << "does not have upper bound " << vi << "\n");); + 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_OLD_ARITH; + } + ~scoped_arith_mode() { + p.m_arith_mode = AS_NEW_ARITH; } }; - theory_lra::theory_lra(ast_manager& m, theory_arith_params& ap): - theory(m.get_family_id("arith")) { - m_imp = alloc(imp, *this, m, ap); + 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_NEW_ARITH) 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; } - theory_lra::~theory_lra() { - dealloc(m_imp); + + 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_NEW_ARITH) 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; } - theory* theory_lra::mk_fresh(context* new_ctx) { - return alloc(theory_lra, new_ctx->get_manager(), new_ctx->get_fparams()); + + bool validate_eq(enode* x, enode* y) { + if (m_arith_params.m_arith_mode == AS_NEW_ARITH) 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 theory_lra::init(context * ctx) { - theory::init(ctx); - m_imp->init(ctx); + + 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)); } - bool theory_lra::internalize_atom(app * atom, bool gate_ctx) { - return m_imp->internalize_atom(atom, gate_ctx); + + theory_lra::inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) { + lp::impq term_max; + lp::lp_status st; + if (!can_get_bound(v)) { + st = lp::lp_status::UNBOUNDED; + } + else { + lp::var_index vi = m_theory_var2var_index[v]; + st = m_solver->maximize_term(vi, term_max); + } + switch (st) { + case lp::lp_status::OPTIMAL: { + inf_rational val(term_max.x, term_max.y); + blocker = mk_gt(v); + return inf_eps(rational::zero(), val); + } + case lp::lp_status::FEASIBLE: { + inf_rational val(term_max.x, term_max.y); + // todo , TODO , not sure what happens here + return inf_eps(rational::zero(), val); + } + default: + SASSERT(st == lp::lp_status::UNBOUNDED); + TRACE("arith", tout << "Unbounded v" << v << "\n";); + has_shared = false; + blocker = m.mk_false(); + return inf_eps(rational::one(), inf_rational()); + } } - bool theory_lra::internalize_term(app * term) { - return m_imp->internalize_term(term); + + 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_gt(obj, e); + } + } + TRACE("opt", tout << "v" << v << " " << val << " " << r << " " << e << "\n";); + return e; } - void theory_lra::internalize_eq_eh(app * atom, bool_var v) { - m_imp->internalize_eq_eh(atom, v); + + theory_var add_objective(app* term) { + return internalize_def(term); } - void theory_lra::assign_eh(bool_var v, bool is_true) { - m_imp->assign_eh(v, is_true); + + 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)); + } + switch (args.size()) { + case 0: + return app_ref(a.mk_numeral(rational::zero(), is_int), m); + case 1: + return app_ref(to_app(args[0].get()), m); + default: + return app_ref(a.mk_add(args.size(), args.c_ptr()), m); + } } - void theory_lra::new_eq_eh(theory_var v1, theory_var v2) { - m_imp->new_eq_eh(v1, v2); + + 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); + } } - bool theory_lra::use_diseqs() const { - return m_imp->use_diseqs(); + + 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)); + } + else { + b = a.mk_ge(mk_obj(v), a.mk_numeral(r, is_int)); + } + if (!ctx().b_internalized(b)) { + fm.hide(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 theory_lra::new_diseq_eh(theory_var v1, theory_var v2) { - m_imp->new_diseq_eh(v1, v2); + + + void display(std::ostream & out) const { + if (m_solver) { + m_solver->print_constraints(out); + m_solver->print_terms(out); + // auto pp = lp ::core_solver_pretty_printer(m_solver->m_mpq_lar_core_solver.m_r_solver, out); + // pp.print(); + } + unsigned nv = th.get_num_vars(); + for (unsigned v = 0; v < nv; ++v) { + if (!ctx().is_relevant(get_enode(v))) out << "irr: "; + out << "v" << v; + if (can_get_value(v)) out << " = " << get_value(v); + if (is_int(v)) out << ", int"; + if (ctx().is_shared(get_enode(v))) out << ", shared"; + out << " := "; th.display_var_flat_def(out, v) << "\n"; + } } - 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 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]; + if (v != null_theory_var) + 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); + st.update("gcd-calls", m_solver->settings().st().m_gcd_calls); + st.update("gcd-conflict", m_solver->settings().st().m_gcd_conflicts); + st.update("cube-calls", m_solver->settings().st().m_cube_calls); + st.update("cube-success", m_solver->settings().st().m_cube_success); + st.update("arith-patches", m_solver->settings().st().m_patches); + st.update("arith-patches-success", m_solver->settings().st().m_patches_success); + st.update("arith-hnf-calls", m_solver->settings().st().m_hnf_cutter_calls); + st.update("arith-hnf-cuts", m_solver->settings().st().m_hnf_cuts); + } +}; + +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::get_lower(enode* n, expr_ref& r) { + return m_imp->get_lower(n, r); +} +bool theory_lra::get_upper(enode* n, expr_ref& r) { + return m_imp->get_upper(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); +} + } diff --git a/src/smt/theory_lra.h b/src/smt/theory_lra.h index 4b1f67b79..074b11ba7 100644 --- a/src/smt/theory_lra.h +++ b/src/smt/theory_lra.h @@ -31,7 +31,7 @@ namespace smt { theory_lra(ast_manager& m, theory_arith_params& ap); ~theory_lra() override; theory* mk_fresh(context* new_ctx) override; - char const* get_name() const override { return "lra"; } + char const* get_name() const override { return "arithmetic"; } void init(context * ctx) override; @@ -78,6 +78,8 @@ namespace smt { model_value_proc * mk_value(enode * n, model_generator & mg) override; bool get_value(enode* n, expr_ref& r) override; + bool get_lower(enode* n, expr_ref& r); + bool get_upper(enode* n, expr_ref& r); bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const override; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 40aae8043..3d80aa2dd 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -26,6 +26,7 @@ Revision History: #include "smt/smt_model_generator.h" #include "smt/theory_seq.h" #include "smt/theory_arith.h" +#include "smt/theory_lra.h" #include "smt/smt_kernel.h" using namespace smt; @@ -4551,6 +4552,8 @@ static bool get_arith_value(context& ctx, theory_id afid, expr* e, expr_ref& v) if (tha) return tha->get_value(ctx.get_enode(e), v); theory_i_arith* thi = get_th_arith(ctx, afid, e); if (thi) return thi->get_value(ctx.get_enode(e), v); + theory_lra* thr = get_th_arith(ctx, afid, e); + if (thr) return thr->get_value(ctx.get_enode(e), v); TRACE("seq", tout << "no arithmetic theory\n";); return false; } @@ -4576,12 +4579,18 @@ bool theory_seq::lower_bound(expr* _e, rational& lo) const { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); expr_ref _lo(m); - theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); - if (tha && !tha->get_lower(ctx.get_enode(e), _lo)) return false; - if (!tha) { - theory_i_arith* thi = get_th_arith(ctx, m_autil.get_family_id(), e); - if (!thi || !thi->get_lower(ctx.get_enode(e), _lo)) return false; + family_id afid = m_autil.get_family_id(); + do { + theory_mi_arith* tha = get_th_arith(ctx, afid, e); + if (tha && tha->get_lower(ctx.get_enode(e), _lo)) break; + theory_i_arith* thi = get_th_arith(ctx, afid, e); + if (thi && thi->get_lower(ctx.get_enode(e), _lo)) break; + theory_lra* thr = get_th_arith(ctx, afid, e); + if (thr && thr->get_lower(ctx.get_enode(e), _lo)) break; + TRACE("seq", tout << "no lower bound " << mk_pp(_e, m) << "\n";); + return false; } + while (false); return m_autil.is_numeral(_lo, lo) && lo.is_int(); } @@ -4627,13 +4636,19 @@ bool theory_seq::lower_bound2(expr* _e, rational& lo) { bool theory_seq::upper_bound(expr* _e, rational& hi) const { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); - theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); + family_id afid = m_autil.get_family_id(); expr_ref _hi(m); - if (tha && !tha->get_upper(ctx.get_enode(e), _hi)) return false; - if (!tha) { - theory_i_arith* thi = get_th_arith(ctx, m_autil.get_family_id(), e); - if (!thi || !thi->get_upper(ctx.get_enode(e), _hi)) return false; + do { + theory_mi_arith* tha = get_th_arith(ctx, afid, e); + if (tha && tha->get_upper(ctx.get_enode(e), _hi)) break; + theory_i_arith* thi = get_th_arith(ctx, afid, e); + if (thi && thi->get_upper(ctx.get_enode(e), _hi)) break; + theory_lra* thr = get_th_arith(ctx, afid, e); + if (thr && thr->get_upper(ctx.get_enode(e), _hi)) break; + TRACE("seq", tout << "no upper bound " << mk_pp(_e, m) << "\n";); + return false; } + while (false); return m_autil.is_numeral(_hi, hi) && hi.is_int(); } diff --git a/src/tactic/smtlogics/qflia_tactic.cpp b/src/tactic/smtlogics/qflia_tactic.cpp index 541a81682..46b766bd4 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -211,17 +211,23 @@ 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); + st->updt_params(p); return st; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 7367a52ff..c2da596fa 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(fuzzing) +add_subdirectory(lp) ################################################################################ # z3-test executable ################################################################################ @@ -120,6 +121,7 @@ add_executable(test-z3 upolynomial.cpp var_subst.cpp vector.cpp + lp/lp.cpp ${z3_test_extra_object_files} ) z3_add_install_tactic_rule(${z3_test_deps}) diff --git a/src/test/lp/CMakeLists.txt b/src/test/lp/CMakeLists.txt new file mode 100644 index 000000000..6683a1758 --- /dev/null +++ b/src/test/lp/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(lp_tst lp_main.cpp lp.cpp $ $ $ $ ) +target_compile_definitions(lp_tst PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) +target_compile_options(lp_tst PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) +target_include_directories(lp_tst PRIVATE ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}) +target_link_libraries(lp_tst PRIVATE ${Z3_DEPENDENT_LIBS}) +z3_append_linker_flag_list_to_target(lp_tst ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) diff --git a/src/test/argument_parser.h b/src/test/lp/argument_parser.h similarity index 100% rename from src/test/argument_parser.h rename to src/test/lp/argument_parser.h diff --git a/src/test/lp/gomory_test.h b/src/test/lp/gomory_test.h new file mode 100644 index 000000000..03ad5b187 --- /dev/null +++ b/src/test/lp/gomory_test.h @@ -0,0 +1,236 @@ +namespace lp { +struct gomory_test { + gomory_test( + std::function name_function_p, + std::function get_value_p, + std::function at_low_p, + std::function at_upper_p, + std::function lower_bound_p, + std::function upper_bound_p, + std::function column_lower_bound_constraint_p, + std::function column_upper_bound_constraint_p + ) : + m_name_function(name_function_p), + get_value(get_value_p), + at_low(at_low_p), + at_upper(at_upper_p), + lower_bound(lower_bound_p), + upper_bound(upper_bound_p), + column_lower_bound_constraint(column_lower_bound_constraint_p), + column_upper_bound_constraint(column_upper_bound_constraint_p) + {} + + std::function m_name_function; + std::function get_value; + std::function at_low; + std::function at_upper; + std::function lower_bound; + std::function upper_bound; + std::function column_lower_bound_constraint; + std::function column_upper_bound_constraint; + + bool is_real(unsigned) { return false; } // todo: test real case + void print_row(std::ostream& out, vector> & row ) { + bool first = true; + for (const auto & it : row) { + 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 << T_to_string(val); + + out << m_name_function(it.second); + } + } + + void real_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, lar_term& pol, explanation & expl, const mpq& f_0, const mpq& one_minus_f_0) { + TRACE("gomory_cut_detail_real", tout << "real\n";); + mpq new_a; + if (at_low(x_j)) { + if (a.is_pos()) { + new_a = a / (1 - f_0); + } + else { + new_a = a / f_0; + new_a.neg(); + } + 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_lower_bound_constraint(x_j), new_a); + } + else { + lp_assert(at_upper(x_j)); + if (a.is_pos()) { + new_a = a / f_0; + new_a.neg(); // the upper terms are inverted. + } + else { + new_a = a / (mpq(1) - f_0); + } + k.addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a; + expl.push_justification(column_upper_bound_constraint(x_j), new_a); + } + TRACE("gomory_cut_detail_real", tout << a << "*v" << x_j << " k: " << k << "\n";); + pol.add_monomial(new_a, x_j); + } + + void int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, lar_term & t, explanation& expl, mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0) { + lp_assert(is_int(x_j)); + lp_assert(!a.is_int()); + lp_assert(f_0 > zero_of_type() && f_0 < one_of_type()); + mpq f_j = int_solver::fractional_part(a); + TRACE("gomory_cut_detail", + tout << a << " x_j = " << x_j << ", k = " << k << "\n"; + tout << "f_j: " << f_j << "\n"; + tout << "f_0: " << f_0 << "\n"; + tout << "1 - f_0: " << one_minus_f_0 << "\n"; + tout << "at_low(" << x_j << ") = " << at_low(x_j) << std::endl; + ); + lp_assert (!f_j.is_zero()); + mpq new_a; + if (at_low(x_j)) { + if (f_j <= one_minus_f_0) { + new_a = f_j / one_minus_f_0; + } + else { + new_a = (1 - f_j) / f_0; + } + 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)); + if (f_j <= f_0) { + new_a = f_j / f_0; + } + else { + new_a = (mpq(1) - f_j) / (one_minus_f_0); + } + new_a.neg(); // the upper terms are inverted + k.addmul(new_a, upper_bound(x_j).x); + expl.push_justification(column_upper_bound_constraint(x_j), new_a); + } + TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << k << "\n";); + t.add_monomial(new_a, x_j); + lcm_den = lcm(lcm_den, denominator(new_a)); + } + + + void report_conflict_from_gomory_cut(mpq &k) { + lp_assert(false); + } + + void adjust_term_and_k_for_some_ints_case_gomory(lar_term& t, mpq& k, mpq &lcm_den) { + lp_assert(!t.is_empty()); + auto pol = t.coeffs_as_vector(); + t.clear(); + if (pol.size() == 1) { + TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); + unsigned v = pol[0].second; + lp_assert(is_int(v)); + const mpq& a = pol[0].first; + k /= a; + if (a.is_pos()) { // we have av >= k + if (!k.is_int()) + k = ceil(k); + // switch size + t.add_monomial(- mpq(1), v); + k.neg(); + } else { + if (!k.is_int()) + k = floor(k); + t.add_monomial(mpq(1), v); + } + } else { + TRACE("gomory_cut_detail", tout << "pol.size() > 1" << std::endl;); + lcm_den = lcm(lcm_den, denominator(k)); + TRACE("gomory_cut_detail", tout << "k: " << k << " lcm_den: " << lcm_den << "\n"; + for (unsigned i = 0; i < pol.size(); i++) { + tout << pol[i].first << " " << pol[i].second << "\n"; + } + tout << "k: " << k << "\n";); + lp_assert(lcm_den.is_pos()); + if (!lcm_den.is_one()) { + // normalize coefficients of integer parameters to be integers. + for (auto & pi: pol) { + pi.first *= lcm_den; + SASSERT(!is_int(pi.second) || pi.first.is_int()); + } + k *= lcm_den; + } + TRACE("gomory_cut_detail", tout << "after *lcm\n"; + for (unsigned i = 0; i < pol.size(); i++) { + tout << pol[i].first << " * v" << pol[i].second << "\n"; + } + tout << "k: " << k << "\n";); + + // negate everything to return -pol <= -k + for (const auto & pi: pol) + t.add_monomial(-pi.first, pi.second); + k.neg(); + } + TRACE("gomory_cut_detail", tout << "k = " << k << std::endl;); + lp_assert(k.is_int()); + } + + void print_term(lar_term & t, std::ostream & out) { + lp_assert(is_zero(t.m_v)); + vector> row; + for (auto p : t.m_coeffs) + row.push_back(std::make_pair(p.second, p.first)); + print_row(out, row); + } + + void mk_gomory_cut(lar_term& t, mpq& k, explanation & expl, unsigned inf_col, vector> & row) { + enable_trace("gomory_cut"); + enable_trace("gomory_cut_detail"); + + TRACE("gomory_cut", + tout << "applying cut at:\n"; print_row(tout, row); + tout << std::endl << "inf_col = " << inf_col << std::endl; + ); + + // gomory will be t >= k + k = 1; + mpq lcm_den(1); + unsigned x_j; + mpq a; + bool some_int_columns = false; + mpq f_0 = int_solver::fractional_part(get_value(inf_col)); + mpq one_min_f_0 = 1 - f_0; + for ( auto pp : row) { + a = pp.first; + x_j = pp.second; + if (x_j == inf_col) + continue; + // make the format compatible with the format used in: Integrating Simplex with DPLL(T) + a.neg(); + if (is_real(x_j)) + real_case_in_gomory_cut(a, x_j, k, t, expl, f_0, one_min_f_0); + else { + if (a.is_int()) continue; // f_j will be zero and no monomial will be added + some_int_columns = true; + int_case_in_gomory_cut(a, x_j, k, t, expl, lcm_den, f_0, one_min_f_0); + } + } + + if (t.is_empty()) + return report_conflict_from_gomory_cut(k); + if (some_int_columns) + adjust_term_and_k_for_some_ints_case_gomory(t, k, lcm_den); + + TRACE("gomory_cut", tout<<"new cut :"; print_term(t, tout); tout << " >= " << k << std::endl;); + + } +}; +} diff --git a/src/test/lp.cpp b/src/test/lp/lp.cpp similarity index 67% rename from src/test/lp.cpp rename to src/test/lp/lp.cpp index 0f4186252..35c9aeccb 100644 --- a/src/test/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1,22 +1,22 @@ /*++ -Copyright (c) 2017 Microsoft Corporation + Copyright (c) 2017 Microsoft Corporation -Module Name: + Module Name: - + -Abstract: + Abstract: - + -Author: + Author: - Lev Nachmanson (levnach) + Lev Nachmanson (levnach) -Revision History: + Revision History: ---*/ + --*/ #include #if _LINUX_ @@ -35,26 +35,38 @@ 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" #include "util/lp/binary_heap_upair_queue.h" #include "util/lp/stacked_value.h" -#include "util/lp/stacked_unordered_set.h" #include "util/lp/int_set.h" #include "util/stopwatch.h" - +#include +#include "test/lp/gomory_test.h" +#include "util/lp/matrix.h" +#include "util/lp/hnf.h" +#include "util/lp/square_sparse_matrix_def.h" +#include "util/lp/lu_def.h" +#include "util/lp/general_matrix.h" +#include "util/lp/bound_propagator.h" namespace lp { - unsigned seed = 1; +unsigned seed = 1; - random_gen g_rand; - static unsigned my_random() { - return g_rand(); - } + class my_bound_propagator : public bound_propagator { + public: + my_bound_propagator(lar_solver & ls): bound_propagator(ls) {} + void consume(mpq const& v, lp::constraint_index j) override {} + }; + +random_gen g_rand; +static unsigned my_random() { + return g_rand(); +} struct simple_column_namer:public column_namer { std::string get_column_name(unsigned j) const override { @@ -64,11 +76,11 @@ struct simple_column_namer:public column_namer template -void test_matrix(sparse_matrix & a) { +void test_matrix(square_sparse_matrix & a) { auto m = a.dimension(); -// copy a to b in the reversed order - sparse_matrix b(m); + // copy a to b in the reversed order + square_sparse_matrix b(m, m); std::cout << "copy b to a"<< std::endl; for (int row = m - 1; row >= 0; row--) for (int col = m - 1; col >= 0; col --) { @@ -95,7 +107,7 @@ void test_matrix(sparse_matrix & a) { a.set(i, j, t); - SASSERT(a.get(i, j) == t); + lp_assert(a.get(i, j) == t); unsigned j1; if (j < m - 1) { @@ -106,7 +118,7 @@ void test_matrix(sparse_matrix & a) { void tst1() { std::cout << "testing the minimial matrix with 1 row and 1 column" << std::endl; - sparse_matrix m0(1); + square_sparse_matrix m0(1, 1); m0.set(0, 0, 1); // print_matrix(m0); m0.set(0, 0, 0); @@ -114,7 +126,7 @@ void tst1() { test_matrix(m0); unsigned rows = 2; - sparse_matrix m(rows); + square_sparse_matrix m(rows, rows); std::cout << "setting m(0,1)=" << std::endl; m.set(0, 1, 11); @@ -124,7 +136,7 @@ void tst1() { test_matrix(m); - sparse_matrix m1(2); + square_sparse_matrix m1(2, 2); m1.set(0, 0, 2); m1.set(1, 0, 3); // print_matrix(m1); @@ -137,7 +149,7 @@ void tst1() { std::cout << "printing zero matrix 3 by 1" << std::endl; - sparse_matrix m2(3); + square_sparse_matrix m2(3, 3); // print_matrix(m2); m2.set(0, 0, 1); @@ -147,7 +159,7 @@ void tst1() { test_matrix(m2); - sparse_matrix m10by9(10); + square_sparse_matrix m10by9(10, 10); m10by9.set(0, 1, 1); m10by9(0, 1) = 4; @@ -187,7 +199,7 @@ vector allocate_basis_heading(unsigned count) { // the rest of initilizatio void init_basic_part_of_basis_heading(vector & basis, vector & basis_heading) { - SASSERT(basis_heading.size() >= basis.size()); + lp_assert(basis_heading.size() >= basis.size()); unsigned m = basis.size(); for (unsigned i = 0; i < m; i++) { unsigned column = basis[i]; @@ -199,9 +211,9 @@ void init_non_basic_part_of_basis_heading(vector & basis_heading, vector(non_basic_columns.size()); + non_basic_columns.push_back(j); + // the index of column j in m_nbasis is (- basis_heading[j] - 1) + basis_heading[j] = - static_cast(non_basic_columns.size()); } } } @@ -222,6 +234,7 @@ void change_basis(unsigned entering, unsigned leaving, vector& basis, } + #ifdef Z3DEBUG void test_small_lu(lp_settings & settings) { std::cout << " test_small_lu" << std::endl; @@ -241,8 +254,8 @@ void test_small_lu(lp_settings & settings) { vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu l(m, basis, settings); - SASSERT(l.is_correct(basis)); + lu> l(m, basis, settings); + lp_assert(l.is_correct(basis)); indexed_vector w(m.row_count()); std::cout << "entering 2, leaving 0" << std::endl; l.prepare_entering(2, w); // to init vector w @@ -252,7 +265,7 @@ void test_small_lu(lp_settings & settings) { // std::cout << "we were factoring " << std::endl; // print_matrix(get_B(l)); // #endif - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); std::cout << "entering 4, leaving 3" << std::endl; l.prepare_entering(4, w); // to init vector w l.replace_column(0, w, heading[3]); @@ -264,7 +277,7 @@ void test_small_lu(lp_settings & settings) { print_matrix(&bl, std::cout); } #endif - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); std::cout << "entering 5, leaving 1" << std::endl; l.prepare_entering(5, w); // to init vector w @@ -277,7 +290,7 @@ void test_small_lu(lp_settings & settings) { print_matrix(&bl, std::cout); } #endif - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); std::cout << "entering 3, leaving 2" << std::endl; l.prepare_entering(3, w); // to init vector w l.replace_column(0, w, heading[2]); @@ -289,7 +302,7 @@ void test_small_lu(lp_settings & settings) { print_matrix(&bl, std::cout); } #endif - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); m.add_row(); m.add_column(); @@ -308,12 +321,12 @@ void test_small_lu(lp_settings & settings) { auto columns_to_replace = l.get_set_of_columns_to_replace_for_add_last_rows(heading); l.add_last_rows_to_B(heading, columns_to_replace); std::cout << "here" << std::endl; - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); } #endif -void fill_long_row(sparse_matrix &m, int i) { +void fill_long_row(square_sparse_matrix &m, int i) { int n = m.dimension(); for (int j = 0; j < n; j ++) { m (i, (j + i) % n) = j * j; @@ -328,7 +341,7 @@ void fill_long_row(static_matrix &m, int i) { } -void fill_long_row_exp(sparse_matrix &m, int i) { +void fill_long_row_exp(square_sparse_matrix &m, int i) { int n = m.dimension(); for (int j = 0; j < n; j ++) { @@ -344,23 +357,23 @@ void fill_long_row_exp(static_matrix &m, int i) { } } -void fill_larger_sparse_matrix_exp(sparse_matrix & m){ +void fill_larger_square_sparse_matrix_exp(square_sparse_matrix & m){ for ( unsigned i = 0; i < m.dimension(); i++ ) fill_long_row_exp(m, i); } -void fill_larger_sparse_matrix_exp(static_matrix & m){ +void fill_larger_square_sparse_matrix_exp(static_matrix & m){ for ( unsigned i = 0; i < m.row_count(); i++ ) fill_long_row_exp(m, i); } -void fill_larger_sparse_matrix(sparse_matrix & m){ +void fill_larger_square_sparse_matrix(square_sparse_matrix & m){ for ( unsigned i = 0; i < m.dimension(); i++ ) fill_long_row(m, i); } -void fill_larger_sparse_matrix(static_matrix & m){ +void fill_larger_square_sparse_matrix(static_matrix & m){ for ( unsigned i = 0; i < m.row_count(); i++ ) fill_long_row(m, i); } @@ -381,16 +394,16 @@ void test_larger_lu_exp(lp_settings & settings) { basis[5] = 6; - fill_larger_sparse_matrix_exp(m); + fill_larger_square_sparse_matrix_exp(m); // print_matrix(m); vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu l(m, basis, settings); + lu> l(m, basis, settings); dense_matrix left_side = l.get_left_side(basis); dense_matrix right_side = l.get_right_side(); - SASSERT(left_side == right_side); + lp_assert(left_side == right_side); int leaving = 3; int entering = 8; for (unsigned i = 0; i < m.row_count(); i++) { @@ -402,12 +415,12 @@ void test_larger_lu_exp(lp_settings & settings) { l.prepare_entering(entering, w); l.replace_column(0, w, heading[leaving]); change_basis(entering, leaving, basis, non_basic_columns, heading); - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); l.prepare_entering(11, w); // to init vector w l.replace_column(0, w, heading[0]); change_basis(11, 0, basis, non_basic_columns, heading); - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); } void test_larger_lu_with_holes(lp_settings & settings) { @@ -430,13 +443,13 @@ void test_larger_lu_with_holes(lp_settings & settings) { vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu l(m, basis, settings); + lu> l(m, basis, settings); std::cout << "printing factorization" << std::endl; for (int i = l.tail_size() - 1; i >=0; i--) { auto lp = l.get_lp_matrix(i); lp->set_number_of_columns(m.row_count()); lp->set_number_of_rows(m.row_count()); - print_matrix( lp, std::cout); + print_matrix( *lp, std::cout); } dense_matrix left_side = l.get_left_side(basis); @@ -449,7 +462,7 @@ void test_larger_lu_with_holes(lp_settings & settings) { l.prepare_entering(8, w); // to init vector w l.replace_column(0, w, heading[0]); change_basis(8, 0, basis, non_basic_columns, heading); - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); } @@ -465,13 +478,13 @@ void test_larger_lu(lp_settings& settings) { basis[5] = 6; - fill_larger_sparse_matrix(m); + fill_larger_square_sparse_matrix(m); print_matrix(m, std::cout); vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - auto l = lu (m, basis, settings); + auto l = lu> (m, basis, settings); // std::cout << "printing factorization" << std::endl; // for (int i = lu.tail_size() - 1; i >=0; i--) { // auto lp = lu.get_lp_matrix(i); @@ -496,7 +509,7 @@ void test_larger_lu(lp_settings& settings) { l.prepare_entering(9, w); // to init vector w l.replace_column(0, w, heading[0]); change_basis(9, 0, basis, non_basic_columns, heading); - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); } @@ -513,7 +526,7 @@ void test_lu(lp_settings & settings) { -void init_b(vector & b, sparse_matrix & m, vector& x) { +void init_b(vector & b, square_sparse_matrix & m, vector& x) { for (unsigned i = 0; i < m.dimension(); i++) { b.push_back(m.dot_product_with_row(i, x)); } @@ -548,14 +561,14 @@ 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; vector nbasis; vector heading; - lp_primal_core_solver lpsolver(m_, b, x_star, basis, nbasis, heading, costs, column_types, upper_bound_values, settings, cn); + lp_primal_core_solver lpsolver(m_, b, x_star, basis, nbasis, heading, costs, column_types, upper_bound_values, settings, cn); lpsolver.solve(); } @@ -594,7 +607,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"; @@ -623,13 +636,13 @@ void test_lp_primal_core_solver() { #ifdef Z3DEBUG template -void test_swap_rows_with_permutation(sparse_matrix& m){ +void test_swap_rows_with_permutation(square_sparse_matrix& m){ std::cout << "testing swaps" << std::endl; unsigned dim = m.row_count(); dense_matrix original(&m); permutation_matrix q(dim); print_matrix(m, std::cout); - SASSERT(original == q * m); + lp_assert(original == q * m); for (int i = 0; i < 100; i++) { unsigned row1 = my_random() % dim; unsigned row2 = my_random() % dim; @@ -637,23 +650,23 @@ void test_swap_rows_with_permutation(sparse_matrix& m){ std::cout << "swap " << row1 << " " << row2 << std::endl; m.swap_rows(row1, row2); q.transpose_from_left(row1, row2); - SASSERT(original == q * m); + lp_assert(original == q * m); print_matrix(m, std::cout); std::cout << std::endl; } } #endif template -void fill_matrix(sparse_matrix& m); // forward definition +void fill_matrix(square_sparse_matrix& m); // forward definition #ifdef Z3DEBUG template -void test_swap_cols_with_permutation(sparse_matrix& m){ +void test_swap_cols_with_permutation(square_sparse_matrix& m){ std::cout << "testing swaps" << std::endl; unsigned dim = m.row_count(); dense_matrix original(&m); permutation_matrix q(dim); print_matrix(m, std::cout); - SASSERT(original == q * m); + lp_assert(original == q * m); for (int i = 0; i < 100; i++) { unsigned row1 = my_random() % dim; unsigned row2 = my_random() % dim; @@ -661,7 +674,7 @@ void test_swap_cols_with_permutation(sparse_matrix& m){ std::cout << "swap " << row1 << " " << row2 << std::endl; m.swap_rows(row1, row2); q.transpose_from_right(row1, row2); - SASSERT(original == q * m); + lp_assert(original == q * m); print_matrix(m, std::cout); std::cout << std::endl; } @@ -669,48 +682,48 @@ void test_swap_cols_with_permutation(sparse_matrix& m){ template -void test_swap_rows(sparse_matrix& m, unsigned i0, unsigned i1){ +void test_swap_rows(square_sparse_matrix& m, unsigned i0, unsigned i1){ std::cout << "test_swap_rows(" << i0 << "," << i1 << ")" << std::endl; - sparse_matrix mcopy(m.dimension()); + square_sparse_matrix mcopy(m.dimension(), 0); for (unsigned i = 0; i < m.dimension(); i++) for (unsigned j = 0; j < m.dimension(); j++) { mcopy(i, j)= m(i, j); - } + } std::cout << "swapping rows "<< i0 << "," << i1 << std::endl; m.swap_rows(i0, i1); for (unsigned j = 0; j < m.dimension(); j++) { - SASSERT(mcopy(i0, j) == m(i1, j)); - SASSERT(mcopy(i1, j) == m(i0, j)); + lp_assert(mcopy(i0, j) == m(i1, j)); + lp_assert(mcopy(i1, j) == m(i0, j)); } } template -void test_swap_columns(sparse_matrix& m, unsigned i0, unsigned i1){ +void test_swap_columns(square_sparse_matrix& m, unsigned i0, unsigned i1){ std::cout << "test_swap_columns(" << i0 << "," << i1 << ")" << std::endl; - sparse_matrix mcopy(m.dimension()); + square_sparse_matrix mcopy(m.dimension(), 0); // the second argument does not matter for (unsigned i = 0; i < m.dimension(); i++) for (unsigned j = 0; j < m.dimension(); j++) { mcopy(i, j)= m(i, j); - } + } m.swap_columns(i0, i1); for (unsigned j = 0; j < m.dimension(); j++) { - SASSERT(mcopy(j, i0) == m(j, i1)); - SASSERT(mcopy(j, i1) == m(j, i0)); + lp_assert(mcopy(j, i0) == m(j, i1)); + lp_assert(mcopy(j, i1) == m(j, i0)); } for (unsigned i = 0; i < m.dimension(); i++) { if (i == i0 || i == i1) continue; for (unsigned j = 0; j < m.dimension(); j++) { - SASSERT(mcopy(j, i)== m(j, i)); + lp_assert(mcopy(j, i)== m(j, i)); } } } #endif template -void fill_matrix(sparse_matrix& m){ +void fill_matrix(square_sparse_matrix& m){ int v = 0; for (int i = m.dimension() - 1; i >= 0; i--) { for (int j = m.dimension() - 1; j >=0; j--){ @@ -720,10 +733,10 @@ void fill_matrix(sparse_matrix& m){ } void test_pivot_like_swaps_and_pivot(){ - sparse_matrix m(10); + square_sparse_matrix m(10, 10); fill_matrix(m); // print_matrix(m); -// pivot at 2,7 + // pivot at 2,7 m.swap_columns(0, 7); // print_matrix(m); m.swap_rows(2, 0); @@ -733,7 +746,7 @@ void test_pivot_like_swaps_and_pivot(){ } // print_matrix(m); -// say pivot at 3,4 + // say pivot at 3,4 m.swap_columns(1, 4); // print_matrix(m); m.swap_rows(1, 3); @@ -765,13 +778,13 @@ void test_pivot_like_swaps_and_pivot(){ m.pivot_row_to_row(pivot_row_0, beta, target_row, settings); // print_matrix(m); for (unsigned j = 0; j < m.dimension(); j++) { - SASSERT(abs(row[j] - m(target_row, j)) < 0.00000001); + lp_assert(abs(row[j] - m(target_row, j)) < 0.00000001); } } #ifdef Z3DEBUG void test_swap_rows() { - sparse_matrix m(10); + square_sparse_matrix m(10, 10); fill_matrix(m); // print_matrix(m); test_swap_rows(m, 3, 5); @@ -791,8 +804,8 @@ void test_swap_rows() { // print_matrix(m); test_swap_rows(m, 0, 7); -// go over some corner cases - sparse_matrix m0(2); + // go over some corner cases + square_sparse_matrix m0(2, 2); test_swap_rows(m0, 0, 1); m0(0, 0) = 3; test_swap_rows(m0, 0, 1); @@ -800,7 +813,7 @@ void test_swap_rows() { test_swap_rows(m0, 0, 1); - sparse_matrix m1(10); + square_sparse_matrix m1(10, 10); test_swap_rows(m1, 0, 1); m1(0, 0) = 3; test_swap_rows(m1, 0, 1); @@ -812,7 +825,7 @@ void test_swap_rows() { test_swap_rows(m1, 0, 1); - sparse_matrix m2(3); + square_sparse_matrix m2(3, 3); test_swap_rows(m2, 0, 1); m2(0, 0) = 3; test_swap_rows(m2, 0, 1); @@ -820,7 +833,7 @@ void test_swap_rows() { test_swap_rows(m2, 0, 2); } -void fill_uniformly(sparse_matrix & m, unsigned dim) { +void fill_uniformly(square_sparse_matrix & m, unsigned dim) { int v = 0; for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { @@ -838,9 +851,9 @@ void fill_uniformly(dense_matrix & m, unsigned dim) { } } -void sparse_matrix_with_permutaions_test() { +void square_sparse_matrix_with_permutaions_test() { unsigned dim = 4; - sparse_matrix m(dim); + square_sparse_matrix m(dim, dim); fill_uniformly(m, dim); dense_matrix dm(dim, dim); fill_uniformly(dm, dim); @@ -870,61 +883,61 @@ void sparse_matrix_with_permutaions_test() { m.multiply_from_left(q0); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { - SASSERT(m(i, j) == dm0.get_elem(q0[i], j)); + lp_assert(m(i, j) == dm0.get_elem(q0[i], j)); } } auto q0_dm = q0 * dm; - SASSERT(m == q0_dm); + lp_assert(m == q0_dm); m.multiply_from_left(q1); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { - SASSERT(m(i, j) == dm0.get_elem(q0[q1[i]], j)); + lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], j)); } } auto q1_q0_dm = q1 * q0_dm; - SASSERT(m == q1_q0_dm); + lp_assert(m == q1_q0_dm); m.multiply_from_right(p0); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { - SASSERT(m(i, j) == dm0.get_elem(q0[q1[i]], p0[j])); + lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p0[j])); } } auto q1_q0_dm_p0 = q1_q0_dm * p0; - SASSERT(m == q1_q0_dm_p0); + lp_assert(m == q1_q0_dm_p0); m.multiply_from_right(p1); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { - SASSERT(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p0[j]])); + lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p0[j]])); } } auto q1_q0_dm_p0_p1 = q1_q0_dm_p0 * p1; - SASSERT(m == q1_q0_dm_p0_p1); + lp_assert(m == q1_q0_dm_p0_p1); m.multiply_from_right(p1); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { - SASSERT(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p1[p0[j]]])); + lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p1[p0[j]]])); } } auto q1_q0_dm_p0_p1_p1 = q1_q0_dm_p0_p1 * p1; - SASSERT(m == q1_q0_dm_p0_p1_p1); + lp_assert(m == q1_q0_dm_p0_p1_p1); } void test_swap_columns() { - sparse_matrix m(10); + square_sparse_matrix m(10, 10); fill_matrix(m); // print_matrix(m); @@ -943,8 +956,8 @@ void test_swap_columns() { test_swap_columns(m, 0, 7); -// go over some corner cases - sparse_matrix m0(2); + // go over some corner cases + square_sparse_matrix m0(2, 2); test_swap_columns(m0, 0, 1); m0(0, 0) = 3; test_swap_columns(m0, 0, 1); @@ -952,7 +965,7 @@ void test_swap_columns() { test_swap_columns(m0, 0, 1); - sparse_matrix m1(10); + square_sparse_matrix m1(10, 10); test_swap_columns(m1, 0, 1); m1(0, 0) = 3; test_swap_columns(m1, 0, 1); @@ -964,7 +977,7 @@ void test_swap_columns() { test_swap_columns(m1, 0, 1); - sparse_matrix m2(3); + square_sparse_matrix m2(3, 3); test_swap_columns(m2, 0, 1); m2(0, 0) = 3; test_swap_columns(m2, 0, 1); @@ -1041,9 +1054,9 @@ void test_apply_reverse_from_right_to_perm(permutation_matrix & #ifdef Z3DEBUG auto rev = l.get_inverse(); auto rs = pclone * rev; - SASSERT(p == rs); + lp_assert(p == rs) #endif -} + } void test_apply_reverse_from_right() { auto vec = vector_of_permutaions(); @@ -1068,8 +1081,8 @@ void test_permutations() { p.apply_reverse_from_right_to_T(v); p.apply_reverse_from_right_to_T(vi); - SASSERT(vectors_are_equal(v, vi.m_data)); - SASSERT(vi.is_OK()); + lp_assert(vectors_are_equal(v, vi.m_data)); + lp_assert(vi.is_OK()); } void lp_solver_test() { @@ -1125,7 +1138,7 @@ void update_settings(argument_parser & args_parser, lp_settings& settings) { settings.harris_feasibility_tolerance = d; } if (get_int_from_args_parser("--random_seed", args_parser, n)) { - settings.random_seed(n); + settings.set_random_seed(n); } if (get_int_from_args_parser("--simplex_strategy", args_parser, n)) { settings.simplex_strategy() = static_cast(n); @@ -1217,7 +1230,7 @@ void solve_mps_double(std::string file_name, bool look_for_min, unsigned max_ite compare_solutions(reader, primal_solver, solver); print_x(reader, primal_solver); std::cout << "dual cost is " << cost << ", but primal cost is " << primal_cost << std::endl; - SASSERT(false); + lp_assert(false); } } } @@ -1335,7 +1348,7 @@ void test_binary_priority_queue() { for (unsigned i = 0; i < 10; i++) { unsigned de = q.dequeue(); - SASSERT(i == de); + lp_assert(i == de); std::cout << de << std::endl; } q.enqueue(2, 2); @@ -1358,7 +1371,7 @@ void test_binary_priority_queue() { unsigned t = 0; while (q.size() > 0) { unsigned d =q.dequeue(); - SASSERT(t++ == d); + lp_assert(t++ == d); std::cout << d << std::endl; } #endif @@ -1387,7 +1400,7 @@ void solve_mps_with_known_solution(std::string file_name, std::unordered_mapget_status()) << std::endl; if (status != solver->get_status()){ std::cout << "status should be " << lp_status_to_string(status) << std::endl; - SASSERT(status == solver->get_status()); + lp_assert(status == solver->get_status()); throw "status is wrong"; } if (solver->get_status() == lp_status::OPTIMAL) { @@ -1398,7 +1411,7 @@ void solve_mps_with_known_solution(std::string file_name, std::unordered_mapget_column_value_by_name(it.first) << std::endl; } - SASSERT(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); + lp_assert(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); } } if (reader.column_names().size() < 20) { @@ -1483,127 +1496,127 @@ void fill_file_names(vector &file_names, std::set & m return; } std::string home_dir_str(home_dir); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l0redund.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l1.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l2.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l3.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l4.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l4fix.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/plan.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/samp2.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/murtagh.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l0.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/AFIRO.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SC50B.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SC50A.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/KB2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SC105.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STOCFOR1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/ADLITTLE.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BLEND.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCAGR7.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SC205.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHARE2B.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/RECIPELP.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/LOTFI.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/VTP-BASE.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHARE1B.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BOEING2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BORE3D.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCORPION.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/CAPRI.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BRANDY.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCAGR25.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCTAP1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/ISRAEL.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCFXM1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BANDM.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/E226.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/AGG.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GROW7.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/ETAMACRO.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FINNIS.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCSD1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STANDATA.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STANDGUB.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BEACONFD.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STAIR.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STANDMPS.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GFRD-PNC.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCRS8.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BOEING1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/MODSZK1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/DEGEN2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FORPLAN.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/AGG2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/AGG3.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCFXM2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHELL.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOT4.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCSD6.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP04S.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SEBA.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GROW15.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FFFFF800.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BNL1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PEROLD.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/QAP8.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCFXM3.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP04L.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GANGES.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCTAP2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GROW22.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP08S.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOT-WE.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/MAROS.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STOCFOR2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/25FV47.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP12S.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCSD8.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FIT1P.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCTAP3.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SIERRA.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOTNOV.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/CZPROB.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FIT1D.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOT-JA.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP08L.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BNL2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/NESM.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/CYCLE.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/acc-tight5.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP12L.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/DEGEN3.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GREENBEA.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GREENBEB.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/80BAU3B.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/TRUSS.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/D2Q06C.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/WOODW.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/QAP12.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/D6CUBE.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOT.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/DFL001.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/WOOD1P.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FIT2P.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOT87.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STOCFOR3.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/QAP15.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FIT2D.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/MAROS-R7.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/FIT2P.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/DFL001.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/D2Q06C.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/80BAU3B.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/GREENBEB.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/GREENBEA.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/BNL2.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/SHIP08L.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/FIT1D.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/SCTAP3.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/SCSD8.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/SCSD6.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/MAROS-R7.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l0redund.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l1.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l2.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l3.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l4.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l4fix.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/plan.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/samp2.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/murtagh.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l0.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AFIRO.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC50B.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC50A.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/KB2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC105.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STOCFOR1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/ADLITTLE.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BLEND.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCAGR7.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC205.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHARE2B.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/RECIPELP.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/LOTFI.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/VTP-BASE.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHARE1B.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BOEING2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BORE3D.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCORPION.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/CAPRI.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BRANDY.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCAGR25.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/ISRAEL.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCFXM1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BANDM.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/E226.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AGG.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GROW7.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/ETAMACRO.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FINNIS.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCSD1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STANDATA.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STANDGUB.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BEACONFD.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STAIR.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STANDMPS.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GFRD-PNC.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCRS8.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BOEING1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/MODSZK1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/DEGEN2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FORPLAN.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AGG2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AGG3.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCFXM2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHELL.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT4.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCSD6.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP04S.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SEBA.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GROW15.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FFFFF800.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BNL1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PEROLD.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/QAP8.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCFXM3.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP04L.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GANGES.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GROW22.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP08S.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT-WE.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/MAROS.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STOCFOR2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/25FV47.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP12S.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCSD8.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT1P.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP3.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SIERRA.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOTNOV.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/CZPROB.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT1D.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT-JA.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP08L.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BNL2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/NESM.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/CYCLE.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/acc-tight5.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP12L.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/DEGEN3.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEA.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEB.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/80BAU3B.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/TRUSS.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/D2Q06C.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/WOODW.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/QAP12.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/D6CUBE.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/DFL001.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/WOOD1P.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT2P.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT87.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STOCFOR3.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/QAP15.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT2D.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/MAROS-R7.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/FIT2P.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/DFL001.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/D2Q06C.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/80BAU3B.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEB.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEA.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/BNL2.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SHIP08L.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/FIT1D.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP3.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SCSD8.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SCSD6.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/MAROS-R7.SIF"); } void test_out_dir(std::string out_dir) { @@ -1673,7 +1686,7 @@ void solve_some_mps(argument_parser & args_parser) { } if (!solve_for_rational) { solve_mps(file_names[6], false, 0, time_limit, false, dual, compare_with_primal, args_parser); - solve_mps_with_known_solution(file_names[3], nullptr, INFEASIBLE, dual); // chvatal: 135(d) + solve_mps_with_known_solution(file_names[3], nullptr, lp_status::INFEASIBLE, dual); // chvatal: 135(d) std::unordered_map sol; sol["X1"] = 0; sol["X2"] = 6; @@ -1683,8 +1696,8 @@ void solve_some_mps(argument_parser & args_parser) { sol["X6"] = 1; sol["X7"] = 1; sol["X8"] = 0; - solve_mps_with_known_solution(file_names[9], &sol, OPTIMAL, dual); - solve_mps_with_known_solution(file_names[0], &sol, OPTIMAL, dual); + solve_mps_with_known_solution(file_names[9], &sol, lp_status::OPTIMAL, dual); + solve_mps_with_known_solution(file_names[0], &sol, lp_status::OPTIMAL, dual); sol.clear(); sol["X1"] = 25.0/14.0; // sol["X2"] = 0; @@ -1693,10 +1706,10 @@ void solve_some_mps(argument_parser & args_parser) { // sol["X5"] = 0; // sol["X6"] = 0; // sol["X7"] = 9.0/14.0; - solve_mps_with_known_solution(file_names[5], &sol, OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[4], &sol, OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[2], nullptr, UNBOUNDED, dual); // chvatal: 135(c) - solve_mps_with_known_solution(file_names[1], nullptr, UNBOUNDED, dual); // chvatal: 135(b) + solve_mps_with_known_solution(file_names[5], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) + solve_mps_with_known_solution(file_names[4], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) + solve_mps_with_known_solution(file_names[2], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(c) + solve_mps_with_known_solution(file_names[1], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(b) solve_mps(file_names[8], false, 0, time_limit, false, dual, compare_with_primal, args_parser); // return; for (auto& s : file_names) { @@ -1748,7 +1761,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])); } @@ -1762,9 +1775,9 @@ void solve_rational() { expected_sol["x7"] = lp::mpq(1); expected_sol["x8"] = lp::mpq(0); solver.find_maximal_solution(); - SASSERT(solver.get_status() == OPTIMAL); + lp_assert(solver.get_status() == lp_status::OPTIMAL); for (auto it : expected_sol) { - SASSERT(it.second == solver.get_column_value_by_name(it.first)); + lp_assert(it.second == solver.get_column_value_by_name(it.first)); } } @@ -1819,10 +1832,10 @@ std::unordered_map * get_solution_from_glpsol_output(std::s } auto split = string_split(s, " \t", false); if (split.size() == 0) { - return ret; + return ret; } - SASSERT(split.size() > 3); + lp_assert(split.size() > 3); (*ret)[split[1]] = atof(split[3].c_str()); } while (true); } @@ -1842,11 +1855,11 @@ void test_init_U() { basis[1] = 2; basis[2] = 4; - sparse_matrix u(m, basis); + square_sparse_matrix u(m, basis); for (unsigned i = 0; i < 3; i++) { for (unsigned j = 0; j < 3; j ++) { - SASSERT(m(i, basis[j]) == u(i, j)); + lp_assert(m(i, basis[j]) == u(i, j)); } } @@ -1855,7 +1868,7 @@ void test_init_U() { } void test_replace_column() { - sparse_matrix m(10); + square_sparse_matrix m(10, 10); fill_matrix(m); m.swap_columns(0, 7); m.swap_columns(6, 3); @@ -1874,13 +1887,17 @@ void test_replace_column() { for (unsigned column_to_replace = 0; column_to_replace < m.dimension(); column_to_replace ++) { m.replace_column(column_to_replace, w, settings); for (unsigned i = 0; i < m.dimension(); i++) { - SASSERT(abs(w[i] - m(i, column_to_replace)) < 0.00000001); + lp_assert(abs(w[i] - m(i, column_to_replace)) < 0.00000001); } } } + void setup_args_parser(argument_parser & parser) { + parser.add_option_with_help_string("-hnf", "test hermite normal form"); + parser.add_option_with_help_string("-gomory", "gomory"); + 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"); @@ -1926,48 +1943,11 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("--test_mpq", "test rationals"); parser.add_option_with_help_string("--test_mpq_np", "test rationals"); parser.add_option_with_help_string("--test_mpq_np_plus", "test rationals using plus instead of +="); + parser.add_option_with_help_string("--maximize_term", "test maximize_term()"); } struct fff { int a; int b;}; -void test_stacked_map_itself() { - vector v(3,0); - for(auto u : v) - std::cout << u << std::endl; - - std::unordered_map foo; - fff l; - l.a = 0; - l.b =1; - foo[1] = l; - int r = 1; - int k = foo[r].a; - std::cout << k << std::endl; - - stacked_map m; - m[0] = 3; - m[1] = 4; - m.push(); - m[1] = 5; - m[2] = 2; - m.pop(); - m.erase(2); - m[2] = 3; - m.erase(1); - m.push(); - m[3] = 100; - m[4] = 200; - m.erase(1); - m.push(); - m[5] = 300; - m[6] = 400; - m[5] = 301; - m.erase(5); - m[3] = 122; - - m.pop(2); - m.pop(); -} void test_stacked_unsigned() { std::cout << "test stacked unsigned" << std::endl; @@ -1978,7 +1958,7 @@ void test_stacked_unsigned() { v = 3; v = 4; v.pop(); - SASSERT(v == 2); + lp_assert(v == 2); v ++; v++; std::cout << "before push v=" << v << std::endl; @@ -1988,7 +1968,7 @@ void test_stacked_unsigned() { v+=1; std::cout << "v = " << v << std::endl; v.pop(2); - SASSERT(v == 4); + lp_assert(v == 4); const unsigned & rr = v; std::cout << rr << std:: endl; @@ -2026,48 +2006,22 @@ void test_stacked_vector() { } -void test_stacked_set() { -#ifdef Z3DEBUG - std::cout << "test_stacked_set" << std::endl; - stacked_unordered_set s; - s.insert(1); - s.insert(2); - s.insert(3); - std::unordered_set scopy = s(); - s.push(); - s.insert(4); - s.pop(); - SASSERT(s() == scopy); - s.push(); - s.push(); - s.insert(4); - s.insert(5); - s.push(); - s.insert(4); - s.pop(3); - SASSERT(s() == scopy); -#endif -} void test_stacked() { - std::cout << "test_stacked_map()" << std::endl; - test_stacked_map_itself(); test_stacked_value(); test_stacked_vector(); - test_stacked_set(); - } char * find_home_dir() { - #ifdef _WINDOWS - #else +#ifdef _WINDOWS +#else char * home_dir = getenv("HOME"); - if (home_dir == nullptr) { + if (home_dir == nullptr) { std::cout << "cannot find home directory" << std::endl; return nullptr; } - #endif - return nullptr; +#endif + return nullptr; } @@ -2089,8 +2043,8 @@ struct mem_cpy_place_holder { void finalize(unsigned ret) { /* - finalize_util_module(); - finalize_numerics_module(); + finalize_util_module(); + finalize_numerics_module(); */ // return ret; } @@ -2228,7 +2182,7 @@ bool values_are_one_percent_close(double a, double b) { // returns true if both are optimal void compare_costs(std::string glpk_out_file_name, - std::string lp_out_file_name, + std::string lp_out_file_name, unsigned & successes, unsigned & failures) { double a = get_glpk_cost(glpk_out_file_name); @@ -2307,78 +2261,78 @@ void process_test_file(std::string test_dir, std::string test_file_name, argumen } /* int my_readdir(DIR *dirp, struct dirent * -#ifndef LEAN_WINDOWS - entry -#endif - , struct dirent **result) { -#ifdef LEAN_WINDOWS - *result = readdir(dirp); // NOLINT - return *result != nullptr? 0 : 1; -#else - return readdir_r(dirp, entry, result); -#endif -} + #ifndef LEAN_WINDOWS + entry + #endif + , struct dirent **result) { + #ifdef LEAN_WINDOWS + *result = readdir(dirp); // NOLINT + return *result != nullptr? 0 : 1; + #else + return readdir_r(dirp, entry, result); + #endif + } */ /* -vector> get_file_list_of_dir(std::string test_file_dir) { - DIR *dir; - if ((dir = opendir(test_file_dir.c_str())) == nullptr) { - std::cout << "Cannot open directory " << test_file_dir << std::endl; - throw 0; - } - vector> ret; - struct dirent entry; - struct dirent* result; - int return_code; - for (return_code = my_readdir(dir, &entry, &result); -#ifndef LEAN_WINDOWS - result != nullptr && -#endif - return_code == 0; - return_code = my_readdir(dir, &entry, &result)) { - DIR *tmp_dp = opendir(result->d_name); - struct stat file_record; - if (tmp_dp == nullptr) { - std::string s = test_file_dir+ "/" + result->d_name; - int stat_ret = stat(s.c_str(), & file_record); - if (stat_ret!= -1) { - ret.push_back(make_pair(result->d_name, file_record.st_size)); - } else { - perror("stat"); - exit(1); - } - } else { - closedir(tmp_dp); - } - } - closedir(dir); - return ret; -} + vector> get_file_list_of_dir(std::string test_file_dir) { + DIR *dir; + if ((dir = opendir(test_file_dir.c_str())) == nullptr) { + std::cout << "Cannot open directory " << test_file_dir << std::endl; + throw 0; + } + vector> ret; + struct dirent entry; + struct dirent* result; + int return_code; + for (return_code = my_readdir(dir, &entry, &result); + #ifndef LEAN_WINDOWS + result != nullptr && + #endif + return_code == 0; + return_code = my_readdir(dir, &entry, &result)) { + DIR *tmp_dp = opendir(result->d_name); + struct stat file_record; + if (tmp_dp == nullptr) { + std::string s = test_file_dir+ "/" + result->d_name; + int stat_ret = stat(s.c_str(), & file_record); + if (stat_ret!= -1) { + ret.push_back(make_pair(result->d_name, file_record.st_size)); + } else { + perror("stat"); + exit(1); + } + } else { + closedir(tmp_dp); + } + } + closedir(dir); + return ret; + } */ /* -struct file_size_comp { - unordered_map& m_file_sizes; - file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} - int operator()(std::string a, std::string b) { - std::cout << m_file_sizes.size() << std::endl; - std::cout << a << std::endl; - std::cout << b << std::endl; + struct file_size_comp { + unordered_map& m_file_sizes; + file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} + int operator()(std::string a, std::string b) { + std::cout << m_file_sizes.size() << std::endl; + std::cout << a << std::endl; + std::cout << b << std::endl; - auto ls = m_file_sizes.find(a); - std::cout << "fa" << std::endl; - auto rs = m_file_sizes.find(b); - std::cout << "fb" << std::endl; - if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { - std::cout << "fc " << std::endl; - int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); - std::cout << "calc r " << std::endl; - return r; - } else { - std::cout << "sc " << std::endl; - return 0; - } - } -}; + auto ls = m_file_sizes.find(a); + std::cout << "fa" << std::endl; + auto rs = m_file_sizes.find(b); + std::cout << "fb" << std::endl; + if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { + std::cout << "fc " << std::endl; + int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); + std::cout << "calc r " << std::endl; + return r; + } else { + std::cout << "sc " << std::endl; + return 0; + } + } + }; */ struct sort_pred { @@ -2390,26 +2344,26 @@ struct sort_pred { void test_files_from_directory(std::string test_file_dir, argument_parser & args_parser) { /* - std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - DIR *out_dir_p = opendir(out_dir.c_str()); - if (out_dir_p == nullptr) { - std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; - return; - } - closedir(out_dir_p); - vector> files = get_file_list_of_dir(test_file_dir); - std::sort(files.begin(), files.end(), sort_pred()); - unsigned max_iters, time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit, max_iters); - unsigned successes = 0, failures = 0, inconclusives = 0; - for (auto & t : files) { - process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; + std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; + std::string out_dir = args_parser.get_option_value("--out_dir"); + if (out_dir.size() == 0) { + out_dir = "/tmp/test"; + } + DIR *out_dir_p = opendir(out_dir.c_str()); + if (out_dir_p == nullptr) { + std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; + return; + } + closedir(out_dir_p); + vector> files = get_file_list_of_dir(test_file_dir); + std::sort(files.begin(), files.end(), sort_pred()); + unsigned max_iters, time_limit; + get_time_limit_and_max_iters_from_parser(args_parser, time_limit, max_iters); + unsigned successes = 0, failures = 0, inconclusives = 0; + for (auto & t : files) { + process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); + } + std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; */ } @@ -2450,12 +2404,12 @@ void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_read sw.start(); lp_status status = solver->solve(); std::cout << "status is " << lp_status_to_string(status) << ", processed for " << sw.get_current_seconds() <<" seconds, and " << solver->get_total_iterations() << " iterations" << std::endl; - if (solver->get_status() == INFEASIBLE) { + if (solver->get_status() == lp_status::INFEASIBLE) { vector> evidence; solver->get_infeasibility_explanation(evidence); } if (args_parser.option_is_used("--randomize_lar")) { - if (solver->get_status() != OPTIMAL) { + if (solver->get_status() != lp_status::OPTIMAL) { std::cout << "cannot check randomize on an infeazible problem" << std::endl; return; } @@ -2538,6 +2492,8 @@ void test_lar_solver(argument_parser & args_parser) { test_lar_on_file(fn, args_parser); return; } + + std::cout << "give option --file or --filelist to test_lar_solver\n"; } void test_numeric_pair() { @@ -2547,22 +2503,22 @@ void test_numeric_pair() { numeric_pair c(0.1, 0.5); a += 2*c; a -= c; - SASSERT (a == b + c); + lp_assert (a == b + c); numeric_pair d = a * 2; std::cout << a << std::endl; - SASSERT(b == b); - SASSERT(b < a); - SASSERT(b <= a); - SASSERT(a > b); - SASSERT(a != b); - SASSERT(a >= b); - SASSERT(-a < b); - SASSERT(a < 2 * b); - SASSERT(b + b > a); - SASSERT(lp::mpq(2.1) * b + b > a); - SASSERT(-b * lp::mpq(2.1) - b < lp::mpq(0.99) * a); + lp_assert(b == b); + lp_assert(b < a); + lp_assert(b <= a); + lp_assert(a > b); + lp_assert(a != b); + lp_assert(a >= b); + lp_assert(-a < b); + lp_assert(a < 2 * b); + lp_assert(b + b > a); + lp_assert(lp::mpq(2.1) * b + b > a); + lp_assert(-b * lp::mpq(2.1) - b < lp::mpq(0.99) * a); std::cout << - b * lp::mpq(2.1) - b << std::endl; - SASSERT(-b *(lp::mpq(2.1) + 1) == - b * lp::mpq(2.1) - b); + lp_assert(-b *(lp::mpq(2.1) + 1) == - b * lp::mpq(2.1) - b); } void get_matrix_dimensions(std::ifstream & f, unsigned & m, unsigned & n) { @@ -2583,7 +2539,7 @@ void read_row_cols(unsigned i, static_matrix& A, std::ifstream & if (line== "row_end") break; auto r = split_and_trim(line); - SASSERT(r.size() == 4); + lp_assert(r.size() == 4); unsigned j = atoi(r[1].c_str()); double v = atof(r[3].c_str()); A.set(i, j, v); @@ -2611,7 +2567,7 @@ void read_basis(vector & basis, std::ifstream & f) { std::cout << "reading basis" << std::endl; std::string line; getline(f, line); - SASSERT(line == "basis_start"); + lp_assert(line == "basis_start"); do { getline(f, line); if (line == "basis_end") @@ -2624,7 +2580,7 @@ void read_basis(vector & basis, std::ifstream & f) { void read_indexed_vector(indexed_vector & v, std::ifstream & f) { std::string line; getline(f, line); - SASSERT(line == "vector_start"); + lp_assert(line == "vector_start"); do { getline(f, line); if (line == "vector_end") break; @@ -2654,8 +2610,8 @@ void check_lu_from_file(std::string lufile_name) { vector basis_heading; lp_settings settings; vector non_basic_columns; - lu lsuhl(A, basis, settings); - indexed_vector d(A.row_count()); + lu> lsuhl(A, basis, settings); + indexed_vector d(A.row_count()); unsigned entering = 26; lsuhl.solve_Bd(entering, d, v); #ifdef Z3DEBUG @@ -2664,14 +2620,14 @@ void check_lu_from_file(std::string lufile_name) { A.copy_column_to_vector(entering, a); indexed_vector cd(d); B.apply_from_left(cd.m_data, settings); - SASSERT(vectors_are_equal(cd.m_data , a)); + lp_assert(vectors_are_equal(cd.m_data , a)); #endif } void test_square_dense_submatrix() { std::cout << "testing square_dense_submatrix" << std::endl; unsigned parent_dim = 7; - sparse_matrix parent(parent_dim); + square_sparse_matrix parent(parent_dim, 0); fill_matrix(parent); unsigned index_start = 3; square_dense_submatrix d; @@ -2712,8 +2668,8 @@ void test_term() { lar_solver solver; unsigned _x = 0; unsigned _y = 1; - var_index x = solver.add_var(_x); - var_index y = solver.add_var(_y); + var_index x = solver.add_var(_x, false); + var_index y = solver.add_var(_y, false); vector> term_ls; term_ls.push_back(std::pair((int)1, x)); @@ -2726,9 +2682,16 @@ void test_term() { ls.push_back(std::pair((int)1, z)); solver.add_constraint(ls, lconstraint_kind::EQ, mpq(0)); + ls.clear(); + ls.push_back(std::pair((int)1, x)); + solver.add_constraint(ls, lconstraint_kind::LT, mpq(0)); + ls.push_back(std::pair((int)2, y)); + solver.add_constraint(ls, lconstraint_kind::GT, mpq(0)); auto status = solver.solve(); std::cout << lp_status_to_string(status) << std::endl; std::unordered_map model; + if (status != lp_status::OPTIMAL) + return; solver.get_model(model); for (auto & t : model) { @@ -2740,8 +2703,8 @@ void test_term() { void test_evidence_for_total_inf_simple(argument_parser & args_parser) { lar_solver solver; - var_index x = solver.add_var(0); - var_index y = solver.add_var(1); + var_index x = solver.add_var(0, false); + var_index y = solver.add_var(1, false); solver.add_var_bound(x, LE, -mpq(1)); solver.add_var_bound(y, GE, mpq(0)); vector> ls; @@ -2755,29 +2718,29 @@ void test_evidence_for_total_inf_simple(argument_parser & args_parser) { auto status = solver.solve(); std::cout << lp_status_to_string(status) << std::endl; std::unordered_map model; - SASSERT(solver.get_status() == INFEASIBLE); + lp_assert(solver.get_status() == lp_status::INFEASIBLE); } void test_bound_propagation_one_small_sample1() { /* -(<= (+ a (* (- 1.0) b)) 0.0) -(<= (+ b (* (- 1.0) x_13)) 0.0) ---> (<= (+ a (* (- 1.0) c)) 0.0) + (<= (+ a (* (- 1.0) b)) 0.0) + (<= (+ b (* (- 1.0) x_13)) 0.0) + --> (<= (+ a (* (- 1.0) c)) 0.0) -the inequality on (<= a c) is obtained from a triangle inequality (<= a b) (<= b c). -If b becomes basic variable, then it is likely the old solver ends up with a row that implies (<= a c). - a - b <= 0.0 - b - c <= 0.0 + the inequality on (<= a c) is obtained from a triangle inequality (<= a b) (<= b c). + If b becomes basic variable, then it is likely the old solver ends up with a row that implies (<= a c). + a - b <= 0.0 + b - c <= 0.0 - got to get a <= c + got to get a <= c */ std::function bound_is_relevant = - [&](unsigned j, bool is_low_bound, bool strict, const rational& bound_val) { - return true; - }; + [&](unsigned j, bool is_lower_bound, bool strict, const rational& bound_val) { + return true; + }; lar_solver ls; - unsigned a = ls.add_var(0); - unsigned b = ls.add_var(1); - unsigned c = ls.add_var(2); + unsigned a = ls.add_var(0, false); + unsigned b = ls.add_var(1, false); + unsigned c = ls.add_var(2, false); vector> coeffs; coeffs.push_back(std::pair(1, a)); coeffs.push_back(std::pair(-1, c)); @@ -2796,7 +2759,7 @@ If b becomes basic variable, then it is likely the old solver ends up with a row vector ev; ls.add_var_bound(a, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + my_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) { @@ -2809,39 +2772,39 @@ void test_bound_propagation_one_small_samples() { test_bound_propagation_one_small_sample1(); /* (>= x_46 0.0) -(<= x_29 0.0) -(not (<= x_68 0.0)) -(<= (+ (* (/ 1001.0 1998.0) x_10) (* (- 1.0) x_151) x_68) (- (/ 1001.0 999.0))) -(<= (+ (* (/ 1001.0 999.0) x_9) - (* (- 1.0) x_152) - (* (/ 1001.0 999.0) x_151) - (* (/ 1001.0 999.0) x_68)) - (- (/ 1502501.0 999000.0))) -(not (<= (+ (* (/ 999.0 2.0) x_10) (* (- 1.0) x_152) (* (- (/ 999.0 2.0)) x_151)) - (/ 1001.0 2.0))) -(not (<= x_153 0.0))z -(>= (+ x_9 (* (- (/ 1001.0 999.0)) x_10) (* (- 1.0) x_153) (* (- 1.0) x_68)) - (/ 5003.0 1998.0)) ---> (not (<= (+ x_10 x_46 (* (- 1.0) x_29)) 0.0)) + (<= x_29 0.0) + (not (<= x_68 0.0)) + (<= (+ (* (/ 1001.0 1998.0) x_10) (* (- 1.0) x_151) x_68) (- (/ 1001.0 999.0))) + (<= (+ (* (/ 1001.0 999.0) x_9) + (* (- 1.0) x_152) + (* (/ 1001.0 999.0) x_151) + (* (/ 1001.0 999.0) x_68)) + (- (/ 1502501.0 999000.0))) + (not (<= (+ (* (/ 999.0 2.0) x_10) (* (- 1.0) x_152) (* (- (/ 999.0 2.0)) x_151)) + (/ 1001.0 2.0))) + (not (<= x_153 0.0))z + (>= (+ x_9 (* (- (/ 1001.0 999.0)) x_10) (* (- 1.0) x_153) (* (- 1.0) x_68)) + (/ 5003.0 1998.0)) + --> (not (<= (+ x_10 x_46 (* (- 1.0) x_29)) 0.0)) -and + and -(<= (+ a (* (- 1.0) b)) 0.0) -(<= (+ b (* (- 1.0) x_13)) 0.0) ---> (<= (+ a (* (- 1.0) x_13)) 0.0) + (<= (+ a (* (- 1.0) b)) 0.0) + (<= (+ b (* (- 1.0) x_13)) 0.0) + --> (<= (+ a (* (- 1.0) x_13)) 0.0) -In the first case, there typically are no atomic formulas for bounding x_10. So there is never some -basic lemma of the form (>= x46 0), (<= x29 0), (>= x10 0) -> (not (<= (+ x10 x46 (- x29)) 0)). -Instead the bound on x_10 falls out from a bigger blob of constraints. + In the first case, there typically are no atomic formulas for bounding x_10. So there is never some + basic lemma of the form (>= x46 0), (<= x29 0), (>= x10 0) -> (not (<= (+ x10 x46 (- x29)) 0)). + Instead the bound on x_10 falls out from a bigger blob of constraints. -In the second case, the inequality on (<= x19 x13) is obtained from a triangle inequality (<= x19 x9) (<= x9 x13). -If x9 becomes basic variable, then it is likely the old solver ends up with a row that implies (<= x19 x13). - */ + In the second case, the inequality on (<= x19 x13) is obtained from a triangle inequality (<= x19 x9) (<= x9 x13). + If x9 becomes basic variable, then it is likely the old solver ends up with a row that implies (<= x19 x13). + */ } void test_bound_propagation_one_row() { lar_solver ls; - unsigned x0 = ls.add_var(0); - unsigned x1 = ls.add_var(1); + unsigned x0 = ls.add_var(0, false); + unsigned x1 = ls.add_var(1, false); vector> c; c.push_back(std::pair(1, x0)); c.push_back(std::pair(-1, x1)); @@ -2849,13 +2812,13 @@ void test_bound_propagation_one_row() { vector ev; ls.add_var_bound(x0, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_one_row_with_bounded_vars() { lar_solver ls; - unsigned x0 = ls.add_var(0); - unsigned x1 = ls.add_var(1); + unsigned x0 = ls.add_var(0, false); + unsigned x1 = ls.add_var(1, false); vector> c; c.push_back(std::pair(1, x0)); c.push_back(std::pair(-1, x1)); @@ -2865,13 +2828,13 @@ 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); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_one_row_mixed() { lar_solver ls; - unsigned x0 = ls.add_var(0); - unsigned x1 = ls.add_var(1); + unsigned x0 = ls.add_var(0, false); + unsigned x1 = ls.add_var(1, false); vector> c; c.push_back(std::pair(1, x0)); c.push_back(std::pair(-1, x1)); @@ -2879,15 +2842,15 @@ void test_bound_propagation_one_row_mixed() { vector ev; ls.add_var_bound(x1, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_two_rows() { lar_solver ls; - unsigned x = ls.add_var(0); - unsigned y = ls.add_var(1); - unsigned z = ls.add_var(2); + unsigned x = ls.add_var(0, false); + unsigned y = ls.add_var(1, false); + unsigned z = ls.add_var(2, false); vector> c; c.push_back(std::pair(1, x)); c.push_back(std::pair(2, y)); @@ -2902,16 +2865,16 @@ void test_bound_propagation_two_rows() { vector ev; ls.add_var_bound(y, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_total_case_u() { std::cout << "test_total_case_u\n"; lar_solver ls; - unsigned x = ls.add_var(0); - unsigned y = ls.add_var(1); - unsigned z = ls.add_var(2); + unsigned x = ls.add_var(0, false); + unsigned y = ls.add_var(1, false); + unsigned z = ls.add_var(2, false); vector> c; c.push_back(std::pair(1, x)); c.push_back(std::pair(2, y)); @@ -2922,7 +2885,7 @@ void test_total_case_u() { vector ev; ls.add_var_bound(z, GE, zero_of_type()); ls.solve(); - lp_bound_propagator bp(ls); + my_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) { @@ -2935,9 +2898,9 @@ bool contains_j_kind(unsigned j, lconstraint_kind kind, const mpq & rs, const ve void test_total_case_l(){ std::cout << "test_total_case_l\n"; lar_solver ls; - unsigned x = ls.add_var(0); - unsigned y = ls.add_var(1); - unsigned z = ls.add_var(2); + unsigned x = ls.add_var(0, false); + unsigned y = ls.add_var(1, false); + unsigned z = ls.add_var(2, false); vector> c; c.push_back(std::pair(1, x)); c.push_back(std::pair(2, y)); @@ -2949,10 +2912,10 @@ void test_total_case_l(){ vector ev; ls.add_var_bound(z, LE, zero_of_type()); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); - SASSERT(ev.size() == 4); - SASSERT(contains_j_kind(x, GE, - one_of_type(), ev)); + lp_assert(ev.size() == 4); + lp_assert(contains_j_kind(x, GE, - one_of_type(), ev)); } void test_bound_propagation() { test_total_case_u(); @@ -2972,17 +2935,17 @@ void test_int_set() { s.insert(1); s.insert(2); s.print(std::cout); - SASSERT(s.contains(2)); - SASSERT(s.size() == 2); + lp_assert(s.contains(2)); + lp_assert(s.size() == 2); s.erase(2); - SASSERT(s.size() == 1); + lp_assert(s.size() == 1); s.erase(2); - SASSERT(s.size() == 1); + lp_assert(s.size() == 1); s.print(std::cout); s.insert(3); s.insert(2); s.clear(); - SASSERT(s.size() == 0); + lp_assert(s.size() == 0); } @@ -3077,11 +3040,507 @@ void test_rationals() { std::cout << T_to_string(r) << std::endl; } -void test_lp_local(int argn, char**argv) { - std::cout << "resize\n"; - vector r; - r.resize(1); +void get_random_interval(bool& neg_inf, bool& pos_inf, int& x, int &y) { + int i = my_random() % 10; + if (i == 0) { + neg_inf = true; + } else { + neg_inf = false; + x = my_random() % 100; + } + i = my_random() % 10; + if (i == 0) { + pos_inf = true; + } else { + pos_inf = false; + if (!neg_inf) { + y = x + my_random() % (101 - x); + lp_assert(y >= x); + } + else { + y = my_random() % 100; + } + } + lp_assert((neg_inf || (0 <= x && x <= 100)) && (pos_inf || (0 <= y && y <= 100))); +} + +void test_gomory_cut_0() { + gomory_test g( + [](unsigned j) { return "v" + T_to_string(j);} // name_function_p + , + [](unsigned j) { //get_value_p + if (j == 1) + return mpq(2730, 1727); + if (j == 2) + return zero_of_type(); + if (j == 3) return mpq(3); + lp_assert(false); + return zero_of_type(); + }, + [](unsigned j) { // at_low_p + if (j == 1) + return false; + if (j == 2) + return true; + if (j == 3) + return true; + lp_assert(false); + return false; + }, + [](unsigned j) { // at_upper + if (j == 1) + return false; + if (j == 2) + return true; + if (j == 3) + return false; + lp_assert(false); + return false; + }, + [](unsigned j) { // lower_bound + if (j == 1) { + lp_assert(false); //unlimited from below + return 0; + } + if (j == 2) + return 0; + if (j == 3) + return 3; + lp_assert(false); + return 0; + }, + [](unsigned j) { // upper + if (j == 1) { + lp_assert(false); //unlimited from above + return 0; + } + if (j == 2) + return 0; + if (j == 3) + return 10; + lp_assert(false); + return 0; + }, + [] (unsigned) { return 0; }, + [] (unsigned) { return 0; } + ); + lar_term t; + mpq k; + explanation expl; + unsigned inf_col = 1; + vector> row; + row.push_back(std::make_pair(mpq(1), 1)); + row.push_back(std::make_pair(mpq(2731, 1727), 2)); + row.push_back(std::make_pair(mpq(-910, 1727), 3)); + g.mk_gomory_cut(t, k, expl, inf_col, row); +} + +void test_gomory_cut_1() { + gomory_test g( + [](unsigned j) { return "v" + T_to_string(j);} // name_function_p + , + [](unsigned j) { //get_value_p + if (j == 1) + return mpq(-2); + if (j == 2) + return mpq(4363334, 2730001); + if (j == 3) + return mpq(1); + lp_assert(false); + return zero_of_type(); + }, + [](unsigned j) { // at_low_p + if (j == 1) + return false; + if (j == 2) + return false; + if (j == 3) + return true; + lp_assert(false); + return false; + }, + [](unsigned j) { // at_upper + if (j == 1) + return true; + if (j == 2) + return false; + if (j == 3) + return true; + lp_assert(false); + return false; + }, + [](unsigned j) { // lower_bound + if (j == 1) { + lp_assert(false); //unlimited from below + return 0; + } + if (j == 2) + return 1; + if (j == 3) + return 1; + lp_assert(false); + return 0; + }, + [](unsigned j) { // upper + if (j == 1) { + return -2; + } + if (j == 2) + return 3333; + if (j == 3) + return 10000; + lp_assert(false); + return 0; + }, + [] (unsigned) { return 0; }, + [] (unsigned) { return 0; } + ); + lar_term t; + mpq k; + explanation expl; + unsigned inf_col = 2; + vector> row; + row.push_back(std::make_pair(mpq(1726667, 2730001), 1)); + row.push_back(std::make_pair(mpq(-910000, 2730001), 3)); + row.push_back(std::make_pair(mpq(1), 2)); + g.mk_gomory_cut(t, k, expl, inf_col, row); +} + +void call_hnf(general_matrix & A); + +void test_hnf_m_less_than_n() { +#ifdef Z3DEBUG + general_matrix A; + vector v; + // example 4.3 from Nemhauser, Wolsey + v.push_back(mpq(2)); + v.push_back(mpq(6)); + v.push_back(mpq(1)); + v.push_back(mpq(3)); + A.push_row(v); + v.clear(); + v.push_back(mpq(4)); + v.push_back(mpq(7)); + v.push_back(mpq(7)); + v.push_back(mpq(3)); + A.push_row(v); + v.clear(); + v.push_back(mpq(0)); + v.push_back(mpq(0)); + v.push_back(mpq(1)); + v.push_back(mpq(5)); + A.push_row(v); + call_hnf(A); +#endif +} +void test_hnf_m_greater_than_n() { +#ifdef Z3DEBUG + general_matrix A; + vector v; + v.push_back(mpq(2)); + v.push_back(mpq(6)); + A.push_row(v); + v.clear(); + v.push_back(mpq(4)); + v.push_back(mpq(7)); + A.push_row(v); + v.clear(); + v.push_back(mpq(0)); + v.push_back(mpq(0)); + A.push_row(v); + v.clear(); + v.push_back(mpq(12)); + v.push_back(mpq(55)); + A.push_row(v); + call_hnf(A); +#endif +} + + +void cutting_the_mix_example_1() { + mpq sev(7); + mpq nine(9); + mpq d, u, vv; + hnf_calc::extended_gcd_minimal_uv(sev, nine, d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + hnf_calc::extended_gcd_minimal_uv(sev, -nine, d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + + hnf_calc::extended_gcd_minimal_uv(-nine, -nine, d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + + hnf_calc::extended_gcd_minimal_uv(-sev*2, sev, d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + + hnf_calc::extended_gcd_minimal_uv(mpq(24), mpq(-7), d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + hnf_calc::extended_gcd_minimal_uv(-mpq(24), mpq(7), d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + hnf_calc::extended_gcd_minimal_uv(mpq(24), mpq(7), d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + hnf_calc::extended_gcd_minimal_uv(-mpq(21), mpq(7), d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + + hnf_calc::extended_gcd_minimal_uv(mpq(21), -mpq(7), d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; +} + +#ifdef Z3DEBUG + +void fill_general_matrix(general_matrix & M) { + unsigned m = M.row_count(); + unsigned n = M.column_count(); + for (unsigned i = 0; i < m; i++) + for (unsigned j = 0; j < n; j++) + M[i][j] = mpq(static_cast(my_random() % 13) - 6); +} + +void call_hnf(general_matrix& A) { + svector r; + mpq d = hnf_calc::determinant_of_rectangular_matrix(A, r, mpq((int)1000000000)); + A.shrink_to_rank(r); + hnf h(A, d); +} + + +void test_hnf_for_dim(int m) { + general_matrix M(m, m + my_random() % m); + fill_general_matrix(M); + call_hnf(M); +} +void test_hnf_1_2() { + std::cout << "test_hnf_1_2" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(5)); + v.push_back(mpq(26)); + A.push_row(v); + call_hnf(A); + std::cout << "test_hnf_1_2 passed" << std::endl; +} +void test_hnf_2_2() { + std::cout << "test_hnf_2_2" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(5)); + v.push_back(mpq(26)); + A.push_row(v); + v.clear(); + v.push_back(mpq(2)); + v.push_back(mpq(11)); + A.push_row(v); + call_hnf(A); + + std::cout << "test_hnf_2_2 passed" << std::endl; +} + +void test_hnf_3_3() { + std::cout << "test_hnf_3_3" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(-3)); + v.push_back(mpq(0)); + v.push_back(mpq(-1)); + A.push_row(v); + v.clear(); + v.push_back(mpq(-1)); + v.push_back(mpq(0)); + v.push_back(mpq(-6)); + A.push_row(v); + v.clear(); + v.push_back(mpq(-2)); + v.push_back(mpq(-4)); + v.push_back(mpq(-3)); + A.push_row(v); + + call_hnf(A); + std::cout << "test_hnf_3_3 passed" << std::endl; +} +void test_hnf_4_4() { + std::cout << "test_hnf_4_4" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(4)); + v.push_back(mpq(3)); + v.push_back(mpq(-5)); + v.push_back(mpq(6)); + A.push_row(v); + v.clear(); + v.push_back(mpq(1)); + v.push_back(mpq(-3)); + v.push_back(mpq(1)); + v.push_back(mpq(-4)); + A.push_row(v); + v.clear(); + v.push_back(mpq(4)); + v.push_back(mpq(4)); + v.push_back(mpq(4)); + v.push_back(mpq(4)); + A.push_row(v); + v.clear(); + v.push_back(mpq(2)); + v.push_back(mpq(-2)); + v.push_back(mpq(-5)); + v.push_back(mpq(6)); + A.push_row(v); + call_hnf(A); + std::cout << "test_hnf_4_4 passed" << std::endl; +} +void test_hnf_5_5() { + std::cout << "test_hnf_5_5" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(-4)); + v.push_back(mpq(5)); + v.push_back(mpq(-5)); + v.push_back(mpq(1)); + v.push_back(mpq(-3)); + A.push_row(v); + v.clear(); + v.push_back(mpq(3)); + v.push_back(mpq(-1)); + v.push_back(mpq(2)); + v.push_back(mpq(3)); + v.push_back(mpq(-5)); + A.push_row(v); + v.clear(); + v.push_back(mpq(0)); + v.push_back(mpq(6)); + v.push_back(mpq(-5)); + v.push_back(mpq(-6)); + v.push_back(mpq(-2)); + A.push_row(v); + v.clear(); + v.push_back(mpq(1)); + v.push_back(mpq(0)); + v.push_back(mpq(-4)); + v.push_back(mpq(-4)); + v.push_back(mpq(4)); + A.push_row(v); + v.clear(); + v.push_back(mpq(-2)); + v.push_back(mpq(3)); + v.push_back(mpq(6)); + v.push_back(mpq(-5)); + v.push_back(mpq(-1)); + A.push_row(v); + call_hnf(A); + std::cout << "test_hnf_5_5 passed" << std::endl; +} + +void test_small_generated_hnf() { + std::cout << "test_small_rank_hnf" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(5)); + v.push_back(mpq(26)); + A.push_row(v); + v.clear(); + v.push_back(zero_of_type()); + v.push_back(zero_of_type()); + A.push_row(v); + call_hnf(A); + std::cout << "test_small_rank_hnf passed" << std::endl; +} +void test_larger_generated_hnf() { + std::cout << "test_larger_generated_rank_hnf" << std::endl; + general_matrix A; + vector v; + v.clear(); + v.push_back(mpq(5)); + v.push_back(mpq(6)); + v.push_back(mpq(3)); + v.push_back(mpq(1)); + A.push_row(v); + v.clear(); + v.push_back(mpq(5)); + v.push_back(mpq(2)); + v.push_back(mpq(3)); + v.push_back(mpq(7)); + A.push_row(v); + v.clear(); + v.push_back(mpq(5)); + v.push_back(mpq(6)); + v.push_back(mpq(3)); + v.push_back(mpq(1)); + A.push_row(v); + v.clear(); + v.push_back(mpq(5)); + v.push_back(mpq(2)); + v.push_back(mpq(3)); + v.push_back(mpq(7)); + A.push_row(v); + call_hnf(A); + std::cout << "test_larger_generated_rank_hnf passed" << std::endl; +} +#endif +void test_maximize_term() { + std::cout << "test_maximize_term\n"; + lar_solver solver; + int_solver i_solver(&solver); // have to create it too + unsigned _x = 0; + unsigned _y = 1; + var_index x = solver.add_var(_x, false); + var_index y = solver.add_var(_y, true); + vector> term_ls; + term_ls.push_back(std::pair((int)1, x)); + term_ls.push_back(std::pair((int)-1, y)); + unsigned term_x_min_y = solver.add_term(term_ls, mpq(0)); + term_ls.clear(); + term_ls.push_back(std::pair((int)2, x)); + term_ls.push_back(std::pair((int)2, y)); + + unsigned term_2x_pl_2y = solver.add_term(term_ls, mpq(0)); + solver.add_var_bound(term_x_min_y, LE, zero_of_type()); + solver.add_var_bound(term_2x_pl_2y, LE, mpq((int)5)); + solver.find_feasible_solution(); + lp_assert(solver.get_status() == lp_status::OPTIMAL); + solver.print_constraints(std::cout); + std::unordered_map model; + solver.get_model(model); + for (auto p : model) { + std::cout<< "v[" << p.first << "] = " << p.second << std::endl; + } + std::cout << "calling int_solver\n"; + lar_term t; mpq k; explanation ex; bool upper; + lia_move lm = i_solver.check(t, k, ex, upper); + lp_assert(lm == lia_move::sat); + impq term_max; + lp_status st = solver.maximize_term(term_2x_pl_2y, term_max); + + std::cout << "status = " << lp_status_to_string(st) << std::endl; + std::cout << "term_max = " << term_max << std::endl; + solver.get_model(model); + for (auto p : model) { + std::cout<< "v[" << p.first << "] = " << p.second << std::endl; + } + +} +#ifdef Z3DEBUG +void test_hnf() { + test_larger_generated_hnf(); + test_small_generated_hnf(); + test_hnf_1_2(); + test_hnf_3_3(); + test_hnf_4_4(); + test_hnf_5_5(); + test_hnf_2_2(); + for (unsigned k=1000; k>0; k--) + for (int i = 1; i < 8; i++) + test_hnf_for_dim(i); + cutting_the_mix_example_1(); + // test_hnf_m_less_than_n(); + // test_hnf_m_greater_than_n(); +} +#endif +void test_gomory_cut() { + test_gomory_cut_0(); + test_gomory_cut_1(); +} + +void test_lp_local(int argn, char**argv) { + // initialize_util_module(); // initialize_numerics_module(); int ret; @@ -3096,6 +3555,18 @@ void test_lp_local(int argn, char**argv) { args_parser.print(); + if (args_parser.option_is_used("-hnf")) { +#ifdef Z3DEBUG + test_hnf(); +#endif + return finalize(0); + } + + if (args_parser.option_is_used("-gomory")) { + test_gomory_cut(); + return finalize(0); + } + if (args_parser.option_is_used("--test_mpq")) { test_rationals(); return finalize(0); @@ -3106,7 +3577,7 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - if (args_parser.option_is_used("--test_mpq_np_plus")) { + if (args_parser.option_is_used("--test_mpq_np_plus")) { test_rationals_no_numeric_pairs_plus(); return finalize(0); } @@ -3131,7 +3602,7 @@ void test_lp_local(int argn, char**argv) { #ifdef Z3DEBUG if (args_parser.option_is_used("--test_swaps")) { - sparse_matrix m(10); + square_sparse_matrix m(10, 0); fill_matrix(m); test_swap_rows_with_permutation(m); test_swap_cols_with_permutation(m); @@ -3199,7 +3670,12 @@ void test_lp_local(int argn, char**argv) { ret = 0; return finalize(ret); } - + + if (args_parser.option_is_used("--maximize_term")) { + test_maximize_term(); + ret = 0; + return finalize(ret); + } if (args_parser.option_is_used("--test_lp_0")) { test_lp_0(); @@ -3241,7 +3717,7 @@ void test_lp_local(int argn, char**argv) { test_init_U(); test_replace_column(); #ifdef Z3DEBUG - sparse_matrix_with_permutaions_test(); + square_sparse_matrix_with_permutaions_test(); test_dense_matrix(); test_swap_operations(); test_permutations(); @@ -3255,3 +3731,8 @@ void test_lp_local(int argn, char**argv) { void tst_lp(char ** argv, int argc, int& i) { lp::test_lp_local(argc - 2, argv + 2); } +#ifdef Z3DEBUG +namespace lp { +template void print_matrix(general_matrix&, std::ostream&); +} +#endif diff --git a/src/test/lp/lp_main.cpp b/src/test/lp/lp_main.cpp new file mode 100644 index 000000000..450664fd0 --- /dev/null +++ b/src/test/lp/lp_main.cpp @@ -0,0 +1,14 @@ +void gparams_register_modules(){} +void mem_initialize() {} +void mem_finalize() {} +#include "util/rational.h" +namespace lp { +void test_lp_local(int argc, char**argv); +} +int main(int argn, char**argv){ + rational::initialize(); + lp::test_lp_local(argn, argv); + rational::finalize(); + return 0; +} + diff --git a/src/test/smt_reader.h b/src/test/lp/smt_reader.h similarity index 94% rename from src/test/smt_reader.h rename to src/test/lp/smt_reader.h index 437cb7a6b..16f44e3b3 100644 --- a/src/test/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -121,13 +121,13 @@ namespace lp { void fill_simple_elem(lisp_elem & lm) { int separator = first_separator(); - SASSERT(-1 != separator && separator != 0); + lp_assert(-1 != separator && separator != 0); lm.m_head = m_line.substr(0, separator); m_line = m_line.substr(separator); } void fill_nested_elem(lisp_elem & lm) { - SASSERT(m_line[0] == '('); + lp_assert(m_line[0] == '('); m_line = m_line.substr(1); int separator = first_separator(); lm.m_head = m_line.substr(0, separator); @@ -194,11 +194,11 @@ namespace lp { } void adjust_rigth_side(formula_constraint & /* c*/, lisp_elem & /*el*/) { - // SASSERT(el.m_head == "0"); // do nothing for the time being + // lp_assert(el.m_head == "0"); // do nothing for the time being } void set_constraint_coeffs(formula_constraint & c, lisp_elem & el) { - SASSERT(el.m_elems.size() == 2); + lp_assert(el.m_elems.size() == 2); set_constraint_coeffs_on_coeff_element(c, el.m_elems[0]); adjust_rigth_side(c, el.m_elems[1]); } @@ -214,7 +214,7 @@ namespace lp { add_mult_elem(c, el.m_elems); } else if (el.m_head == "~") { lisp_elem & minel = el.m_elems[0]; - SASSERT(minel.is_simple()); + lp_assert(minel.is_simple()); c.m_right_side += mpq(str_to_int(minel.m_head)); } else { std::cout << "unexpected input " << el.m_head << std::endl; @@ -224,14 +224,14 @@ namespace lp { } std::string get_name(lisp_elem & name) { - SASSERT(name.is_simple()); - SASSERT(!is_integer(name.m_head)); + lp_assert(name.is_simple()); + lp_assert(!is_integer(name.m_head)); return name.m_head; } void add_mult_elem(formula_constraint & c, std::vector & els) { - SASSERT(els.size() == 2); + lp_assert(els.size() == 2); mpq coeff = get_coeff(els[0]); std::string col_name = get_name(els[1]); c.add_pair(coeff, col_name); @@ -241,16 +241,16 @@ namespace lp { if (le.is_simple()) { return mpq(str_to_int(le.m_head)); } else { - SASSERT(le.m_head == "~"); - SASSERT(le.size() == 1); + lp_assert(le.m_head == "~"); + lp_assert(le.size() == 1); lisp_elem & el = le.m_elems[0]; - SASSERT(el.is_simple()); + lp_assert(el.is_simple()); return -mpq(str_to_int(el.m_head)); } } int str_to_int(std::string & s) { - SASSERT(is_integer(s)); + lp_assert(is_integer(s)); return atoi(s.c_str()); } @@ -258,7 +258,7 @@ namespace lp { if (el.size()) { add_complex_sum_elem(c, el); } else { - SASSERT(is_integer(el.m_head)); + lp_assert(is_integer(el.m_head)); int v = atoi(el.m_head.c_str()); mpq vr(v); c.m_right_side -= vr; @@ -276,7 +276,7 @@ namespace lp { } else if (el.m_head == "+") { add_sum(c, el.m_elems); } else { - SASSERT(false); // unexpected input + lp_assert(false); // unexpected input } } @@ -389,7 +389,7 @@ namespace lp { void add_constraint_to_solver(lar_solver * solver, formula_constraint & fc) { vector> ls; for (auto & it : fc.m_coeffs) { - ls.push_back(std::make_pair(it.first, solver->add_var(register_name(it.second)))); + ls.push_back(std::make_pair(it.first, solver->add_var(register_name(it.second), false))); } solver->add_constraint(ls, fc.m_kind, fc.m_right_side); } diff --git a/src/test/test_file_reader.h b/src/test/lp/test_file_reader.h similarity index 100% rename from src/test/test_file_reader.h rename to src/test/lp/test_file_reader.h diff --git a/src/test/simplex.cpp b/src/test/simplex.cpp index 6620dc83b..782be7ea5 100644 --- a/src/test/simplex.cpp +++ b/src/test/simplex.cpp @@ -4,7 +4,6 @@ Copyright (c) 2015 Microsoft Corporation --*/ -#include "util/lp/sparse_matrix.h" #include "math/simplex/sparse_matrix_def.h" #include "math/simplex/simplex.h" #include "math/simplex/simplex_def.h" diff --git a/src/util/lp/CMakeLists.txt b/src/util/lp/CMakeLists.txt index 70c5f9e3b..c67c55759 100644 --- a/src/util/lp/CMakeLists.txt +++ b/src/util/lp/CMakeLists.txt @@ -1,33 +1,37 @@ z3_add_component(lp SOURCES lp_utils.cpp - binary_heap_priority_queue_instances.cpp - binary_heap_upair_queue_instances.cpp - lp_bound_propagator.cpp - core_solver_pretty_printer_instances.cpp - dense_matrix_instances.cpp - eta_matrix_instances.cpp - indexed_vector_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 - permutation_matrix_instances.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 + binary_heap_priority_queue.cpp + binary_heap_upair_queue.cpp + bound_propagator.cpp + core_solver_pretty_printer.cpp + dense_matrix.cpp + eta_matrix.cpp + indexed_vector.cpp + int_solver.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.cpp + row_eta_matrix.cpp + scaler.cpp + square_sparse_matrix.cpp + square_dense_submatrix.cpp + static_matrix.cpp + random_updater.cpp COMPONENT_DEPENDENCIES util + polynomial + nlsat PYG_FILES lp_params.pyg ) diff --git a/src/util/lp/active_set.h b/src/util/lp/active_set.h new file mode 100644 index 000000000..587570559 --- /dev/null +++ b/src/util/lp/active_set.h @@ -0,0 +1,76 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ + +#pragma once +#include "util/lp/binary_heap_priority_queue.h" +namespace lp { +class active_set { + std::unordered_set m_cs; + binary_heap_priority_queue m_q; + std::unordered_map m_id_to_constraint; +public: + std::unordered_set cs() const { return m_cs;} + + bool contains(const constraint* c) const { + return m_id_to_constraint.find(c->id()) != m_id_to_constraint.end(); + } + + bool is_empty() const { return m_cs.size() == 0; } + // low priority will be dequeued first + void add_constraint(constraint* c, int priority) { + if (contains(c)) + return; + m_cs.insert(c); + m_id_to_constraint[c->id()] = c; + m_q.enqueue(c->id(), priority); + } + + void clear() { + m_cs.clear(); + m_id_to_constraint.clear(); + m_q.clear(); + } + + + constraint* remove_constraint() { + if (m_cs.size() == 0) + return nullptr; + unsigned id = m_q.dequeue(); + auto it = m_id_to_constraint.find(id); + lp_assert(it != m_id_to_constraint.end()); + constraint* c = it->second; + m_cs.erase(c); + m_id_to_constraint.erase(it); + return c; + } + + unsigned size() const { + return static_cast(m_cs.size()); + } + + void remove_constraint(constraint * c) { + if (! contains(c)) return; + + m_cs.erase(c); + m_id_to_constraint.erase(c->id()); + m_q.remove(c->id()); + } +}; +} 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.h b/src/util/lp/binary_heap_priority_queue.h index 8282ece9c..9a71fc01e 100644 --- a/src/util/lp/binary_heap_priority_queue.h +++ b/src/util/lp/binary_heap_priority_queue.h @@ -45,7 +45,7 @@ public: unsigned size() const { return m_heap_size; } binary_heap_priority_queue(): m_heap(1), m_heap_size(0) {} // the empty constructror // n is the initial queue capacity. - // The capacity will be enlarged two times automatically if needed + // The capacity will be enlarged each time twice if needed binary_heap_priority_queue(unsigned n); void clear() { @@ -75,7 +75,7 @@ public: /// return the first element of the queue and removes it from the queue unsigned dequeue(); unsigned peek() const { - SASSERT(m_heap_size > 0); + lp_assert(m_heap_size > 0); return m_heap[1]; } #ifdef Z3DEBUG diff --git a/src/util/lp/binary_heap_priority_queue.hpp b/src/util/lp/binary_heap_priority_queue_def.h similarity index 91% rename from src/util/lp/binary_heap_priority_queue.hpp rename to src/util/lp/binary_heap_priority_queue_def.h index e7378309d..8a39ecdfa 100644 --- a/src/util/lp/binary_heap_priority_queue.hpp +++ b/src/util/lp/binary_heap_priority_queue_def.h @@ -20,7 +20,7 @@ Revision History: #include "util/vector.h" #include "util/lp/binary_heap_priority_queue.h" namespace lp { -// this is the child place in the heap +// "i" is the child's place in the heap template void binary_heap_priority_queue::swap_with_parent(unsigned i) { unsigned parent = m_heap[i >> 1]; put_at(i >> 1, m_heap[i]); @@ -48,8 +48,8 @@ template void binary_heap_priority_queue::decrease_priority(unsi template bool binary_heap_priority_queue::is_consistent() const { for (int i = 0; i < m_heap_inverse.size(); i++) { int i_index = m_heap_inverse[i]; - SASSERT(i_index <= static_cast(m_heap_size)); - SASSERT(i_index == -1 || m_heap[i_index] == i); + lp_assert(i_index <= static_cast(m_heap_size)); + lp_assert(i_index == -1 || m_heap[i_index] == i); } for (unsigned i = 1; i < m_heap_size; i++) { unsigned ch = i << 1; @@ -71,7 +71,7 @@ template void binary_heap_priority_queue::remove(unsigned o) { if (o_in_heap == -1) { return; // nothing to do } - SASSERT(static_cast(o_in_heap) <= m_heap_size); + lp_assert(static_cast(o_in_heap) <= m_heap_size); if (static_cast(o_in_heap) < m_heap_size) { put_at(o_in_heap, m_heap[m_heap_size--]); if (m_priorities[m_heap[o_in_heap]] > priority_of_o) { @@ -88,11 +88,11 @@ template void binary_heap_priority_queue::remove(unsigned o) { } } } else { - SASSERT(static_cast(o_in_heap) == m_heap_size); + lp_assert(static_cast(o_in_heap) == m_heap_size); m_heap_size--; } m_heap_inverse[o] = -1; - // SASSERT(is_consistent()); + // lp_assert(is_consistent()); } // n is the initial queue capacity. // The capacity will be enlarged two times automatically if needed @@ -118,7 +118,7 @@ template void binary_heap_priority_queue::put_to_heap(unsigned i template void binary_heap_priority_queue::enqueue_new(unsigned o, const T& priority) { m_heap_size++; int i = m_heap_size; - SASSERT(o < m_priorities.size()); + lp_assert(o < m_priorities.size()); m_priorities[o] = priority; put_at(i, o); while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) { @@ -130,8 +130,12 @@ template void binary_heap_priority_queue::enqueue_new(unsigned o // In this case the priority will be changed and the queue adjusted. template void binary_heap_priority_queue::enqueue(unsigned o, const T & priority) { if (o >= m_priorities.size()) { - resize(o << 1); // make the size twice larger + if (o == 0) + resize(2); + else + resize(o << 1); // make the size twice larger } + if (m_heap_inverse[o] == -1) enqueue_new(o, priority); else @@ -150,7 +154,7 @@ template void binary_heap_priority_queue::change_priority_for_ex /// return the first element of the queue and removes it from the queue template unsigned binary_heap_priority_queue::dequeue_and_get_priority(T & priority) { - SASSERT(m_heap_size != 0); + lp_assert(m_heap_size != 0); int ret = m_heap[1]; priority = m_priorities[ret]; put_the_last_at_the_top_and_fix_the_heap(); @@ -184,7 +188,7 @@ template void binary_heap_priority_queue::put_the_last_at_the_to } /// return the first element of the queue and removes it from the queue template unsigned binary_heap_priority_queue::dequeue() { - SASSERT(m_heap_size > 0); + lp_assert(m_heap_size > 0); int ret = m_heap[1]; put_the_last_at_the_top_and_fix_the_heap(); m_heap_inverse[ret] = -1; 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 95% rename from src/util/lp/binary_heap_upair_queue.hpp rename to src/util/lp/binary_heap_upair_queue_def.h index d12be9707..e9f3d424a 100644 --- a/src/util/lp/binary_heap_upair_queue.hpp +++ b/src/util/lp/binary_heap_upair_queue_def.h @@ -29,7 +29,7 @@ template binary_heap_upair_queue::binary_heap_upair_queue(unsign template unsigned binary_heap_upair_queue::dequeue_available_spot() { - SASSERT(m_available_spots.empty() == false); + lp_assert(m_available_spots.empty() == false); unsigned ret = m_available_spots.back(); m_available_spots.pop_back(); return ret; @@ -69,7 +69,7 @@ template void binary_heap_upair_queue::enqueue(unsigned i, unsig m_pairs.resize(new_size); } ij_index = dequeue_available_spot(); - // SASSERT(ij_index void binary_heap_upair_queue::enqueue(unsigned i, unsig } template void binary_heap_upair_queue::dequeue(unsigned & i, unsigned &j) { - SASSERT(!m_q.is_empty()); + lp_assert(!m_q.is_empty()); unsigned ij_index = m_q.dequeue(); upair & p = m_pairs[ij_index]; i = p.first; diff --git a/src/util/lp/bound_analyzer_on_row.h b/src/util/lp/bound_analyzer_on_row.h index 52b8ece64..196551f20 100644 --- a/src/util/lp/bound_analyzer_on_row.h +++ b/src/util/lp/bound_analyzer_on_row.h @@ -19,37 +19,93 @@ 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. // In the same loop trying to pin variables by pushing the partial sum up, denoting the variable related to it by _l namespace lp { - +template // C plays a role of a container class bound_analyzer_on_row { + struct term_with_basis_col { + const C & m_row; + unsigned m_bj; + struct ival { + unsigned m_var; + const mpq & m_coeff; + ival(unsigned var, const mpq & val) : m_var(var), m_coeff(val) { + } + unsigned var() const { return m_var;} + const mpq & coeff() const { return m_coeff; } + }; + + term_with_basis_col(const C& row, unsigned bj) : m_row(row), m_bj(bj) {} + struct const_iterator { + // fields + typename C::const_iterator m_it; + unsigned m_bj; + - linear_combination_iterator & m_it; - lp_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 - int m_column_of_l; // index of an unlimited from below monoid - impq m_rs; + //typedefs + + + typedef const_iterator self_type; + typedef ival value_type; + typedef ival reference; + typedef int difference_type; + typedef std::forward_iterator_tag iterator_category; + + reference operator*() const { + if (m_bj == static_cast(-1)) + return ival((*m_it).var(), (*m_it).coeff()); + return ival(m_bj, - 1); + } + self_type operator++() { self_type i = *this; operator++(1); return i; } + + self_type operator++(int) { + if (m_bj == static_cast(-1)) + m_it++; + else + m_bj = static_cast(-1); + return *this; + } + + // constructor + const_iterator(const typename C::const_iterator& it, unsigned bj) : + m_it(it), + m_bj(bj) + {} + bool operator==(const self_type &other) const { + return m_it == other.m_it && m_bj == other.m_bj ; + } + bool operator!=(const self_type &other) const { return !(*this == other); } + }; + const_iterator begin() const { + return const_iterator( m_row.begin(), m_bj); + } + const_iterator end() const { return const_iterator(m_row.end(), m_bj); } + }; + term_with_basis_col m_row; + 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 + 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 + const C & it, + unsigned bj, // basis column for the row + const numeric_pair& rs, + unsigned row_or_term_index, + bound_propagator & bp ) : - m_it(it), + m_row(it, bj), m_bp(bp), m_row_or_term_index(row_or_term_index), m_column_of_u(-1), @@ -60,11 +116,11 @@ 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); - + for (const auto & c : m_row) { + if ((m_column_of_l == -2) && (m_column_of_u == -2)) + break; + analyze_bound_on_var_on_coeff(c.var(), c.coeff()); + } if (m_column_of_u >= 0) limit_monoid_u_from_below(); else if (m_column_of_u == -1) @@ -76,42 +132,42 @@ 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 { - SASSERT(upper_bound_is_available(j)); + lp_assert(upper_bound_is_available(j)); return m_bp.get_upper_bound(j); } const impq & lb(unsigned j) const { - SASSERT(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 +207,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,34 +216,32 @@ public : if (is_neg(a)) { return a * ub(j).x; } - + return a * lb(j).x; } - + void limit_all_monoids_from_above() { int strict = 0; mpq total; - SASSERT(is_zero(total)); - m_it.reset(); - mpq a; unsigned j; - while (m_it.next(a, j)) { + lp_assert(is_zero(total)); + for (const auto& p : m_row) { bool str; - total -= monoid_min(a, j, str); + total -= monoid_min(p.coeff(), p.var(), str); if (str) strict++; } - m_it.reset(); - while (m_it.next(a, j)) { + + for (const auto &p : m_row) { bool str; - bool a_is_pos = is_pos(a); - mpq bound = total / a + monoid_min_no_mult(a_is_pos, j, str); + bool a_is_pos = is_pos(p.coeff()); + mpq bound = total / p.coeff() + monoid_min_no_mult(a_is_pos, p.var(), str); if (a_is_pos) { - limit_j(j, bound, true, false, strict - static_cast(str) > 0); + limit_j(p.var(), bound, true, false, strict - static_cast(str) > 0); } else { - limit_j(j, bound, false, true, strict - static_cast(str) > 0); + limit_j(p.var(), bound, false, true, strict - static_cast(str) > 0); } } } @@ -195,52 +249,50 @@ public : void limit_all_monoids_from_below() { int strict = 0; mpq total; - SASSERT(is_zero(total)); - m_it.reset(); - mpq a; unsigned j; - while (m_it.next(a, j)) { + lp_assert(is_zero(total)); + for (const auto &p : m_row) { bool str; - total -= monoid_max(a, j, str); + total -= monoid_max(p.coeff(), p.var(), str); if (str) strict++; } - m_it.reset(); - while (m_it.next(a, j)) { + + for (const auto& p : m_row) { 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 a_is_pos = is_pos(p.coeff()); + mpq bound = total / p.coeff() + monoid_max_no_mult(a_is_pos, p.var(), str); + bool astrict = strict - static_cast(str) > 0; if (a_is_pos) { - limit_j(j, bound, true, true, astrict); + limit_j(p.var(), bound, true, true, astrict); } else { - limit_j(j, bound, false, false, astrict); + limit_j(p.var(), bound, false, false, astrict); } } } - + 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 - mpq u_coeff, a; + mpq u_coeff; unsigned j; mpq bound = -m_rs.x; - m_it.reset(); bool strict = false; - while (m_it.next(a, j)) { + for (const auto& p : m_row) { + j = p.var(); if (j == static_cast(m_column_of_u)) { - u_coeff = a; + u_coeff = p.coeff(); continue; } bool str; - bound -= monoid_max(a, j, str); + bound -= monoid_max(p.coeff(), j, str); if (str) strict = true; } bound /= u_coeff; - + if (numeric_traits::is_pos(u_coeff)) { limit_j(m_column_of_u, bound, true, true, strict); } else { @@ -252,19 +304,19 @@ public : void limit_monoid_l_from_above() { // we are going to limit from above the monoid m_column_of_l, // every other monoid is impossible to limit from above - mpq l_coeff, a; + mpq l_coeff; unsigned j; mpq bound = -m_rs.x; bool strict = false; - m_it.reset(); - while (m_it.next(a, j)) { + for (const auto &p : m_row) { + j = p.var(); if (j == static_cast(m_column_of_l)) { - l_coeff = a; + l_coeff = p.coeff(); continue; } bool str; - bound -= monoid_min(a, j, str); + bound -= monoid_min(p.coeff(), j, str); if (str) strict = true; } @@ -275,51 +327,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; - // auto it = m_it.clone(); + // lower_bound = !lower_bound; + // auto it = m_row.clone(); // mpq a; unsigned j; // while (it->next(a, j)) { // if (be.m_j == j) continue; - // SASSERT(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: @@ -337,14 +389,16 @@ public : } } - static void analyze_row(linear_combination_iterator &it, + static void analyze_row(const C & row, + unsigned bj, // basis column for the row 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); + bound_analyzer_on_row a(row, bj, rs, row_or_term_index, bp); a.analyze(); } }; } + diff --git a/src/util/lp/bound_propagator.cpp b/src/util/lp/bound_propagator.cpp new file mode 100644 index 000000000..c4fa2aefa --- /dev/null +++ b/src/util/lp/bound_propagator.cpp @@ -0,0 +1,58 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Lev Nachmanson +*/ +#include "util/lp/lar_solver.h" +namespace lp { +bound_propagator::bound_propagator(lar_solver & ls): + m_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_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]; +} +void bound_propagator::try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { + j = m_lar_solver.adjust_column_index_to_term_index(j); + if (m_lar_solver.is_term(j)) { + // lp treats terms as not having a free coefficient, restoring it below for the outside consumption + v += m_lar_solver.get_term(j).m_v; + } + + lconstraint_kind kind = is_low? GE : LE; + if (strict) + kind = static_cast(kind / 2); + + if (!bound_is_interesting(j, kind, v)) + return; + unsigned k; // index to ibounds + if (is_low) { + 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_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);); + } + } else { // the upper bound case + if (try_get_value(m_improved_upper_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_upper_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..a1f0301aa --- /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, lp::constraint_index j) = 0; +}; +} diff --git a/src/util/lp/column_info.h b/src/util/lp/column_info.h index e4b449bbf..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 { - SASSERT(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 { - SASSERT(m_upper_bound_is_set); + 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) { @@ -171,7 +171,7 @@ public: } T get_fixed_value() const { - SASSERT(m_is_fixed); + lp_assert(m_is_fixed); return m_fixed_value; } @@ -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 97d371f48..e6e8e53a2 100644 --- a/src/util/lp/column_namer.h +++ b/src/util/lp/column_namer.h @@ -19,31 +19,19 @@ Revision History: --*/ #include -#include "util/lp/linear_combination_iterator.h" +#include "util/lp/static_matrix.h" namespace lp { class column_namer { public: virtual std::string get_column_name(unsigned j) const = 0; template - void print_linear_iterator(linear_combination_iterator* it, std::ostream & out) const { + void print_row(const row_strip & row, std::ostream & out) const { vector> coeff; - T a; - unsigned i; - while (it->next(a, i)) { - coeff.push_back(std::make_pair(a, i)); + for (auto & p : row) { + coeff.push_back(std::make_pair(p.coeff(), p.var())); } print_linear_combination_of_column_indices(coeff, out); } - template - void print_linear_iterator_indices_only(linear_combination_iterator* it, std::ostream & out) const { - vector> coeff; - T a; - unsigned i; - while (it->next(a, i)) { - coeff.emplace_back(a, i); - } - print_linear_combination_of_column_indices_only(coeff, out); - } template void print_linear_combination_of_column_indices_only(const vector> & coeffs, std::ostream & out) const { @@ -65,10 +53,34 @@ public: else if (val != numeric_traits::one()) out << T_to_string(val); - out << "_" << it.second; + 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/constraint.h b/src/util/lp/constraint.h new file mode 100644 index 000000000..84ec188d6 --- /dev/null +++ b/src/util/lp/constraint.h @@ -0,0 +1,99 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ + +#pragma once +namespace lp { +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 + std::unordered_set m_assert_origins; // these indices come from the client and get collected during tightening +public : + unsigned id() const { return m_id; } + const polynomial & poly() const { return m_poly; } + polynomial & poly() { return m_poly; } + std::unordered_set & assert_origins() { return m_assert_origins;} + const std::unordered_set & assert_origins() const { return m_assert_origins;} + bool is_lemma() const { return !is_assert(); } + bool is_assert() const { return m_assert_origins.size() == 1; } + bool is_ineq() const { return m_is_ineq; } + const mpq & divider() const { return m_d; } +public: + constraint( + unsigned id, + constraint_index assert_origin, + const polynomial & p, + bool is_ineq): + m_id(id), + m_is_ineq(is_ineq), + m_poly(p) + { // creates an assert + m_assert_origins.insert(assert_origin); + } + constraint( + unsigned id, + const std::unordered_set& origins, + const polynomial & p, + bool is_ineq): + m_id(id), + m_is_ineq(is_ineq), + m_poly(p), + m_assert_origins(origins) + {} + + + + constraint( + unsigned id, + const polynomial & p, + bool is_ineq): + m_id(id), + m_is_ineq(is_ineq), + m_poly(p) { // 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; + } + void add_predecessor(const constraint* p) { + lp_assert(p != nullptr); + for (auto m : p->assert_origins()) + m_assert_origins.insert(m); } +}; +} 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 93% rename from src/util/lp/core_solver_pretty_printer.hpp rename to src/util/lp/core_solver_pretty_printer_def.h index 4ae49a550..58ffbb481 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"; @@ -105,6 +105,8 @@ template void core_solver_pretty_printer::init_m_ string name = m_core_solver.column_name(column); for (unsigned row = 0; row < nrows(); row ++) { + m_A[row].resize(ncols(), ""); + m_signs[row].resize(ncols(),""); set_coeff( m_A[row], m_signs[row], @@ -139,9 +141,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 +153,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); @@ -163,7 +165,7 @@ template void core_solver_pretty_printer::adjust_ case column_type::free_column: break; default: - SASSERT(false); + lp_assert(false); break; } } @@ -236,13 +238,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 +270,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 @@ -372,7 +374,7 @@ template void core_solver_pretty_printer::print_g unsigned width = m_column_widths[col]; string s = row[col]; int number_of_blanks = width - static_cast(s.size()); - SASSERT(number_of_blanks >= 0); + lp_assert(number_of_blanks >= 0); print_blanks(number_of_blanks, m_out); m_out << s << ' '; if (col < row.size() - 1) { @@ -383,7 +385,7 @@ template void core_solver_pretty_printer::print_g string rs = T_to_string(rst); int nb = m_rs_width - static_cast(rs.size()); - SASSERT(nb >= 0); + lp_assert(nb >= 0); print_blanks(nb + 1, m_out); m_out << rs << std::endl; } 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 e2ee54058..e1f34ecd9 100644 --- a/src/util/lp/dense_matrix.h +++ b/src/util/lp/dense_matrix.h @@ -46,7 +46,7 @@ public: dense_matrix(unsigned m, unsigned n); dense_matrix operator*=(matrix const & a) { - SASSERT(column_count() == a.row_count()); + lp_assert(column_count() == a.row_count()); dense_matrix c(row_count(), a.column_count()); for (unsigned i = 0; i < row_count(); i++) { for (unsigned j = 0; j < a.column_count(); j++) { @@ -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 97% rename from src/util/lp/dense_matrix.hpp rename to src/util/lp/dense_matrix_def.h index a1f815109..f615c412d 100644 --- a/src/util/lp/dense_matrix.hpp +++ b/src/util/lp/dense_matrix_def.h @@ -23,7 +23,6 @@ Revision History: #include "util/lp/numeric_pair.h" #include "util/lp/dense_matrix.h" namespace lp { -template void print_vector(const vector & t, std::ostream & out); template dense_matrix::dense_matrix(unsigned m, unsigned n) : m_m(m), m_n(n), m_values(m * n, numeric_traits::zero()) { } @@ -185,7 +184,7 @@ template void dense_matrix::multiply_row_by_const template dense_matrix operator* (matrix & a, matrix & b){ - SASSERT(a.column_count() == b.row_count()); + lp_assert(a.column_count() == b.row_count()); dense_matrix ret(a.row_count(), b.column_count()); for (unsigned i = 0; i < ret.m_m; i++) for (unsigned j = 0; j< ret.m_n; j++) { diff --git a/src/util/lp/disjoint_intervals.h b/src/util/lp/disjoint_intervals.h deleted file mode 100644 index 5f4f31af6..000000000 --- a/src/util/lp/disjoint_intervals.h +++ /dev/null @@ -1,334 +0,0 @@ -/* - Copyright (c) 2017 Microsoft Corporation - Author: Lev Nachmanson -*/ -#pragma once -#include -namespace lp { -// represents the set of disjoint intervals of integer number -struct disjoint_intervals { - std::map m_endpoints; // 0 means start, 1 means end, 2 means both - for a point interval - bool m_empty; - // constructors create an interval containing all integer numbers or an empty interval - disjoint_intervals() : m_empty(false) {} - disjoint_intervals(bool is_empty) : m_empty(is_empty) {} - - bool is_start(short x) const { return x == 0 || x == 2; } - bool is_start(const std::map::iterator & it) const { - return is_start(it->second); - } - bool is_start(const std::map::reverse_iterator & it) const { - return is_start(it->second); - } - bool is_end(short x) const { return x == 1 || x == 2; } - bool is_end(const std::map::iterator & it) const { - return is_end(it->second); - } - bool is_end(const std::map::reverse_iterator & it) const { - return is_end(it->second); - } - - int pos(const std::map::iterator & it) const { - return it->first; - } - int pos(const std::map::reverse_iterator & it) const { - return it->first; - } - - int bound_kind(const std::map::iterator & it) const { - return it->second; - } - - int bound_kind(const std::map::reverse_iterator & it) const { - return it->second; - } - - bool is_proper_start(short x) const { return x == 0; } - bool is_proper_end(short x) const { return x == 1; } - bool is_proper_end(const std::map::iterator & it) const { - return is_proper_end(it->second); - } - bool is_proper_end(const std::map::reverse_iterator & it) const { - return is_proper_end(it->second); - } - - bool is_one_point_interval(short x) const { return x == 2; } - bool is_one_point_interval(const std::map::iterator & it) const { - return is_one_point_interval(it->second); - } - bool is_one_point_interval(const std::map::reverse_iterator & it) const { - return is_one_point_interval(it->second); - } - - - void erase(int x) { - m_endpoints.erase(x); - } - - void set_one_point_segment(int x) { - m_endpoints[x] = 2; - } - - void set_start(int x) { - m_endpoints[x] = 0; - } - - void set_end(int x) { - m_endpoints[x] = 1; - } - - void remove_all_endpoints_below(int x) { - while (m_endpoints.begin() != m_endpoints.end() && m_endpoints.begin()->first < x) - m_endpoints.erase(m_endpoints.begin()); - } - // we intersect the existing set with the half open to the right interval - void intersect_with_lower_bound(int x) { - if (m_empty) - return; - if (m_endpoints.empty()) { - set_start(x); - return; - } - bool pos_inf = has_pos_inf(); - auto it = m_endpoints.begin(); - while (it != m_endpoints.end() && pos(it) < x) { - m_endpoints.erase(it); - it = m_endpoints.begin(); - } - if (m_endpoints.empty()) { - if (!pos_inf) { - m_empty = true; - return; - } - set_start(x); - return; - } - lp_assert(pos(it) >= x); - if (pos(it) == x) { - if (is_proper_end(it)) - set_one_point_segment(x); - } - else { // x(it) > x - if (is_proper_end(it)) { - set_start(x); - } - } - - lp_assert(is_correct()); - } - - // we intersect the existing set with the half open interval - void intersect_with_upper_bound(int x) { - if (m_empty) - return; - if (m_endpoints.empty()) { - set_end(x); - return; - } - bool neg_inf = has_neg_inf(); - auto it = m_endpoints.rbegin(); - - while (!m_endpoints.empty() && pos(it) > x) { - m_endpoints.erase(std::prev(m_endpoints.end())); - it = m_endpoints.rbegin(); - } - if (m_endpoints.empty()) { - if (!neg_inf) { - m_empty = true; - return; - } - set_end(x); - } - 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_segment(x); - } - } - else { // pos(it) < x} - if (is_start(it)) - set_end(x); - } - lp_assert(is_correct()); - } - - bool has_pos_inf() const { - if (m_empty) - return false; - - if (m_endpoints.empty()) - return true; - - lp_assert(m_endpoints.rbegin() != m_endpoints.rend()); - return m_endpoints.rbegin()->second == 0; - } - - bool has_neg_inf() const { - if (m_empty) - return false; - - if (m_endpoints.empty()) - return true; - auto it = m_endpoints.begin(); - return is_proper_end(it->second);//m_endpoints.begin()); - } - - // we are intersecting - void intersect_with_interval(int x, int y) { - if (m_empty) - return; - lp_assert(x <= y); - intersect_with_lower_bound(x); - intersect_with_upper_bound(y); - } - - // add an intervar [x, inf] - void unite_with_interval_x_pos_inf(int x) { - if (m_empty) { - set_start(x); - m_empty = false; - return; - } - - while (!m_endpoints.empty() && pos(m_endpoints.rbegin()) > x) { - m_endpoints.erase(std::prev(m_endpoints.end())); - } - - if (m_endpoints.empty()) { - set_start(x); - return; - } - auto it = m_endpoints.rbegin(); - lp_assert(pos(it) <= x); - if (pos(it) == x) { - if (is_end(it)) { - m_endpoints.erase(x); - } else { - set_start(x); - } - } else if (pos(it) == x - 1 && is_end(it)) { - m_endpoints.erase(x - 1); // closing the gap - } else { - if (!has_pos_inf()) - set_start(x); - } - } - - // add an interval [-inf, x] - void unite_with_interval_neg_inf_x(int x) { - if (m_empty) { - set_end(x); - m_empty = false; - return; - } - auto it = m_endpoints.upper_bound(x); - - if (it == m_endpoints.end()) { - bool pos_inf = has_pos_inf(); - m_endpoints.clear(); - // it could be the case where x is inside of the last infinite interval with pos inf - if (!pos_inf) - set_end(x); - return; - } - lp_assert(pos(it) > x); - if (is_one_point_interval(pos(it))) { - set_end(it->second); - } else { - if (is_start(it->second)) { - set_end(x); - } - } - - while (!m_endpoints.empty() && m_endpoints.begin()->first < x) { - m_endpoints.erase(m_endpoints.begin()); - } - lp_assert(is_correct()); - } - - void unite_with_interval(int x, int y) { - lp_assert(false); // not implemented - } - - bool is_correct() const { - if (m_empty) { - if (m_endpoints.size() > 0) { - std::cout << "is empty is true but m_endpoints.size() = " << m_endpoints.size() << std::endl; - return false; - } - return true; - } - bool expect_end; - bool prev = false; - int prev_x; - for (auto t : m_endpoints) { - if (prev && (expect_end != t.second > 0)) { - std::cout << "x = " << t.first << "\n"; - if (expect_end) { - std::cout << "expecting an interval end\n"; - } else { - std::cout << "expecting an interval start\n"; - } - return false; - } - - if (t.second == 2) { - expect_end = false; // swallow a point interval - } else { - if (prev) - expect_end = !expect_end; - else - expect_end = is_start(t.second); - } - if (prev) { - if (t.first - prev_x <= 1) { - std::cout << "the sequence is not increasing or the gap is too small: " << prev_x << ", " << t.first << std::endl; - return false; - } - } - prev = true; - prev_x = t.first; - } - - return true; - } - - void print(std::ostream & out) const { - if (m_empty) { - out << "empty\n"; - return; - } - if (m_endpoints.empty()){ - out << "[-oo,oo]\n"; - return; - } - bool first = true; - for (auto t : m_endpoints) { - if (first) { - if (t.second == 1) { - out << "[-oo," << t.first << "]"; - } - 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 (has_pos_inf()) - out << "oo]"; - out << "\n"; - } - - -}; -} diff --git a/src/util/lp/eta_matrix_instances.cpp b/src/util/lp/eta_matrix.cpp similarity index 98% rename from src/util/lp/eta_matrix_instances.cpp rename to src/util/lp/eta_matrix.cpp index 87e12c913..4cb43d87f 100644 --- a/src/util/lp/eta_matrix_instances.cpp +++ b/src/util/lp/eta_matrix.cpp @@ -20,7 +20,7 @@ Revision History: #include #include "util/vector.h" #include "util/lp/numeric_pair.h" -#include "util/lp/eta_matrix.hpp" +#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; diff --git a/src/util/lp/eta_matrix.h b/src/util/lp/eta_matrix.h index abed6d06b..a811492f7 100644 --- a/src/util/lp/eta_matrix.h +++ b/src/util/lp/eta_matrix.h @@ -76,7 +76,7 @@ public: void push_back(unsigned row_index, T val ) { - SASSERT(row_index != m_column_index); + lp_assert(row_index != m_column_index); m_column_vector.push_back(row_index, val); } diff --git a/src/util/lp/eta_matrix.hpp b/src/util/lp/eta_matrix_def.h similarity index 95% rename from src/util/lp/eta_matrix.hpp rename to src/util/lp/eta_matrix_def.h index ae4ed712e..5c7661e24 100644 --- a/src/util/lp/eta_matrix.hpp +++ b/src/util/lp/eta_matrix_def.h @@ -75,7 +75,7 @@ void eta_matrix::apply_from_right(vector & w) { } w[m_column_index] = t; #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(clone_w, w, get_number_of_rows())); + // lp_assert(vectors_are_equal(clone_w, w, get_number_of_rows())); // delete clone_w; #endif } @@ -115,8 +115,8 @@ void eta_matrix::apply_from_right(indexed_vector & w) { } #ifdef Z3DEBUG - // SASSERT(w.is_OK()); - // SASSERT(vectors_are_equal(wcopy, w.m_data)); + // lp_assert(w.is_OK()); + // lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif } #ifdef Z3DEBUG @@ -145,7 +145,7 @@ void eta_matrix::conjugate_by_permutation(permutation_matrix & p) { pair.first = p.get_rev(pair.first); } #ifdef Z3DEBUG - // SASSERT(deb == *this); + // lp_assert(deb == *this); #endif } } diff --git a/src/util/lp/explanation.h b/src/util/lp/explanation.h new file mode 100644 index 000000000..f811e1d0e --- /dev/null +++ b/src/util/lp/explanation.h @@ -0,0 +1,32 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +namespace lp { +struct explanation { + void clear() { m_explanation.clear(); } + 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)); + } +}; +} diff --git a/src/util/lp/general_matrix.h b/src/util/lp/general_matrix.h new file mode 100644 index 000000000..715f2cb08 --- /dev/null +++ b/src/util/lp/general_matrix.h @@ -0,0 +1,259 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include +namespace lp { +class general_matrix { + // fields + permutation_matrix m_row_permutation; + permutation_matrix m_column_permutation; + vector> m_data; + +public: + unsigned adjust_row(unsigned row) const{ + return m_row_permutation[row]; + } + + void push_row(vector & v) { + m_data.push_back(v); + m_row_permutation.resize(m_data.size()); + m_column_permutation.resize(v.size()); + } + + unsigned adjust_column(unsigned col) const{ + return m_column_permutation.apply_reverse(col); + } + + unsigned adjust_row_inverse(unsigned row) const{ + return m_row_permutation.apply_reverse(row); + } + + unsigned adjust_column_inverse(unsigned col) const{ + return m_column_permutation[col]; + } + + + unsigned row_count() const { return m_data.size(); } + unsigned column_count() const { return m_data.size() > 0? m_data[0].size() : 0; } + + class ref_row { + general_matrix& m_matrix; + vector& m_row_data; + public: + ref_row(general_matrix& m, vector& row_data) : m_matrix(m), m_row_data(row_data) {} + mpq & operator[](unsigned col) { return m_row_data[m_matrix.adjust_column(col)]; } + }; + class ref_row_const { + const general_matrix& m_matrix; + const vector& m_row_data; + public: + ref_row_const(const general_matrix& m, const vector& row_data) : m_matrix(m), m_row_data(row_data) {} + const mpq& operator[](unsigned col) const { return m_row_data[m_matrix.adjust_column(col)]; } + }; + + ref_row operator[](unsigned i) { return ref_row(*this, m_data[adjust_row(i)]); } + ref_row_const operator[](unsigned i) const { return ref_row_const(*this, m_data[adjust_row(i)]); } + +#ifdef Z3DEBUG + void print(std::ostream & out, unsigned blanks = 0) const { + unsigned m = row_count(); + unsigned n = column_count(); + general_matrix g(m, n); + for (unsigned i = 0; i < m; i++) + for (unsigned j = 0; j < n; j++) + g[i][j] = (*this)[i][j]; + print_matrix(g.m_data, out, blanks); + } + void print(std::ostream & out, const char * ss) const { + std::string s(ss); + out << s; + print(out, static_cast(s.size())); + } + + void print_submatrix(std::ostream & out, unsigned k, unsigned blanks = 0) const { + general_matrix m(row_count() - k, column_count() - k); + for (unsigned i = k; i < row_count(); i++) { + for (unsigned j = k; j < column_count(); j++) + m[i-k][j-k] = (*this)[i][j]; + } + print_matrix(m.m_data, out, blanks); + } + +#endif + + void clear() { m_data.clear(); } + + bool row_is_initialized_correctly(const vector& row) { + lp_assert(row.size() == column_count()); + for (unsigned j = 0; j < row.size(); j ++) + lp_assert(is_zero(row[j])); + return true; + } + + template + void init_row_from_container(int i, const T & c, std::function column_fix) { + auto & row = m_data[adjust_row(i)]; + lp_assert(row_is_initialized_correctly(row)); + for (const auto & p : c) { + unsigned j = adjust_column(column_fix(p.var())); + row[j] = p.coeff(); + } + } + + void copy_column_to_indexed_vector(unsigned entering, indexed_vector &w ) const { + lp_assert(false); // not implemented + } + general_matrix operator*(const general_matrix & m) const { + lp_assert(m.row_count() == column_count()); + general_matrix ret(row_count(), m.column_count()); + for (unsigned i = 0; i < row_count(); i ++) { + for (unsigned j = 0; j < m.column_count(); j++) { + mpq a(0); + for (unsigned k = 0; k < column_count(); k++) + a += ((*this)[i][k])*m[k][j]; + ret[i][j] = a; + } + } + return ret; + } + + bool elements_are_equal(const general_matrix& m) const { + for (unsigned i = 0; i < row_count(); i++) + for (unsigned j = 0; j < column_count(); j++) + if ( (*this)[i][j] != m[i][j]) + return false; + return true; + } + + bool elements_are_equal_modulo(const general_matrix& m, const mpq & d) const { + for (unsigned i = 0; i < row_count(); i++) + for (unsigned j = 0; j < column_count(); j++) + if (!is_zero(((*this)[i][j] - m[i][j]) % d)) + return false; + return true; + } + bool operator==(const general_matrix& m) const { + return row_count() == m.row_count() && column_count() == m.column_count() && elements_are_equal(m); + } + + bool operator!=(const general_matrix& m) const { + return !(*this == m); + } + + bool equal_modulo(const general_matrix& m, const mpq & d) const { + return row_count() == m.row_count() && column_count() == m.column_count() && elements_are_equal_modulo(m, d); + } + + + vector operator*(const vector & x) const { + vector r; + lp_assert(x.size() == column_count()); + for (unsigned i = 0; i < row_count(); i++) { + mpq v(0); + for (unsigned j = 0; j < column_count(); j++) { + v += (*this)[i][j] * x[j]; + } + r.push_back(v); + } + return r; + } + + // bool create_upper_triangle(general_matrix& m, vector& x) { + // for (unsigned i = 1; i < m.row_count(); i++) { + // lp_assert(false); // to be continued + // } + // } + + // bool solve_A_x_equal_b(const general_matrix& m, vector& x, const vector& b) const { + // auto m_copy = m; + // // for square matrices + // lp_assert(row_count() == b.size()); + // lp_assert(x.size() == column_count()); + // lp_assert(row_count() == column_count()); + // x = b; + // create_upper_triangle(copy_of_m, x); + // solve_on_triangle(copy_of_m, x); + // } + // + + void transpose_rows(unsigned i, unsigned l) { + lp_assert(i != l); + m_row_permutation.transpose_from_right(i, l); + } + + void transpose_columns(unsigned j, unsigned k) { + lp_assert(j != k); + m_column_permutation.transpose_from_left(j, k); + } + + general_matrix(){} + general_matrix(unsigned n) : + m_row_permutation(n), + m_column_permutation(n), + m_data(n) + { + for (auto& v : m_data){ + v.resize(n); + } + } + + general_matrix(unsigned m, unsigned n) : + m_row_permutation(m), + m_column_permutation(n), + m_data(m) { + for (auto& v : m_data){ + v.resize(n); + } + } + + void shrink_to_rank(const svector& basis_rows) { + if (basis_rows.size() == row_count()) return; + vector> data; // todo : not efficient code + for (unsigned i : basis_rows) + data.push_back(m_data[i]); + m_data = data; + } + + // used for debug only + general_matrix take_first_n_columns(unsigned n) const { + lp_assert(n <= column_count()); + if (n == column_count()) + return *this; + general_matrix ret(row_count(), n); + for (unsigned i = 0; i < row_count(); i++) + for (unsigned j = 0; j < n; j++) + ret[i][j] = (*this)[i][j]; + return ret; + } + inline + friend vector operator*(const vector & f, const general_matrix& a) { + vector r(a.column_count()); + for (unsigned j = 0; j < a.column_count(); j ++) { + mpq t = zero_of_type(); + for (unsigned i = 0; i < a.row_count(); i++) { + t += f[i] * a[i][j]; + } + r[j] = t; + } + return r; + } +}; + +} diff --git a/src/util/lp/hash_helper.h b/src/util/lp/hash_helper.h deleted file mode 100644 index ab5fa844b..000000000 --- a/src/util/lp/hash_helper.h +++ /dev/null @@ -1,54 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include -#include -#include "util/numerics/mpq.h" -#ifdef __CLANG__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmismatched-tags" -#endif -namespace std { -template<> -struct hash { - inline size_t operator()(const lp::mpq & v) const { - return v.hash(); - } -}; -} - -template -inline void hash_combine(std::size_t & seed, const T & v) { - seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); -} - -namespace std { -template struct hash> { - inline size_t operator()(const pair & v) const { - size_t seed = 0; - hash_combine(seed, v.first); - hash_combine(seed, v.second); - return seed; - } -}; -} -#ifdef __CLANG__ -#pragma clang diagnostic pop -#endif diff --git a/src/util/lp/hnf.h b/src/util/lp/hnf.h new file mode 100644 index 000000000..3cdeac466 --- /dev/null +++ b/src/util/lp/hnf.h @@ -0,0 +1,623 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + Creates the Hermite Normal Form of a matrix in place. + We suppose that $A$ is an integral $m$ by $n$ matrix or rank $m$, where $n >= m$. + The paragraph below is applicable to the usage of HNF. +We have $H = AU$ where $H$ is in Hermite Normal Form +and $U$ is a unimodular matrix. We do not have an explicit + representation of $U$. For a given $i$ we need to find the $i$-th + row of $U^{-1}$. +Let $e_i$ be a vector of length $m$ with all elements equal to $0$ and +$1$ at $i$-th position. Then we need to find the row vector $e_iU^{-1}=t$. Noticing that $U^{-1} = H^{-1}A$, we have $e_iH^{-1}A=t$. +We find $e_iH^{-1} = f$ by solving $e_i = fH$ and then $fA$ gives us $t$. + +Author: + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/lp/numeric_pair.h" +#include "util/ext_gcd.h" +namespace lp { +namespace hnf_calc { + + // d = u * a + v * b and the sum of abs(u) + abs(v) is minimal, d is positive +inline +void extended_gcd_minimal_uv(const mpq & a, const mpq & b, mpq & d, mpq & u, mpq & v) { + if (is_zero(a)) { + u = zero_of_type(); + v = one_of_type(); + d = b; + return; + } + if (is_zero(b)) { + u = one_of_type(); + v = zero_of_type(); + d = a; + return; + } +#if 1 + d = gcd(a, b, u, v); +#else + extended_gcd(a, b, d, u, v); +#endif + if (is_neg(d)) { + d = -d; + u = -u; + v = -v; + } + + if (d == a) { + u = one_of_type(); + v = zero_of_type(); + return; + } + if (d == -a) { + u = - one_of_type(); + v = zero_of_type(); + return; + } + + mpq a_over_d = abs(a) / d; + mpq r; + + mpq k = machine_div_rem(v, a_over_d, r); + if (is_neg(r)) { + r += a_over_d; + k -= one_of_type(); + } + + lp_assert(v == k * a_over_d + r); + + if (is_pos(b)) { + v = r - a_over_d; // v -= (k + 1) * a_over_d; + lp_assert(- a_over_d < v && v <= zero_of_type()); + + if (is_pos(a)) { + u += (k + 1) * (b / d); + lp_assert( one_of_type() <= u && u <= abs(b)/d); + } else { + u -= (k + 1) * (b / d); + lp_assert( one_of_type() <= -u && -u <= abs(b)/d); + } + } else { + v = r; // v -= k * a_over_d; + lp_assert(- a_over_d < -v && -v <= zero_of_type()); + if (is_pos(a)) { + u += k * (b / d); + lp_assert( one_of_type() <= u && u <= abs(b)/d); + } else { + u -= k * (b / d); + lp_assert( one_of_type() <= -u && -u <= abs(b)/d); + } + } + lp_assert(d == u * a + v * b); +} + + + +template +bool prepare_pivot_for_lower_triangle(M &m, unsigned r) { + for (unsigned i = r; i < m.row_count(); i++) { + for (unsigned j = r; j < m.column_count(); j++) { + if (!is_zero(m[i][j])) { + if (i != r) { + m.transpose_rows(i, r); + } + if (j != r) { + m.transpose_columns(j, r); + } + return true; + } + } + } + return false; +} + +template +void pivot_column_non_fractional(M &m, unsigned r, bool & overflow, const mpq & big_number) { + lp_assert(!is_zero(m[r][r])); + for (unsigned j = r + 1; j < m.column_count(); j++) { + for (unsigned i = r + 1; i < m.row_count(); i++) { + if ( + (m[i][j] = (r > 0) ? (m[r][r]*m[i][j] - m[i][r]*m[r][j]) / m[r-1][r-1] : + (m[r][r]*m[i][j] - m[i][r]*m[r][j])) + >= big_number) { + overflow = true; + return; + } + lp_assert(is_int(m[i][j])); + } + } +} + +// returns the rank of the matrix +template +unsigned to_lower_triangle_non_fractional(M &m, bool & overflow, const mpq& big_number) { + unsigned i = 0; + for (; i < m.row_count(); i++) { + if (!prepare_pivot_for_lower_triangle(m, i)) { + return i; + } + pivot_column_non_fractional(m, i, overflow, big_number); + if (overflow) + return 0; + } + lp_assert(i == m.row_count()); + return i; +} + +// returns gcd of values below diagonal i,i +template +mpq gcd_of_row_starting_from_diagonal(const M& m, unsigned i) { + mpq g = zero_of_type(); + unsigned j = i; + for (; j < m.column_count() && is_zero(g); j++) { + const auto & t = m[i][j]; + if (!is_zero(t)) + g = abs(t); + } + lp_assert(!is_zero(g)); + for (; j < m.column_count(); j++) { + const auto & t = m[i][j]; + if (!is_zero(t)) + g = gcd(g, t); + } + return g; +} + +// It fills "r" - the basic rows of m. +// The plan is to transform m to the lower triangular form by using non-fractional Gaussian Elimination by columns. +// Then the trailing after the diagonal elements of the following elements of the last non-zero row of the matrix, +// namely, m[r-1][r-1], m[r-1][r], ..., m[r-1]m[m.column_count() - 1] give the determinants of all minors of rank r. +// The gcd of these minors is the return value. + +template +mpq determinant_of_rectangular_matrix(const M& m, svector & basis_rows, const mpq& big_number) { + auto m_copy = m; + bool overflow = false; + unsigned rank = to_lower_triangle_non_fractional(m_copy, overflow, big_number); + if (overflow) + return big_number; + if (rank == 0) + return one_of_type(); + + for (unsigned i = 0; i < rank; i++) { + basis_rows.push_back(m_copy.adjust_row(i)); + } + TRACE("hnf_calc", tout << "basis_rows = "; print_vector(basis_rows, tout); m_copy.print(tout, "m_copy = ");); + return gcd_of_row_starting_from_diagonal(m_copy, rank - 1); +} +} // end of namespace hnf_calc + +template // M is the matrix type +class hnf { + // fields + +#ifdef Z3DEBUG + M m_H; + M m_U; + M m_U_reverse; + M m_A_orig; +#endif + M m_W; + vector m_buffer; + unsigned m_m; + unsigned m_n; + mpq m_d; // it is a positive number and a multiple of gcd of r-minors of m_A_orig, where r is the rank of m_A_orig + // we suppose that the rank of m_A is equal to row_count(), and that row_count() <= column_count(), that is m_A has the full rank + unsigned m_i; + unsigned m_j; + mpq m_R; + mpq m_half_R; + mpq mod_R_balanced(const mpq & a) const { + mpq t = a % m_R; + return t > m_half_R? t - m_R : (t < - m_half_R? t + m_R : t); + } + + mpq mod_R(const mpq & a) const { + mpq t = a % m_R; + t = is_neg(t) ? t + m_R : t; + CTRACE("hnf", is_neg(t), tout << "a=" << a << ", m_R= " << m_R << std::endl;); + return t; + + } + +#ifdef Z3DEBUG + void buffer_p_col_i_plus_q_col_j_H(const mpq & p, unsigned i, const mpq & q, unsigned j) { + for (unsigned k = i; k < m_m; k++) { + m_buffer[k] = p * m_H[k][i] + q * m_H[k][j]; + } + } +#endif + bool zeros_in_column_W_above(unsigned i) { + for (unsigned k = 0; k < i; k++) + if (!is_zero(m_W[k][i])) + return false; + return true; + } + + void buffer_p_col_i_plus_q_col_j_W_modulo(const mpq & p, const mpq & q) { + lp_assert(zeros_in_column_W_above(m_i)); + for (unsigned k = m_i; k < m_m; k++) { + m_buffer[k] = mod_R_balanced(mod_R_balanced(p * m_W[k][m_i]) + mod_R_balanced(q * m_W[k][m_j])); + } + } +#ifdef Z3DEBUG + void buffer_p_col_i_plus_q_col_j_U(const mpq & p, unsigned i, const mpq & q, unsigned j) { + for (unsigned k = 0; k < m_n; k++) { + m_buffer[k] = p * m_U[k][i] + q * m_U[k][j]; + } + } + + void pivot_column_i_to_column_j_H(mpq u, unsigned i, mpq v, unsigned j) { + lp_assert(is_zero(u * m_H[i][i] + v * m_H[i][j])); + m_H[i][j] = zero_of_type(); + for (unsigned k = i + 1; k < m_m; k ++) + m_H[k][j] = u * m_H[k][i] + v * m_H[k][j]; + + } +#endif + void pivot_column_i_to_column_j_W_modulo(mpq u, mpq v) { + lp_assert(is_zero((u * m_W[m_i][m_i] + v * m_W[m_i][m_j]) % m_R)); + m_W[m_i][m_j] = zero_of_type(); + for (unsigned k = m_i + 1; k < m_m; k ++) + m_W[k][m_j] = mod_R_balanced(mod_R_balanced(u * m_W[k][m_i]) + mod_R_balanced(v * m_W[k][m_j])); + } + +#ifdef Z3DEBUG + void pivot_column_i_to_column_j_U(mpq u, unsigned i, mpq v, unsigned j) { + for (unsigned k = 0; k < m_n; k ++) + m_U[k][j] = u * m_U[k][i] + v * m_U[k][j]; + + } + + void copy_buffer_to_col_i_H(unsigned i) { + for (unsigned k = i; k < m_m; k++) { + m_H[k][i] = m_buffer[k]; + } + } + void copy_buffer_to_col_i_U(unsigned i) { + for (unsigned k = 0; k < m_n; k++) + m_U[k][i] = m_buffer[k]; + } + + // multiply by (a, b) + // (c, d) + // from the left where i and j are the modified columns + // the [i][i] = a, and [i][j] = b for the matrix we multiply by + + + void multiply_U_reverse_from_left_by(unsigned i, unsigned j, const mpq & a, const mpq & b, const mpq & c, const mpq d) { + // the new i-th row goes to the buffer + for (unsigned k = 0; k < m_n; k++) { + m_buffer[k] = a * m_U_reverse[i][k] + b * m_U_reverse[j][k]; + } + + // calculate the new j-th row in place + for (unsigned k = 0; k < m_n; k++) { + m_U_reverse[j][k] = c * m_U_reverse[i][k] + d * m_U_reverse[j][k]; + } + + // copy the buffer into i-th row + for (unsigned k = 0; k < m_n; k++) { + m_U_reverse[i][k] = m_buffer[k]; + } + } + + void handle_column_ij_in_row_i(unsigned i, unsigned j) { + lp_assert(is_correct_modulo()); + const mpq& aii = m_H[i][i]; + const mpq& aij = m_H[i][j]; + mpq p,q,r; + extended_gcd(aii, aij, r, p, q); + mpq aii_over_r = aii / r; + mpq aij_over_r = aij / r; + + + buffer_p_col_i_plus_q_col_j_H(p, i, q, j); + pivot_column_i_to_column_j_H(- aij_over_r, i, aii_over_r, j); + copy_buffer_to_col_i_H(i); + + + buffer_p_col_i_plus_q_col_j_U(p, i, q, j); + pivot_column_i_to_column_j_U(- aij_over_r, i, aii_over_r, j); + copy_buffer_to_col_i_U(i); + + // U was multiplied from the right by (p, - aij_over_r) + // (q, aii_over_r ) + // We need to multiply U_reverse by (aii_over_r, aij_over_r) + // (-q , p) + // from the left + + multiply_U_reverse_from_left_by(i, j, aii_over_r, aij_over_r, -q, p); + lp_assert(is_correct_modulo()); + } + + + void switch_sign_for_column(unsigned i) { + for (unsigned k = i; k < m_m; k++) + m_H[k][i].neg(); + for (unsigned k = 0; k < m_n; k++) + m_U[k][i].neg(); + + // switch sign for the i-th row in the reverse m_U_reverse + for (unsigned k = 0; k < m_n; k++) + m_U_reverse[i][k].neg(); + + } + + void process_row_column(unsigned i, unsigned j){ + if (is_zero(m_H[i][j])) + return; + handle_column_ij_in_row_i(i, j); + } + + void replace_column_j_by_j_minus_u_col_i_H(unsigned i, unsigned j, const mpq & u) { + lp_assert(j < i); + for (unsigned k = i; k < m_m; k++) { + m_H[k][j] -= u * m_H[k][i]; + } + } + void replace_column_j_by_j_minus_u_col_i_U(unsigned i, unsigned j, const mpq & u) { + + lp_assert(j < i); + for (unsigned k = 0; k < m_n; k++) { + m_U[k][j] -= u * m_U[k][i]; + } + // Here we multiply from m_U from the right by the matrix ( 1, 0) + // ( -u, 1). + // To adjust the reverse we multiply it from the left by (1, 0) + // (u, 1) + + for (unsigned k = 0; k < m_n; k++) { + m_U_reverse[i][k] += u * m_U_reverse[j][k]; + } + + + } + + void work_on_columns_less_than_i_in_the_triangle(unsigned i) { + const mpq & mii = m_H[i][i]; + if (is_zero(mii)) return; + for (unsigned j = 0; j < i; j++) { + const mpq & mij = m_H[i][j]; + if (!is_pos(mij) && - mij < mii) + continue; + mpq u = ceil(mij / mii); + replace_column_j_by_j_minus_u_col_i_H(i, j, u); + replace_column_j_by_j_minus_u_col_i_U(i, j, u); + } + } + + void process_row(unsigned i) { + + lp_assert(is_correct_modulo()); + for (unsigned j = i + 1; j < m_n; j++) { + process_row_column(i, j); + } + if (i >= m_n) { + lp_assert(m_H == m_A_orig * m_U); + return; + } + if (is_neg(m_H[i][i])) + switch_sign_for_column(i); + work_on_columns_less_than_i_in_the_triangle(i); + lp_assert(is_correct_modulo()); + } + + void calculate() { + for (unsigned i = 0; i < m_m; i++) { + process_row(i); + } + } + + void prepare_U_and_U_reverse() { + m_U = M(m_H.column_count()); + for (unsigned i = 0; i < m_U.column_count(); i++) + m_U[i][i] = 1; + + m_U_reverse = m_U; + + lp_assert(m_H == m_A_orig * m_U); + } + + bool row_is_correct_form(unsigned i) const { + if (i >= m_n) + return true; + const mpq& hii = m_H[i][i]; + if (is_neg(hii)) + return false; + for (unsigned j = 0; j < i; j++) { + const mpq & hij = m_H[i][j]; + if (is_pos(hij)) + return false; + if (!is_zero(hii) && - hij >= hii) + return false; + } + + return true; + } + + bool is_correct_form() const { + for (unsigned i = 0; i < m_m; i++) + if (!row_is_correct_form(i)) + return false; + return true; + } + + bool is_correct() const { + return m_H == m_A_orig * m_U && is_unit_matrix(m_U * m_U_reverse); + } + + bool is_correct_modulo() const { + return m_H.equal_modulo(m_A_orig * m_U, m_d) && is_unit_matrix(m_U * m_U_reverse); + } + + bool is_correct_final() const { + if (!is_correct()) { + TRACE("hnf_calc", + tout << "m_H = "; m_H.print(tout, 17); + tout << "\nm_A_orig * m_U = "; (m_A_orig * m_U).print(tout, 17); + tout << "is_correct() does not hold" << std::endl;); + return false; + } + if (!is_correct_form()) { + TRACE("hnf_calc", tout << "is_correct_form() does not hold" << std::endl;); + return false; + } + return true; + } +public: + const M& H() const { return m_H;} + const M& U() const { return m_U;} + const M& U_reverse() const { return m_U_reverse; } +private: +#endif + void copy_buffer_to_col_i_W_modulo() { + for (unsigned k = m_i; k < m_m; k++) { + m_W[k][m_i] = m_buffer[k]; + } + } + + void replace_column_j_by_j_minus_u_col_i_W(unsigned j, const mpq & u) { + lp_assert(j < m_i); + for (unsigned k = m_i; k < m_m; k++) { + m_W[k][j] -= u * m_W[k][m_i]; + // m_W[k][j] = mod_R_balanced(m_W[k][j]); + } + } + + bool is_unit_matrix(const M& u) const { + unsigned m = u.row_count(); + unsigned n = u.column_count(); + if (m != n) return false; + for (unsigned i = 0; i < m; i ++) + for (unsigned j = 0; j < n; j++) { + if (i == j) { + if (one_of_type() != u[i][j]) + return false; + } else { + if (!is_zero(u[i][j])) + return false; + } + } + return true; + } + + + // follows Algorithm 2.4.8 of Henri Cohen's "A course on computational algebraic number theory", + // with some changes related to that we create a low triangle matrix + // with non-positive elements under the diagonal + void process_column_in_row_modulo() { + const mpq& aii = m_W[m_i][m_i]; + const mpq& aij = m_W[m_i][m_j]; + mpq d, p,q; + hnf_calc::extended_gcd_minimal_uv(aii, aij, d, p, q); + if (is_zero(d)) + return; + mpq aii_over_d = mod_R(aii / d); + mpq aij_over_d = mod_R(aij / d); + buffer_p_col_i_plus_q_col_j_W_modulo(p, q); + pivot_column_i_to_column_j_W_modulo(- aij_over_d, aii_over_d); + copy_buffer_to_col_i_W_modulo(); + } + + void fix_row_under_diagonal_W_modulo() { + mpq d, u, v; + if (is_zero(m_W[m_i][m_i])) { + m_W[m_i][m_i] = m_R; + u = one_of_type(); + d = m_R; + } else { + hnf_calc::extended_gcd_minimal_uv(m_W[m_i][m_i], m_R, d, u, v); + } + auto & mii = m_W[m_i][m_i]; + mii *= u; + mii = mod_R(mii); + if (is_zero(mii)) + mii = d; + + lp_assert(is_pos(mii)); + + // adjust column m_i + for (unsigned k = m_i + 1; k < m_m; k++) { + m_W[k][m_i] *= u; + m_W[k][m_i] = mod_R_balanced(m_W[k][m_i]); + } + + lp_assert(is_pos(mii)); + for (unsigned j = 0; j < m_i; j++) { + const mpq & mij = m_W[m_i][j]; + if (!is_pos(mij) && - mij < mii) + continue; + mpq q = ceil(mij / mii); + replace_column_j_by_j_minus_u_col_i_W(j, q); + } + } + + + void process_row_modulo() { + for (m_j = m_i + 1; m_j < m_n; m_j++) { + process_column_in_row_modulo(); + } + fix_row_under_diagonal_W_modulo(); + } + + void calculate_by_modulo() { + for (m_i = 0; m_i < m_m; m_i ++) { + process_row_modulo(); + lp_assert(is_pos(m_W[m_i][m_i])); + m_R /= m_W[m_i][m_i]; + lp_assert(is_int(m_R)); + m_half_R = floor(m_R / 2); + } + } + +public: + hnf(M & A, const mpq & d) : +#ifdef Z3DEBUG + m_H(A), + m_A_orig(A), +#endif + m_W(A), + m_buffer(std::max(A.row_count(), A.column_count())), + m_m(A.row_count()), + m_n(A.column_count()), + m_d(d), + m_R(m_d), + m_half_R(floor(m_R / 2)) + { + if (m_m == 0 || m_n == 0 || is_zero(m_d)) + return; +#ifdef Z3DEBUG + prepare_U_and_U_reverse(); + calculate(); + lp_assert(is_correct_final()); +#endif + calculate_by_modulo(); +#ifdef Z3DEBUG + CTRACE("hnf_calc", m_H != m_W, + tout << "A = "; m_A_orig.print(tout, 4); tout << std::endl; + tout << "H = "; m_H.print(tout, 4); tout << std::endl; + tout << "W = "; m_W.print(tout, 4); tout << std::endl;); + lp_assert (m_H == m_W); +#endif + } + + const M & W() const { return m_W; } + +}; + +} diff --git a/src/util/lp/hnf_cutter.h b/src/util/lp/hnf_cutter.h new file mode 100644 index 000000000..90cdd5a6d --- /dev/null +++ b/src/util/lp/hnf_cutter.h @@ -0,0 +1,232 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/lp/lar_term.h" +#include "util/lp/hnf.h" +#include "util/lp/general_matrix.h" +#include "util/lp/var_register.h" +#include "util/lp/lia_move.h" +#include "util/lp/explanation.h" + +namespace lp { +class hnf_cutter { + var_register m_var_register; + general_matrix m_A; + vector m_terms; + svector m_constraints_for_explanation; + vector m_right_sides; + lp_settings & m_settings; + mpq m_abs_max; + bool m_overflow; +public: + + const mpq & abs_max() const { return m_abs_max; } + + hnf_cutter(lp_settings & settings) : m_settings(settings), + m_abs_max(zero_of_type()) {} + + unsigned terms_count() const { + return m_terms.size(); + } + + const vector& terms() const { return m_terms; } + const svector& constraints_for_explanation() const { + return m_constraints_for_explanation; + } + const vector & right_sides() const { return m_right_sides; } + void clear() { + // m_A will be filled from scratch in init_matrix_A + m_var_register.clear(); + m_terms.clear(); + m_constraints_for_explanation.clear(); + m_right_sides.clear(); + m_abs_max = zero_of_type(); + m_overflow = false; + } + void add_term(const lar_term* t, const mpq &rs, constraint_index ci) { + m_terms.push_back(t); + m_right_sides.push_back(rs); + m_constraints_for_explanation.push_back(ci); + for (const auto &p : *t) { + m_var_register.add_var(p.var()); + mpq t = abs(ceil(p.coeff())); + if (t > m_abs_max) + m_abs_max = t; + } + } + + void print(std::ostream & out) { + out << "terms = " << m_terms.size() << ", var = " << m_var_register.size() << std::endl; + } + + void initialize_row(unsigned i) { + m_A.init_row_from_container(i, * m_terms[i], [this](unsigned j) { return m_var_register.add_var(j);}); + } + + void init_matrix_A() { + m_A = general_matrix(terms_count(), vars().size()); + for (unsigned i = 0; i < terms_count(); i++) + initialize_row(i); + } + + // todo: as we need only one row i with non integral b[i] need to optimize later + void find_h_minus_1_b(const general_matrix& H, vector & b) { + // the solution will be put into b + for (unsigned i = 0; i < H.row_count() ;i++) { + for (unsigned j = 0; j < i; j++) { + b[i] -= H[i][j]*b[j]; + } + b[i] /= H[i][i]; + // consider return from here if b[i] is not an integer and return i + } + } + + vector create_b(const svector & basis_rows) { + if (basis_rows.size() == m_right_sides.size()) + return m_right_sides; + vector b; + for (unsigned i : basis_rows) { + b.push_back(m_right_sides[i]); + } + return b; + } + + int find_cut_row_index(const vector & b) { + int ret = -1; + int n = 0; + for (int i = 0; i < static_cast(b.size()); i++) { + if (is_int(b[i])) continue; + if (n == 0 ) { + lp_assert(ret == -1); + n = 1; + ret = i; + } else { + if (m_settings.random_next() % (++n) == 0) { + ret = i; + } + } + } + return ret; + } + + // fills e_i*H_minus_1 + void get_ei_H_minus_1(unsigned i, const general_matrix& H, vector & row) { + // we solve x = ei * H_min_1 + // or x * H = ei + unsigned m = H.row_count(); + for (unsigned k = i + 1; k < m; k++) { + row[k] = zero_of_type(); + } + row[i] = one_of_type() / H[i][i]; + for(int k = i - 1; k >= 0; k--) { + mpq t = zero_of_type(); + for (unsigned l = k + 1; l <= i; l++) { + t += H[l][k]*row[l]; + } + row[k] = -t / H[k][k]; + } + + // // test region + // vector ei(H.row_count(), zero_of_type()); + // ei[i] = one_of_type(); + // vector pr = row * H; + // pr.shrink(ei.size()); + // lp_assert(ei == pr); + // // end test region + + } + + void fill_term(const vector & row, lar_term& t) { + for (unsigned j = 0; j < row.size(); j++) { + if (!is_zero(row[j])) + t.add_monomial(row[j], m_var_register.local_to_external(j)); + } + } +#ifdef Z3DEBUG + vector transform_to_local_columns(const vector & x) const { + vector ret; + for (unsigned j = 0; j < vars().size(); j++) { + lp_assert(is_zero(x[m_var_register.local_to_external(j)].y)); + ret.push_back(x[m_var_register.local_to_external(j)].x); + } + return ret; + } +#endif + void shrink_explanation(const svector& basis_rows) { + svector new_expl; + for (unsigned i : basis_rows) { + new_expl.push_back(m_constraints_for_explanation[i]); + } + m_constraints_for_explanation = new_expl; + } + + bool overflow() const { return m_overflow; } + + lia_move create_cut(lar_term& t, mpq& k, explanation& ex, bool & upper + #ifdef Z3DEBUG + , + const vector & x0 + #endif + ) { + // we suppose that x0 has at least one non integer element + init_matrix_A(); + svector basis_rows; + mpq big_number = m_abs_max.expt(3); + mpq d = hnf_calc::determinant_of_rectangular_matrix(m_A, basis_rows, big_number); + + // std::cout << "max = " << m_abs_max << ", d = " << d << ", d/max = " << ceil (d /m_abs_max) << std::endl; + //std::cout << "max cube " << m_abs_max * m_abs_max * m_abs_max << std::endl; + + if (d >= big_number) { + return lia_move::undef; + } + + if (m_settings.get_cancel_flag()) + return lia_move::undef; + if (basis_rows.size() < m_A.row_count()) { + m_A.shrink_to_rank(basis_rows); + shrink_explanation(basis_rows); + } + + hnf h(m_A, d); + // general_matrix A_orig = m_A; + + vector b = create_b(basis_rows); + lp_assert(m_A * x0 == b); + // vector bcopy = b; + find_h_minus_1_b(h.W(), b); + // lp_assert(bcopy == h.W().take_first_n_columns(b.size()) * b); + int cut_row = find_cut_row_index(b); + if (cut_row == -1) + return lia_move::undef; + // the matrix is not square - we can get + // all integers in b's projection + + vector row(m_A.column_count()); + get_ei_H_minus_1(cut_row, h.W(), row); + vector f = row * m_A; + fill_term(f, t); + k = floor(b[cut_row]); + upper = true; + return lia_move::cut; + } + + svector vars() const { return m_var_register.vars(); } +}; +} diff --git a/src/util/lp/implied_bound.h b/src/util/lp/implied_bound.h index f1c711ffa..6c5f26cf2 100644 --- a/src/util/lp/implied_bound.h +++ b/src/util/lp/implied_bound.h @@ -24,34 +24,34 @@ 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) {} + m_strict(strict) { + } }; } diff --git a/src/util/lp/indexed_vector_instances.cpp b/src/util/lp/indexed_vector.cpp similarity index 70% rename from src/util/lp/indexed_vector_instances.cpp rename to src/util/lp/indexed_vector.cpp index 79c3ee1a1..180291705 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(); @@ -42,11 +42,12 @@ template void lp::indexed_vector::print(std::basic_ostream >::print(std::ostream&); #endif } -template void lp::print_vector(vector const&, std::ostream&); -template void lp::print_vector(vector const&, std::ostream&); -template void lp::print_vector(vector const&, std::ostream&); -template void lp::print_vector >(vector> const&, std::ostream&); +// template void lp::print_vector(vector const&, std::ostream&); +// template void lp::print_vector(vector const&, std::ostream&); +// template void lp::print_vector(vector const&, std::ostream&); +// template void lp::print_vector >(vector> const&, std::ostream&); template void lp::indexed_vector::resize(unsigned int); -template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream > &); -template void lp::print_vector >(vector> const&, std::ostream&); +// template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream > &); +// template void lp::print_vector >(vector> const&, std::ostream&); template void lp::indexed_vector >::erase_from_index(unsigned int); + diff --git a/src/util/lp/indexed_vector.h b/src/util/lp/indexed_vector.h index 3b1258ed7..d6ff4e76a 100644 --- a/src/util/lp/indexed_vector.h +++ b/src/util/lp/indexed_vector.h @@ -28,11 +28,9 @@ Revision History: #include namespace lp { -template void print_vector(const vector & t, std::ostream & out); -template void print_vector(const buffer & t, std::ostream & out); template void print_sparse_vector(const vector & t, std::ostream & out); -void print_vector(const vector & t, std::ostream & out); +void print_vector_as_doubles(const vector & t, std::ostream & out); template class indexed_vector { public: @@ -90,16 +88,7 @@ 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(); const T& operator[] (unsigned i) const { @@ -175,6 +164,55 @@ public: } } } + + struct ival { + unsigned m_var; + const T & m_coeff; + ival(unsigned var, const T & val) : m_var(var), m_coeff(val) { + } + unsigned var() const { return m_var;} + const T & coeff() const { return m_coeff; } + }; + + struct const_iterator { + // fields + const unsigned *m_i; + const indexed_vector& m_v; + + //typedefs + + + typedef const_iterator self_type; + typedef ival value_type; + typedef const ival reference; + // typedef const column_cell* pointer; + typedef int difference_type; + typedef std::forward_iterator_tag iterator_category; + + reference operator*() const { + return ival(*m_i, m_v[*m_i]); + } + self_type operator++() { self_type i = *this; m_i++; return i; } + self_type operator++(int) { m_i++; return *this; } + + const_iterator(const unsigned* it, const indexed_vector& v) : + m_i(it), + m_v(v) + {} + bool operator==(const self_type &other) const { + return m_i == other.m_i; + } + bool operator!=(const self_type &other) const { return !(*this == other); } + }; + + const_iterator begin() const { + return const_iterator(m_index.begin(), *this); + } + + const_iterator end() const { + return const_iterator(m_index.end(), *this); + } + #ifdef Z3DEBUG bool is_OK() const; diff --git a/src/util/lp/indexed_vector.hpp b/src/util/lp/indexed_vector_def.h similarity index 82% rename from src/util/lp/indexed_vector.hpp rename to src/util/lp/indexed_vector_def.h index 73055d6da..2f7706089 100644 --- a/src/util/lp/indexed_vector.hpp +++ b/src/util/lp/indexed_vector_def.h @@ -22,21 +22,6 @@ Revision History: #include "util/lp/lp_settings.h" namespace lp { -template -void print_vector(const vector & t, std::ostream & out) { - for (unsigned i = 0; i < t.size(); i++) - out << t[i] << " "; - out << std::endl; -} - - -template -void print_vector(const buffer & t, std::ostream & out) { - for (unsigned i = 0; i < t.size(); i++) - out << t[i] << " "; - out << std::endl; -} - template void print_sparse_vector(const vector & t, std::ostream & out) { for (unsigned i = 0; i < t.size(); i++) { @@ -46,7 +31,7 @@ void print_sparse_vector(const vector & t, std::ostream & out) { out << std::endl; } -void print_vector(const vector & t, std::ostream & out) { +void print_vector_as_doubles(const vector & t, std::ostream & out) { for (unsigned i = 0; i < t.size(); i++) out << t[i].get_double() << std::setprecision(3) << " "; out << std::endl; @@ -56,13 +41,13 @@ template void indexed_vector::resize(unsigned data_size) { clear(); m_data.resize(data_size, numeric_traits::zero()); - SASSERT(is_OK()); + lp_assert(is_OK()); } template void indexed_vector::set_value(const T& value, unsigned index) { m_data[index] = value; - SASSERT(std::find(m_index.begin(), m_index.end(), index) == m_index.end()); + lp_assert(std::find(m_index.begin(), m_index.end(), index) == m_index.end()); m_index.push_back(index); } diff --git a/src/util/lp/indexer_of_constraints.h b/src/util/lp/indexer_of_constraints.h new file mode 100644 index 000000000..30976d496 --- /dev/null +++ b/src/util/lp/indexer_of_constraints.h @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ + +#pragma once +#include "util/lp/binary_heap_priority_queue.h" +namespace lp { + +class indexer_of_constraints { + binary_heap_priority_queue m_queue_of_released_indices; + unsigned m_max; +public: + indexer_of_constraints() :m_max(0) {} + unsigned get_new_index() { + unsigned ret; + if (m_queue_of_released_indices.is_empty()) { + ret = m_max++; + } + else { + ret = m_queue_of_released_indices.dequeue(); + } + return ret; + }; + void release_index(unsigned i) { + m_queue_of_released_indices.enqueue(i, i); + }; + unsigned max() const { return m_max; } +}; +} diff --git a/src/util/lp/init_lar_solver.h b/src/util/lp/init_lar_solver.h deleted file mode 100644 index 5d78c3ba7..000000000 --- a/src/util/lp/init_lar_solver.h +++ /dev/null @@ -1,591 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -// here we are inside lp::lar_solver class - -bool strategy_is_undecided() const { - return m_settings.simplex_strategy() == simplex_strategy_enum::undecided; -} - -var_index add_var(unsigned ext_j) { - var_index i; - SASSERT (ext_j < m_terms_start_index); - - if (ext_j >= m_terms_start_index) - throw 0; // todo : what is the right way to exit? - - if (try_get_val(m_ext_vars_to_columns, ext_j, i)) { - return i; - } - SASSERT(m_vars_to_ul_pairs.size() == A_r().column_count()); - i = A_r().column_count(); - m_vars_to_ul_pairs.push_back (ul_pair(static_cast(-1))); - add_non_basic_var_to_core_fields(ext_j); - SASSERT(sizes_are_correct()); - return i; -} - -void register_new_ext_var_index(unsigned ext_v) { - SASSERT(!contains(m_ext_vars_to_columns, ext_v)); - unsigned j = static_cast(m_ext_vars_to_columns.size()); - m_ext_vars_to_columns[ext_v] = j; - SASSERT(m_columns_to_ext_vars_or_term_indices.size() == j); - m_columns_to_ext_vars_or_term_indices.push_back(ext_v); -} - -void add_non_basic_var_to_core_fields(unsigned ext_j) { - register_new_ext_var_index(ext_j); - m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); - m_columns_with_changed_bound.increase_size_by_one(); - add_new_var_to_core_fields_for_mpq(false); - if (use_lu()) - add_new_var_to_core_fields_for_doubles(false); -} - -void add_new_var_to_core_fields_for_doubles(bool register_in_basis) { - unsigned j = A_d().column_count(); - A_d().add_column(); - SASSERT(m_mpq_lar_core_solver.m_d_x.size() == j); - // SASSERT(m_mpq_lar_core_solver.m_d_low_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_upper_bounds.resize(j + 1); - SASSERT(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method - if (register_in_basis) { - A_d().add_row(); - m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); - m_mpq_lar_core_solver.m_d_basis.push_back(j); - }else { - m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); - m_mpq_lar_core_solver.m_d_nbasis.push_back(j); - } -} - -void add_new_var_to_core_fields_for_mpq(bool register_in_basis) { - unsigned j = A_r().column_count(); - A_r().add_column(); - SASSERT(m_mpq_lar_core_solver.m_r_x.size() == j); - // SASSERT(m_mpq_lar_core_solver.m_r_low_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_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); - m_mpq_lar_core_solver.m_r_solver.m_d.resize(j + 1); - SASSERT(m_mpq_lar_core_solver.m_r_heading.size() == j); // as A().column_count() on the entry to the method - if (register_in_basis) { - A_r().add_row(); - m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size()); - m_mpq_lar_core_solver.m_r_basis.push_back(j); - if (m_settings.bound_propagation()) - m_rows_with_changed_bounds.insert(A_r().row_count() - 1); - } else { - m_mpq_lar_core_solver.m_r_heading.push_back(- static_cast(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1); - m_mpq_lar_core_solver.m_r_nbasis.push_back(j); - } -} - - -var_index add_term_undecided(const vector> & coeffs, - const mpq &m_v) { - m_terms.push_back(new lar_term(coeffs, m_v)); - m_orig_terms.push_back(new lar_term(coeffs, m_v)); - return m_terms_start_index + m_terms.size() - 1; -} - -// terms -var_index add_term(const vector> & coeffs, - const mpq &m_v) { - if (strategy_is_undecided()) - return add_term_undecided(coeffs, m_v); - - m_terms.push_back(new lar_term(coeffs, m_v)); - m_orig_terms.push_back(new lar_term(coeffs, m_v)); - unsigned adjusted_term_index = m_terms.size() - 1; - var_index ret = m_terms_start_index + adjusted_term_index; - if (use_tableau() && !coeffs.empty()) { - add_row_for_term(m_orig_terms.back(), ret); - if (m_settings.bound_propagation()) - m_rows_with_changed_bounds.insert(A_r().row_count() - 1); - } - SASSERT(m_ext_vars_to_columns.size() == A_r().column_count()); - return ret; -} - -void add_row_for_term(const lar_term * term, unsigned term_ext_index) { - SASSERT(sizes_are_correct()); - add_row_from_term_no_constraint(term, term_ext_index); - SASSERT(sizes_are_correct()); -} - -void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) { - register_new_ext_var_index(term_ext_index); - // j will be a new variable - unsigned j = A_r().column_count(); - ul_pair ul(j); - m_vars_to_ul_pairs.push_back(ul); - add_basic_var_to_core_fields(); - if (use_tableau()) { - auto it = iterator_on_term_with_basis_var(*term, j); - A_r().fill_last_row_with_pivoting(it, - m_mpq_lar_core_solver.m_r_solver.m_basis_heading); - m_mpq_lar_core_solver.m_r_solver.m_b.resize(A_r().column_count(), zero_of_type()); - } else { - fill_last_row_of_A_r(A_r(), term); - } - m_mpq_lar_core_solver.m_r_x[j] = get_basic_var_value_from_row_directly(A_r().row_count() - 1); - if (use_lu()) - fill_last_row_of_A_d(A_d(), term); -} - -void add_basic_var_to_core_fields() { - bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver(); - SASSERT(!use_lu || A_r().column_count() == A_d().column_count()); - m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); - m_columns_with_changed_bound.increase_size_by_one(); - m_rows_with_changed_bounds.increase_size_by_one(); - add_new_var_to_core_fields_for_mpq(true); - if (use_lu) - add_new_var_to_core_fields_for_doubles(true); -} - -constraint_index add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) { - constraint_index ci = m_constraints.size(); - if (!is_term(j)) { // j is a var - auto vc = new lar_var_constraint(j, kind, right_side); - m_constraints.push_back(vc); - update_column_type_and_bound(j, kind, right_side, ci); - } else { - add_var_bound_on_constraint_for_term(j, kind, right_side, ci); - } - SASSERT(sizes_are_correct()); - return ci; -} - -void update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index) { - switch(m_mpq_lar_core_solver.m_column_types[j]) { - case column_type::free_column: - update_free_column_type_and_bound(j, kind, right_side, constr_index); - break; - 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); - break; - case column_type::upper_bound: - update_upper_bound_column_type_and_bound(j, kind, right_side, constr_index); - break; - case column_type::fixed: - update_fixed_column_type_and_bound(j, kind, right_side, constr_index); - break; - default: - SASSERT(false); // cannot be here - } -} - -void add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - SASSERT(is_term(j)); - unsigned adjusted_term_index = adjust_term_index(j); - unsigned term_j; - if (try_get_val(m_ext_vars_to_columns, j, term_j)) { - mpq rs = right_side - m_orig_terms[adjusted_term_index]->m_v; - m_constraints.push_back(new lar_term_constraint(m_orig_terms[adjusted_term_index], kind, right_side)); - update_column_type_and_bound(term_j, kind, rs, ci); - } - else { - add_constraint_from_term_and_create_new_column_row(j, m_orig_terms[adjusted_term_index], kind, right_side); - } -} - - -void add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, - lconstraint_kind kind, const mpq & right_side) { - - add_row_from_term_no_constraint(term, term_j); - unsigned j = A_r().column_count() - 1; - update_column_type_and_bound(j, kind, right_side - term->m_v, m_constraints.size()); - m_constraints.push_back(new lar_term_constraint(term, kind, right_side)); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); -} - -void decide_on_strategy_and_adjust_initial_state() { - SASSERT(strategy_is_undecided()); - if (m_vars_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) { - m_settings.simplex_strategy() = simplex_strategy_enum::lu; - } else { - m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows; // todo: when to switch to tableau_costs? - } - adjust_initial_state(); -} - -void adjust_initial_state() { - switch (m_settings.simplex_strategy()) { - case simplex_strategy_enum::lu: - adjust_initial_state_for_lu(); - break; - case simplex_strategy_enum::tableau_rows: - adjust_initial_state_for_tableau_rows(); - break; - case simplex_strategy_enum::tableau_costs: - SASSERT(false); // not implemented - case simplex_strategy_enum::undecided: - adjust_initial_state_for_tableau_rows(); - break; - } -} - -void 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_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; - - /* - unsigned j = A_d().column_count(); - A_d().add_column(); - SASSERT(m_mpq_lar_core_solver.m_d_x.size() == j); - // SASSERT(m_mpq_lar_core_solver.m_d_low_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_upper_bounds.resize(j + 1); - SASSERT(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method - if (register_in_basis) { - A_d().add_row(); - m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); - m_mpq_lar_core_solver.m_d_basis.push_back(j); - }else { - m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); - m_mpq_lar_core_solver.m_d_nbasis.push_back(j); - }*/ -} - -void adjust_initial_state_for_tableau_rows() { - for (unsigned j = 0; j < m_terms.size(); j++) { - if (contains(m_ext_vars_to_columns, j + m_terms_start_index)) - continue; - add_row_from_term_no_constraint(m_terms[j], j + m_terms_start_index); - } -} - -// 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) { - SASSERT(A.row_count() > 0); - SASSERT(A.column_count() > 0); - unsigned last_row = A.row_count() - 1; - SASSERT(A.m_rows[last_row].empty()); - - for (auto & t : ls->m_coeffs) { - SASSERT(!is_zero(t.second)); - var_index j = t.first; - A.set(last_row, j, - t.second.get_double()); - } - - unsigned basis_j = A.column_count() - 1; - A.set(last_row, basis_j, - 1 ); -} - -void update_free_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_ind) { - mpq y_of_bound(0); - switch (kind) { - case LT: - y_of_bound = -1; - case LE: - m_mpq_lar_core_solver.m_column_types[j] = column_type::upper_bound; - SASSERT(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound); - SASSERT(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j); - { - auto up = numeric_pair(right_side, y_of_bound); - m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; - } - set_upper_bound_witness(j, constr_ind); - break; - case GT: - y_of_bound = 1; - case GE: - m_mpq_lar_core_solver.m_column_types[j] = column_type::low_bound; - SASSERT(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; - } - set_low_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()); - set_upper_bound_witness(j, constr_ind); - set_low_bound_witness(j, constr_ind); - break; - - default: - SASSERT(false); - - } - m_columns_with_changed_bound.insert(j); -} - -void update_upper_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - SASSERT(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound); - mpq y_of_bound(0); - switch (kind) { - case LT: - y_of_bound = -1; - case LE: - { - auto up = numeric_pair(right_side, y_of_bound); - if (up < m_mpq_lar_core_solver.m_r_upper_bounds()[j]) { - m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; - set_upper_bound_witness(j, ci); - m_columns_with_changed_bound.insert(j); - } - } - break; - case GT: - y_of_bound = 1; - case GE: - 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_columns_with_changed_bound.insert(j); - if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - m_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; - } - } - break; - case EQ: - { - auto v = numeric_pair(right_side, zero_of_type()); - if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - m_status = INFEASIBLE; - set_low_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_columns_with_changed_bound.insert(j); - set_low_bound_witness(j, ci); - set_upper_bound_witness(j, ci); - m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; - } - break; - } - break; - - default: - SASSERT(false); - - } -} - -void update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - SASSERT(m_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])); - mpq y_of_bound(0); - switch (kind) { - case LT: - y_of_bound = -1; - case LE: - { - auto up = numeric_pair(right_side, y_of_bound); - if (up < m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; - 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]) { - m_status = INFEASIBLE; - SASSERT(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]) - m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; - } - } - break; - case GT: - y_of_bound = 1; - 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; - m_columns_with_changed_bound.insert(j); - set_low_bound_witness(j, ci); - } - if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - m_status = INFEASIBLE; - m_infeasible_column_index = j; - } else if ( low == m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; - } - } - break; - case EQ: - { - auto v = numeric_pair(right_side, zero_of_type()); - if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { - m_status = INFEASIBLE; - m_infeasible_column_index = j; - set_upper_bound_witness(j, ci); - } else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - m_status = INFEASIBLE; - m_infeasible_column_index = j; - set_low_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); - 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); - } - - break; - } - - default: - SASSERT(false); - - } -} -void update_low_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - SASSERT(m_mpq_lar_core_solver.m_column_types()[j] == column_type::low_bound); - mpq y_of_bound(0); - switch (kind) { - case LT: - y_of_bound = -1; - case LE: - { - auto up = numeric_pair(right_side, y_of_bound); - m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; - 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]) { - m_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; - } - } - break; - case GT: - y_of_bound = 1; - 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; - m_columns_with_changed_bound.insert(j); - set_low_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]) { - m_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); - 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); - break; - } - - default: - SASSERT(false); - - } -} - -void update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - SASSERT(m_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])); - SASSERT(m_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())); - 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]) { - m_status = INFEASIBLE; - m_infeasible_column_index = j; - set_upper_bound_witness(j, ci); - } - break; - case LE: - { - if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { - m_status = INFEASIBLE; - m_infeasible_column_index = j; - set_upper_bound_witness(j, ci); - } - } - break; - case GT: - { - if (v >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - m_status = INFEASIBLE; - m_infeasible_column_index =j; - set_low_bound_witness(j, ci); - } - } - break; - case GE: - { - if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - m_status = INFEASIBLE; - m_infeasible_column_index = j; - set_low_bound_witness(j, ci); - } - } - break; - case EQ: - { - if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { - m_status = INFEASIBLE; - m_infeasible_column_index = j; - set_upper_bound_witness(j, ci); - } else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { - m_status = INFEASIBLE; - m_infeasible_column_index = j; - set_low_bound_witness(j, ci); - } - break; - } - - default: - SASSERT(false); - - } -} - diff --git a/src/util/lp/int_set.h b/src/util/lp/int_set.h index 698b8bc49..058cf4114 100644 --- a/src/util/lp/int_set.h +++ b/src/util/lp/int_set.h @@ -35,7 +35,7 @@ public: return m_data[j] >= 0; } void insert(unsigned j) { - SASSERT(j < m_data.size()); + lp_assert(j < m_data.size()); if (contains(j)) return; m_data[j] = m_index.size(); m_index.push_back(j); diff --git a/src/util/lp/int_solver.cpp b/src/util/lp/int_solver.cpp new file mode 100644 index 000000000..0691b5887 --- /dev/null +++ b/src/util/lp/int_solver.cpp @@ -0,0 +1,1294 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Lev Nachmanson +*/ + +#include "util/lp/int_solver.h" +#include "util/lp/lar_solver.h" +#include "util/lp/lp_utils.h" +#include +#include "util/lp/monomial.h" +namespace lp { + + +void int_solver::trace_inf_rows() const { + TRACE("arith_int_rows", + unsigned num = m_lar_solver->A_r().column_count(); + for (unsigned v = 0; v < num; v++) { + if (is_int(v) && !get_value(v).is_int()) { + display_column(tout, v); + } + } + + num = 0; + for (unsigned i = 0; i < m_lar_solver->A_r().row_count(); i++) { + unsigned j = m_lar_solver->m_mpq_lar_core_solver.m_r_basis[i]; + if (column_is_int_inf(j)) { + num++; + m_lar_solver->print_row(m_lar_solver->A_r().m_rows[i], tout); + tout << "\n"; + } + } + tout << "num of int infeasible: " << num << "\n"; + ); +} + +bool int_solver::has_inf_int() const { + return m_lar_solver->has_inf_int(); +} + +int int_solver::find_inf_int_base_column() { + unsigned inf_int_count = 0; + int j = find_inf_int_boxed_base_column_with_smallest_range(inf_int_count); + if (j != -1) + return j; + if (inf_int_count == 0) + return -1; + unsigned k = random() % inf_int_count; + return get_kth_inf_int(k); +} + +int int_solver::get_kth_inf_int(unsigned k) const { + for (unsigned j : m_lar_solver->r_basis()) + if (column_is_int_inf(j) && k-- == 0) + return j; + lp_assert(false); + return -1; +} + +int int_solver::find_inf_int_nbasis_column() const { + for (unsigned j : m_lar_solver->r_nbasis()) + if (!column_is_int_inf(j)) + return j; + return -1; +} + +int int_solver::find_inf_int_boxed_base_column_with_smallest_range(unsigned & inf_int_count) { + inf_int_count = 0; + int result = -1; + mpq range; + mpq new_range; + mpq small_range_thresold(1024); + unsigned n = 0; + lar_core_solver & lcs = m_lar_solver->m_mpq_lar_core_solver; + + for (unsigned j : m_lar_solver->r_basis()) { + if (!column_is_int_inf(j)) + continue; + inf_int_count++; + if (!is_boxed(j)) + continue; + lp_assert(!is_fixed(j)); + 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 || new_range < range) { + result = j; + range = new_range; + n = 1; + } + else if (new_range == range && settings().random_next() % (++n) == 0) { + lp_assert(n > 1); + result = j; + } + } + return result; + +} + +bool int_solver::is_gomory_cut_target(const row_strip& row) { + // All non base variables must be at their bounds and assigned to rationals (that is, infinitesimals are not allowed). + unsigned j; + for (const auto & p : row) { + j = p.var(); + if (is_base(j)) continue; + if (!at_bound(j)) + return false; + if (!is_zero(get_value(j).y)) { + TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; + display_column(tout, j); + tout << "infinitesimal: " << !is_zero(get_value(j).y) << "\n";); + return false; + } + } + return true; +} + + +void int_solver::real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0) { + TRACE("gomory_cut_detail_real", tout << "real\n";); + mpq new_a; + if (at_low(x_j)) { + if (a.is_pos()) { + new_a = a / one_minus_f_0; + } + else { + new_a = a / f_0; + new_a.neg(); + } + m_k->addmul(new_a, lower_bound(x_j).x); // is it a faster operation than + // k += lower_bound(x_j).x * new_a; + m_ex->push_justification(column_lower_bound_constraint(x_j), new_a); + } + else { + lp_assert(at_upper(x_j)); + if (a.is_pos()) { + new_a = a / f_0; + new_a.neg(); // the upper terms are inverted. + } + else { + new_a = a / one_minus_f_0; + } + m_k->addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a; + m_ex->push_justification(column_upper_bound_constraint(x_j), new_a); + } + TRACE("gomory_cut_detail_real", tout << a << "*v" << x_j << " k: " << *m_k << "\n";); + m_t->add_monomial(new_a, x_j); +} + +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_lower_bound_constraint(unsigned j) const { + return m_lar_solver->get_column_lower_bound_witness(j); +} + + +void int_solver::int_case_in_gomory_cut(const mpq & a, unsigned x_j, + mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0) { + lp_assert(is_int(x_j)); + lp_assert(!a.is_int()); + mpq f_j = fractional_part(a); + TRACE("gomory_cut_detail", + tout << a << " x_j" << x_j << " k = " << *m_k << "\n"; + tout << "f_j: " << f_j << "\n"; + tout << "f_0: " << f_0 << "\n"; + tout << "1 - f_0: " << 1 - f_0 << "\n"; + tout << "at_low(" << x_j << ") = " << at_low(x_j) << std::endl; + ); + lp_assert (!f_j.is_zero()); + mpq new_a; + if (at_low(x_j)) { + if (f_j <= one_minus_f_0) { + new_a = f_j / one_minus_f_0; + } + else { + new_a = (1 - f_j) / f_0; + } + m_k->addmul(new_a, lower_bound(x_j).x); + m_ex->push_justification(column_lower_bound_constraint(x_j), new_a); + } + else { + lp_assert(at_upper(x_j)); + if (f_j <= f_0) { + new_a = f_j / f_0; + } + else { + new_a = (mpq(1) - f_j) / one_minus_f_0; + } + new_a.neg(); // the upper terms are inverted + m_k->addmul(new_a, upper_bound(x_j).x); + m_ex->push_justification(column_upper_bound_constraint(x_j), new_a); + } + TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << *m_k << "\n";); + m_t->add_monomial(new_a, x_j); + lcm_den = lcm(lcm_den, denominator(new_a)); +} + +lia_move int_solver::report_conflict_from_gomory_cut() { + TRACE("empty_pol",); + lp_assert(m_k->is_pos()); + // conflict 0 >= k where k is positive + m_k->neg(); // returning 0 <= -k + return lia_move::conflict; +} + +void int_solver::gomory_cut_adjust_t_and_k(vector> & pol, + lar_term & t, + mpq &k, + bool some_ints, + mpq & lcm_den) { + if (!some_ints) + return; + + t.clear(); + if (pol.size() == 1) { + unsigned v = pol[0].second; + lp_assert(is_int(v)); + bool k_is_int = k.is_int(); + const mpq& a = pol[0].first; + k /= a; + if (a.is_pos()) { // we have av >= k + if (!k_is_int) + k = ceil(k); + // switch size + t.add_monomial(- mpq(1), v); + k.neg(); + } else { + if (!k_is_int) + k = floor(k); + t.add_monomial(mpq(1), v); + } + } else if (some_ints) { + lcm_den = lcm(lcm_den, denominator(k)); + lp_assert(lcm_den.is_pos()); + if (!lcm_den.is_one()) { + // normalize coefficients of integer parameters to be integers. + for (auto & pi: pol) { + pi.first *= lcm_den; + SASSERT(!is_int(pi.second) || pi.first.is_int()); + } + k *= lcm_den; + } + // negate everything to return -pol <= -k + for (const auto & pi: pol) + t.add_monomial(-pi.first, pi.second); + k.neg(); + } +} + +bool int_solver::current_solution_is_inf_on_cut() const { + const auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x; + impq v = m_t->apply(x); + mpq sign = *m_upper ? one_of_type() : -one_of_type(); + CTRACE("current_solution_is_inf_on_cut", v * sign <= (*m_k) * sign, + tout << "m_upper = " << *m_upper << std::endl; + tout << "v = " << v << ", k = " << (*m_k) << std::endl; + ); + return v * sign > (*m_k) * sign; +} + +void int_solver::adjust_term_and_k_for_some_ints_case_gomory(mpq &lcm_den) { + lp_assert(!m_t->is_empty()); + auto pol = m_t->coeffs_as_vector(); + m_t->clear(); + if (pol.size() == 1) { + TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); + unsigned v = pol[0].second; + lp_assert(is_int(v)); + const mpq& a = pol[0].first; + (*m_k) /= a; + if (a.is_pos()) { // we have av >= k + if (!(*m_k).is_int()) + (*m_k) = ceil((*m_k)); + // switch size + m_t->add_monomial(- mpq(1), v); + (*m_k).neg(); + } else { + if (!(*m_k).is_int()) + (*m_k) = floor((*m_k)); + m_t->add_monomial(mpq(1), v); + } + } else { + TRACE("gomory_cut_detail", tout << "pol.size() > 1" << std::endl;); + lcm_den = lcm(lcm_den, denominator((*m_k))); + lp_assert(lcm_den.is_pos()); + if (!lcm_den.is_one()) { + // normalize coefficients of integer parameters to be integers. + for (auto & pi: pol) { + pi.first *= lcm_den; + SASSERT(!is_int(pi.second) || pi.first.is_int()); + } + (*m_k) *= lcm_den; + } + // negate everything to return -pol <= -(*m_k) + for (const auto & pi: pol) + m_t->add_monomial(-pi.first, pi.second); + (*m_k).neg(); + } + TRACE("gomory_cut_detail", tout << "k = " << (*m_k) << std::endl;); + lp_assert((*m_k).is_int()); +} + + + + +lia_move int_solver::mk_gomory_cut( unsigned inf_col, const row_strip & row) { + + lp_assert(column_is_int_inf(inf_col)); + + TRACE("gomory_cut", + tout << "applying cut at:\n"; m_lar_solver->print_row(row, tout); tout << std::endl; + for (auto & p : row) { + m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(p.var(), tout); + } + tout << "inf_col = " << inf_col << std::endl; + ); + + // gomory will be t <= k and the current solution has a property t > k + *m_k = 1; + mpq lcm_den(1); + unsigned x_j; + mpq a; + bool some_int_columns = false; + mpq f_0 = int_solver::fractional_part(get_value(inf_col)); + mpq one_min_f_0 = 1 - f_0; + for (auto & p : row) { + x_j = p.var(); + if (x_j == inf_col) + continue; + // make the format compatible with the format used in: Integrating Simplex with DPLL(T) + a = p.coeff(); + a.neg(); + if (is_real(x_j)) + real_case_in_gomory_cut(a, x_j, f_0, one_min_f_0); + else if (!a.is_int()) { // f_j will be zero and no monomial will be added + some_int_columns = true; + int_case_in_gomory_cut(a, x_j, lcm_den, f_0, one_min_f_0); + } + } + + if (m_t->is_empty()) + return report_conflict_from_gomory_cut(); + if (some_int_columns) + adjust_term_and_k_for_some_ints_case_gomory(lcm_den); + + lp_assert(current_solution_is_inf_on_cut()); + m_lar_solver->subs_term_columns(*m_t); + TRACE("gomory_cut", tout<<"precut:"; m_lar_solver->print_term(*m_t, tout); tout << " <= " << *m_k << std::endl;); + return lia_move::cut; +} + +int int_solver::find_free_var_in_gomory_row(const row_strip& row) { + unsigned j; + for (const auto & p : row) { + j = p.var(); + if (!is_base(j) && is_free(j)) + return static_cast(j); + } + return -1; +} + +lia_move int_solver::proceed_with_gomory_cut(unsigned j) { + const row_strip& row = m_lar_solver->get_row(row_of_basic_column(j)); + + if (-1 != find_free_var_in_gomory_row(row)) + return lia_move::undef; + + if (!is_gomory_cut_target(row)) + return lia_move::undef; + + *m_upper = true; + return mk_gomory_cut(j, row); +} + + +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_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); +// } + + +typedef monomial mono; + + +// this will allow to enable and disable tracking of the pivot rows +struct pivoted_rows_tracking_control { + lar_solver * m_lar_solver; + bool m_track_pivoted_rows; + pivoted_rows_tracking_control(lar_solver* ls) : + m_lar_solver(ls), + m_track_pivoted_rows(ls->get_track_pivoted_rows()) + { + TRACE("pivoted_rows", tout << "pivoted rows = " << ls->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;); + m_lar_solver->set_track_pivoted_rows(false); + } + ~pivoted_rows_tracking_control() { + TRACE("pivoted_rows", tout << "pivoted rows = " << m_lar_solver->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;); + m_lar_solver->set_track_pivoted_rows(m_track_pivoted_rows); + } +}; + + + +impq int_solver::get_cube_delta_for_term(const lar_term& t) const { + if (t.size() == 2) { + bool seen_minus = false; + bool seen_plus = false; + for(const auto & p : t) { + if (!is_int(p.var())) + goto usual_delta; + const mpq & c = p.coeff(); + if (c == one_of_type()) { + seen_plus = true; + } else if (c == -one_of_type()) { + seen_minus = true; + } else { + goto usual_delta; + } + } + if (seen_minus && seen_plus) + return zero_of_type(); + return impq(0, 1); + } + usual_delta: + mpq delta = zero_of_type(); + for (const auto & p : t) + if (is_int(p.var())) + delta += abs(p.coeff()); + + delta *= mpq(1, 2); + return impq(delta); +} + +bool int_solver::tighten_term_for_cube(unsigned i) { + unsigned ti = i + m_lar_solver->terms_start_index(); + if (!m_lar_solver->term_is_used_as_row(ti)) + return true; + const lar_term* t = m_lar_solver->terms()[i]; + impq delta = get_cube_delta_for_term(*t); + TRACE("cube", m_lar_solver->print_term_as_indices(*t, tout); tout << ", delta = " << delta;); + if (is_zero(delta)) + return true; + return m_lar_solver->tighten_term_bounds_by_delta(i, delta); +} + +bool int_solver::tighten_terms_for_cube() { + for (unsigned i = 0; i < m_lar_solver->terms().size(); i++) + if (!tighten_term_for_cube(i)) { + TRACE("cube", tout << "cannot tighten";); + return false; + } + return true; +} + +lia_move int_solver::find_cube() { + if (m_number_of_calls % settings().m_int_find_cube_period != 0) + return lia_move::undef; + + settings().st().m_cube_calls++; + TRACE("cube", + for (unsigned j = 0; j < m_lar_solver->A_r().column_count(); j++) + display_column(tout, j); + m_lar_solver->print_terms(tout); + ); + + lar_solver::scoped_push _sp(*m_lar_solver); + if (!tighten_terms_for_cube()) { + return lia_move::undef; + } + + lp_status st = m_lar_solver->find_feasible_solution(); + if (st != lp_status::FEASIBLE && st != lp_status::OPTIMAL) { + TRACE("cube", tout << "cannot find a feasiblie solution";); + _sp.pop(); + move_non_basic_columns_to_bounds(); + find_feasible_solution(); + // it can happen that we found an integer solution here + return !m_lar_solver->r_basis_has_inf_int()? lia_move::sat: lia_move::undef; + } + _sp.pop(); + m_lar_solver->round_to_integer_solution(); + settings().st().m_cube_success++; + return lia_move::sat; +} + +void int_solver::find_feasible_solution() { + m_lar_solver->find_feasible_solution(); + lp_assert(lp_status::OPTIMAL == m_lar_solver->get_status() || lp_status::FEASIBLE == m_lar_solver->get_status()); +} + +lia_move int_solver::run_gcd_test() { + if (settings().m_int_run_gcd_test) { + settings().st().m_gcd_calls++; + if (!gcd_test()) { + settings().st().m_gcd_conflicts++; + return lia_move::conflict; + } + } + return lia_move::undef; +} + +lia_move int_solver::gomory_cut() { + if ((m_number_of_calls) % settings().m_int_gomory_cut_period != 0) + return lia_move::undef; + + if (move_non_basic_columns_to_bounds()) { +#if Z3DEBUG + lp_status st = +#endif + m_lar_solver->find_feasible_solution(); +#if Z3DEBUG + lp_assert(st == lp_status::FEASIBLE || st == lp_status::OPTIMAL); +#endif + } + + int j = find_inf_int_base_column(); + if (j == -1) { + j = find_inf_int_nbasis_column(); + return j == -1? lia_move::sat : create_branch_on_column(j); + } + return proceed_with_gomory_cut(j); +} + + +void int_solver::try_add_term_to_A_for_hnf(unsigned i) { + mpq rs; + const lar_term* t = m_lar_solver->terms()[i]; + constraint_index ci; + if (!hnf_cutter_is_full() && m_lar_solver->get_equality_and_right_side_for_term_on_current_x(i, rs, ci)) { + m_hnf_cutter.add_term(t, rs, ci); + } +} + +bool int_solver::hnf_cutter_is_full() const { + return + m_hnf_cutter.terms_count() >= settings().limit_on_rows_for_hnf_cutter + || + m_hnf_cutter.vars().size() >= settings().limit_on_columns_for_hnf_cutter; +} + +lp_settings& int_solver::settings() { + return m_lar_solver->settings(); +} + +const lp_settings& int_solver::settings() const { + return m_lar_solver->settings(); +} + +bool int_solver::hnf_has_var_with_non_integral_value() const { + for (unsigned j : m_hnf_cutter.vars()) + if (get_value(j).is_int() == false) + return true; + return false; +} + +bool int_solver::init_terms_for_hnf_cut() { + m_hnf_cutter.clear(); + for (unsigned i = 0; i < m_lar_solver->terms().size() && !hnf_cutter_is_full(); i++) { + try_add_term_to_A_for_hnf(i); + } + return hnf_has_var_with_non_integral_value(); +} + +lia_move int_solver::make_hnf_cut() { + if (!init_terms_for_hnf_cut()) { + return lia_move::undef; + } + settings().st().m_hnf_cutter_calls++; + TRACE("hnf_cut", tout << "settings().st().m_hnf_cutter_calls = " << settings().st().m_hnf_cutter_calls;); +#ifdef Z3DEBUG + vector x0 = m_hnf_cutter.transform_to_local_columns(m_lar_solver->m_mpq_lar_core_solver.m_r_x); +#endif + lia_move r = m_hnf_cutter.create_cut(*m_t, *m_k, *m_ex, *m_upper +#ifdef Z3DEBUG + , x0 +#endif + ); + CTRACE("hnf_cut", r == lia_move::cut, tout<< "cut:"; m_lar_solver->print_term(*m_t, tout); tout << " <= " << *m_k << std::endl;); + if (r == lia_move::cut) { + lp_assert(current_solution_is_inf_on_cut()); + settings().st().m_hnf_cuts++; + m_ex->clear(); + for (unsigned i : m_hnf_cutter.constraints_for_explanation()) { + m_ex->push_justification(i); + } + } + return r; +} + +lia_move int_solver::hnf_cut() { + if ((m_number_of_calls) % settings().m_hnf_cut_period == 0) { + return make_hnf_cut(); + } + return lia_move::undef; +} + +lia_move int_solver::check(lar_term& t, mpq& k, explanation& ex, bool & upper) { + if (!has_inf_int()) return lia_move::sat; + + m_t = &t; m_k = &k; m_ex = &ex; m_upper = &upper; + lia_move r = run_gcd_test(); + if (r != lia_move::undef) return r; + + pivoted_rows_tracking_control pc(m_lar_solver); + + if(settings().m_int_pivot_fixed_vars_from_basis) + m_lar_solver->pivot_fixed_vars_from_basis(); + + r = patch_nbasic_columns(); + if (r != lia_move::undef) return r; + ++m_number_of_calls; + r = find_cube(); + if (r != lia_move::undef) return r; + + r = hnf_cut(); + if (r != lia_move::undef) return r; + + r = gomory_cut(); + return (r == lia_move::undef)? branch_or_sat() : r; +} + +lia_move int_solver::branch_or_sat() { + int j = find_any_inf_int_column_basis_first(); + return j == -1? lia_move::sat : create_branch_on_column(j); +} + +int int_solver::find_any_inf_int_column_basis_first() { + int j = find_inf_int_base_column(); + if (j != -1) + return j; + return find_inf_int_nbasis_column(); +} + +bool int_solver::move_non_basic_column_to_bounds(unsigned j) { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + auto & val = lcs.m_r_x[j]; + switch (lcs.m_column_types()[j]) { + case column_type::boxed: + 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_lower_bounds()[j]); + else + set_value_for_nbasic_column(j, lcs.m_r_upper_bounds()[j]); + return true; + } + break; + 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; + case column_type::upper_bound: + if (val != lcs.m_r_upper_bounds()[j]) { + set_value_for_nbasic_column(j, lcs.m_r_upper_bounds()[j]); + return true; + } + break; + default: + if (is_int(j) && !val.is_int()) { + set_value_for_nbasic_column(j, impq(floor(val))); + return true; + } + break; + } + return false; +} + +bool int_solver::move_non_basic_columns_to_bounds() { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + bool change = false; + for (unsigned j : lcs.m_r_nbasis) { + if (move_non_basic_column_to_bounds(j)) + change = true; + } + + if (settings().simplex_strategy() == simplex_strategy_enum::tableau_costs) + m_lar_solver->update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); + return change; +} + +void int_solver::set_value_for_nbasic_column_ignore_old_values(unsigned j, const impq & new_val) { + lp_assert(!is_base(j)); + auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x[j]; + auto delta = new_val - x; + x = new_val; + m_lar_solver->change_basic_columns_dependend_on_a_given_nb_column(j, delta); +} + + +void int_solver::set_value_for_nbasic_column(unsigned j, const impq & new_val) { + lp_assert(!is_base(j)); + auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x[j]; + auto delta = new_val - x; + x = new_val; + m_lar_solver->change_basic_columns_dependend_on_a_given_nb_column(j, delta); +} + +void int_solver::patch_nbasic_column(unsigned j, bool patch_only_int_vals) { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + impq & val = lcs.m_r_x[j]; + bool val_is_int = val.is_int(); + if (patch_only_int_vals && !val_is_int) + return; + + bool inf_l, inf_u; + impq l, u; + mpq m; + if (!get_freedom_interval_for_column(j, inf_l, l, inf_u, u, m)) { + return; + } + bool m_is_one = m.is_one(); + if (m.is_one() && val_is_int) + return; + // check whether value of j is already a multiple of m. + if (val_is_int && (val.x / m).is_int()) + return; + TRACE("patch_int", + tout << "TARGET j" << j << " -> ["; + if (inf_l) tout << "-oo"; else tout << l; + tout << ", "; + if (inf_u) tout << "oo"; else tout << u; + tout << "]"; + tout << ", m: " << m << ", val: " << val << ", is_int: " << m_lar_solver->column_is_int(j) << "\n";); + if (!inf_l) { + l = m_is_one ? ceil(l) : m * ceil(l / m); + if (inf_u || l <= u) { + TRACE("patch_int", + tout << "patching with l: " << l << '\n';); + set_value_for_nbasic_column(j, l); + } + else { + TRACE("patch_int", + tout << "not patching " << l << "\n";); + } + } + else if (!inf_u) { + u = m_is_one ? floor(u) : m * floor(u / m); + set_value_for_nbasic_column(j, u); + TRACE("patch_int", + tout << "patching with u: " << u << '\n';); + } + else { + set_value_for_nbasic_column(j, impq(0)); + TRACE("patch_int", + tout << "patching with 0\n";); + } +} + +lia_move int_solver::patch_nbasic_columns() { + settings().st().m_patches++; + lp_assert(is_feasible()); + for (unsigned j : m_lar_solver->m_mpq_lar_core_solver.m_r_nbasis) { + patch_nbasic_column(j, settings().m_int_patch_only_integer_values); + } + lp_assert(is_feasible()); + if (!has_inf_int()) { + settings().st().m_patches_success++; + return lia_move::sat; + } + return lia_move::undef; +} + +mpq get_denominators_lcm(const row_strip & row) { + mpq r(1); + for (auto & c : row) { + r = lcm(r, denominator(c.coeff())); + } + return r; +} + +bool int_solver::gcd_test_for_row(static_matrix> & A, unsigned i) { + mpq lcm_den = get_denominators_lcm(A.m_rows[i]); + mpq consts(0); + mpq gcds(0); + mpq least_coeff(0); + bool least_coeff_is_bounded = false; + unsigned j; + for (auto &c : A.m_rows[i]) { + j = c.var(); + const mpq& a = c.coeff(); + if (m_lar_solver->column_is_fixed(j)) { + mpq aux = lcm_den * a; + consts += aux * m_lar_solver->column_lower_bound(j).x; + } + else if (m_lar_solver->column_is_real(j)) { + return true; + } + else if (gcds.is_zero()) { + gcds = abs(lcm_den * a); + least_coeff = gcds; + least_coeff_is_bounded = m_lar_solver->column_is_bounded(j); + } + else { + mpq aux = abs(lcm_den * a); + gcds = gcd(gcds, aux); + if (aux < least_coeff) { + least_coeff = aux; + least_coeff_is_bounded = m_lar_solver->column_is_bounded(j); + } + else if (least_coeff_is_bounded && aux == least_coeff) { + least_coeff_is_bounded = m_lar_solver->column_is_bounded(j); + } + } + SASSERT(gcds.is_int()); + SASSERT(least_coeff.is_int()); + TRACE("gcd_test_bug", tout << "coeff: " << a << ", gcds: " << gcds + << " least_coeff: " << least_coeff << " consts: " << consts << "\n";); + + } + + if (gcds.is_zero()) { + // All variables are fixed. + // This theory guarantees that the assignment satisfies each row, and + // fixed integer variables are assigned to integer values. + return true; + } + + if (!(consts / gcds).is_int()) { + TRACE("gcd_test", tout << "row failed the GCD test:\n"; display_row_info(tout, i);); + fill_explanation_from_fixed_columns(A.m_rows[i]); + return false; + } + + if (least_coeff.is_one() && !least_coeff_is_bounded) { + SASSERT(gcds.is_one()); + return true; + } + + if (least_coeff_is_bounded) { + return ext_gcd_test(A.m_rows[i], least_coeff, lcm_den, consts); + } + return true; +} + + +void int_solver::add_to_explanation_from_fixed_or_boxed_column(unsigned j) { + constraint_index lc, uc; + m_lar_solver->get_bound_constraint_witnesses_for_column(j, lc, uc); + m_ex->m_explanation.push_back(std::make_pair(mpq(1), lc)); + m_ex->m_explanation.push_back(std::make_pair(mpq(1), uc)); +} +void int_solver::fill_explanation_from_fixed_columns(const row_strip & row) { + for (const auto & c : row) { + if (!m_lar_solver->column_is_fixed(c.var())) + continue; + add_to_explanation_from_fixed_or_boxed_column(c.var()); + } +} + +bool int_solver::gcd_test() { + auto & A = m_lar_solver->A_r(); // getting the matrix + for (unsigned i = 0; i < A.row_count(); i++) + if (!gcd_test_for_row(A, i)) + return false; + return true; +} + +bool int_solver::ext_gcd_test(const row_strip & row, + mpq const & least_coeff, + mpq const & lcm_den, + mpq const & consts) { + mpq gcds(0); + mpq l(consts); + mpq u(consts); + + mpq a; + unsigned j; + for (const auto & c : row) { + j = c.var(); + const mpq & a = c.coeff(); + if (m_lar_solver->column_is_fixed(j)) + continue; + SASSERT(!m_lar_solver->column_is_real(j)); + mpq ncoeff = lcm_den * a; + SASSERT(ncoeff.is_int()); + mpq abs_ncoeff = abs(ncoeff); + if (abs_ncoeff == least_coeff) { + SASSERT(m_lar_solver->column_is_bounded(j)); + if (ncoeff.is_pos()) { + // 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 * 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); + } + else if (gcds.is_zero()) { + gcds = abs_ncoeff; + } + else { + gcds = gcd(gcds, abs_ncoeff); + } + SASSERT(gcds.is_int()); + } + + if (gcds.is_zero()) { + return true; + } + + mpq l1 = ceil(l/gcds); + mpq u1 = floor(u/gcds); + + if (u1 < l1) { + fill_explanation_from_fixed_columns(row); + return false; + } + + return true; + +} +/* +linear_combination_iterator * int_solver::get_column_iterator(unsigned j) { + if (m_lar_solver->use_tableau()) + return new iterator_on_column(m_lar_solver->A_r().m_columns[j], m_lar_solver->A_r()); + return new iterator_on_indexed_vector(m_lar_solver->get_column_in_lu_mode(j)); +} +*/ + + +int_solver::int_solver(lar_solver* lar_slv) : + m_lar_solver(lar_slv), + m_number_of_calls(0), + m_hnf_cutter(settings()) { + m_lar_solver->set_int_solver(this); +} + + +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::lower_bound: + return true; + default: + return false; + } +} + +bool int_solver::has_upper(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::upper_bound: + return true; + default: + return false; + } +} + + +void set_lower(impq & l, + bool & inf_l, + impq const & v ) { + if (inf_l || v > l) { + l = v; + inf_l = false; + } +} + + +void set_upper(impq & u, + bool & inf_u, + impq const & v) { + if (inf_u || v < u) { + u = v; + inf_u = false; + } +} + +bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m) { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + if (lcs.m_r_heading[j] >= 0) // the basic var + return false; + + impq const & xj = get_value(j); + + inf_l = true; + inf_u = true; + l = u = zero_of_type(); + m = mpq(1); + + if (has_low(j)) { + set_lower(l, inf_l, lower_bound(j)); + } + if (has_upper(j)) { + set_upper(u, inf_u, upper_bound(j)); + } + + mpq a; // the coefficient in the column + unsigned row_index; + lp_assert(settings().use_tableau()); + const auto & A = m_lar_solver->A_r(); + for (const auto &c : A.column(j)) { + row_index = c.var(); + const mpq & a = c.coeff(); + + unsigned i = lcs.m_r_basis[row_index]; + impq const & xi = get_value(i); + if (is_int(i) && is_int(j) && !a.is_int()) + m = lcm(m, denominator(a)); + if (a.is_neg()) { + if (has_low(i)) + 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); + } + else { + 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_lower_bounds()[i]) / a); + } + if (!inf_l && !inf_u && l >= u) break; + } + + TRACE("freedom_interval", + tout << "freedom variable for:\n"; + tout << m_lar_solver->get_column_name(j); + tout << "["; + if (inf_l) tout << "-oo"; else tout << l; + tout << "; "; + if (inf_u) tout << "oo"; else tout << u; + tout << "]\n"; + tout << "val = " << get_value(j) << "\n"; + tout << "return " << (inf_l || inf_u || l <= u); + ); + return (inf_l || inf_u || l <= u); +} + +bool int_solver::is_int(unsigned j) const { + return m_lar_solver->column_is_int(j); +} + +bool int_solver::is_real(unsigned j) const { + return !is_int(j); +} + +bool int_solver::value_is_int(unsigned j) const { + return m_lar_solver->column_value_is_int(j); +} + + + +bool int_solver::is_feasible() const { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + lp_assert( + lcs.m_r_solver.calc_current_x_is_feasible_include_non_basis() == + lcs.m_r_solver.current_x_is_feasible()); + return lcs.m_r_solver.current_x_is_feasible(); +} +const impq & int_solver::get_value(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_r_x[j]; +} + +void int_solver::display_column(std::ostream & out, unsigned j) const { + m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(j, out); +} + +bool int_solver::column_is_int_inf(unsigned j) const { + return is_int(j) && (!value_is_int(j)); +} + +bool int_solver::is_base(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_r_heading[j] >= 0; +} + +bool int_solver::is_boxed(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_column_types[j] == column_type::boxed; +} + +bool int_solver::is_fixed(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_column_types[j] == column_type::fixed; +} + +bool int_solver::is_free(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_column_types[j] == column_type::free_column; +} + +bool int_solver::at_bound(unsigned j) const { + auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; + switch (mpq_solver.m_column_types[j] ) { + case column_type::fixed: + case column_type::boxed: + return + mpq_solver.m_lower_bounds[j] == get_value(j) || + mpq_solver.m_upper_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: + return false; + } +} + +bool int_solver::at_low(unsigned j) const { + auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; + switch (mpq_solver.m_column_types[j] ) { + case column_type::fixed: + case column_type::boxed: + case column_type::lower_bound: + return mpq_solver.m_lower_bounds[j] == get_value(j); + default: + return false; + } +} + +bool int_solver::at_upper(unsigned j) const { + auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; + switch (mpq_solver.m_column_types[j] ) { + case column_type::fixed: + case column_type::boxed: + case column_type::upper_bound: + return mpq_solver.m_upper_bounds[j] == get_value(j); + default: + return false; + } +} + +void int_solver::display_row_info(std::ostream & out, unsigned row_index) const { + auto & rslv = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; + for (auto &c: rslv.m_A.m_rows[row_index]) { + if (numeric_traits::is_pos(c.coeff())) + out << "+"; + out << c.coeff() << rslv.column_name(c.var()) << " "; + } + + for (auto& c: rslv.m_A.m_rows[row_index]) { + rslv.print_column_bound_info(c.var(), out); + } + rslv.print_column_bound_info(rslv.m_basis[row_index], out); +} + +unsigned int_solver::random() { + return m_lar_solver->get_core_solver().settings().random_next(); +} + +bool int_solver::shift_var(unsigned j, unsigned range) { + if (is_fixed(j) || is_base(j)) + return false; + + bool inf_l, inf_u; + impq l, u; + mpq m; + get_freedom_interval_for_column(j, inf_l, l, inf_u, u, m); + if (inf_l && inf_u) { + impq new_val = impq(random() % (range + 1)); + set_value_for_nbasic_column_ignore_old_values(j, new_val); + return true; + } + if (is_int(j)) { + if (!inf_l) { + l = ceil(l); + if (!m.is_one()) + l = m*ceil(l/m); + } + if (!inf_u) { + u = floor(u); + if (!m.is_one()) + u = m*floor(u/m); + } + } + if (!inf_l && !inf_u && l >= u) + return false; + if (inf_u) { + SASSERT(!inf_l); + impq delta = impq(random() % (range + 1)); + impq new_val = l + m*delta; + set_value_for_nbasic_column_ignore_old_values(j, new_val); + return true; + } + if (inf_l) { + SASSERT(!inf_u); + impq delta = impq(random() % (range + 1)); + impq new_val = u - m*delta; + set_value_for_nbasic_column_ignore_old_values(j, new_val); + return true; + } + if (!is_int(j)) { + SASSERT(!inf_l && !inf_u); + mpq delta = mpq(random() % (range + 1)); + impq new_val = l + ((delta * (u - l)) / mpq(range)); + set_value_for_nbasic_column_ignore_old_values(j, new_val); + return true; + } + else { + mpq r = (u.x - l.x) / m; + if (r < mpq(range)) + range = static_cast(r.get_uint64()); + impq new_val = l + m * (impq(random() % (range + 1))); + set_value_for_nbasic_column_ignore_old_values(j, new_val); + return true; + } +} + +bool int_solver::non_basic_columns_are_at_bounds() const { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + for (unsigned j :lcs.m_r_nbasis) { + auto & val = lcs.m_r_x[j]; + switch (lcs.m_column_types()[j]) { + case column_type::boxed: + if (val != lcs.m_r_lower_bounds()[j] && val != lcs.m_r_upper_bounds()[j]) + return false; + break; + case column_type::lower_bound: + if (val != lcs.m_r_lower_bounds()[j]) + return false; + break; + case column_type::upper_bound: + if (val != lcs.m_r_upper_bounds()[j]) + return false; + break; + default: + if (is_int(j) && !val.is_int()) { + return false; + } + } + } + return true; +} + +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) { + TRACE("check_main_int", tout << "branching" << std::endl;); + lp_assert(m_t->is_empty()); + lp_assert(j != -1); + m_t->add_monomial(mpq(1), m_lar_solver->adjust_column_index_to_term_index(j)); + if (is_free(j)) { + *m_upper = true; + *m_k = mpq(0); + } else { + *m_upper = left_branch_is_more_narrow_than_right(j); + *m_k = *m_upper? floor(get_value(j)) : ceil(get_value(j)); + } + + TRACE("arith_int", tout << "branching v" << j << " = " << get_value(j) << "\n"; + display_column(tout, j); + tout << "k = " << *m_k << std::endl; + ); + return lia_move::branch; + +} + +bool int_solver::left_branch_is_more_narrow_than_right(unsigned j) { + switch (m_lar_solver->m_mpq_lar_core_solver.m_r_solver.m_column_types[j] ) { + case column_type::fixed: + return false; + case column_type::boxed: + { + auto k = floor(get_value(j)); + return k - lower_bound(j).x < upper_bound(j).x - (k + mpq(1)); + } + case column_type::lower_bound: + return true; + case column_type::upper_bound: + return false; + default: + return false; + } +} + +const impq& int_solver::upper_bound(unsigned j) const { + return m_lar_solver->column_upper_bound(j); +} + +bool int_solver::is_term(unsigned j) const { + return m_lar_solver->column_corresponds_to_term(j); +} + +unsigned int_solver::column_count() const { return m_lar_solver->column_count(); } + +} diff --git a/src/util/lp/int_solver.h b/src/util/lp/int_solver.h new file mode 100644 index 000000000..65c818d1e --- /dev/null +++ b/src/util/lp/int_solver.h @@ -0,0 +1,166 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/lp/lp_settings.h" +#include "util/lp/static_matrix.h" +#include "util/lp/int_set.h" +#include "util/lp/lar_term.h" +#include "util/lp/lar_constraints.h" +#include "util/lp/hnf_cutter.h" +#include "util/lp/lia_move.h" +#include "util/lp/explanation.h" + +namespace lp { +class lar_solver; + +template +struct lp_constraint; + + +class int_solver { +public: + // fields + lar_solver *m_lar_solver; + unsigned m_number_of_calls; + lar_term *m_t; // the term to return in the cut + mpq *m_k; // the right side of the cut + explanation *m_ex; // the conflict explanation + bool *m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise + hnf_cutter m_hnf_cutter; + // methods + int_solver(lar_solver* lp); + + // 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 & upper); + lia_move check_(lar_term& t, mpq& k, explanation& ex, bool & upper); + bool move_non_basic_column_to_bounds(unsigned j); + lia_move check_wrapper(lar_term& t, mpq& k, explanation& ex); + bool is_base(unsigned j) const; + +private: + + // how to tighten bounds for integer variables. + + bool gcd_test_for_row(static_matrix> & A, unsigned i); + + // gcd test + // 5*x + 3*y + 6*z = 5 + // suppose x is fixed at 2. + // so we have 10 + 3(y + 2z) = 5 + // 5 = -3(y + 2z) + // this is unsolvable because 5/3 is not an integer. + // so we create a lemma that rules out this condition. + // + bool gcd_test(); // returns false in case of failure. Creates a theory lemma in case of failure. + + bool branch(const lp_constraint & new_inequality); + bool ext_gcd_test(const row_strip& row, + mpq const & least_coeff, + mpq const & lcm_den, + mpq const & consts); + void fill_explanation_from_fixed_columns(const row_strip & row); + void add_to_explanation_from_fixed_or_boxed_column(unsigned j); + lia_move patch_nbasic_columns(); + bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m); + 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; + bool is_boxed(unsigned j) const; + bool is_fixed(unsigned j) const; + bool is_free(unsigned j) const; + bool value_is_int(unsigned j) const; + void set_value_for_nbasic_column(unsigned j, const impq & new_val); + void set_value_for_nbasic_column_ignore_old_values(unsigned j, const impq & new_val); + bool non_basic_columns_are_at_bounds() const; + bool is_feasible() const; + const impq & get_value(unsigned j) const; + bool column_is_int_inf(unsigned j) const; + void trace_inf_rows() const; + lia_move branch_or_sat(); + int find_any_inf_int_column_basis_first(); + int find_inf_int_base_column(); + int find_inf_int_boxed_base_column_with_smallest_range(unsigned&); + int get_kth_inf_int(unsigned) const; + lp_settings& settings(); + const lp_settings& settings() const; + bool move_non_basic_columns_to_bounds(); + void branch_infeasible_int_var(unsigned); + lia_move mk_gomory_cut(unsigned inf_col, const row_strip& row); + lia_move report_conflict_from_gomory_cut(); + void adjust_term_and_k_for_some_ints_case_gomory(mpq& lcm_den); + lia_move proceed_with_gomory_cut(unsigned j); + int find_free_var_in_gomory_row(const row_strip& ); + bool is_gomory_cut_target(const row_strip&); + bool at_bound(unsigned j) const; + bool at_low(unsigned j) const; + bool at_upper(unsigned j) const; + bool has_low(unsigned j) const; + bool has_upper(unsigned j) const; + unsigned row_of_basic_column(unsigned j) const; + inline static bool is_rational(const impq & n) { + return is_zero(n.y); + } + +public: + void display_column(std::ostream & out, unsigned j) const; + inline static + mpq fractional_part(const impq & n) { + lp_assert(is_rational(n)); + return n.x - floor(n.x); + } +private: + void real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0); + void int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0); + constraint_index column_upper_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; +public: + bool shift_var(unsigned j, unsigned range); +private: + unsigned random(); + bool has_inf_int() const; + lia_move create_branch_on_column(int j); +public: + bool is_term(unsigned j) const; + bool left_branch_is_more_narrow_than_right(unsigned); + lia_move find_cube(); + bool tighten_terms_for_cube(); + bool tighten_term_for_cube(unsigned); + unsigned column_count() const; + bool all_columns_are_bounded() const; + impq get_cube_delta_for_term(const lar_term&) const; + void find_feasible_solution(); + int find_inf_int_nbasis_column() const; + lia_move run_gcd_test(); + lia_move gomory_cut(); + lia_move hnf_cut(); + lia_move make_hnf_cut(); + bool init_terms_for_hnf_cut(); + bool hnf_matrix_is_empty() const; + void try_add_term_to_A_for_hnf(unsigned term_index); + bool hnf_has_var_with_non_integral_value() const; + bool hnf_cutter_is_full() const; + void patch_nbasic_column(unsigned j, bool patch_only_int_vals); +}; +} diff --git a/src/util/lp/iterator_on_column.h b/src/util/lp/iterator_on_column.h deleted file mode 100644 index c9112a064..000000000 --- a/src/util/lp/iterator_on_column.h +++ /dev/null @@ -1,65 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/linear_combination_iterator.h" -#include "util/lp/static_matrix.h" -#include "util/lp/lar_term.h" -namespace lp { -template -struct iterator_on_column:linear_combination_iterator { - const vector& m_column; // the offset in term coeffs - const static_matrix & m_A; - int m_i; // the initial offset in the column - unsigned size() const override { return m_column.size(); } - iterator_on_column(const vector& column, const static_matrix & A) // the offset in term coeffs - : - m_column(column), - m_A(A), - m_i(-1) {} - - bool next(mpq & a, unsigned & i) override { - if (++m_i >= static_cast(m_column.size())) - return false; - - const column_cell& c = m_column[m_i]; - a = m_A.get_val(c); - i = c.m_i; - return true; - } - - bool next(unsigned & i) override { - if (++m_i >= static_cast(m_column.size())) - return false; - - const column_cell& c = m_column[m_i]; - i = c.m_i; - return true; - } - - void reset() override { - m_i = -1; - } - - linear_combination_iterator * clone() override { - iterator_on_column * r = new iterator_on_column(m_column, m_A); - return r; - } -}; -} diff --git a/src/util/lp/iterator_on_indexed_vector.h b/src/util/lp/iterator_on_indexed_vector.h deleted file mode 100644 index 6cb98b8f2..000000000 --- a/src/util/lp/iterator_on_indexed_vector.h +++ /dev/null @@ -1,53 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/linear_combination_iterator.h" -namespace lp { -template -struct iterator_on_indexed_vector:linear_combination_iterator { - const indexed_vector & m_v; - unsigned m_offset; - iterator_on_indexed_vector(const indexed_vector & v) : - m_v(v), - m_offset(0) - {} - unsigned size() const override { return m_v.m_index.size(); } - bool next(T & a, unsigned & i) override { - if (m_offset >= m_v.m_index.size()) - return false; - i = m_v.m_index[m_offset++]; - a = m_v.m_data[i]; - return true; - } - - bool next(unsigned & i) override { - if (m_offset >= m_v.m_index.size()) - return false; - i = m_v.m_index[m_offset++]; - return true; - } - void reset() override { - m_offset = 0; - } - linear_combination_iterator* clone() override { - return new iterator_on_indexed_vector(m_v); - } -}; -} diff --git a/src/util/lp/iterator_on_pivot_row.h b/src/util/lp/iterator_on_pivot_row.h deleted file mode 100644 index 721502bc2..000000000 --- a/src/util/lp/iterator_on_pivot_row.h +++ /dev/null @@ -1,59 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/iterator_on_indexed_vector.h" -namespace lp { -template -struct iterator_on_pivot_row:linear_combination_iterator { - bool m_basis_returned; - const indexed_vector & m_v; - unsigned m_basis_j; - iterator_on_indexed_vector m_it; - unsigned size() const override { return m_it.size(); } - iterator_on_pivot_row(const indexed_vector & v, unsigned basis_j) : - m_basis_returned(false), - m_v(v), m_basis_j(basis_j), m_it(v) {} - bool next(T & a, unsigned & i) override { - if (m_basis_returned == false) { - m_basis_returned = true; - a = one_of_type(); - i = m_basis_j; - return true; - } - return m_it.next(a, i); - } - bool next(unsigned & i) override { - if (m_basis_returned == false) { - m_basis_returned = true; - i = m_basis_j; - return true; - } - return m_it.next(i); - } - void reset() override { - m_basis_returned = false; - m_it.reset(); - } - linear_combination_iterator * clone() override { - iterator_on_pivot_row * r = new iterator_on_pivot_row(m_v, m_basis_j); - return r; - } -}; -} diff --git a/src/util/lp/iterator_on_row.h b/src/util/lp/iterator_on_row.h deleted file mode 100644 index 55fbda907..000000000 --- a/src/util/lp/iterator_on_row.h +++ /dev/null @@ -1,52 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/linear_combination_iterator.h" -namespace lp { -template -struct iterator_on_row:linear_combination_iterator { - const vector> & m_row; - unsigned m_i; // offset - iterator_on_row(const vector> & row) : m_row(row), m_i(0) - {} - unsigned size() const override { return m_row.size(); } - bool next(T & a, unsigned & i) override { - if (m_i == m_row.size()) - return false; - auto &c = m_row[m_i++]; - i = c.m_j; - a = c.get_val(); - return true; - } - bool next(unsigned & i) override { - if (m_i == m_row.size()) - return false; - auto &c = m_row[m_i++]; - i = c.m_j; - return true; - } - void reset() override { - m_i = 0; - } - linear_combination_iterator* clone() override { - return new iterator_on_row(m_row); - } -}; -} diff --git a/src/util/lp/iterator_on_term_with_basis_var.h b/src/util/lp/iterator_on_term_with_basis_var.h deleted file mode 100644 index 85d11cf36..000000000 --- a/src/util/lp/iterator_on_term_with_basis_var.h +++ /dev/null @@ -1,72 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/linear_combination_iterator.h" -#include "util/lp/numeric_pair.h" -#include "util/lp/lar_term.h" -namespace lp { -struct iterator_on_term_with_basis_var:linear_combination_iterator { - const lar_term & m_term; - std::unordered_map::const_iterator m_i; // the offset in term coeffs - bool m_term_j_returned; - unsigned m_term_j; - unsigned size() const override {return static_cast(m_term.m_coeffs.size() + 1);} - iterator_on_term_with_basis_var(const lar_term & t, unsigned term_j) : - m_term(t), - m_i(t.m_coeffs.begin()), - m_term_j_returned(false), - m_term_j(term_j) {} - - bool next(mpq & a, unsigned & i) override { - if (m_term_j_returned == false) { - m_term_j_returned = true; - a = - one_of_type(); - i = m_term_j; - return true; - } - if (m_i == m_term.m_coeffs.end()) - return false; - i = m_i->first; - a = m_i->second; - m_i++; - return true; - } - bool next(unsigned & i) override { - if (m_term_j_returned == false) { - m_term_j_returned = true; - i = m_term_j; - return true; - } - if (m_i == m_term.m_coeffs.end()) - return false; - i = m_i->first; - m_i++; - return true; - } - void reset() override { - m_term_j_returned = false; - m_i = m_term.m_coeffs.begin(); - } - linear_combination_iterator * clone() override { - iterator_on_term_with_basis_var * r = new iterator_on_term_with_basis_var(m_term, m_term_j); - return r; - } -}; -} diff --git a/src/util/lp/lar_constraints.h b/src/util/lp/lar_constraints.h index 5c33db8c6..ac15028bb 100644 --- a/src/util/lp/lar_constraints.h +++ b/src/util/lp/lar_constraints.h @@ -40,12 +40,11 @@ inline std::string lconstraint_kind_string(lconstraint_kind t) { case GT: return std::string(">"); case EQ: return std::string("="); } - SASSERT(false); + lp_unreachable(); return std::string(); // it is unreachable } -class lar_base_constraint { -public: +struct lar_base_constraint { lconstraint_kind m_kind; mpq m_right_side; virtual vector> get_left_side_coefficients() const = 0; @@ -88,7 +87,7 @@ public: : lar_base_constraint(kind, right_side), m_coeffs(left_side) {} lar_constraint(const lar_base_constraint & c) { - SASSERT(false); // should not be called : todo! + lp_assert(false); // should not be called : todo! } unsigned size() const override { 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 32229cf27..5f5518528 100644 --- a/src/util/lp/lar_core_solver.h +++ b/src/util/lp/lar_core_solver.h @@ -26,12 +26,9 @@ Revision History: #include "util/lp/indexed_vector.h" #include "util/lp/binary_heap_priority_queue.h" #include "util/lp/breakpoint.h" -#include "util/lp/stacked_unordered_set.h" #include "util/lp/lp_primal_core_solver.h" #include "util/lp/stacked_vector.h" #include "util/lp/lar_solution_signature.h" -#include "util/lp/iterator_on_column.h" -#include "util/lp/iterator_on_indexed_vector.h" #include "util/lp/stacked_value.h" namespace lp { @@ -50,7 +47,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 +59,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; @@ -155,11 +152,11 @@ public: void fill_evidence(unsigned row); - + unsigned get_number_of_non_ints() const; 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; @@ -183,16 +180,16 @@ public: } void push() { - SASSERT(m_r_solver.basis_heading_is_correct()); - SASSERT(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); - SASSERT(m_column_types.size() == m_r_A.column_count()); + lp_assert(m_r_solver.basis_heading_is_correct()); + lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); + lp_assert(m_column_types.size() == m_r_A.column_count()); m_stacked_simplex_strategy = settings().simplex_strategy(); m_stacked_simplex_strategy.push(); m_column_types.push(); // 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); @@ -207,7 +204,7 @@ public: template void push_vector(stacked_vector & pushed_vector, const vector & vector) { - SASSERT(pushed_vector.size() <= vector.size()); + lp_assert(pushed_vector.size() <= vector.size()); for (unsigned i = 0; i < vector.size();i++) { if (i == pushed_vector.size()) { pushed_vector.push_back(vector[i]); @@ -234,7 +231,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); @@ -257,8 +254,8 @@ public: pop_basis(k); m_stacked_simplex_strategy.pop(k); settings().simplex_strategy() = m_stacked_simplex_strategy; - SASSERT(m_r_solver.basis_heading_is_correct()); - SASSERT(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); + lp_assert(m_r_solver.basis_heading_is_correct()); + lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); } bool need_to_presolve_with_double_solver() const { @@ -276,11 +273,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,30 +297,30 @@ 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: - SASSERT(false); + lp_assert(false); } break; default: - SASSERT(false); + lp_unreachable(); } m_r_solver.remove_column_from_inf_set(j); return true; @@ -332,7 +329,7 @@ public: void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) { - SASSERT(m_r_solver.inf_set_is_correct()); + lp_assert(m_r_solver.inf_set_is_correct()); for (auto &t : signature) { unsigned j = t.first; if (m_r_heading[j] >= 0) @@ -344,12 +341,11 @@ public: for (const auto & cc : m_r_solver.m_A.m_columns[j]){ unsigned i = cc.m_i; unsigned jb = m_r_solver.m_basis[i]; - m_r_solver.m_x[jb] -= delta * m_r_solver.m_A.get_val(cc); - m_r_solver.update_column_in_inf_set(jb); + m_r_solver.update_x_with_delta_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc)); } - SASSERT(m_r_solver.A_mult_x_is_off() == false); + CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); } - SASSERT(m_r_solver.inf_set_is_correct()); + lp_assert(m_r_solver.inf_set_is_correct()); } @@ -357,11 +353,11 @@ public: void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver & s) { for (auto &t : signature) { unsigned j = t.first; - SASSERT(m_r_heading[j] < 0); + 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: @@ -374,33 +370,33 @@ public: case not_at_bound: switch (m_column_types[j]) { case column_type::free_column: - SASSERT(false); // unreachable + lp_assert(false); // unreachable 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: - SASSERT(false); + lp_assert(false); } break; default: - SASSERT(false); + lp_unreachable(); } } - SASSERT(is_zero_vector(s.m_b)); + lp_assert(is_zero_vector(s.m_b)); s.solve_Ax_eq_b(); } @@ -433,7 +429,7 @@ public: // the queues of delayed indices std::queue entr_q, leav_q; auto * l = cs.m_factorization; - SASSERT(l->get_status() == LU_status::OK); + lp_assert(l->get_status() == LU_status::OK); for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { unsigned entering = trace_of_basis_change[i]; unsigned leaving = trace_of_basis_change[i+1]; @@ -461,8 +457,8 @@ public: continue; } } - SASSERT(cs.m_basis_heading[entering] < 0); - SASSERT(cs.m_basis_heading[leaving] >= 0); + lp_assert(cs.m_basis_heading[entering] < 0); + lp_assert(cs.m_basis_heading[leaving] >= 0); if (l->get_status() == LU_status::OK) { l->prepare_entering(entering, w); // to init vector w l->replace_column(zero_of_type(), w, cs.m_basis_heading[leaving]); @@ -486,7 +482,7 @@ public: void solve_on_signature_tableau(const lar_solution_signature & signature, const vector & changes_of_basis) { r_basis_is_OK(); - SASSERT(settings().use_tableau()); + lp_assert(settings().use_tableau()); bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading); if (!r) { // it is the case where m_d_solver gives a degenerated basis @@ -505,10 +501,10 @@ public: return; m_r_solver.stop_tracing_basis_changes(); // and now catch up in the double solver - SASSERT(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); + lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); } - SASSERT(r_basis_is_OK()); + lp_assert(r_basis_is_OK()); } bool adjust_x_of_column(unsigned j) { @@ -522,16 +518,16 @@ public: } m_r_solver.snap_column_to_bound_tableau(j); - SASSERT(m_r_solver.column_is_feasible(j)); + lp_assert(m_r_solver.column_is_feasible(j)); m_r_solver.m_inf_set.erase(j); */ - SASSERT(false); + lp_assert(false); return true; } bool catch_up_in_lu_tableau(const vector & trace_of_basis_change, const vector & basis_heading) { - SASSERT(r_basis_is_OK()); + lp_assert(r_basis_is_OK()); // the queues of delayed indices std::queue entr_q, leav_q; for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { @@ -561,8 +557,8 @@ public: continue; } } - SASSERT(m_r_solver.m_basis_heading[entering] < 0); - SASSERT(m_r_solver.m_basis_heading[leaving] >= 0); + lp_assert(m_r_solver.m_basis_heading[entering] < 0); + lp_assert(m_r_solver.m_basis_heading[leaving] >= 0); m_r_solver.change_basis_unconditionally(entering, leaving); if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) { // unroll the last step @@ -572,12 +568,12 @@ public: #endif m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]); #ifdef Z3DEBUG - SASSERT(t); + lp_assert(t); #endif return false; } } - SASSERT(r_basis_is_OK()); + lp_assert(r_basis_is_OK()); return true; } @@ -587,14 +583,14 @@ public: if (!m_r_solver.m_settings.use_tableau()) return true; for (unsigned j : m_r_solver.m_basis) { - SASSERT(m_r_solver.m_A.m_columns[j].size() == 1); - SASSERT(m_r_solver.m_A.get_val(m_r_solver.m_A.m_columns[j][0]) == one_of_type()); + lp_assert(m_r_solver.m_A.m_columns[j].size() == 1); + lp_assert(m_r_solver.m_A.get_val(m_r_solver.m_A.m_columns[j][0]) == one_of_type()); } for (unsigned j =0; j < m_r_solver.m_basis_heading.size(); j++) { if (m_r_solver.m_basis_heading[j] >= 0) continue; if (m_r_solver.m_column_types[j] == column_type::fixed) continue; - SASSERT(static_cast(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size()); - SASSERT( m_r_solver.m_basis_heading[j] <= -1); + lp_assert(static_cast(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size()); + lp_assert( m_r_solver.m_basis_heading[j] <= -1); } #endif return true; @@ -614,7 +610,6 @@ public: } if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back - // std::cout << "no_r_lu" << std::endl; catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver); m_r_solver.find_feasible_solution(); m_d_basis = m_r_basis; @@ -630,7 +625,7 @@ public: return; m_r_solver.stop_tracing_basis_changes(); // and now catch up in the double solver - SASSERT(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); + lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); } } @@ -656,7 +651,7 @@ public: template void extract_signature_from_lp_core_solver(const lp_primal_core_solver & solver, lar_solution_signature & signature) { signature.clear(); - SASSERT(signature.size() == 0); + lp_assert(signature.size() == 0); for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) { if (solver.m_basis_heading[j] < 0) { signature[j] = solver.get_non_basic_column_value_position(j); @@ -666,27 +661,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(); - SASSERT(!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()); @@ -706,8 +701,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]; } } } @@ -734,17 +729,17 @@ 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; default: - SASSERT(false); + lp_assert(false); } return false; } @@ -752,27 +747,27 @@ 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: case column_type::fixed: return true; default: - SASSERT(false); + lp_assert(false); } return false; } void update_delta(mpq& delta, numeric_pair const& l, numeric_pair const& u) const { - SASSERT(l <= u); + lp_assert(l <= u); if (l.x < u.x && l.y > u.y) { mpq delta1 = (u.x - l.x) / (l.y - u.y); if (delta1 < delta) { delta = delta1; } } - SASSERT(l.x + delta * l.y <= u.x + delta * u.y); + lp_assert(l.x + delta * l.y <= u.x + delta * u.y); } @@ -781,8 +776,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; } @@ -791,8 +785,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]); } @@ -803,14 +797,38 @@ public: m_r_solver.init_column_row_non_zeroes(); } - linear_combination_iterator * get_column_iterator(unsigned j) { - if (settings().use_tableau()) { - return new iterator_on_column>(m_r_solver.m_A.m_columns[j], m_r_solver.m_A); - } else { - m_r_solver.solve_Bd(j); - return new iterator_on_indexed_vector(m_r_solver.m_ed); + 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_lower_bounds[j] == m_r_solver.m_upper_bounds[j]); + } + + 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::lower_bound); + return m_r_lower_bounds[j]; + } + + const impq & upper_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::upper_bound); + return m_r_upper_bounds[j]; + } + + + const bool column_is_bounded(unsigned j) const { + switch(m_column_types()[j]) { + case column_type::fixed: + case column_type::boxed: + return true; + default: + return false; } } - + + const vector& r_basis() const { return m_r_basis; } + const vector& r_nbasis() const { return m_r_nbasis; } }; } diff --git a/src/util/lp/lar_core_solver.hpp b/src/util/lp/lar_core_solver_def.h similarity index 79% rename from src/util/lp/lar_core_solver.hpp rename to src/util/lp/lar_core_solver_def.h index 62a5c7887..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,15 +66,15 @@ 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){} void lar_core_solver::init_costs(bool first_time) { - SASSERT(false); // should not be called - // SASSERT(this->m_x.size() >= this->m_n()); - // SASSERT(this->m_column_types.size() >= this->m_n()); + lp_assert(false); // should not be called + // lp_assert(this->m_x.size() >= this->m_n()); + // lp_assert(this->m_column_types.size() >= this->m_n()); // if (first_time) // this->m_costs.resize(this->m_n()); // X inf = this->m_infeasibility; @@ -84,7 +84,7 @@ void lar_core_solver::init_costs(bool first_time) { // if (!(first_time || inf >= this->m_infeasibility)) { // LP_OUT(this->m_settings, "iter = " << this->total_iterations() << std::endl); // LP_OUT(this->m_settings, "inf was " << T_to_string(inf) << " and now " << T_to_string(this->m_infeasibility) << std::endl); - // SASSERT(false); + // lp_assert(false); // } // if (inf == this->m_infeasibility) // this->m_iters_with_no_cost_growing++; @@ -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(); } @@ -135,7 +135,7 @@ void lar_core_solver::init_cost_for_column(unsigned j) { this->m_costs[j] = numeric_traits::zero(); break; default: - SASSERT(false); + lp_assert(false); break; }*/ } @@ -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; } @@ -168,30 +168,14 @@ int lar_core_solver::column_is_out_of_bounds(unsigned j) { return 0; break; }*/ - SASSERT(false); + lp_assert(false); return true; } void lar_core_solver::calculate_pivot_row(unsigned i) { - SASSERT(!m_r_solver.use_tableau()); - SASSERT(m_r_solver.m_pivot_row.is_OK()); - m_r_solver.m_pivot_row_of_B_1.clear(); - m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m()); - m_r_solver.m_pivot_row.clear(); - m_r_solver.m_pivot_row.resize(m_r_solver.m_n()); - if (m_r_solver.m_settings.use_tableau()) { - unsigned basis_j = m_r_solver.m_basis[i]; - for (auto & c : m_r_solver.m_A.m_rows[i]) { - if (c.m_j != basis_j) - m_r_solver.m_pivot_row.set_value(c.get_val(), c.m_j); - } - return; - } - - m_r_solver.calculate_pivot_row_of_B_1(i); - m_r_solver.calculate_pivot_row_when_pivot_row_of_B1_is_ready(i); + m_r_solver.calculate_pivot_row(i); } @@ -238,7 +222,7 @@ void lar_core_solver::calculate_pivot_row(unsigned i) { } void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { - SASSERT(m_r_solver.A_mult_x_is_off() == false); + CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); m_infeasible_linear_combination.clear(); @@ -271,34 +255,44 @@ void lar_core_solver::fill_not_improvable_zero_sum() { } } +unsigned lar_core_solver::get_number_of_non_ints() const { + unsigned n = 0; + for (auto & x : m_r_solver.m_x) { + if (x.is_int() == false) + n++; + } + return n; +} void lar_core_solver::solve() { - SASSERT(m_r_solver.non_basic_columns_are_set_correctly()); - SASSERT(m_r_solver.inf_set_is_correct()); - if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) { - m_r_solver.set_status(OPTIMAL); - return; - } + lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); + lp_assert(m_r_solver.inf_set_is_correct()); + TRACE("find_feas_stats", tout << "infeasibles = " << m_r_solver.m_inf_set.size() << ", int_infs = " << get_number_of_non_ints() << std::endl;); + if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) { + m_r_solver.set_status(lp_status::OPTIMAL); + return; + } ++settings().st().m_need_to_solve_inf; - SASSERT(!m_r_solver.A_mult_x_is_off()); - SASSERT((!settings().use_tableau()) || r_basis_is_OK()); + CASSERT("A_off", !m_r_solver.A_mult_x_is_off()); + lp_assert((!settings().use_tableau()) || r_basis_is_OK()); if (need_to_presolve_with_double_solver()) { prefix_d(); lar_solution_signature solution_signature; vector changes_of_basis = find_solution_signature_with_doubles(solution_signature); - if (m_d_solver.get_status() == TIME_EXHAUSTED) { - m_r_solver.set_status(TIME_EXHAUSTED); + if (m_d_solver.get_status() == lp_status::TIME_EXHAUSTED) { + m_r_solver.set_status(lp_status::TIME_EXHAUSTED); return; } if (settings().use_tableau()) solve_on_signature_tableau(solution_signature, changes_of_basis); else solve_on_signature(solution_signature, changes_of_basis); - SASSERT(!settings().use_tableau() || r_basis_is_OK()); + + lp_assert(!settings().use_tableau() || r_basis_is_OK()); } else { if (!settings().use_tableau()) { bool snapped = m_r_solver.snap_non_basic_x_to_bound(); - SASSERT(m_r_solver.non_basic_columns_are_set_correctly()); + lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); if (snapped) m_r_solver.solve_Ax_eq_b(); } @@ -306,16 +300,16 @@ void lar_core_solver::solve() { m_r_solver.find_feasible_solution(); else m_r_solver.solve(); - SASSERT(!settings().use_tableau() || r_basis_is_OK()); + lp_assert(!settings().use_tableau() || r_basis_is_OK()); } - if (m_r_solver.get_status() == INFEASIBLE) { + if (m_r_solver.get_status() == lp_status::INFEASIBLE) { fill_not_improvable_zero_sum(); - } else if (m_r_solver.get_status() != UNBOUNDED) { - m_r_solver.set_status(OPTIMAL); + } else if (m_r_solver.get_status() != lp_status::UNBOUNDED) { + m_r_solver.set_status(lp_status::OPTIMAL); } - SASSERT(r_basis_is_OK()); - SASSERT(m_r_solver.non_basic_columns_are_set_correctly()); - SASSERT(m_r_solver.inf_set_is_correct()); + lp_assert(r_basis_is_OK()); + lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); + lp_assert(m_r_solver.inf_set_is_correct()); } diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp new file mode 100644 index 000000000..8925a151a --- /dev/null +++ b/src/util/lp/lar_solver.cpp @@ -0,0 +1,2264 @@ +#include "util/lp/lar_solver.h" +/* + Copyright (c) 2017 Microsoft Corporation + Author: Nikolaj Bjorner, Lev Nachmanson +*/ + +namespace lp { + +unsigned lar_solver::constraint_count() const { + return m_constraints.size(); +} +const lar_base_constraint& lar_solver::get_constraint(unsigned ci) const { + return *(m_constraints[ci]); +} + +////////////////// methods //////////////////////////////// +static_matrix> & lar_solver::A_r() { return m_mpq_lar_core_solver.m_r_A;} +static_matrix> const & lar_solver::A_r() const { return m_mpq_lar_core_solver.m_r_A;} +static_matrix & lar_solver::A_d() { return m_mpq_lar_core_solver.m_d_A;} +static_matrix const & lar_solver::A_d() const { return m_mpq_lar_core_solver.m_d_A;} + +lp_settings & lar_solver::settings() { return m_settings;} + +lp_settings const & lar_solver::settings() const { return m_settings;} + +void clear() {lp_assert(false); // not implemented +} + + +lar_solver::lar_solver() : m_status(lp_status::OPTIMAL), + m_infeasible_column_index(-1), + m_terms_start_index(1000000), + m_mpq_lar_core_solver(m_settings, *this), + m_int_solver(nullptr) +{} + +void lar_solver::set_track_pivoted_rows(bool v) { + m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows = v? (& m_rows_with_changed_bounds) : nullptr; +} + +bool lar_solver::get_track_pivoted_rows() const { + return m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows != nullptr; +} + + +lar_solver::~lar_solver(){ + for (auto c : m_constraints) + delete c; + for (auto t : m_terms) + delete t; +} + +bool lar_solver::is_term(var_index j) const { + return j >= m_terms_start_index && j - m_terms_start_index < m_terms.size(); +} + +unsigned lar_solver::adjust_term_index(unsigned j) const { + lp_assert(is_term(j)); + return j - m_terms_start_index; +} + + +bool lar_solver::use_lu() const { return m_settings.simplex_strategy() == simplex_strategy_enum::lu; } + +bool lar_solver::sizes_are_correct() const { + lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_x.size()); + return true; +} + + +void lar_solver::print_implied_bound(const implied_bound& be, std::ostream & out) const { + out << "implied bound\n"; + unsigned v = be.m_j; + if (is_term(v)) { + out << "it is a term number " << be.m_j << std::endl; + print_term(*m_terms[be.m_j - m_terms_start_index], out); + } + else { + out << get_column_name(v); + } + out << " " << lconstraint_kind_string(be.kind()) << " " << be.m_bound << std::endl; + out << "end of implied bound" << std::endl; +} + +bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const { + std::unordered_map coeff_map; + auto rs_of_evidence = zero_of_type(); + unsigned n_of_G = 0, n_of_L = 0; + bool strict = false; + for (auto & it : explanation) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + const auto & constr = *m_constraints[con_ind]; + lconstraint_kind kind = coeff.is_pos() ? constr.m_kind : flip_kind(constr.m_kind); + register_in_map(coeff_map, constr, coeff); + if (kind == GT || kind == LT) + strict = true; + if (kind == GE || kind == GT) n_of_G++; + else if (kind == LE || kind == LT) n_of_L++; + rs_of_evidence += coeff*constr.m_right_side; + } + lp_assert(n_of_G == 0 || n_of_L == 0); + lconstraint_kind kind = n_of_G ? GE : (n_of_L ? LE : EQ); + if (strict) + kind = static_cast((static_cast(kind) / 2)); + + if (!is_term(be.m_j)) { + if (coeff_map.size() != 1) + return false; + auto it = coeff_map.find(be.m_j); + if (it == coeff_map.end()) return false; + mpq ratio = it->second; + if (ratio < zero_of_type()) { + kind = static_cast(-kind); + } + rs_of_evidence /= ratio; + } else { + const lar_term * t = m_terms[adjust_term_index(be.m_j)]; + const auto first_coeff = *t->m_coeffs.begin(); + unsigned j = first_coeff.first; + auto it = coeff_map.find(j); + if (it == coeff_map.end()) + return false; + mpq ratio = it->second / first_coeff.second; + for (auto & p : t->m_coeffs) { + it = coeff_map.find(p.first); + if (it == coeff_map.end()) + return false; + if (p.second * ratio != it->second) + return false; + } + if (ratio < zero_of_type()) { + kind = static_cast(-kind); + } + rs_of_evidence /= ratio; + rs_of_evidence += t->m_v * ratio; + } + + return kind == be.kind() && rs_of_evidence == be.m_bound; +} + + +void lar_solver::analyze_new_bounds_on_row( + unsigned row_index, + bound_propagator & bp) { + lp_assert(!use_tableau()); + unsigned j = m_mpq_lar_core_solver.m_r_basis[row_index]; // basis column for the row + bound_analyzer_on_row> + ra_pos(m_mpq_lar_core_solver.get_pivot_row(), + j, + zero_of_type>(), + row_index, + bp + ); + ra_pos.analyze(); +} + +void lar_solver::analyze_new_bounds_on_row_tableau( + unsigned row_index, + bound_propagator & bp ) { + + if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation) + return; + + lp_assert(use_tableau()); + bound_analyzer_on_row>::analyze_row(A_r().m_rows[row_index], + static_cast(-1), + zero_of_type>(), + row_index, + bp + ); +} + + +void lar_solver::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 lar_solver::calculate_implied_bounds_for_row(unsigned i, 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); + } +} + +unsigned lar_solver::adjust_column_index_to_term_index(unsigned j) const { + unsigned ext_var_or_term = m_var_register.local_to_external(j); + return ext_var_or_term < m_terms_start_index ? j : ext_var_or_term; +} + +void lar_solver::propagate_bounds_on_a_term(const lar_term& t, bound_propagator & bp, unsigned term_offset) { + lp_assert(false); // not implemented +} + + +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_lower_bound? 1: -1; + int j_sign = (ib.m_coeff_before_j_is_pos ? 1 :-1) * bound_sign; + unsigned bound_j = ib.m_j; + if (is_term(bound_j)) { + bound_j = m_var_register.external_to_local(bound_j); + } + for (auto const& r : A_r().m_rows[i]) { + unsigned j = r.m_j; + if (j == bound_j) continue; + mpq const& a = r.get_val(); + 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.lower_bound_witness(); + lp_assert(is_valid(witness)); + bp.consume(a, witness); + } + // lp_assert(implied_bound_is_correctly_explained(ib, explanation)); +} + + +bool lar_solver::term_is_used_as_row(unsigned term) const { + lp_assert(is_term(term)); + return m_var_register.external_is_used(term); +} + +void lar_solver::propagate_bounds_on_terms(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); + } +} + + +// goes over touched rows and tries to induce bounds +void lar_solver::propagate_bounds_for_touched_rows(bound_propagator & bp) { + if (!use_tableau()) + return; // todo: consider to remove the restriction + + 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 lar_solver::get_status() const { return m_status;} + +void lar_solver::set_status(lp_status s) {m_status = s;} + +lp_status lar_solver::find_feasible_solution() { + m_settings.st().m_make_feasible++; + if (A_r().column_count() > m_settings.st().m_max_cols) + m_settings.st().m_max_cols = A_r().column_count(); + if (A_r().row_count() > m_settings.st().m_max_rows) + m_settings.st().m_max_rows = A_r().row_count(); + if (strategy_is_undecided()) + decide_on_strategy_and_adjust_initial_state(); + + m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; + auto ret = solve(); + return ret; +} + +lp_status lar_solver::solve() { + if (m_status == lp_status::INFEASIBLE) { + return m_status; + } + solve_with_core_solver(); + if (m_status != lp_status::INFEASIBLE) { + if (m_settings.bound_propagation()) + detect_rows_with_changed_bounds(); + } + + m_columns_with_changed_bound.clear(); + return m_status; +} + +void lar_solver::fill_explanation_from_infeasible_column(vector> & evidence) const{ + + // this is the case when the lower bound is in conflict with the upper one + const ul_pair & ul = m_columns_to_ul_pairs[m_infeasible_column_index]; + evidence.push_back(std::make_pair(numeric_traits::one(), ul.upper_bound_witness())); + evidence.push_back(std::make_pair(-numeric_traits::one(), ul.lower_bound_witness())); +} + + +unsigned lar_solver::get_total_iterations() const { return m_mpq_lar_core_solver.m_r_solver.total_iterations(); } +vector lar_solver::get_list_of_all_var_indices() const { + vector ret; + for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_heading.size(); j++) + ret.push_back(j); + return ret; +} +void lar_solver::push() { + m_simplex_strategy = m_settings.simplex_strategy(); + m_simplex_strategy.push(); + m_columns_to_ul_pairs.push(); + m_infeasible_column_index.push(); + m_mpq_lar_core_solver.push(); + m_term_count = m_terms.size(); + m_term_count.push(); + m_constraint_count = m_constraints.size(); + m_constraint_count.push(); +} + +void lar_solver::clean_popped_elements(unsigned n, int_set& set) { + vector to_remove; + for (unsigned j: set.m_index) + if (j >= n) + to_remove.push_back(j); + for (unsigned j : to_remove) + set.erase(j); +} + +void lar_solver::shrink_inf_set_after_pop(unsigned n, int_set & set) { + clean_popped_elements(n, set); + set.resize(n); +} + + +void lar_solver::pop(unsigned k) { + TRACE("arith_int", tout << "pop" << std::endl;); + TRACE("lar_solver", tout << "k = " << k << std::endl;); + + m_infeasible_column_index.pop(k); + unsigned n = m_columns_to_ul_pairs.peek_size(k); + m_var_register.shrink(n); + TRACE("arith_int", tout << "pop" << std::endl;); + if (m_settings.use_tableau()) { + pop_tableau(); + } + lp_assert(A_r().column_count() == n); + m_columns_to_ul_pairs.pop(k); + + m_mpq_lar_core_solver.pop(k); + clean_popped_elements(n, m_columns_with_changed_bound); + unsigned m = A_r().row_count(); + clean_popped_elements(m, m_rows_with_changed_bounds); + clean_inf_set_of_r_solver_after_pop(); + lp_assert(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || + (!use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + + + lp_assert(ax_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++) { +#if Z3DEBUG_CHECK_UNIQUE_TERMS + m_set_of_terms.erase(m_terms[i]); +#endif + delete m_terms[i]; + } + m_terms.resize(m_term_count); + m_simplex_strategy.pop(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; +} + +vector lar_solver::get_all_constraint_indices() const { + vector ret; + constraint_index i = 0; + while ( i < m_constraints.size()) + ret.push_back(i++); + return ret; +} + +bool lar_solver::maximize_term_on_tableau(const lar_term & term, + impq &term_max) { + if (settings().simplex_strategy() == simplex_strategy_enum::undecided) + decide_on_strategy_and_adjust_initial_state(); + + m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::FEASIBLE); + m_mpq_lar_core_solver.solve(); + if (m_mpq_lar_core_solver.m_r_solver.get_status() == lp_status::UNBOUNDED) + return false; + + term_max = term.apply(m_mpq_lar_core_solver.m_r_x); + + return true; +} + +bool lar_solver::costs_are_zeros_for_r_solver() const { + for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_costs.size(); j++) { + lp_assert(is_zero(m_mpq_lar_core_solver.m_r_solver.m_costs[j])); + } + return true; +} +bool lar_solver::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++) { + lp_assert(is_zero(m_mpq_lar_core_solver.m_r_solver.m_d[j])); + } + return true; +} + +void lar_solver::set_costs_to_zero(const lar_term& 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 + lp_assert(jset.m_index.size()==0); + + for (const auto & p : term) { + unsigned j = p.var(); + 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(); + + lp_assert(reduced_costs_are_zeroes_for_r_solver()); + lp_assert(costs_are_zeros_for_r_solver()); +} + +void lar_solver::prepare_costs_for_r_solver(const lar_term & term) { + + auto & rslv = m_mpq_lar_core_solver.m_r_solver; + rslv.m_using_infeas_costs = false; + lp_assert(costs_are_zeros_for_r_solver()); + lp_assert(reduced_costs_are_zeroes_for_r_solver()); + rslv.m_costs.resize(A_r().column_count(), zero_of_type()); + for (const auto & p : term) { + unsigned j = p.var(); + rslv.m_costs[j] = p.coeff(); + if (rslv.m_basis_heading[j] < 0) + rslv.m_d[j] += p.coeff(); + else + rslv.update_reduced_cost_for_basic_column_cost_change(- p.coeff(), j); + } + lp_assert(rslv.reduced_costs_are_correct_tableau()); +} + +bool lar_solver::maximize_term_on_corrected_r_solver(lar_term & 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(lp_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(lp_status::OPTIMAL); + return ret; + } + + case simplex_strategy_enum::lu: + lp_assert(false); // not implemented + return false; + default: + lp_unreachable(); // wrong mode + } + return false; +} + + +bool lar_solver::remove_from_basis(unsigned j) { + return m_mpq_lar_core_solver.m_r_solver.remove_from_basis(j); +} + +lar_term lar_solver::get_term_to_maximize(unsigned ext_j) const { + unsigned local_j; + if (m_var_register.external_is_used(ext_j, local_j)) { + lar_term r; + r. add_monomial(one_of_type(), local_j); + return r; + } + if (!is_term(ext_j) || adjust_term_index(ext_j) >= m_terms.size()) + return lar_term(); // return an empty term + return get_term(ext_j); +} + +lp_status lar_solver::maximize_term(unsigned ext_j, + impq &term_max) { + bool was_feasible = m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis(); + impq prev_value; + lar_term term = get_term_to_maximize(ext_j); + if (term.is_empty()) { + return lp_status::UNBOUNDED; + } + + auto backup = m_mpq_lar_core_solver.m_r_x; + if (was_feasible) { + prev_value = term.apply(m_mpq_lar_core_solver.m_r_x); + } + + m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = false; + if (!maximize_term_on_corrected_r_solver(term, term_max)) { + m_mpq_lar_core_solver.m_r_x = backup; + return lp_status::UNBOUNDED; + } + + impq opt_val = term_max; + + bool change = false; + for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_x.size(); j++) { + if (!column_is_int(j)) + continue; + if (column_value_is_integer(j)) + continue; + if (m_int_solver->is_base(j)) { + if (!remove_from_basis(j)) // consider a special version of remove_from_basis that would not remove inf_int columns + return lp_status::FEASIBLE; // it should not happen + } + m_int_solver->patch_nbasic_column(j, false); + if (!column_value_is_integer(j)) + return lp_status::FEASIBLE; + change = true; + } + if (change) { + term_max = term.apply(m_mpq_lar_core_solver.m_r_x); + } + if (was_feasible && term_max < prev_value) { + term_max = prev_value; + m_mpq_lar_core_solver.m_r_x = backup; + } + return term_max == opt_val? lp_status::OPTIMAL :lp_status::FEASIBLE; +} + + + +const lar_term & lar_solver::get_term(unsigned j) const { + lp_assert(j >= m_terms_start_index); + return *m_terms[j - m_terms_start_index]; +} + +void lar_solver::pop_core_solver_params() { + pop_core_solver_params(1); +} + +void lar_solver::pop_core_solver_params(unsigned k) { + A_r().pop(k); + A_d().pop(k); +} + + +void lar_solver::set_upper_bound_witness(var_index j, constraint_index ci) { + ul_pair ul = m_columns_to_ul_pairs[j]; + ul.upper_bound_witness() = ci; + m_columns_to_ul_pairs[j] = ul; +} + +void lar_solver::set_lower_bound_witness(var_index j, constraint_index ci) { + ul_pair ul = m_columns_to_ul_pairs[j]; + ul.lower_bound_witness() = ci; + m_columns_to_ul_pairs[j] = ul; +} + +void lar_solver::register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j) { + auto it = coeffs.find(j); + if (it == coeffs.end()) { + coeffs[j] = a; + } else { + it->second += a; + } +} + + +void lar_solver::substitute_terms_in_linear_expression(const vector>& left_side_with_terms, + vector> &left_side, mpq & free_coeff) const { + std::unordered_map coeffs; + for (auto & t : left_side_with_terms) { + unsigned j = t.second; + if (!is_term(j)) { + register_monoid_in_map(coeffs, t.first, j); + } else { + const lar_term & term = * m_terms[adjust_term_index(t.second)]; + for (auto & p : term.coeffs()){ + register_monoid_in_map(coeffs, t.first * p.second , p.first); + } + free_coeff += t.first * term.m_v; + } + } + + for (auto & p : coeffs) + if (!is_zero(p.second)) + left_side.push_back(std::make_pair(p.second, p.first)); +} + + +void lar_solver::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(); + lp_assert(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 lar_solver::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); +} + +bool lar_solver::use_tableau() const { return m_settings.use_tableau(); } + +bool lar_solver::use_tableau_costs() const { + return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; +} + +void lar_solver::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; + } + + 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 lar_solver::adjust_x_of_column(unsigned j) { + lp_assert(false); +} + +bool lar_solver::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 lar_solver::ax_is_correct() const { + for (unsigned i = 0; i < A_r().row_count(); i++) { + if (!row_is_correct(i)) + return false; + } + return true; +} + +bool lar_solver::tableau_with_costs() const { + return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; +} + +bool lar_solver::costs_are_used() const { + return m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows; +} + +void lar_solver::change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta) { + if (use_tableau()) { + for (const auto & c : A_r().m_columns[j]) { + unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.m_i]; + if (tableau_with_costs()) { + m_basic_columns_with_changed_cost.insert(bj); + } + m_mpq_lar_core_solver.m_r_solver.update_x_with_delta_and_track_feasibility(bj, - A_r().get_val(c) * delta); + TRACE("change_x_del", + tout << "changed basis column " << bj << ", it is " << + ( m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj)? "feas":"inf") << std::endl;); + + } + } else { + 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); + for (unsigned i : m_column_buffer.m_index) { + unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; + m_mpq_lar_core_solver.m_r_solver.update_x_with_delta_and_track_feasibility(bj, -m_column_buffer[i] * delta); + } + } +} + +void lar_solver::update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { + if (costs_are_used()) { + bool was_infeas = m_mpq_lar_core_solver.m_r_solver.m_inf_set.contains(j); + m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); + if (was_infeas != m_mpq_lar_core_solver.m_r_solver.m_inf_set.contains(j)) + m_basic_columns_with_changed_cost.insert(j); + } else { + m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); + } + } else { + numeric_pair delta; + if (m_mpq_lar_core_solver.m_r_solver.make_column_feasible(j, delta)) + change_basic_columns_dependend_on_a_given_nb_column(j, delta); + } +} + + +void lar_solver::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; + } + + 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 lar_solver::detect_rows_with_changed_bounds() { + for (auto j : m_columns_with_changed_bound.m_index) + detect_rows_with_changed_bounds_for_column(j); +} + +void lar_solver::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 lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { + 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); + lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + } +} + + +void lar_solver::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()); + lp_assert(m_status != lp_status::OPTIMAL || all_constraints_hold()); +} + + +numeric_pair lar_solver::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 lar_solver::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) { + lp_assert(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; +} + +template +void lar_solver::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 lar_solver::x_is_correct() const { + if (m_mpq_lar_core_solver.m_r_x.size() != A_r().column_count()) { + 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()) { + return false; + } + } + return true;; + +} + +bool lar_solver::var_is_registered(var_index vj) const { + if (vj >= m_terms_start_index) { + if (vj - m_terms_start_index >= m_terms.size()) + return false; + } else if ( vj >= A_r().column_count()) { + return false; + } + return true; +} + +unsigned lar_solver::constraint_stack_size() const { + return m_constraint_count.stack_size(); +} + +void lar_solver::fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls) { + lp_assert(A.row_count() > 0); + lp_assert(A.column_count() > 0); + unsigned last_row = A.row_count() - 1; + lp_assert(A.m_rows[last_row].size() == 0); + for (auto & t : ls->m_coeffs) { + lp_assert(!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)); +} + +template +void lar_solver::create_matrix_A(static_matrix & matr) { + lp_assert(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); + */ +} + +template +void lar_solver::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())); + } + } +} + + +bool lar_solver::try_to_set_fixed(column_info & ci) { + 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; + } + return false; +} + +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_lower_bound() == ci.get_upper_bound()) + ret = column_type::fixed; + } + return ret; +} + +std::string lar_solver::get_column_name(unsigned j) const { + if (j >= m_terms_start_index) + return std::string("_t") + T_to_string(j); + if (j >= m_var_register.size()) + return std::string("_s") + T_to_string(j); + + return std::string("v") + T_to_string(m_var_register.local_to_external(j)); +} + +bool lar_solver::all_constrained_variables_are_registered(const vector>& left_side) { + for (auto it : left_side) { + if (! var_is_registered(it.second)) + return false; + } + return true; +} + +bool lar_solver::all_constraints_hold() const { + if (m_settings.get_cancel_flag()) + return true; + std::unordered_map var_map; + get_model_do_not_care_about_diff_vars(var_map); + + for (unsigned i = 0; i < m_constraints.size(); i++) { + if (!constraint_holds(*m_constraints[i], var_map)) { + return false; + } + } + return true; +} + +bool lar_solver::constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const { + return true; + 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: + lp_unreachable(); + } + return false; // it is unreachable +} + +bool lar_solver::the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const { + unsigned n_of_G = 0, n_of_L = 0; + bool strict = false; + for (auto & it : evidence) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + lconstraint_kind kind = coeff.is_pos() ? + m_constraints[con_ind]->m_kind : + flip_kind(m_constraints[con_ind]->m_kind); + if (kind == GT || kind == LT) + strict = true; + if (kind == GE || kind == GT) n_of_G++; + else if (kind == LE || kind == LT) n_of_L++; + } + the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ); + if (strict) + the_kind_of_sum = static_cast((static_cast(the_kind_of_sum) / 2)); + + return n_of_G == 0 || n_of_L == 0; +} + +void lar_solver::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 lar_solver::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; + lp_assert(con_ind < m_constraints.size()); + register_in_map(coeff_map, *m_constraints[con_ind], coeff); + } + + if (!coeff_map.empty()) { + return false; + } + + return true; +} + +bool lar_solver::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; + lp_assert(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); +} + +bool lar_solver::explanation_is_correct(const vector>& explanation) const { +#ifdef Z3DEBUG + lconstraint_kind kind; + lp_assert(the_relations_are_of_same_type(explanation, kind)); + lp_assert(the_left_sides_sum_to_zero(explanation)); + mpq rs = sum_of_right_sides_of_explanation(explanation); + switch (kind) { + case LE: lp_assert(rs < zero_of_type()); + break; + case LT: lp_assert(rs <= zero_of_type()); + break; + case GE: lp_assert(rs > zero_of_type()); + break; + case GT: lp_assert(rs >= zero_of_type()); + break; + case EQ: lp_assert(rs != zero_of_type()); + break; + default: + lp_assert(false); + return false; + } +#endif + return true; +} + +bool lar_solver::inf_explanation_is_correct() const { +#ifdef Z3DEBUG + vector> explanation; + get_infeasibility_explanation(explanation); + return explanation_is_correct(explanation); +#endif + return true; +} + +mpq lar_solver::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; + lp_assert(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 lar_solver::has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const { + + if (var >= m_columns_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_columns_to_ul_pairs[var]; + ci = ul.lower_bound_witness(); + if (ci != static_cast(-1)) { + auto& p = m_mpq_lar_core_solver.m_r_lower_bounds()[var]; + value = p.x; + is_strict = p.y.is_pos(); + return true; + } + else { + return false; + } +} + +bool lar_solver::has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const { + + if (var >= m_columns_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_columns_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; + } +} + +void lar_solver::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); + lp_assert(explanation_is_correct(explanation)); +} + + + +void lar_solver::get_infeasibility_explanation_for_inf_sign( + vector> & explanation, + const vector> & inf_row, + int inf_sign) const { + + for (auto & it : inf_row) { + mpq coeff = it.first; + unsigned j = it.second; + + 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.lower_bound_witness(); + lp_assert(bound_constr_i < m_constraints.size()); + explanation.push_back(std::make_pair(coeff, bound_constr_i)); + } +} + +void lar_solver::get_model(std::unordered_map & variable_values) const { + lp_assert(m_status == lp_status::OPTIMAL); + mpq delta = mpq(1, 2); // start from 0.5 to have less clashes + 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; + delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(delta); + 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()); +} + +void lar_solver::get_model_do_not_care_about_diff_vars(std::unordered_map & variable_values) const { + mpq delta = mpq(1); + delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(delta); + for (unsigned i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) { + const impq & rp = m_mpq_lar_core_solver.m_r_x[i]; + variable_values[i] = rp.x + delta * rp.y; + } +} + + +std::string lar_solver::get_variable_name(var_index vi) const { + return get_column_name(vi); +} + +// ********** print region start +void lar_solver::print_constraint(constraint_index ci, std::ostream & out) const { + if (ci >= m_constraints.size()) { + out << "constraint " << T_to_string(ci) << " is not found"; + out << std::endl; + return; + } + + print_constraint(m_constraints[ci], out); +} + +void lar_solver::print_constraints(std::ostream& out) const { + out << "number of constraints = " << m_constraints.size() << std::endl; + for (auto c : m_constraints) { + print_constraint(c, out); + } +} + +void lar_solver::print_terms(std::ostream& out) const { + for (auto it : m_terms) { + print_term(*it, out); + out << "\n"; + } +} + +void lar_solver::print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const { + print_linear_combination_of_column_indices(c->get_left_side_coefficients(), out); + mpq free_coeff = c->get_free_coeff_of_left_side(); + if (!is_zero(free_coeff)) + out << " + " << free_coeff; + +} + +void lar_solver::print_term(lar_term const& term, std::ostream & out) const { + if (!numeric_traits::is_zero(term.m_v)) { + out << term.m_v << " + "; + } + bool first = true; + for (const auto p : term) { + mpq val = p.coeff(); + if (first) { + first = false; + } else { + if (is_pos(val)) { + out << " + "; + } else { + out << " - "; + val = -val; + } + } + if (val == -numeric_traits::one()) + out << " - "; + else if (val != numeric_traits::one()) + out << T_to_string(val); + out << this->get_column_name(p.var()); + } + +} + +void lar_solver::print_term_as_indices(lar_term const& term, std::ostream & out) const { + if (!numeric_traits::is_zero(term.m_v)) { + out << term.m_v << " + "; + } + print_linear_combination_of_column_indices_only(term.coeffs_as_vector(), out); +} + +mpq lar_solver::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); + lp_assert(vi != var_map.end()); + ret += it.first * vi->second; + } + return ret; +} + +void lar_solver::print_constraint(const lar_base_constraint * c, std::ostream & out) const { + print_left_side_of_constraint(c, out); + out << " " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side << std::endl; +} + +void lar_solver::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 lar_solver::random_update(unsigned sz, var_index const * vars) { + vector column_list; + fill_var_set_for_random_update(sz, vars, column_list); + random_updater ru(*this, column_list); + ru.update(); +} + + +void lar_solver::pivot_fixed_vars_from_basis() { + m_mpq_lar_core_solver.m_r_solver.pivot_fixed_vars_from_basis(); +} + +void lar_solver::pop() { + pop(1); +} + +bool lar_solver::column_represents_row_in_tableau(unsigned j) { + return m_columns_to_ul_pairs()[j].m_i != static_cast(-1); +} + +void lar_solver::make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j) { + // i, j - is the indices of the bottom-right element of the tableau + lp_assert(A_r().row_count() == i + 1 && A_r().column_count() == j + 1); + auto & last_column = A_r().m_columns[j]; + int non_zero_column_cell_index = -1; + for (unsigned k = last_column.size(); k-- > 0;){ + auto & cc = last_column[k]; + if (cc.m_i == i) + return; + non_zero_column_cell_index = k; + } + + lp_assert(non_zero_column_cell_index != -1); + lp_assert(static_cast(non_zero_column_cell_index) != i); + m_mpq_lar_core_solver.m_r_solver.transpose_rows_tableau(last_column[non_zero_column_cell_index].m_i, i); +} + +void lar_solver::remove_last_row_and_column_from_tableau(unsigned j) { + lp_assert(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); + } + lp_assert(last_row.size() == 0); + lp_assert(A_r().m_columns[j].size() == 0); + A_r().m_rows.pop_back(); + A_r().m_columns.pop_back(); + slv.m_b.pop_back(); +} + +void lar_solver::remove_last_column_from_A() { + // the last column has to be empty + lp_assert(A_r().m_columns.back().size() == 0); + A_r().m_columns.pop_back(); +} + +void lar_solver::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; + lp_assert(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; + lp_assert(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(); + lp_assert(rslv.m_basis.size() == A_r().row_count()); + lp_assert(rslv.basis_heading_is_correct()); +} + +void lar_solver::remove_last_column_from_tableau() { + auto& rslv = m_mpq_lar_core_solver.m_r_solver; + unsigned j = A_r().column_count() - 1; + lp_assert(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_A(); + } + rslv.m_x.pop_back(); + rslv.m_d.pop_back(); + rslv.m_costs.pop_back(); + + remove_last_column_from_basis_tableau(j); + lp_assert(m_mpq_lar_core_solver.r_basis_is_OK()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); +} + +void lar_solver::pop_tableau() { + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); + + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); + // We remove last variables starting from m_column_names.size() to m_vec_of_canonic_left_sides.size(). + // At this moment m_column_names is already popped + unsigned size = m_var_register.size(); + while (A_r().column_count() > size) + remove_last_column_from_tableau(); + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); +} + +void lar_solver::clean_inf_set_of_r_solver_after_pop() { + vector became_feas; + clean_popped_elements(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_columns_dependend_on_a_given_nb_column(j, delta); + } + became_feas.push_back(j); + } + + for (unsigned j : became_feas) { + lp_assert(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) { + lp_assert(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); + lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + } +} + +bool lar_solver::model_is_int_feasible() const { + unsigned n = A_r().column_count(); + for (unsigned j = 0; j < n; j++) { + if (column_is_int(j) && !column_value_is_integer(j)) + return false; + } + return true; +} + +bool lar_solver::term_is_int(const lar_term * t) const { + for (auto const & p : t->m_coeffs) + if (! (column_is_int(p.first) && p.second.is_int())) + return false; + return t->m_v.is_int(); +} + +bool lar_solver::var_is_int(var_index v) const { + if (is_term(v)) { + lar_term const& t = get_term(v); + return term_is_int(&t); + } + else { + return column_is_int(v); + } +} + +bool lar_solver::column_is_int(unsigned j) const { + return m_var_register.local_is_int(j); +} + +bool lar_solver::column_is_fixed(unsigned j) const { + return m_mpq_lar_core_solver.column_is_fixed(j); +} + + +bool lar_solver::ext_var_is_int(var_index ext_var) const { + return m_var_register.external_is_int(ext_var); +} + +// below is the initialization functionality of lar_solver + +bool lar_solver::strategy_is_undecided() const { + return m_settings.simplex_strategy() == simplex_strategy_enum::undecided; +} + +var_index lar_solver::add_var(unsigned ext_j, bool is_int) { + TRACE("add_var", tout << "adding var " << ext_j << (is_int? " int" : " nonint") << std::endl;); + var_index local_j; + lp_assert(ext_j < m_terms_start_index); + if (m_var_register.external_is_used(ext_j, local_j)) + return local_j; + lp_assert(m_columns_to_ul_pairs.size() == A_r().column_count()); + local_j = A_r().column_count(); + m_columns_to_ul_pairs.push_back(ul_pair(static_cast(-1))); + add_non_basic_var_to_core_fields(ext_j, is_int); + lp_assert(sizes_are_correct()); + return local_j; +} + +void lar_solver::register_new_ext_var_index(unsigned ext_v, bool is_int) { + lp_assert(!m_var_register.external_is_used(ext_v)); + m_var_register.add_var(ext_v, is_int); +} + +void lar_solver::add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int) { + register_new_ext_var_index(ext_j, is_int); + m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); + m_columns_with_changed_bound.increase_size_by_one(); + add_new_var_to_core_fields_for_mpq(false); + if (use_lu()) + add_new_var_to_core_fields_for_doubles(false); +} + +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_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_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) { + A_d().add_row(); + m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); + m_mpq_lar_core_solver.m_d_basis.push_back(j); + } + else { + m_mpq_lar_core_solver.m_d_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); + m_mpq_lar_core_solver.m_d_nbasis.push_back(j); + } +} + +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_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_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); + m_mpq_lar_core_solver.m_r_solver.m_d.resize(j + 1); + lp_assert(m_mpq_lar_core_solver.m_r_heading.size() == j); // as A().column_count() on the entry to the method + if (register_in_basis) { + A_r().add_row(); + m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size()); + m_mpq_lar_core_solver.m_r_basis.push_back(j); + if (m_settings.bound_propagation()) + m_rows_with_changed_bounds.insert(A_r().row_count() - 1); + } + else { + m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1); + m_mpq_lar_core_solver.m_r_nbasis.push_back(j); + } +} + + +var_index lar_solver::add_term_undecided(const vector> & coeffs, + const mpq &m_v) { + push_and_register_term(new lar_term(coeffs, m_v)); + return m_terms_start_index + m_terms.size() - 1; +} + +#if Z3DEBUG_CHECK_UNIQUE_TERMS +bool lar_solver::term_coeffs_are_ok(const vector> & coeffs, const mpq& v) { + if (coeffs.empty()) { + return is_zero(v); + } + + for (const auto & p : coeffs) { + if (column_is_real(p.second)) + return true; + } + + mpq g; + bool g_is_set = false; + for (const auto & p : coeffs) { + if (!p.first.is_int()) { + return false; + } + if (!g_is_set) { + g_is_set = true; + g = p.first; + } else { + g = gcd(g, p.first); + } + } + if (g == one_of_type()) + return true; + + return false; +} +#endif +void lar_solver::push_and_register_term(lar_term* t) { +#if Z3DEBUG_CHECK_UNIQUE_TERMS + lp_assert(m_set_of_terms.find(t) == m_set_of_terms.end()); + m_set_of_terms.insert(t); +#endif + m_terms.push_back(t); +} + +// terms +var_index lar_solver::add_term(const vector> & coeffs, + const mpq &m_v) { + if (strategy_is_undecided()) + return add_term_undecided(coeffs, m_v); + + push_and_register_term(new lar_term(coeffs, m_v)); + unsigned adjusted_term_index = m_terms.size() - 1; + var_index ret = m_terms_start_index + adjusted_term_index; + if (use_tableau() && !coeffs.empty()) { + add_row_from_term_no_constraint(m_terms.back(), ret); + if (m_settings.bound_propagation()) + m_rows_with_changed_bounds.insert(A_r().row_count() - 1); + } + lp_assert(m_var_register.size() == A_r().column_count()); + return ret; +} + +void lar_solver::add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) { + TRACE("dump_terms", print_term(*term, tout); tout << std::endl;); + register_new_ext_var_index(term_ext_index, term_is_int(term)); + // j will be a new variable + unsigned j = A_r().column_count(); + ul_pair ul(j); + m_columns_to_ul_pairs.push_back(ul); + add_basic_var_to_core_fields(); + if (use_tableau()) { + A_r().fill_last_row_with_pivoting(*term, + j, + m_mpq_lar_core_solver.m_r_solver.m_basis_heading); + m_mpq_lar_core_solver.m_r_solver.m_b.resize(A_r().column_count(), zero_of_type()); + } + else { + fill_last_row_of_A_r(A_r(), term); + } + m_mpq_lar_core_solver.m_r_solver.update_x_and_call_tracker(j, get_basic_var_value_from_row_directly(A_r().row_count() - 1)); + if (use_lu()) + fill_last_row_of_A_d(A_d(), term); +} + +void lar_solver::add_basic_var_to_core_fields() { + bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver(); + lp_assert(!use_lu || A_r().column_count() == A_d().column_count()); + m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); + m_columns_with_changed_bound.increase_size_by_one(); + m_rows_with_changed_bounds.increase_size_by_one(); + add_new_var_to_core_fields_for_mpq(true); + if (use_lu) + add_new_var_to_core_fields_for_doubles(true); +} + +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(); +} + +constraint_index lar_solver::add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) { + 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_for_integer_column(j, right_side)); + auto vc = new lar_var_constraint(j, kind, right_side); + m_constraints.push_back(vc); + update_column_type_and_bound(j, kind, right_side, ci); + } + else { + add_var_bound_on_constraint_for_term(j, kind, right_side, ci); + } + lp_assert(sizes_are_correct()); + return ci; +} + +void lar_solver::update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index) { + switch (m_mpq_lar_core_solver.m_column_types[j]) { + case column_type::free_column: + update_free_column_type_and_bound(j, kind, right_side, constr_index); + break; + case column_type::boxed: + update_boxed_column_type_and_bound(j, kind, right_side, constr_index); + break; + 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); + break; + case column_type::fixed: + update_fixed_column_type_and_bound(j, kind, right_side, constr_index); + break; + default: + lp_assert(false); // cannot be here + } +} + +void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(is_term(j)); + unsigned adjusted_term_index = adjust_term_index(j); + // lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int()); + unsigned term_j; + if (m_var_register.external_is_used(j, term_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)); + update_column_type_and_bound(term_j, kind, rs, ci); + } + else { + add_constraint_from_term_and_create_new_column_row(j, m_terms[adjusted_term_index], kind, right_side); + } +} + +constraint_index lar_solver::add_constraint(const vector>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm) { + vector> left_side; + mpq rs = -right_side_parm; + substitute_terms_in_linear_expression(left_side_with_terms, left_side, rs); + unsigned term_index = add_term(left_side, zero_of_type()); + constraint_index ci = m_constraints.size(); + add_var_bound_on_constraint_for_term(term_index, kind_par, -rs, ci); + return ci; +} + +void lar_solver::add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, + lconstraint_kind kind, const mpq & right_side) { + + add_row_from_term_no_constraint(term, term_j); + unsigned j = A_r().column_count() - 1; + update_column_type_and_bound(j, kind, right_side - term->m_v, m_constraints.size()); + m_constraints.push_back(new lar_term_constraint(term, kind, right_side)); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); +} + +void lar_solver::decide_on_strategy_and_adjust_initial_state() { + lp_assert(strategy_is_undecided()); + if (m_columns_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) { + m_settings.simplex_strategy() = simplex_strategy_enum::lu; + } + else { + m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows; // todo: when to switch to tableau_costs? + } + adjust_initial_state(); +} + +void lar_solver::adjust_initial_state() { + switch (m_settings.simplex_strategy()) { + case simplex_strategy_enum::lu: + adjust_initial_state_for_lu(); + break; + case simplex_strategy_enum::tableau_rows: + adjust_initial_state_for_tableau_rows(); + break; + case simplex_strategy_enum::tableau_costs: + lp_assert(false); // not implemented + case simplex_strategy_enum::undecided: + adjust_initial_state_for_tableau_rows(); + break; + } +} + +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_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; + + /* + 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_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_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) { + A_d().add_row(); + m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); + m_mpq_lar_core_solver.m_d_basis.push_back(j); + }else { + m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); + m_mpq_lar_core_solver.m_d_nbasis.push_back(j); + }*/ +} + +void lar_solver::adjust_initial_state_for_tableau_rows() { + for (unsigned i = 0; i < m_terms.size(); i++) { + if (m_var_register.external_is_used(i + m_terms_start_index)) + continue; + add_row_from_term_no_constraint(m_terms[i], i + m_terms_start_index); + } +} + +// this fills the last row of A_d and sets the basis column: -1 in the last column of the row +void lar_solver::fill_last_row_of_A_d(static_matrix & A, const lar_term* ls) { + lp_assert(A.row_count() > 0); + lp_assert(A.column_count() > 0); + unsigned last_row = A.row_count() - 1; + lp_assert(A.m_rows[last_row].empty()); + + for (auto & t : ls->m_coeffs) { + lp_assert(!is_zero(t.second)); + var_index j = t.first; + A.set(last_row, j, -t.second.get_double()); + } + + unsigned basis_j = A.column_count() - 1; + A.set(last_row, basis_j, -1); +} + +void lar_solver::update_free_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_ind) { + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + m_mpq_lar_core_solver.m_column_types[j] = column_type::upper_bound; + lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound); + lp_assert(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j); + { + auto up = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + } + set_upper_bound_witness(j, constr_ind); + break; + case GT: + y_of_bound = 1; + case GE: + 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_lower_bounds[j] = low; + } + 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_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_lower_bound_witness(j, constr_ind); + break; + + default: + lp_unreachable(); + + } + m_columns_with_changed_bound.insert(j); +} + +void lar_solver::update_upper_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::upper_bound); + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + { + auto up = numeric_pair(right_side, y_of_bound); + if (up < m_mpq_lar_core_solver.m_r_upper_bounds()[j]) { + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_columns_with_changed_bound.insert(j); + } + } + break; + case GT: + y_of_bound = 1; + case GE: + 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_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_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; + } + } + break; + case EQ: + { + 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_lower_bound_witness(j, ci); + m_infeasible_column_index = j; + } + else { + 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_lower_bound_witness(j, ci); + set_upper_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + } + break; + } + break; + + default: + lp_unreachable(); + + } +} + +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_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j])); + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + { + auto up = numeric_pair(right_side, y_of_bound); + if (up < m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_columns_with_changed_bound.insert(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_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; + } + } + break; + case GT: + y_of_bound = 1; + case GE: + { + auto low = numeric_pair(right_side, y_of_bound); + 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_lower_bound_witness(j, ci); + } + if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + } + else if (low == m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + } + } + break; + case EQ: + { + auto v = numeric_pair(right_side, zero_of_type()); + 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 if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_lower_bound_witness(j, ci); + } + else { + 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); + } + + break; + } + + default: + lp_unreachable(); + + } +} +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: + y_of_bound = -1; + case LE: + { + auto up = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_columns_with_changed_bound.insert(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_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; + } + } + break; + case GT: + y_of_bound = 1; + case GE: + { + auto low = numeric_pair(right_side, y_of_bound); + 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_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_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_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); + break; + } + + default: + lp_unreachable(); + + } +} + +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_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_lower_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + break; + case LE: + { + 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); + } + } + break; + case GT: + { + if (v >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_lower_bound_witness(j, ci); + } + } + break; + case GE: + { + if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_lower_bound_witness(j, ci); + } + } + break; + case EQ: + { + 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 if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_lower_bound_witness(j, ci); + } + break; + } + + default: + lp_unreachable(); + + } +} + +bool lar_solver::column_corresponds_to_term(unsigned j) const { + return m_var_register.local_to_external(j) >= m_terms_start_index; +} + +var_index lar_solver::to_column(unsigned ext_j) const { + return m_var_register.external_to_local(ext_j); +} + +bool lar_solver::tighten_term_bounds_by_delta(unsigned term_index, const impq& delta) { + unsigned tj = term_index + m_terms_start_index; + unsigned j; + if (m_var_register.external_is_used(tj, j) == false) + return true; // the term is not a column so it has no bounds + auto & slv = m_mpq_lar_core_solver.m_r_solver; + TRACE("cube", tout << "delta = " << delta << std::endl; + m_int_solver->display_column(tout, j); ); + if (slv.column_has_upper_bound(j) && slv.column_has_lower_bound(j)) { + if (slv.m_upper_bounds[j] - delta < slv.m_lower_bounds[j] + delta) { + TRACE("cube", tout << "cannot tighten, delta = " << delta;); + return false; + } + } + TRACE("cube", tout << "can tighten";); + if (slv.column_has_upper_bound(j)) { + if (!is_zero(delta.y)) + add_var_bound(tj, lconstraint_kind::LT, slv.m_upper_bounds[j].x - delta.x); + else + add_var_bound(tj, lconstraint_kind::LE, slv.m_upper_bounds[j].x - delta.x); + } + if (slv.column_has_lower_bound(j)) { + if (!is_zero(delta.y)) + add_var_bound(tj, lconstraint_kind::GT, slv.m_lower_bounds[j].x + delta.x); + else + add_var_bound(tj, lconstraint_kind::GE, slv.m_lower_bounds[j].x + delta.x); + } + return true; +} + +void lar_solver::update_delta_for_terms(const impq & delta, unsigned j, const vector& terms_of_var) { + for (unsigned i : terms_of_var) { + lar_term & t = *m_terms[i]; + auto it = t.m_coeffs.find(j); + unsigned tj = to_column(i + m_terms_start_index); + TRACE("cube", + tout << "t.apply = " << t.apply(m_mpq_lar_core_solver.m_r_x) << ", m_mpq_lar_core_solver.m_r_x[tj]= " << m_mpq_lar_core_solver.m_r_x[tj];); + TRACE("cube", print_term_as_indices(t, tout); + tout << ", it->second = " << it->second; + tout << ", tj = " << tj << ", "; + m_int_solver->display_column(tout, tj); + ); + + m_mpq_lar_core_solver.m_r_x[tj] += it->second * delta; + lp_assert(t.apply(m_mpq_lar_core_solver.m_r_x) == m_mpq_lar_core_solver.m_r_x[tj]); + TRACE("cube", m_int_solver->display_column(tout, tj); ); + } +} + + +void lar_solver::fill_vars_to_terms(vector> & vars_to_terms) { + for (unsigned j = 0; j < m_terms.size(); j++) { + if (!term_is_used_as_row(j + m_terms_start_index)) + continue; + for (const auto & p : *m_terms[j]) { + if (p.var() >= vars_to_terms.size()) + vars_to_terms.resize(p.var() + 1); + vars_to_terms[p.var()].push_back(j); + } + } +} + +void lar_solver::round_to_integer_solution() { + vector> vars_to_terms; + fill_vars_to_terms(vars_to_terms); + + for (unsigned j = 0; j < column_count(); j++) { + if (column_is_int(j)) continue; + if (column_corresponds_to_term(j)) continue; + TRACE("cube", m_int_solver->display_column(tout, j);); + impq& v = m_mpq_lar_core_solver.m_r_x[j]; + if (v.is_int()) + continue; + impq flv = floor(v); + auto del = flv - v; // del is negative + if (del < - mpq(1, 2)) { + del = impq(one_of_type()) + del; + v = ceil(v); + } else { + v = flv; + } + TRACE("cube", m_int_solver->display_column(tout, j); tout << "v = " << v << " ,del = " << del;); + update_delta_for_terms(del, j, vars_to_terms[j]); + } +} + +bool lar_solver::get_equality_and_right_side_for_term_on_current_x(unsigned term_index, mpq & rs, constraint_index& ci) const { + unsigned tj = term_index + m_terms_start_index; + unsigned j; + bool is_int; + if (m_var_register.external_is_used(tj, j, is_int) == false) + return false; // the term does not have bound because it does not correspond to a column + if (!is_int) // todo - allow for the next version of hnf + return false; + impq term_val; + bool term_val_ready = false; + mpq b; + bool is_strict; + if (has_upper_bound(j, ci, b, is_strict) && !is_strict) { + lp_assert(b.is_int()); + term_val = terms()[term_index]->apply(m_mpq_lar_core_solver.m_r_x); + term_val_ready = true; + if (term_val.x == b) { + rs = b; + return true; + } + } + if (has_lower_bound(j, ci, b, is_strict) && !is_strict) { + if (!term_val_ready) + term_val = terms()[term_index]->apply(m_mpq_lar_core_solver.m_r_x); + lp_assert(b.is_int()); + + if (term_val.x == b) { + rs = b; + return true; + } + } + return false; +} + +void lar_solver::set_cut_strategy(unsigned cut_frequency) { + if (cut_frequency < 4) { // enable only gomory cut + settings().m_int_gomory_cut_period = 2; + settings().m_hnf_cut_period = 100000000; + } else if (cut_frequency == 4) { // enable all cuts and cube equally + settings().m_int_gomory_cut_period = 4; + settings().m_hnf_cut_period = 4; + } else { + // disable all heuristics + settings().m_int_gomory_cut_period = 10000000; + settings().m_hnf_cut_period = 100000000; + } +} + +} // namespace lp + + diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index c26333edb..7209efced 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -10,7 +10,7 @@ Abstract: Author: - + Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: @@ -34,1530 +34,549 @@ Revision History: #include "util/lp/lp_primal_core_solver.h" #include "util/lp/random_updater.h" #include -#include "util/lp/stacked_map.h" #include "util/lp/stacked_value.h" #include "util/lp/stacked_vector.h" -#include "util/lp/stacked_unordered_set.h" -#include "util/lp/iterator_on_pivot_row.h" #include "util/lp/implied_bound.h" #include "util/lp/bound_analyzer_on_row.h" -#include "util/lp/iterator_on_term_with_basis_var.h" -#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" +#include "util/lp/bound_propagator.h" + namespace lp { + class lar_solver : public column_namer { +#if Z3DEBUG_CHECK_UNIQUE_TERMS + struct term_hasher { + std::size_t operator()(const lar_term *t) const + { + using std::size_t; + using std::hash; + using std::string; + size_t seed = 0; + for (const auto& p : t->m_coeffs) { + hash_combine(seed, p); + } + return seed; + } + }; + + struct term_ls_comparer { + bool operator()(const lar_term *a, const lar_term* b) const + { + // a is contained in b + for (auto & p : a->m_coeffs) { + auto t = b->m_coeffs.find(p.first); + if (t == b->m_coeffs.end()) + return false; + if (p.second != t->second) + return false; + } + // zz is contained in b + for (auto & p : b->m_coeffs) { + auto t = a->m_coeffs.find(p.first); + if (t == a->m_coeffs.end()) + return false; + if (p.second != t->second) + return false; + } + return true; + } + }; + std::unordered_set m_set_of_terms; +#endif + //////////////////// fields ////////////////////////// - lp_settings m_settings; - stacked_value m_status; - stacked_value m_simplex_strategy; - std::unordered_map m_ext_vars_to_columns; - vector m_columns_to_ext_vars_or_term_indices; - stacked_vector m_vars_to_ul_pairs; - vector m_constraints; - stacked_value m_constraint_count; + lp_settings m_settings; + lp_status m_status; + stacked_value m_simplex_strategy; + var_register m_var_register; + stacked_vector m_columns_to_ul_pairs; + vector m_constraints; +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; - unsigned constraint_count() const { - return m_constraints.size(); + lar_core_solver m_mpq_lar_core_solver; +private: + int_solver * m_int_solver; + +public : + unsigned terms_start_index() const { return m_terms_start_index; } + const vector terms() const { return m_terms; } + const vector& constraints() const { + return m_constraints; } - const lar_base_constraint& get_constraint(unsigned ci) const { - return *(m_constraints[ci]); + void set_int_solver(int_solver * int_slv) { + m_int_solver = int_slv; } - + int_solver * get_int_solver() { + return m_int_solver; + } + unsigned constraint_count() const; + const lar_base_constraint& get_constraint(unsigned ci) const; ////////////////// 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; + bool column_value_is_int(unsigned j) const { + return m_mpq_lar_core_solver.m_r_x[j].is_int(); + } + const impq& get_column_value(unsigned j) const { + return m_mpq_lar_core_solver.m_r_x[j]; + } + bool is_term(var_index j) const; + bool column_is_fixed(unsigned j) const; public: - lp_settings & settings() { return m_settings;} - lp_settings const & settings() const { return m_settings;} + // 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); + + bool term_is_int(const lar_term * t) const; + + bool var_is_int(var_index v) const; + + 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); + + void add_new_var_to_core_fields_for_mpq(bool register_in_basis); - 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; - } + // terms + var_index add_term(const vector> & coeffs, + const mpq &m_v); - virtual ~lar_solver(){ - for (auto c : m_constraints) - delete c; - for (auto t : m_terms) - delete t; - for (auto t : m_orig_terms) - delete t; - } + var_index add_term_undecided(const vector> & coeffs, + const mpq &m_v); -#include "util/lp/init_lar_solver.h" + bool term_coeffs_are_ok(const vector> & coeffs, const mpq& v); + void push_and_register_term(lar_term* t); - numeric_pair const& get_value(var_index vi) const { return m_mpq_lar_core_solver.m_r_x[vi]; } + void add_row_for_term(const lar_term * term, unsigned term_ext_index); - bool is_term(var_index j) const { - return j >= m_terms_start_index && j - m_terms_start_index < m_terms.size(); - } + void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index); - unsigned adjust_term_index(unsigned j) const { - SASSERT(is_term(j)); - return j - m_terms_start_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); - bool use_lu() const { return m_settings.simplex_strategy() == simplex_strategy_enum::lu; } + void add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, + lconstraint_kind kind, const mpq & right_side); - bool sizes_are_correct() const { - SASSERT(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_x.size()); - return true; - } - - - void print_implied_bound(const implied_bound& be, std::ostream & out) const { - out << "implied bound\n"; - unsigned v = be.m_j; - if (is_term(v)) { - out << "it is a term number " << be.m_j << std::endl; - print_term(*m_orig_terms[be.m_j - m_terms_start_index], out); - } - else { - out << get_column_name(v); - } - out << " " << lconstraint_kind_string(be.kind()) << " " << be.m_bound << std::endl; - // for (auto & p : be.m_explanation) { - // out << p.first << " : "; - // print_constraint(p.second, out); - // } - - // m_mpq_lar_core_solver.m_r_solver.print_column_info(be.m_j< m_terms_start_index? be.m_j : adjust_term_index(be.m_j), out); - out << "end of implied bound" << std::endl; - } - - bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const { - std::unordered_map coeff_map; - auto rs_of_evidence = zero_of_type(); - unsigned n_of_G = 0, n_of_L = 0; - bool strict = false; - for (auto & it : explanation) { - mpq coeff = it.first; - constraint_index con_ind = it.second; - const auto & constr = *m_constraints[con_ind]; - lconstraint_kind kind = coeff.is_pos() ? constr.m_kind : flip_kind(constr.m_kind); - register_in_map(coeff_map, constr, coeff); - if (kind == GT || kind == LT) - strict = true; - if (kind == GE || kind == GT) n_of_G++; - else if (kind == LE || kind == LT) n_of_L++; - rs_of_evidence += coeff*constr.m_right_side; - } - SASSERT(n_of_G == 0 || n_of_L == 0); - lconstraint_kind kind = n_of_G ? GE : (n_of_L ? LE : EQ); - if (strict) - kind = static_cast((static_cast(kind) / 2)); - - if (!is_term(be.m_j)) { - if (coeff_map.size() != 1) - return false; - auto it = coeff_map.find(be.m_j); - if (it == coeff_map.end()) return false; - mpq ratio = it->second; - if (ratio < zero_of_type()) { - kind = static_cast(-kind); - } - rs_of_evidence /= ratio; - } else { - const lar_term * t = m_orig_terms[adjust_term_index(be.m_j)]; - const auto first_coeff = *t->m_coeffs.begin(); - unsigned j = first_coeff.first; - auto it = coeff_map.find(j); - if (it == coeff_map.end()) - return false; - mpq ratio = it->second / first_coeff.second; - for (auto & p : t->m_coeffs) { - it = coeff_map.find(p.first); - if (it == coeff_map.end()) - return false; - if (p.second * ratio != it->second) - return false; - } - if (ratio < zero_of_type()) { - kind = static_cast(-kind); - } - rs_of_evidence /= ratio; - rs_of_evidence += t->m_v * ratio; - } - - return kind == be.kind() && rs_of_evidence == be.m_bound; - } + void decide_on_strategy_and_adjust_initial_state(); + + 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(); + void set_track_pivoted_rows(bool v); + + bool get_track_pivoted_rows() const; + + virtual ~lar_solver(); + + 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, - lp_bound_propagator & bp) { - SASSERT(!use_tableau()); - iterator_on_pivot_row it(m_mpq_lar_core_solver.get_pivot_row(), m_mpq_lar_core_solver.m_r_basis[row_index]); - - bound_analyzer_on_row ra_pos(it, - zero_of_type>(), - row_index, - bp - ); - ra_pos.analyze(); - } + unsigned row_index, + bound_propagator & bp); void analyze_new_bounds_on_row_tableau( - unsigned row_index, - lp_bound_propagator & bp - ) { - - if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation) - return; - iterator_on_row it(A_r().m_rows[row_index]); - SASSERT(use_tableau()); - bound_analyzer_on_row::analyze_row(it, - zero_of_type>(), - row_index, - bp + unsigned row_index, + bound_propagator & bp ); - } + + + void substitute_basis_var_in_terms_for_row(unsigned i); + + void calculate_implied_bounds_for_row(unsigned i, bound_propagator & bp); + + 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 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 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); - } - } - - /* - 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 - } + void explain_implied_bound(implied_bound & ib, bound_propagator & bp); - 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)); - } - - - 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! + void 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()) { - propagate_bounds_on_terms(bp); - } - } + lp_status get_status() const; - lp_status get_status() const { return m_status;} + void set_status(lp_status s); - void set_status(lp_status s) {m_status = s;} + lp_status find_feasible_solution(); + + lp_status solve(); - lp_status find_feasible_solution() { - if (strategy_is_undecided()) - decide_on_strategy_and_adjust_initial_state(); + void fill_explanation_from_infeasible_column(explanation_t & evidence) const; - 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{ - - // this is the case when the lower bound is in conflict with the upper one - const ul_pair & ul = m_vars_to_ul_pairs[m_infeasible_column_index]; - evidence.push_back(std::make_pair(numeric_traits::one(), ul.upper_bound_witness())); - evidence.push_back(std::make_pair(-numeric_traits::one(), ul.low_bound_witness())); - } - - - 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 - vector get_list_of_all_var_indices() const { - vector ret; - for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_heading.size(); j++) - ret.push_back(j); - return ret; - } - void push() { - m_simplex_strategy = m_settings.simplex_strategy(); - m_simplex_strategy.push(); - m_status.push(); - m_vars_to_ul_pairs.push(); - m_infeasible_column_index.push(); - m_mpq_lar_core_solver.push(); - m_term_count = m_terms.size(); - m_term_count.push(); - m_constraint_count = m_constraints.size(); - m_constraint_count.push(); - } + vector get_list_of_all_var_indices() const; + void push(); - static void clean_large_elements_after_pop(unsigned n, int_set& set) { - vector to_remove; - for (unsigned j: set.m_index) - if (j >= n) - to_remove.push_back(j); - for (unsigned j : to_remove) - set.erase(j); - } + static void clean_popped_elements(unsigned n, int_set& set); - static void shrink_inf_set_after_pop(unsigned n, int_set & set) { - clean_large_elements_after_pop(n, set); - set.resize(n); - } + static void shrink_inf_set_after_pop(unsigned n, int_set & set); + + void pop(unsigned k); + class scoped_push { + lar_solver& m_solver; + bool m_pop; + public: + scoped_push(lar_solver& s):m_solver(s), m_pop(true) { s.push(); } + ~scoped_push() { if (m_pop) m_solver.pop(); } + void pop() { SASSERT(m_pop); m_solver.pop(); m_pop = false; } + }; + + vector get_all_constraint_indices() const; - 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); + bool maximize_term_on_tableau(const lar_term & term, + impq &term_max); - 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()); + bool costs_are_zeros_for_r_solver() const; + bool reduced_costs_are_zeroes_for_r_solver() const; + + void set_costs_to_zero(const lar_term & term); - - 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; - } - - 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(); - - 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 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; - } + void prepare_costs_for_r_solver(const lar_term & term); + + bool maximize_term_on_corrected_r_solver(lar_term & term, + 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); - } + lp_status maximize_term(unsigned ext_j , + impq &term_max); + + + + const lar_term & get_term(unsigned j) const; + + void pop_core_solver_params(); + + void pop_core_solver_params(unsigned k); + void set_upper_bound_witness(var_index j, constraint_index ci); - const lar_term & get_term(unsigned j) const { - SASSERT(j >= m_terms_start_index); - return *m_terms[j - m_terms_start_index]; - } - - void pop_core_solver_params() { - pop_core_solver_params(1); - } - - void pop_core_solver_params(unsigned k) { - A_r().pop(k); - A_d().pop(k); - } + void set_lower_bound_witness(var_index j, constraint_index ci); - void set_upper_bound_witness(var_index j, constraint_index ci) { - ul_pair ul = m_vars_to_ul_pairs[j]; - ul.upper_bound_witness() = ci; - m_vars_to_ul_pairs[j] = ul; - } - - void set_low_bound_witness(var_index j, constraint_index ci) { - ul_pair ul = m_vars_to_ul_pairs[j]; - ul.low_bound_witness() = ci; - m_vars_to_ul_pairs[j] = ul; - } + void substitute_terms_in_linear_expression( const vector>& left_side_with_terms, + vector> &left_side, mpq & free_coeff) const; - void substitute_terms(const mpq & mult, - const vector>& left_side_with_terms, - vector> &left_side, mpq & right_side) const { - for (auto & t : left_side_with_terms) { - if (t.second < m_terms_start_index) { - SASSERT(t.second < A_r().column_count()); - left_side.push_back(std::pair(mult * t.first, t.second)); - } else { - const lar_term & term = * m_terms[adjust_term_index(t.second)]; - substitute_terms(mult * t.first, left_side_with_terms, left_side, right_side); - right_side -= mult * term.m_v; - } - } - } + 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); - 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()); + bool use_tableau() const; - 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); - } + bool use_tableau_costs() const; + + void detect_rows_of_column_with_bound_change(unsigned j); + void adjust_x_of_column(unsigned j); + bool row_is_correct(unsigned i) const; + + bool ax_is_correct() const; - 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); - } + bool tableau_with_costs() const; - bool use_tableau() const { return m_settings.use_tableau(); } + bool costs_are_used() const; + + void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta); - bool use_tableau_costs() const { - return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; - } + void update_x_and_inf_costs_for_column_with_changed_bounds(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 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 adjust_x_of_column(unsigned j) { - SASSERT(false); - } + void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); - 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); - } + + void solve_with_core_solver(); - 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 tableau_with_costs() const { - return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; - } - - bool costs_are_used() const { - return m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows; - } - - void change_basic_x_by_delta_on_column(unsigned j, const numeric_pair & delta) { - if (use_tableau()) { - for (const auto & c : A_r().m_columns[j]) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.m_i]; - m_mpq_lar_core_solver.m_r_x[bj] -= A_r().get_val(c) * delta; - if (tableau_with_costs()) { - m_basic_columns_with_changed_cost.insert(bj); - } - m_mpq_lar_core_solver.m_r_solver.update_column_in_inf_set(bj); - } - } else { - 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); - for (unsigned i : m_column_buffer.m_index) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; - m_mpq_lar_core_solver.m_r_x[bj] -= m_column_buffer[i] * delta; - m_mpq_lar_core_solver.m_r_solver.update_column_in_inf_set(bj); - } - } - } - - void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j) { - if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { - if (costs_are_used()) { - bool was_infeas = m_mpq_lar_core_solver.m_r_solver.m_inf_set.contains(j); - m_mpq_lar_core_solver.m_r_solver.update_column_in_inf_set(j); - if (was_infeas != m_mpq_lar_core_solver.m_r_solver.m_inf_set.contains(j)) - m_basic_columns_with_changed_cost.insert(j); - } else { - m_mpq_lar_core_solver.m_r_solver.update_column_in_inf_set(j); - } - } else { - 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); - } - } - - - 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; - } - - 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 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() { - for (auto j : m_columns_with_changed_bound.m_index) - update_x_and_inf_costs_for_column_with_changed_bounds(j); - } - - 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; - } - } + void add_last_rows_to_lu(lp_primal_core_solver & s); + + bool x_is_correct() const; - } + bool var_is_registered(var_index vj) const; - 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;; + unsigned constraint_stack_size() const; - } - - bool var_is_registered(var_index vj) const { - if (vj >= m_terms_start_index) { - if (vj - m_terms_start_index >= m_terms.size()) - return false; - } else if ( vj >= A_r().column_count()) { - return false; - } - return true; - } - - unsigned constraint_stack_size() const { - return m_constraint_count.stack_size(); - } - - 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); + + column_type get_column_type(const column_info & ci); + + std::string get_column_name(unsigned j) const; + + bool all_constrained_variables_are_registered(const vector>& left_side); + + constraint_index add_constraint(const vector>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm); + bool all_constraints_hold() const; + bool constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const; + bool the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const; + + static void register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a); + static void register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j); + + + bool the_left_sides_sum_to_zero(const vector> & evidence) const; + + bool the_right_sides_do_not_sum_to_zero(const vector> & evidence); + + bool explanation_is_correct(const vector>& explanation) const; + + bool inf_explanation_is_correct() const; + + mpq sum_of_right_sides_of_explanation(const vector> & explanation) const; + + bool has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const; + + bool has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const; + + + void get_infeasibility_explanation(vector> & explanation) const; + + void get_infeasibility_explanation_for_inf_sign( + vector> & explanation, + const vector> & inf_row, + int inf_sign) const; + + + + void get_model(std::unordered_map & variable_values) const; + + void get_model_do_not_care_about_diff_vars(std::unordered_map & variable_values) const; + + std::string get_variable_name(var_index vi) const; + + // ********** print region start + void print_constraint(constraint_index ci, std::ostream & out) const; + + void print_constraints(std::ostream& out) const ; + + void print_terms(std::ostream& out) const; + + void print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const; + + 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 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 random_update(unsigned sz, var_index const * vars); + void pivot_fixed_vars_from_basis(); + void pop(); + bool column_represents_row_in_tableau(unsigned j); + void make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j); + void remove_last_row_and_column_from_tableau(unsigned j); + void remove_last_column_from_A(); + + void remove_last_column_from_basis_tableau(unsigned j); + void remove_last_column_from_tableau(); + void pop_tableau(); + 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(); + } + + bool column_is_real(unsigned j) const { + return !column_is_int(j); + } + + bool model_is_int_feasible() const; + + const impq & column_lower_bound(unsigned j) const { + return m_mpq_lar_core_solver.lower_bound(j); + } + + const impq & column_upper_bound(unsigned j) const { + return m_mpq_lar_core_solver.upper_bound(j); + } + + 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.lower_bound_witness(); + uc = ul.upper_bound_witness(); + } + 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_for_integer_column(unsigned j, const mpq & right_side) const; + + const row_strip & get_row(unsigned i) { + return A_r().m_rows[i]; + } + + + unsigned get_base_column_in_row(unsigned row_index) const { + return m_mpq_lar_core_solver.m_r_solver.get_base_column_in_row(row_index); + } + + constraint_index get_column_upper_bound_witness(unsigned j) const { + return m_columns_to_ul_pairs()[j].upper_bound_witness(); + } + + constraint_index get_column_lower_bound_witness(unsigned j) const { + return m_columns_to_ul_pairs()[j].lower_bound_witness(); + } + + void subs_term_columns(lar_term& t) { + vector> columns_to_subs; + for (const auto & m : t.m_coeffs) { + unsigned tj = adjust_column_index_to_term_index(m.first); + if (tj == m.first) continue; + columns_to_subs.push_back(std::make_pair(m.first, tj)); + } + for (const auto & p : columns_to_subs) { + auto it = t.m_coeffs.find(p.first); + lp_assert(it != t.m_coeffs.end()); + mpq v = it->second; + t.m_coeffs.erase(it); + t.m_coeffs[p.second] = v; } } - - 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; + bool has_int_var() const; + bool has_inf_int() const { + for (unsigned j = 0; j < column_count(); j++) { + if (column_is_int(j) && ! column_value_is_int(j)) + 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; + bool r_basis_has_inf_int() const { + for (unsigned j : r_basis()) { + if (column_is_int(j) && ! column_value_is_int(j)) + return true; } - 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 the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const { - unsigned n_of_G = 0, n_of_L = 0; - bool strict = false; - for (auto & it : evidence) { - mpq coeff = it.first; - constraint_index con_ind = it.second; - lconstraint_kind kind = coeff.is_pos() ? - m_constraints[con_ind]->m_kind : - flip_kind(m_constraints[con_ind]->m_kind); - if (kind == GT || kind == LT) - strict = true; - if (kind == GE || kind == GT) n_of_G++; - else if (kind == LE || kind == LT) n_of_L++; - } - the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ); - if (strict) - the_kind_of_sum = static_cast((static_cast(the_kind_of_sum) / 2)); - - return n_of_G == 0 || n_of_L == 0; - } - - 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); - } - - 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; - } - - return true; - } - - 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); - } - - 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; - } - } - - - 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_for_inf_sign( - vector> & explanation, - const vector> & inf_row, - int inf_sign) const { - - for (auto & it : inf_row) { - mpq coeff = it.first; - unsigned j = it.second; - - 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 { - 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); - } - - // ********** print region start - void print_constraint(constraint_index ci, std::ostream & out) const { - if (ci >= m_constraints.size()) { - out << "constraint " << T_to_string(ci) << " is not found"; - out << std::endl; - return; - } - - print_constraint(m_constraints[ci], out); - } - - void print_constraints(std::ostream& out) const { - for (auto c : m_constraints) { - print_constraint(c, out); - } - } - - void print_terms(std::ostream& out) const { - for (auto it : m_terms) { - print_term(*it, out); - out << "\n"; - } - } - - void print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const { - print_linear_combination_of_column_indices(c->get_left_side_coefficients(), out); - mpq free_coeff = c->get_free_coeff_of_left_side(); - if (!is_zero(free_coeff)) - out << " + " << free_coeff; - - } - - void print_term(lar_term const& term, std::ostream & out) const { - if (!numeric_traits::is_zero(term.m_v)) { - out << term.m_v << " + "; - } - print_linear_combination_of_column_indices(term.coeffs_as_vector(), out); - } - - 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_constraint(const lar_base_constraint * c, std::ostream & out) const { - print_left_side_of_constraint(c, out); - out << " " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side << std::endl; - } - - 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 random_update(unsigned sz, var_index const * vars) { - vector column_list; - fill_var_set_for_random_update(sz, vars, column_list); - random_updater ru(m_mpq_lar_core_solver, column_list); - ru.update(); - } - - - void try_pivot_fixed_vars_from_basis() { - m_mpq_lar_core_solver.m_r_solver.pivot_fixed_vars_from_basis(); - } - - void pop() { - pop(1); - } - - - bool column_represents_row_in_tableau(unsigned j) { - return m_vars_to_ul_pairs()[j].m_i != static_cast(-1); - } - - void make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j) { - // i, j - is the indices of the bottom-right element of the tableau - SASSERT(A_r().row_count() == i + 1 && A_r().column_count() == j + 1); - auto & last_column = A_r().m_columns[j]; - int non_zero_column_cell_index = -1; - for (unsigned k = last_column.size(); k-- > 0;){ - auto & cc = last_column[k]; - if (cc.m_i == i) - return; - non_zero_column_cell_index = k; - } - - SASSERT(non_zero_column_cell_index != -1); - SASSERT(static_cast(non_zero_column_cell_index) != i); - m_mpq_lar_core_solver.m_r_solver.transpose_rows_tableau(last_column[non_zero_column_cell_index].m_i, i); - } - - 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(); - } - - 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(); - } - - 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()); - } - - - void pop_tableau() { - SASSERT(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); - - SASSERT(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); - SASSERT(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); - // We remove last variables starting from m_column_names.size() to m_vec_of_canonic_left_sides.size(). - // At this moment m_column_names is already popped - for (unsigned j = A_r().column_count(); j-- > m_columns_to_ext_vars_or_term_indices.size();) - remove_column_from_tableau(j); - SASSERT(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); - SASSERT(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); - SASSERT(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); - } - - - - - 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)); + return false; } + + 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_column(unsigned ext_j) const; + bool tighten_term_bounds_by_delta(unsigned, const impq&); + void round_to_integer_solution(); + void update_delta_for_terms(const impq & delta, unsigned j, const vector&); + void fill_vars_to_terms(vector> & vars_to_terms); + unsigned column_count() const { return A_r().column_count(); } + const vector & r_basis() const { return m_mpq_lar_core_solver.r_basis(); } + const vector & r_nbasis() const { return m_mpq_lar_core_solver.r_nbasis(); } + bool get_equality_and_right_side_for_term_on_current_x(unsigned i, mpq &rs, constraint_index& ci) const; + bool remove_from_basis(unsigned); + lar_term get_term_to_maximize(unsigned ext_j) const; + void set_cut_strategy(unsigned cut_frequency); }; } diff --git a/src/util/lp/lar_solver_instances.cpp b/src/util/lp/lar_solver_instances.cpp new file mode 100644 index 000000000..602df0326 --- /dev/null +++ b/src/util/lp/lar_solver_instances.cpp @@ -0,0 +1,13 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Lev Nachmanson +*/ + +#include "util/lp/lar_solver.cpp" + +template void lp::lar_solver::copy_from_mpq_matrix(class lp::static_matrix &); + + + + + diff --git a/src/util/lp/lar_term.h b/src/util/lp/lar_term.h index 16b5a938d..519847848 100644 --- a/src/util/lp/lar_term.h +++ b/src/util/lp/lar_term.h @@ -1,22 +1,22 @@ /*++ -Copyright (c) 2017 Microsoft Corporation + Copyright (c) 2017 Microsoft Corporation -Module Name: + Module Name: - + -Abstract: + Abstract: - + -Author: + Author: - Lev Nachmanson (levnach) + Lev Nachmanson (levnach) -Revision History: + Revision History: ---*/ + --*/ #pragma once #include "util/lp/indexed_vector.h" namespace lp { @@ -25,7 +25,7 @@ struct lar_term { std::unordered_map m_coeffs; mpq m_v; lar_term() {} - void add_to_map(unsigned j, const mpq& c) { + void add_monomial(const mpq& c, unsigned j) { auto it = m_coeffs.find(j); if (it == m_coeffs.end()) { m_coeffs.emplace(j, c); @@ -36,6 +36,10 @@ struct lar_term { } } + bool is_empty() const { + return m_coeffs.size() == 0 && is_zero(m_v); + } + unsigned size() const { return static_cast(m_coeffs.size()); } const std::unordered_map & coeffs() const { @@ -45,7 +49,7 @@ struct lar_term { lar_term(const vector>& coeffs, const mpq & v) : m_v(v) { for (const auto & p : coeffs) { - add_to_map(p.second, p.first); + add_monomial(p.first, p.second); } } bool operator==(const lar_term & a) const { return false; } // take care not to create identical terms @@ -67,7 +71,7 @@ struct lar_term { if (it == m_coeffs.end()) return; const mpq & b = it->second; for (unsigned it_j :li.m_index) { - add_to_map(it_j, - b * li.m_data[it_j]); + add_monomial(- b * li.m_data[it_j], it_j); } m_coeffs.erase(it); } @@ -75,5 +79,61 @@ struct lar_term { bool contains(unsigned j) const { return m_coeffs.find(j) != m_coeffs.end(); } + + void negate() { + for (auto & t : m_coeffs) + t.second.neg(); + } + + template + T apply(const vector& x) const { + T ret = T(m_v); + for (const auto & t : m_coeffs) { + ret += t.second * x[t.first]; + } + return ret; + } + + void clear() { + m_coeffs.clear(); + m_v = zero_of_type(); + } + + struct ival { + unsigned m_var; + const mpq & m_coeff; + ival(unsigned var, const mpq & val) : m_var(var), m_coeff(val) { + } + unsigned var() const { return m_var;} + const mpq & coeff() const { return m_coeff; } + }; + + struct const_iterator { + //fields + std::unordered_map::const_iterator m_it; + + typedef const_iterator self_type; + typedef ival value_type; + typedef ival reference; + // typedef std::pair* pointer; + typedef int difference_type; + typedef std::forward_iterator_tag iterator_category; + + reference operator*() const { + return ival(m_it->first, m_it->second); + } + + self_type operator++() { self_type i = *this; m_it++; return i; } + self_type operator++(int) { m_it++; return *this; } + + const_iterator(std::unordered_map::const_iterator it) : m_it(it) {} + bool operator==(const self_type &other) const { + return m_it == other.m_it; + } + bool operator!=(const self_type &other) const { return !(*this == other); } + }; + + const_iterator begin() const { return m_coeffs.begin();} + const_iterator end() const { return m_coeffs.end(); } }; } diff --git a/src/util/lp/lia_move.h b/src/util/lp/lia_move.h new file mode 100644 index 000000000..ec4643e20 --- /dev/null +++ b/src/util/lp/lia_move.h @@ -0,0 +1,31 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +namespace lp { +enum class lia_move { + sat, + branch, + cut, + conflict, + continue_with_check, + undef, + unsat +}; +} diff --git a/src/util/lp/linear_combination_iterator.h b/src/util/lp/linear_combination_iterator.h deleted file mode 100644 index 417bdcf82..000000000 --- a/src/util/lp/linear_combination_iterator.h +++ /dev/null @@ -1,65 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -namespace lp { -template -struct linear_combination_iterator { - virtual bool next(T & a, unsigned & i) = 0; - virtual bool next(unsigned & i) = 0; - virtual void reset() = 0; - virtual linear_combination_iterator * clone() = 0; - virtual ~linear_combination_iterator(){} - virtual unsigned size() const = 0; -}; -template -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()) - return false; - auto & p = m_vector[m_offset]; - a = p.first; - i = p.second; - m_offset++; - return true; - } - - bool next(unsigned & i) { - if(m_offset >= m_vector.size()) - return false; - auto & p = m_vector[m_offset]; - i = p.second; - m_offset++; - return true; - } - - void reset() {m_offset = 0;} - linear_combination_iterator * clone() { - return new linear_combination_iterator_on_vector(m_vector); - } - linear_combination_iterator_on_vector(vector> & vec): - m_vector(vec), - m_offset(0) - {} - unsigned size() const { return m_vector.size(); } -}; - -} diff --git a/src/util/lp/lp_bound_propagator.cpp b/src/util/lp/lp_bound_propagator.cpp deleted file mode 100644 index 53218fced..000000000 --- a/src/util/lp/lp_bound_propagator.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "util/lp/lar_solver.h" -namespace lp { - lp_bound_propagator::lp_bound_propagator(lar_solver & ls): - m_lar_solver(ls) {} -column_type lp_bound_propagator::get_column_type(unsigned j) const { - return m_lar_solver.m_mpq_lar_core_solver.m_column_types()[j]; -} -const impq & lp_bound_propagator::get_low_bound(unsigned j) const { - return m_lar_solver.m_mpq_lar_core_solver.m_r_low_bounds()[j]; -} -const impq & lp_bound_propagator::get_upper_bound(unsigned j) const { - return m_lar_solver.m_mpq_lar_core_solver.m_r_upper_bounds()[j]; -} -void lp_bound_propagator::try_add_bound(const mpq & v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { - unsigned term_j = m_lar_solver.adjust_column_index_to_term_index(j); - mpq w = v; - if (term_j != j) { - j = term_j; - w += m_lar_solver.get_term(term_j).m_v; // when terms are turned into the columns they "lose" the right side, at this moment they aquire it back - } - lconstraint_kind kind = is_low? GE : LE; - if (strict) - kind = static_cast(kind / 2); - - if (!bound_is_interesting(j, kind, w)) - return; - unsigned k; // index to ibounds - if (is_low) { - if (try_get_val(m_improved_low_bounds, j, k)) { - auto & found_bound = m_ibounds[k]; - if (w > found_bound.m_bound || (w == found_bound.m_bound && found_bound.m_strict == false && strict)) - found_bound = implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); - } else { - m_improved_low_bounds[j] = m_ibounds.size(); - m_ibounds.push_back(implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); - } - } else { // the upper bound case - if (try_get_val(m_improved_upper_bounds, j, k)) { - auto & found_bound = m_ibounds[k]; - if (w < found_bound.m_bound || (w == found_bound.m_bound && found_bound.m_strict == false && strict)) - found_bound = implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); - } else { - m_improved_upper_bounds[j] = m_ibounds.size(); - m_ibounds.push_back(implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); - } - } -} -} diff --git a/src/util/lp/lp_bound_propagator.h b/src/util/lp/lp_bound_propagator.h deleted file mode 100644 index 76870f457..000000000 --- a/src/util/lp/lp_bound_propagator.h +++ /dev/null @@ -1,42 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#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 - std::unordered_map m_improved_upper_bounds; - lar_solver & m_lar_solver; -public: - vector m_ibounds; -public: - lp_bound_propagator(lar_solver & ls); - column_type get_column_type(unsigned) const; - const impq & get_low_bound(unsigned) const; - const impq & get_upper_bound(unsigned) const; - void try_add_bound(const 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/lp_core_solver_base_instances.cpp b/src/util/lp/lp_core_solver_base.cpp similarity index 97% rename from src/util/lp/lp_core_solver_base_instances.cpp rename to src/util/lp/lp_core_solver_base.cpp index f5853eecf..00c1322c2 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; @@ -144,3 +144,5 @@ template bool lp::lp_core_solver_base::inf_set_is_correct() co template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; +template void lp::lp_core_solver_base >::calculate_pivot_row(unsigned int); +template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); diff --git a/src/util/lp/lp_core_solver_base.h b/src/util/lp/lp_core_solver_base.h index fd115669c..b8b7b7c0d 100644 --- a/src/util/lp/lp_core_solver_base.h +++ b/src/util/lp/lp_core_solver_base.h @@ -28,6 +28,7 @@ Revision History: #include "util/lp/lu.h" #include "util/lp/permutation_matrix.h" #include "util/lp/column_namer.h" + namespace lp { template // X represents the type of the x variable and the bounds @@ -38,7 +39,17 @@ class lp_core_solver_base { private: lp_status m_status; public: - bool current_x_is_feasible() const { return m_inf_set.size() == 0; } + bool current_x_is_feasible() const { + TRACE("feas", + if (m_inf_set.size()) { + tout << "column " << m_inf_set.m_index[0] << " is infeasible" << std::endl; + print_column_info(m_inf_set.m_index[0], tout); + } else { + tout << "x is feasible\n"; + } + ); + return m_inf_set.size() == 0; + } bool current_x_is_infeasible() const { return m_inf_set.size() != 0; } int_set m_inf_set; bool m_using_infeas_costs; @@ -58,13 +69,13 @@ public: lp_settings & m_settings; vector m_y; // the buffer for yB = cb // a device that is able to solve Bx=c, xB=d, and change the basis - lu * m_factorization; + lu> * m_factorization; const column_namer & m_column_names; indexed_vector m_w; // the vector featuring in 24.3 of the Chvatal book 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; @@ -74,6 +85,7 @@ public: bool m_tracing_basis_changes; int_set* m_pivoted_rows; bool m_look_for_feasible_solution_only; + void start_tracing_basis_changes() { m_trace_of_basis_change_vector.resize(0); m_tracing_basis_changes = true; @@ -108,7 +120,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(); @@ -197,11 +209,11 @@ public: bool need_to_pivot_to_basis_tableau() const { - SASSERT(m_A.is_correct()); + lp_assert(m_A.is_correct()); unsigned m = m_A.row_count(); for (unsigned i = 0; i < m; i++) { unsigned bj = m_basis[i]; - SASSERT(m_A.m_columns[bj].size() > 0); + lp_assert(m_A.m_columns[bj].size() > 0); if (m_A.m_columns[bj].size() > 1 || m_A.get_val(m_A.m_columns[bj][0]) != one_of_type()) return true; } return false; @@ -210,10 +222,9 @@ public: bool reduced_costs_are_correct_tableau() const { if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) return true; - SASSERT(m_A.is_correct()); + lp_assert(m_A.is_correct()); if (m_using_infeas_costs) { if (infeasibility_costs_are_correct() == false) { - std::cout << "infeasibility_costs_are_correct() does not hold" << std::endl; return false; } } @@ -222,9 +233,6 @@ public: for (unsigned j = 0; j < n; j++) { if (m_basis_heading[j] >= 0) { if (!is_zero(m_d[j])) { - - std::cout << "case a\n"; - print_column_info(j, std::cout); return false; } } else { @@ -233,8 +241,6 @@ public: d -= this->m_costs[this->m_basis[cc.m_i]] * this->m_A.get_val(cc); } if (m_d[j] != d) { - std::cout << "case b\n"; - print_column_info(j, std::cout); return false; } } @@ -253,14 +259,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 { @@ -271,15 +277,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; @@ -318,8 +324,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]; } @@ -329,7 +335,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; @@ -357,21 +363,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)) @@ -385,50 +391,47 @@ public: } bool make_column_feasible(unsigned j, numeric_pair & delta) { - SASSERT(m_basis_heading[j] < 0); + bool ret = false; + lp_assert(m_basis_heading[j] < 0); auto & x = m_x[j]; switch (m_column_types[j]) { case column_type::fixed: - SASSERT(m_low_bounds[j] == m_upper_bounds[j]); - if (x != m_low_bounds[j]) { - delta = m_low_bounds[j] - x; - x = m_low_bounds[j]; - return true; + 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; - x = m_low_bounds[j]; - return true; + if (x < m_lower_bounds[j]) { + delta = m_lower_bounds[j] - x; + ret = true;; } if (x > m_upper_bounds[j]) { delta = m_upper_bounds[j] - x; - x = m_upper_bounds[j]; - return true; + ret = true; } break; - case column_type::low_bound: - if (x < m_low_bounds[j]) { - delta = m_low_bounds[j] - x; - x = m_low_bounds[j]; - return true; + case column_type::lower_bound: + if (x < m_lower_bounds[j]) { + delta = m_lower_bounds[j] - x; + ret = true; } break; case column_type::upper_bound: if (x > m_upper_bounds[j]) { delta = m_upper_bounds[j] - x; - x = m_upper_bounds[j]; - return true; + ret = true; } break; - case column_type::free_column: - break; default: - SASSERT(false); break; } - return false; + if (ret) + add_delta_to_x_and_call_tracker(j, delta); + + return ret; + } @@ -444,6 +447,8 @@ public: void init_lu(); int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); + bool remove_from_basis(unsigned j); + bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w); bool pivot_for_tableau_on_basis(); bool pivot_row_for_tableau_on_basis(unsigned row); void init_basic_part_of_basis_heading() { @@ -473,7 +478,7 @@ public: } void change_basis_unconditionally(unsigned entering, unsigned leaving) { - SASSERT(m_basis_heading[entering] < 0); + lp_assert(m_basis_heading[entering] < 0); int place_in_non_basis = -1 - m_basis_heading[entering]; if (static_cast(place_in_non_basis) >= m_nbasis.size()) { // entering variable in not in m_nbasis, we need to put it back; @@ -492,7 +497,8 @@ public: } void change_basis(unsigned entering, unsigned leaving) { - SASSERT(m_basis_heading[entering] < 0); + lp_assert(m_basis_heading[entering] < 0); + lp_assert(m_basis_heading[leaving] >= 0); int place_in_basis = m_basis_heading[leaving]; int place_in_non_basis = - m_basis_heading[entering] - 1; @@ -522,8 +528,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: @@ -533,7 +539,7 @@ public: case column_type::free_column: break; default: - SASSERT(false); + lp_assert(false); break; } return true; @@ -541,7 +547,6 @@ public: bool non_basic_columns_are_set_correctly() const { for (unsigned j : this->m_nbasis) if (!column_is_feasible(j)) { - print_column_info(j, std::cout); return false; } return true; @@ -552,10 +557,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; @@ -566,36 +571,38 @@ public: } void print_column_info(unsigned j, std::ostream & out) const { - out << "column_index = " << j << ", name = "<< column_name(j) << " type = " << column_type_to_string(m_column_types[j]) << std::endl; + out << "j = " << j << ", name = "<< column_name(j); 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] << "]"; break; - case column_type::low_bound: - out << m_low_bounds[j] << std::endl; + case column_type::lower_bound: + out << " [" << m_lower_bounds[j] << "," << "oo" << "]"; break; case column_type::upper_bound: - out << m_upper_bounds[j] << std::endl; + out << " [-oo, " << m_upper_bounds[j] << ']'; break; case column_type::free_column: + out << " [-oo, oo]"; break; default: - SASSERT(false); + lp_assert(false); } - std::cout << "basis heading = " << m_basis_heading[j] << std::endl; - std::cout << "x = " << m_x[j] << std::endl; - /* - std::cout << "cost = " << m_costs[j] << std::endl; - std:: cout << "m_d = " << m_d[j] << std::endl;*/ + // out << "basis heading = " << m_basis_heading[j] << std::endl; + out << " x = " << m_x[j]; + if (m_basis_heading[j] >= 0) + out << " base\n"; + else + out << " nbas\n"; } - bool column_is_free(unsigned j) { return this->m_column_type[j] == free; } + bool column_is_free(unsigned j) const { return this->m_column_type[j] == free; } - bool column_has_upper_bound(unsigned j) { + bool column_has_upper_bound(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; default: return true; @@ -605,13 +612,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) const { switch(m_column_types[j]) { case column_type::free_column: case column_type::upper_bound: @@ -671,26 +678,43 @@ public: return m_inf_set.contains(j); } - void update_column_in_inf_set(unsigned j) { - if (column_is_feasible(j)) { - m_inf_set.erase(j); - } else { - m_inf_set.insert(j); - } + void update_x_with_feasibility_tracking(unsigned j, const X & v) { + m_x[j] = v; + track_column_feasibility(j); + } + + void update_x_with_delta_and_track_feasibility(unsigned j, const X & del) { + m_x[j] += del; + track_column_feasibility(j); + } + + void update_x_and_call_tracker(unsigned j, const X & v) { + m_x[j] = v; + } + + void add_delta_to_x_and_call_tracker(unsigned j, const X & delta) { + m_x[j] += delta; + } + + void track_column_feasibility(unsigned j) { + if (column_is_feasible(j)) + remove_column_from_inf_set(j); + else + insert_column_into_inf_set(j); } void insert_column_into_inf_set(unsigned j) { m_inf_set.insert(j); - SASSERT(!column_is_feasible(j)); + lp_assert(!column_is_feasible(j)); } void remove_column_from_inf_set(unsigned j) { m_inf_set.erase(j); - SASSERT(column_is_feasible(j)); + lp_assert(column_is_feasible(j)); } bool costs_on_nbasis_are_zeros() const { - SASSERT(this->basis_heading_is_correct()); + lp_assert(this->basis_heading_is_correct()); for (unsigned j = 0; j < this->m_n(); j++) { if (this->m_basis_heading[j] < 0) - SASSERT(is_zero(this->m_costs[j])); + lp_assert(is_zero(this->m_costs[j])); } return true; } @@ -701,5 +725,10 @@ public: const unsigned & iters_with_no_cost_growing() const { return m_iters_with_no_cost_growing; } + + void calculate_pivot_row(unsigned i); + unsigned get_base_column_in_row(unsigned row_index) const { + return m_basis[row_index]; + } }; } diff --git a/src/util/lp/lp_core_solver_base.hpp b/src/util/lp/lp_core_solver_base_def.h similarity index 84% rename from src/util/lp/lp_core_solver_base.hpp rename to src/util/lp/lp_core_solver_base_def.h index b49dd0638..f20eaf042 100644 --- a/src/util/lp/lp_core_solver_base.hpp +++ b/src/util/lp/lp_core_solver_base_def.h @@ -35,11 +35,11 @@ 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), - m_status(FEASIBLE), + m_status(lp_status::FEASIBLE), m_inf_set(A.column_count()), m_using_infeas_costs(false), m_pivot_row_of_B_1(A.row_count()), @@ -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()), @@ -68,7 +68,7 @@ lp_core_solver_base(static_matrix & A, m_tracing_basis_changes(false), m_pivoted_rows(nullptr), m_look_for_feasible_solution_only(false) { - SASSERT(bounds_for_boxed_are_set_correctly()); + lp_assert(bounds_for_boxed_are_set_correctly()); init(); init_basis_heading_and_non_basic_columns_vector(); } @@ -76,7 +76,7 @@ lp_core_solver_base(static_matrix & A, template void lp_core_solver_base:: allocate_basis_heading() { // the rest of initilization will be handled by the factorization class init_basis_heading_and_non_basic_columns_vector(); - SASSERT(basis_heading_is_correct()); + lp_assert(basis_heading_is_correct()); } template void lp_core_solver_base:: init() { @@ -142,7 +142,7 @@ solve_yB(vector & y) { // } // } template void lp_core_solver_base::solve_Bd(unsigned entering, indexed_vector & column) { - SASSERT(!m_settings.use_tableau()); + lp_assert(!m_settings.use_tableau()); if (m_factorization == nullptr) { init_factorization(m_factorization, m_A, m_basis, m_settings); } @@ -152,19 +152,19 @@ template void lp_core_solver_base::solve_Bd(unsig template void lp_core_solver_base:: solve_Bd(unsigned entering) { - SASSERT(m_ed.is_OK()); + lp_assert(m_ed.is_OK()); m_factorization->solve_Bd(entering, m_ed, m_w); if (this->precise()) m_columns_nz[entering] = m_ed.m_index.size(); - SASSERT(m_ed.is_OK()); - SASSERT(m_w.is_OK()); + lp_assert(m_ed.is_OK()); + lp_assert(m_w.is_OK()); #ifdef Z3DEBUG // auto B = get_B(*m_factorization, m_basis); // vector a(m_m()); // m_A.copy_column_to_vector(entering, a); // vector cd(m_ed.m_data); // B.apply_from_left(cd, m_settings); - // SASSERT(vectors_are_equal(cd , a)); + // lp_assert(vectors_are_equal(cd , a)); #endif } @@ -223,16 +223,11 @@ restore_m_ed(T * buffer) { template bool lp_core_solver_base:: A_mult_x_is_off() const { - SASSERT(m_x.size() == m_A.column_count()); + lp_assert(m_x.size() == m_A.column_count()); if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_m(); i++) { + for (unsigned i = 0; i < m_m(); i++) { X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); if (delta != numeric_traits::zero()) { - std::cout << "precise x is off ("; - std::cout << "m_b[" << i << "] = " << m_b[i] << " "; - std::cout << "left side = " << m_A.dot_product_with_row(i, m_x) << ' '; - std::cout << "delta = " << delta << ' '; - std::cout << "iters = " << total_iterations() << ")" << std::endl; return true; } } @@ -259,17 +254,12 @@ A_mult_x_is_off() const { } template bool lp_core_solver_base:: A_mult_x_is_off_on_index(const vector & index) const { - SASSERT(m_x.size() == m_A.column_count()); + lp_assert(m_x.size() == m_A.column_count()); if (numeric_traits::precise()) return false; #if RUN_A_MULT_X_IS_OFF_FOR_PRECESE for (unsigned i : index) { X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); if (delta != numeric_traits::zero()) { - // std::cout << "x is off ("; - // std::cout << "m_b[" << i << "] = " << m_b[i] << " "; - // std::cout << "left side = " << m_A.dot_product_with_row(i, m_x) << ' '; - // std::cout << "delta = " << delta << ' '; - // std::cout << "iters = " << total_iterations() << ")" << std::endl; return true; } } @@ -299,13 +289,13 @@ A_mult_x_is_off_on_index(const vector & index) const { // from page 182 of Istvan Maros's book template void lp_core_solver_base:: calculate_pivot_row_of_B_1(unsigned pivot_row) { - SASSERT(! use_tableau()); - SASSERT(m_pivot_row_of_B_1.is_OK()); + lp_assert(! use_tableau()); + lp_assert(m_pivot_row_of_B_1.is_OK()); m_pivot_row_of_B_1.clear(); m_pivot_row_of_B_1.set_value(numeric_traits::one(), pivot_row); - SASSERT(m_pivot_row_of_B_1.is_OK()); + lp_assert(m_pivot_row_of_B_1.is_OK()); m_factorization->solve_yB_with_error_check_indexed(m_pivot_row_of_B_1, m_basis_heading, m_basis, m_settings); - SASSERT(m_pivot_row_of_B_1.is_OK()); + lp_assert(m_pivot_row_of_B_1.is_OK()); } @@ -391,15 +381,15 @@ 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]; - SASSERT(column_is_dual_feasible(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: m_x[j] = m_upper_bounds[j]; - SASSERT(column_is_dual_feasible(j)); + lp_assert(column_is_dual_feasible(j)); break; default: break; @@ -411,21 +401,18 @@ 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); - SASSERT(false); // impossible case + lp_assert(false); // impossible case case column_type::free_column: return numeric_traits::is_zero(m_d[j]); default: - LP_OUT(m_settings, "column = " << j << std::endl); - LP_OUT(m_settings, "unexpected column type = " << column_type_to_string(m_column_types[j]) << std::endl); - SASSERT(false); + lp_unreachable(); } - SASSERT(false); + lp_unreachable(); return false; } template bool lp_core_solver_base:: @@ -484,14 +471,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; @@ -508,7 +495,7 @@ template bool lp_core_solver_base::column_is_feas return true; break; default: - SASSERT(false); + lp_unreachable(); } return false; // it is unreachable } @@ -530,9 +517,6 @@ template bool lp_core_solver_base::inf_set_is_cor bool is_feas = column_is_feasible(j); if (is_feas == belongs_to_set) { - print_column_info(j, std::cout); - std::cout << "belongs_to_set = " << belongs_to_set << std::endl; - std::cout <<( is_feas? "feas":"inf") << std::endl; return false; } } @@ -549,7 +533,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) { if (!find_x_by_solving()) { restore_x(entering, tt); if(A_mult_x_is_off()) { - m_status = FLOATING_POINT_ERROR; + m_status = lp_status::FLOATING_POINT_ERROR; m_iters_with_no_cost_growing++; return false; } @@ -559,7 +543,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) { if (m_factorization->get_status() != LU_status::OK) { std::stringstream s; // s << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations(); - m_status = FLOATING_POINT_ERROR; + m_status = lp_status::FLOATING_POINT_ERROR; return false; } return false; @@ -581,19 +565,19 @@ update_basis_and_x(int entering, int leaving, X const & tt) { init_lu(); if (m_factorization->get_status() != LU_status::OK) { if (m_look_for_feasible_solution_only && !precise()) { - m_status = UNSTABLE; + m_status = lp_status::UNSTABLE; delete m_factorization; m_factorization = nullptr; return false; } // LP_OUT(m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations() << std::endl); restore_x_and_refactor(entering, leaving, tt); - if (m_status == FLOATING_POINT_ERROR) + if (m_status == lp_status::FLOATING_POINT_ERROR) return false; - SASSERT(!A_mult_x_is_off()); + CASSERT("A_off", !A_mult_x_is_off()); m_iters_with_no_cost_growing++; // LP_OUT(m_settings, "rolled back after failing of init_factorization()" << std::endl); - m_status = UNSTABLE; + m_status = lp_status::UNSTABLE; return false; } return true; @@ -602,7 +586,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) { template bool lp_core_solver_base:: divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { - SASSERT(numeric_traits::precise()); + lp_assert(numeric_traits::precise()); int pivot_index = -1; auto & row = m_A.m_rows[pivot_row]; unsigned size = row.size(); @@ -629,7 +613,7 @@ divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { } template bool lp_core_solver_base:: pivot_column_tableau(unsigned j, unsigned piv_row_index) { - if (!divide_row_by_pivot(piv_row_index, j)) + if (!divide_row_by_pivot(piv_row_index, j)) return false; auto &column = m_A.m_columns[j]; int pivot_col_cell_index = -1; @@ -643,7 +627,7 @@ pivot_column_tableau(unsigned j, unsigned piv_row_index) { return false; if (pivot_col_cell_index != 0) { - SASSERT(column.size() > 1); + lp_assert(column.size() > 1); // swap the pivot column cell with the head cell auto c = column[0]; column[0] = column[pivot_col_cell_index]; @@ -654,7 +638,7 @@ pivot_column_tableau(unsigned j, unsigned piv_row_index) { } while (column.size() > 1) { auto & c = column.back(); - SASSERT(c.m_i != piv_row_index); + lp_assert(c.m_i != piv_row_index); if(! m_A.pivot_row_to_row_given_cell(piv_row_index, c, j)) { return false; } @@ -702,7 +686,7 @@ non_basis_is_correctly_represented_in_heading() const { } for (unsigned j = 0; j < m_A.column_count(); j++) { if (m_basis_heading[j] >= 0) { - SASSERT(static_cast(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j); + lp_assert(static_cast(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j); } } return true; @@ -710,26 +694,22 @@ non_basis_is_correctly_represented_in_heading() const { template bool lp_core_solver_base:: basis_heading_is_correct() const { - SASSERT(m_basis_heading.size() == m_A.column_count()); - SASSERT(m_basis.size() == m_A.row_count()); - SASSERT(m_nbasis.size() <= m_A.column_count() - m_A.row_count()); // for the dual the size of non basis can be smaller + lp_assert(m_basis_heading.size() == m_A.column_count()); + lp_assert(m_basis.size() == m_A.row_count()); + lp_assert(m_nbasis.size() <= m_A.column_count() - m_A.row_count()); // for the dual the size of non basis can be smaller if (!basis_has_no_doubles()) { - // std::cout << "basis_has_no_doubles" << std::endl; return false; } if (!non_basis_has_no_doubles()) { - // std::cout << "non_basis_has_no_doubles" << std::endl; return false; } if (!basis_is_correctly_represented_in_heading()) { - // std::cout << "basis_is_correctly_represented_in_heading" << std::endl; return false; } if (!non_basis_is_correctly_represented_in_heading()) { - // std::cout << "non_basis_is_correctly_represented_in_heading" << std::endl; return false; } @@ -856,12 +836,12 @@ solve_Ax_eq_b() { template void lp_core_solver_base:: snap_non_basic_x_to_bound_and_free_to_zeroes() { for (unsigned j : non_basis()) { - SASSERT(j < m_x.size()); + lp_assert(j < m_x.size()); 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]; @@ -894,23 +874,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: - SASSERT(false); + lp_unreachable(); } - SASSERT(false); - return at_low_bound; + lp_unreachable(); + return at_lower_bound; } template void lp_core_solver_base::init_lu() { @@ -938,59 +918,80 @@ template void lp_core_solver_base::transpose_row transpose_basis(i, j); m_A.transpose_rows(i, j); } +// j is the new basic column, j_basic - the leaving column +template bool lp_core_solver_base::pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w) { + lp_assert(m_basis_heading[j] < 0); + lp_assert(m_basis_heading[j_basic] >= 0); + unsigned row_index = m_basis_heading[j_basic]; + if (m_settings.m_simplex_strategy == simplex_strategy_enum::lu) { + if (m_factorization->need_to_refactor()) { + init_lu(); + } + else { + m_factorization->prepare_entering(j, w); // to init vector w + m_factorization->replace_column(zero_of_type(), w, row_index); + } + if (m_factorization->get_status() != LU_status::OK) { + init_lu(); + return false; + } + else { + change_basis(j, j_basic); + } + } + else { // the tableau case + if (pivot_column_tableau(j, row_index)) + change_basis(j, j_basic); + else return false; + } + return true; +} template void lp_core_solver_base::pivot_fixed_vars_from_basis() { // run over basis and non-basis at the same time indexed_vector w(m_basis.size()); // the buffer unsigned i = 0; // points to basis - unsigned j = 0; // points to nonbasis - for (; i < m_basis.size() && j < m_nbasis.size(); i++) { - unsigned ii = m_basis[i]; - unsigned jj; + for (; i < m_basis.size(); i++) { + unsigned basic_j = m_basis[i]; - if (get_column_type(ii) != column_type::fixed) continue; - while (j < m_nbasis.size()) { - for (; j < m_nbasis.size(); j++) { - jj = m_nbasis[j]; - if (get_column_type(jj) != column_type::fixed) + if (get_column_type(basic_j) != column_type::fixed) continue; + T a; + unsigned j; + for (auto &c : m_A.m_rows[i]) { + j = c.var(); + if (j == basic_j) + continue; + if (get_column_type(j) != column_type::fixed) { + if (pivot_column_general(j, basic_j, w)) break; } - if (j >= m_nbasis.size()) - break; - j++; - if (m_factorization->need_to_refactor()) { - change_basis(jj, ii); - init_lu(); - } else { - m_factorization->prepare_entering(jj, w); // to init vector w - m_factorization->replace_column(zero_of_type(), w, m_basis_heading[ii]); - change_basis(jj, ii); - } - if (m_factorization->get_status() != LU_status::OK) { - change_basis(ii, jj); - init_lu(); - } else { - break; - } } - SASSERT(m_factorization->get_status()== LU_status::OK); } } +template bool lp_core_solver_base::remove_from_basis(unsigned basic_j) { + indexed_vector w(m_basis.size()); // the buffer + unsigned i = m_basis_heading[basic_j]; + for (auto &c : m_A.m_rows[i]) { + if (c.var() == basic_j) + continue; + if (pivot_column_general(c.var(), basic_j, w)) + return true; + } + return false; +} + + template bool lp_core_solver_base::infeasibility_costs_are_correct() const { if (! this->m_using_infeas_costs) return true; - SASSERT(costs_on_nbasis_are_zeros()); + lp_assert(costs_on_nbasis_are_zeros()); for (unsigned j :this->m_basis) { if (!infeasibility_cost_is_correct_for_column(j)) { - std::cout << "infeasibility_cost_is_correct_for_column does not hold\n"; - print_column_info(j, std::cout); return false; } if (!is_zero(m_d[j])) { - std::cout << "m_d is not zero\n"; - print_column_info(j, std::cout); return false; } } @@ -1012,7 +1013,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; } @@ -1026,9 +1027,31 @@ lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) case column_type::free_column: return is_zero(this->m_costs[j]); default: - SASSERT(false); + lp_assert(false); return true; } } +template +void lp_core_solver_base::calculate_pivot_row(unsigned i) { + lp_assert(!use_tableau()); + lp_assert(m_pivot_row.is_OK()); + m_pivot_row_of_B_1.clear(); + m_pivot_row_of_B_1.resize(m_m()); + m_pivot_row.clear(); + m_pivot_row.resize(m_n()); + if (m_settings.use_tableau()) { + unsigned basic_j = m_basis[i]; + for (auto & c : m_A.m_rows[i]) { + if (c.m_j != basic_j) + m_pivot_row.set_value(c.get_val(), c.m_j); + } + return; + } + + calculate_pivot_row_of_B_1(i); + calculate_pivot_row_when_pivot_row_of_B1_is_ready(i); +} + + } 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 84% rename from src/util/lp/lp_dual_core_solver.hpp rename to src/util/lp/lp_dual_core_solver_def.h index 5d48fe24d..86e6231fc 100644 --- a/src/util/lp/lp_dual_core_solver.hpp +++ b/src/util/lp/lp_dual_core_solver_def.h @@ -38,7 +38,7 @@ template void lp_dual_core_solver::restore_non_ba while (j--) { if (this->m_basis_heading[j] >= 0 ) continue; if (m_can_enter_basis[j]) { - SASSERT(std::find(nb.begin(), nb.end(), j) == nb.end()); + lp_assert(std::find(nb.begin(), nb.end(), j) == nb.end()); nb.push_back(j); this->m_basis_heading[j] = - static_cast(nb.size()); } @@ -97,25 +97,25 @@ template void lp_dual_core_solver::start_with_ini } template bool lp_dual_core_solver::done() { - if (this->get_status() == OPTIMAL) { + if (this->get_status() == lp_status::OPTIMAL) { return true; } if (this->total_iterations() > this->m_settings.max_total_number_of_iterations) { // debug !!!! - this->set_status(ITERATIONS_EXHAUSTED); + this->set_status(lp_status::ITERATIONS_EXHAUSTED); return true; } return false; // todo, need to be more cases } -template T lp_dual_core_solver::get_edge_steepness_for_low_bound(unsigned p) { - SASSERT(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]; +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_lower_bounds[p]; del *= del; return del / this->m_betas[this->m_basis_heading[p]]; } template T lp_dual_core_solver::get_edge_steepness_for_upper_bound(unsigned p) { - SASSERT(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); + 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_upper_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(); @@ -150,12 +150,12 @@ template T lp_dual_core_solver::pricing_for_row(u return numeric_traits::zero(); break; case column_type::free_column: - SASSERT(numeric_traits::is_zero(this->m_d[p])); + lp_assert(numeric_traits::is_zero(this->m_d[p])); return numeric_traits::zero(); default: - SASSERT(false); + lp_unreachable(); } - SASSERT(false); + lp_unreachable(); return numeric_traits::zero(); } @@ -185,8 +185,8 @@ template void lp_dual_core_solver::pricing_loop(u } } while (i != initial_offset_in_rows && rows_left); if (m_r == -1) { - if (this->get_status() != UNSTABLE) { - this->set_status(OPTIMAL); + if (this->get_status() != lp_status::UNSTABLE) { + this->set_status(lp_status::OPTIMAL); } } else { m_p = this->m_basis[m_r]; @@ -196,10 +196,10 @@ template void lp_dual_core_solver::pricing_loop(u return; } // failure in advance_on_known_p - if (this->get_status() == FLOATING_POINT_ERROR) { + if (this->get_status() == lp_status::FLOATING_POINT_ERROR) { return; } - this->set_status(UNSTABLE); + this->set_status(lp_status::UNSTABLE); m_forbidden_rows.insert(m_r); } } @@ -224,9 +224,9 @@ template bool lp_dual_core_solver::advance_on_kno int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p); if (!pivot_compare_result){;} else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - SASSERT(false); // not implemented yet + lp_unreachable(); // not implemented yet } else { - SASSERT(pivot_compare_result == 1); + lp_assert(pivot_compare_result == 1); this->init_lu(); } DSE_FTran(); @@ -243,38 +243,38 @@ template int lp_dual_core_solver::define_sign_of_ if (this->x_above_upper_bound(m_p)) { return 1; } - SASSERT(false); - case column_type::low_bound: + lp_unreachable(); + case column_type::lower_bound: if (this->x_below_low_bound(m_p)) { return -1; } - SASSERT(false); + lp_unreachable(); case column_type::upper_bound: if (this->x_above_upper_bound(m_p)) { return 1; } - SASSERT(false); + lp_unreachable(); default: - SASSERT(false); + lp_unreachable(); } - SASSERT(false); + lp_unreachable(); return 0; } 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: - SASSERT(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: - SASSERT(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j])); + 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,28 +302,28 @@ 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]; } - SASSERT(false); - case column_type::low_bound: + lp_unreachable(); + 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]; } - SASSERT(false); + lp_unreachable(); case column_type::upper_bound: if (this->x_above_upper_bound(m_p)) { return get_edge_steepness_for_upper_bound(m_p); } - SASSERT(false); + lp_unreachable(); case column_type::fixed: return this->m_x[m_p] - this->m_upper_bounds[m_p]; default: - SASSERT(false); + lp_unreachable(); } - SASSERT(false); + lp_unreachable(); return zero_of_type(); } @@ -370,11 +370,11 @@ template void lp_dual_core_solver::update_betas() template void lp_dual_core_solver::apply_flips() { for (unsigned j : m_flipped_boxed) { - SASSERT(this->x_is_at_bound(j)); - if (this->x_is_at_low_bound(j)) { + lp_assert(this->x_is_at_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]; @@ -400,7 +400,7 @@ template void lp_dual_core_solver::snap_xN_column case column_type::free_column: break; default: - SASSERT(false); + lp_unreachable(); } } @@ -456,15 +456,15 @@ template bool lp_dual_core_solver::basis_change_a return false; } - SASSERT(d_is_correct()); + lp_assert(d_is_correct()); return true; } 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]; @@ -472,7 +472,7 @@ template void lp_dual_core_solver::recover_leavin case free_of_bounds: this->m_x[m_q] = zero_of_type(); default: - SASSERT(false); + lp_unreachable(); } } @@ -481,12 +481,12 @@ template void lp_dual_core_solver::revert_to_prev this->change_basis_unconditionally(m_p, m_q); init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); // complete failure + this->set_status(lp_status::FLOATING_POINT_ERROR); // complete failure return; } recover_leaving(); if (!this->find_x_by_solving()) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); return; } recalculate_xB_and_d(); @@ -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; } } @@ -535,12 +535,6 @@ template bool lp_dual_core_solver::snap_runaway_n template bool lp_dual_core_solver::problem_is_dual_feasible() const { for (unsigned j : this->non_basis()){ if (!this->column_is_dual_feasible(j)) { - // std::cout << "column " << j << " is not dual feasible" << std::endl; - // 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 << "total_iterations = " << this->total_iterations() << std::endl; return false; } } @@ -566,10 +560,10 @@ template bool lp_dual_core_solver::delta_keeps_th } template void lp_dual_core_solver::set_status_to_tentative_dual_unbounded_or_dual_unbounded() { - if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) { - this->set_status(DUAL_UNBOUNDED); + if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { + this->set_status(lp_status::DUAL_UNBOUNDED); } else { - this->set_status(TENTATIVE_DUAL_UNBOUNDED); + this->set_status(lp_status::TENTATIVE_DUAL_UNBOUNDED); } } @@ -599,10 +593,10 @@ template bool lp_dual_core_solver::tight_breakpoi template T lp_dual_core_solver::calculate_harris_delta_on_breakpoint_set() { bool first_time = true; T ret = zero_of_type(); - SASSERT(m_breakpoint_set.size() > 0); + 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 +614,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); } @@ -648,7 +642,7 @@ template void lp_dual_core_solver::find_q_on_tigh } } m_tight_set.erase(m_q); - SASSERT(m_q != -1); + lp_assert(m_q != -1); } template void lp_dual_core_solver::find_q_and_tight_set() { @@ -675,7 +669,7 @@ template bool lp_dual_core_solver::ratio_test() { set_status_to_tentative_dual_unbounded_or_dual_unbounded(); return false; } - this->set_status(FEASIBLE); + this->set_status(lp_status::FEASIBLE); find_q_and_tight_set(); if (!tight_breakpoinst_are_all_boxed()) break; T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign; @@ -731,19 +725,19 @@ template void lp_dual_core_solver::update_xb_afte template void lp_dual_core_solver::one_iteration() { unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving(); unsigned offset_in_rows = this->m_settings.random_next() % this->m_m(); - if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) { + if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { number_of_rows_to_try = this->m_m(); } else { - this->set_status(FEASIBLE); + this->set_status(lp_status::FEASIBLE); } pricing_loop(number_of_rows_to_try, offset_in_rows); - SASSERT(problem_is_dual_feasible()); + lp_assert(problem_is_dual_feasible()); } template void lp_dual_core_solver::solve() { // see the page 35 - SASSERT(d_is_correct()); - SASSERT(problem_is_dual_feasible()); - SASSERT(this->basis_heading_is_correct()); + lp_assert(d_is_correct()); + lp_assert(problem_is_dual_feasible()); + lp_assert(this->basis_heading_is_correct()); this->set_total_iterations(0); this->iters_with_no_cost_growing() = 0; do { @@ -751,7 +745,7 @@ template void lp_dual_core_solver::solve() { // s return; } one_iteration(); - } while (this->get_status() != FLOATING_POINT_ERROR && this->get_status() != DUAL_UNBOUNDED && this->get_status() != OPTIMAL && + } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && this->get_status() != lp_status::DUAL_UNBOUNDED && this->get_status() != lp_status::OPTIMAL && this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements && this->total_iterations() <= this->m_settings.max_total_number_of_iterations); } 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 81% rename from src/util/lp/lp_dual_simplex.hpp rename to src/util/lp/lp_dual_simplex_def.h index 248dff448..e32a5e97f 100644 --- a/src/util/lp/lp_dual_simplex.hpp +++ b/src/util/lp/lp_dual_simplex_def.h @@ -22,61 +22,61 @@ namespace lp{ template void lp_dual_simplex::decide_on_status_after_stage1() { switch (m_core_solver->get_status()) { - case OPTIMAL: + case lp_status::OPTIMAL: if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - this->m_status = FEASIBLE; + this->m_status = lp_status::FEASIBLE; } else { - this->m_status = UNBOUNDED; + this->m_status = lp_status::UNBOUNDED; } break; - case DUAL_UNBOUNDED: - SASSERT(false); - case ITERATIONS_EXHAUSTED: - this->m_status = ITERATIONS_EXHAUSTED; + case lp_status::DUAL_UNBOUNDED: + lp_unreachable(); + case lp_status::ITERATIONS_EXHAUSTED: + this->m_status = lp_status::ITERATIONS_EXHAUSTED; break; - case TIME_EXHAUSTED: - this->m_status = TIME_EXHAUSTED; + case lp_status::TIME_EXHAUSTED: + this->m_status = lp_status::TIME_EXHAUSTED; break; - case FLOATING_POINT_ERROR: - this->m_status = FLOATING_POINT_ERROR; + case lp_status::FLOATING_POINT_ERROR: + this->m_status = lp_status::FLOATING_POINT_ERROR; break; default: - SASSERT(false); + lp_unreachable(); } } template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { - SASSERT(j >= this->number_of_core_structurals()); + 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; default: - SASSERT(false); + lp_unreachable(); } } 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: case column_type::upper_bound: - SASSERT(false); + 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; @@ -85,7 +85,7 @@ template void lp_dual_simplex::fix_structural_for m_column_types_of_core_solver[j] = column_type::free_column; break; default: - SASSERT(false); + lp_unreachable(); } // T cost_was = this->m_costs[j]; this->set_scaled_cost(j); @@ -114,23 +114,23 @@ template void lp_dual_simplex::solve_for_stage2() m_core_solver->solve_yB(m_core_solver->m_y); m_core_solver->fill_reduced_costs_from_m_y_by_rows(); m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - m_core_solver->set_status(FEASIBLE); + m_core_solver->set_status(lp_status::FEASIBLE); m_core_solver->solve(); switch (m_core_solver->get_status()) { - case OPTIMAL: - this->m_status = OPTIMAL; + case lp_status::OPTIMAL: + this->m_status = lp_status::OPTIMAL; break; - case DUAL_UNBOUNDED: - this->m_status = INFEASIBLE; + case lp_status::DUAL_UNBOUNDED: + this->m_status = lp_status::INFEASIBLE; break; - case TIME_EXHAUSTED: - this->m_status = TIME_EXHAUSTED; + case lp_status::TIME_EXHAUSTED: + this->m_status = lp_status::TIME_EXHAUSTED; break; - case FLOATING_POINT_ERROR: - this->m_status = FLOATING_POINT_ERROR; + case lp_status::FLOATING_POINT_ERROR: + this->m_status = lp_status::FLOATING_POINT_ERROR; break; default: - SASSERT(false); + lp_unreachable(); } this->m_second_stage_iterations = m_core_solver->total_iterations(); this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); @@ -144,7 +144,7 @@ template void lp_dual_simplex::fill_x_with_zeros( } template void lp_dual_simplex::stage1() { - SASSERT(m_core_solver == nullptr); + lp_assert(m_core_solver == nullptr); this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); if (this->m_settings.get_message_ostream() != nullptr) this->print_statistics_on_A(*this->m_settings.get_message_ostream()); @@ -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); @@ -166,7 +166,7 @@ template void lp_dual_simplex::stage1() { m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { // skipping stage 1 - m_core_solver->set_status(OPTIMAL); + m_core_solver->set_status(lp_status::OPTIMAL); m_core_solver->set_total_iterations(0); } else { m_core_solver->solve(); @@ -192,7 +192,7 @@ template void lp_dual_simplex::fill_first_stage_s } template column_type lp_dual_simplex::get_column_type(unsigned j) { - SASSERT(j < this->m_A->column_count()); + lp_assert(j < this->m_A->column_count()); if (j >= this->number_of_core_structurals()) { return m_column_types_of_logicals[j - this->number_of_core_structurals()]; } @@ -201,12 +201,12 @@ template column_type lp_dual_simplex::get_column_ template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { // see 4.7 in the dissertation of Achim Koberstein - SASSERT(this->m_core_solver_columns_to_external_columns.find(j) != + lp_assert(this->m_core_solver_columns_to_external_columns.find(j) != this->m_core_solver_columns_to_external_columns.end()); T free_bound = T(1e4); // see 4.8 unsigned jj = this->m_core_solver_columns_to_external_columns[j]; - SASSERT(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); + lp_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); column_info * ci = this->m_map_from_var_index_to_column_info[jj]; switch (ci->get_column_type()) { case column_type::upper_bound: { @@ -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,30 +227,30 @@ 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: - SASSERT(false); + lp_unreachable(); } m_column_types_of_core_solver[j] = column_type::boxed; } 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; - SASSERT(get_column_type(j) != column_type::upper_bound); - if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::low_bound))) { + lp_assert(get_column_type(j) != column_type::upper_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(); } } @@ -269,7 +269,7 @@ template void lp_dual_simplex::fill_costs_and_bou template void lp_dual_simplex::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, unsigned & slack_var, unsigned & artificial) { - SASSERT(row < this->row_count()); + lp_assert(row < this->row_count()); auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; // we need to bring the program to the form Ax = b T rs = this->m_b[row]; @@ -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()); } @@ -351,7 +351,7 @@ template void lp_dual_simplex::find_maximal_solut this->flip_costs(); // do it for now, todo ( remove the flipping) this->cleanup(); - if (this->m_status == INFEASIBLE) { + if (this->m_status == lp_status::INFEASIBLE) { return; } this->fill_matrix_A_and_init_right_side(); @@ -361,7 +361,7 @@ template void lp_dual_simplex::find_maximal_solut fill_first_stage_solver_fields(); copy_m_b_aside_and_set_it_to_zeros(); stage1(); - if (this->m_status == FEASIBLE) { + if (this->m_status == lp_status::FEASIBLE) { stage2(); } } 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 fd5f42d67..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.h" +#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 139cf3ad5..26fc550b3 100644 --- a/src/util/lp/lp_primal_core_solver.h +++ b/src/util/lp/lp_primal_core_solver.h @@ -37,10 +37,9 @@ Revision History: #include "util/lp/breakpoint.h" #include "util/lp/binary_heap_priority_queue.h" #include "util/lp/int_set.h" -#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 { @@ -85,7 +84,7 @@ public: // unsigned len = 100000000; // for (unsigned j : this->m_inf_set.m_index) { // int i = this->m_basis_heading[j]; - // SASSERT(i >= 0); + // lp_assert(i >= 0); // unsigned row_len = this->m_A.m_rows[i].size(); // if (row_len < len) { // choices.clear(); @@ -113,52 +112,52 @@ public: bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const { // sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos // we have xbj = -aj * xj - SASSERT(this->m_basis_heading[j] < 0); - SASSERT(this->column_is_feasible(j)); + lp_assert(this->m_basis_heading[j] < 0); + lp_assert(this->column_is_feasible(j)); 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); } - SASSERT(false); // cannot be here + lp_assert(false); // cannot be here return false; } bool needs_to_grow(unsigned bj) const { - SASSERT(!this->column_is_feasible(bj)); + lp_assert(!this->column_is_feasible(bj)); switch(this->m_column_types[bj]) { 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: return false; } - SASSERT(false); // unreachable + lp_assert(false); // unreachable return false; } int inf_sign_of_column(unsigned bj) const { - SASSERT(!this->column_is_feasible(bj)); + lp_assert(!this->column_is_feasible(bj)); 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: @@ -166,7 +165,7 @@ public: default: return -1; } - SASSERT(false); // unreachable + lp_assert(false); // unreachable return 0; } @@ -174,15 +173,15 @@ public: bool monoid_can_decrease(const row_cell & rc) const { unsigned j = rc.m_j; - SASSERT(this->column_is_feasible(j)); + lp_assert(this->column_is_feasible(j)); 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 (is_pos(rc.get_val())) { - return this->x_above_low_bound(j); + return this->x_above_lower_bound(j); } return true; @@ -194,28 +193,28 @@ 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); default: return false; } - SASSERT(false); // unreachable + lp_assert(false); // unreachable return false; } bool monoid_can_increase(const row_cell & rc) const { unsigned j = rc.m_j; - SASSERT(this->column_is_feasible(j)); + lp_assert(this->column_is_feasible(j)); 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 (is_neg(rc.get_val())) { - return this->x_above_low_bound(j); + return this->x_above_lower_bound(j); } return true; @@ -227,14 +226,14 @@ 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); default: return false; } - SASSERT(false); // unreachable + lp_assert(false); // unreachable return false; } @@ -344,24 +343,24 @@ public: } void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - SASSERT(m < 0 && this->m_column_types[j] == column_type::upper_bound); + lp_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound); limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); } - void limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - SASSERT(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) { - SASSERT(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) { - SASSERT(m > 0 && this->m_column_types[j] == column_type::upper_bound); + lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); }; @@ -370,7 +369,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); @@ -403,7 +402,7 @@ public: bool need_to_switch_costs() const { if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) return false; - // SASSERT(calc_current_x_is_feasible() == current_x_is_feasible()); + // lp_assert(calc_current_x_is_feasible() == current_x_is_feasible()); return this->current_x_is_feasible() == this->m_using_infeas_costs; } @@ -420,7 +419,7 @@ public: // returns the number of iterations unsigned solve(); - lu * factorization() {return this->m_factorization;} + lu> * factorization() {return this->m_factorization;} void delete_factorization(); @@ -445,7 +444,7 @@ public: void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, const X &theta ) { this->update_basis_and_x_tableau(entering, leaving, theta); - this->update_column_in_inf_set(entering); + this->track_column_feasibility(entering); } @@ -458,23 +457,23 @@ public: if (j == -1) return -1; - SASSERT(!this->column_is_feasible(j)); + lp_assert(!this->column_is_feasible(j)); switch (this->m_column_types[j]) { case column_type::fixed: 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: - SASSERT(false); + lp_assert(false); new_val_for_leaving = numeric_traits::zero(); // does not matter } return j; @@ -484,7 +483,7 @@ public: X new_val_for_leaving; int leaving = find_leaving_tableau_rows(new_val_for_leaving); if (leaving == -1) { - this->set_status(OPTIMAL); + this->set_status(lp_status::OPTIMAL); return; } @@ -500,14 +499,14 @@ public: T a_ent; int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent); if (entering == -1) { - this->set_status(INFEASIBLE); + this->set_status(lp_status::INFEASIBLE); return; } X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta ); - SASSERT(this->m_x[leaving] == new_val_for_leaving); + lp_assert(this->m_x[leaving] == new_val_for_leaving); if (this->current_x_is_feasible()) - this->set_status(OPTIMAL); + this->set_status(lp_status::OPTIMAL); } void fill_breakpoints_array(unsigned entering); @@ -522,30 +521,30 @@ public: void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta); void decide_on_status_when_cannot_find_entering() { - SASSERT(!need_to_switch_costs()); - this->set_status(this->current_x_is_feasible()? OPTIMAL: INFEASIBLE); + lp_assert(!need_to_switch_costs()); + this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE); } // void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) { - // SASSERT(m < 0); - // SASSERT(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(m < 0); + // 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) { - SASSERT(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); + lp_assert(m < 0); + 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(); } bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller - SASSERT(m < 0); + lp_assert(m < 0); if (numeric_traits::precise()) { if (this->below_bound(x, bound)) return false; if (this->above_bound(x, bound)) { @@ -569,7 +568,7 @@ public: bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets larger - SASSERT(m > 0); + lp_assert(m > 0); if (numeric_traits::precise()) { if (this->above_bound(x, bound)) return false; if (this->below_bound(x, bound)) { @@ -591,17 +590,17 @@ 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 - SASSERT(m > 0); + lp_assert(m > 0); if (this->below_bound(x, bound)) { limit_theta((bound - x) / m, theta, unlimited); } } else { // x gets larger - SASSERT(m > 0); + lp_assert(m > 0); const X& eps = harris_eps_for_bound(bound); if (this->below_bound(x, bound)) { limit_theta((bound - x + eps) / m, theta, unlimited); @@ -611,7 +610,7 @@ public: void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller - SASSERT(m < 0); + lp_assert(m < 0); const X& eps = harris_eps_for_bound(bound); if (this->above_bound(x, bound)) { limit_theta((bound - x - eps) / m, theta, unlimited); @@ -619,9 +618,9 @@ public: } void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // SASSERT(m > 0 && this->m_column_type[j] == column_type::boxed); + // 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]); @@ -639,14 +638,14 @@ public: } void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // SASSERT(m < 0 && this->m_column_type[j] == column_type::boxed); + // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); const X & x = this->m_x[j]; const X & ubound = this->m_upper_bounds[j]; if (this->above_bound(x, ubound)) { 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); @@ -657,7 +656,7 @@ public: } } void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { - SASSERT(m > 0); + lp_assert(m > 0); const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); @@ -669,7 +668,7 @@ public: } void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { - SASSERT(m > 0); + lp_assert(m > 0); const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); if (theta < zero_of_type()) { @@ -679,7 +678,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 +695,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: @@ -735,7 +734,7 @@ public: break; default: - SASSERT(false); + lp_unreachable(); } if (!unlimited && theta < zero_of_type()) { theta = zero_of_type(); @@ -770,7 +769,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); @@ -794,7 +793,7 @@ public: if (this->m_basis_heading[j] < 0) continue; if (!this->column_is_feasible(j)) - this->m_inf_set.insert(j); + this->insert_column_into_inf_set(j); } } @@ -807,7 +806,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; @@ -818,7 +817,7 @@ public: case column_type::free_column: return 0; default: - SASSERT(false); + lp_assert(false); } return 0; } @@ -842,18 +841,18 @@ 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: return -1; break; default: - SASSERT(false); + lp_assert(false); } return 0; @@ -879,7 +878,7 @@ public: // the delta is between the old and the new cost (old - new) void update_reduced_cost_for_basic_column_cost_change(const T & delta, unsigned j) { - SASSERT(this->m_basis_heading[j] >= 0); + lp_assert(this->m_basis_heading[j] >= 0); unsigned i = static_cast(this->m_basis_heading[j]); for (const row_cell & rc : this->m_A.m_rows[i]) { unsigned k = rc.m_j; @@ -906,7 +905,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 +918,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)), @@ -930,7 +929,7 @@ public: } else { m_converted_harris_eps = zero_of_type(); } - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } // constructor @@ -954,12 +953,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)) { - SASSERT(initial_x_is_correct()); - m_low_bounds_dummy.resize(A.column_count(), zero_of_type()); + lp_assert(initial_x_is_correct()); + 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 +971,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 +982,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 86% rename from src/util/lp/lp_primal_core_solver.hpp rename to src/util/lp/lp_primal_core_solver_def.h index 5f81def51..1e9edbd31 100644 --- a/src/util/lp/lp_primal_core_solver.hpp +++ b/src/util/lp/lp_primal_core_solver_def.h @@ -25,12 +25,12 @@ 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 void lp_primal_core_solver::sort_non_basis_rational() { - SASSERT(numeric_traits::precise()); + lp_assert(numeric_traits::precise()); if (this->m_settings.use_tableau()) { std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_A.number_of_non_zeroes_in_column(a); @@ -84,12 +84,12 @@ 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: - SASSERT(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: - SASSERT(this->x_is_at_upper_bound(j)); + lp_assert(this->x_is_at_upper_bound(j)); ret = d > m_epsilon_of_reduced_cost; break; case column_type::fixed: @@ -97,16 +97,16 @@ 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); - SASSERT(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: ret = d > m_epsilon_of_reduced_cost || d < - m_epsilon_of_reduced_cost; break; default: - SASSERT(false); + lp_unreachable(); ret = false; break; } @@ -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,19 +137,19 @@ 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; default: - SASSERT(false); + lp_unreachable(); break; } return false; } template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precise(unsigned j) const { - SASSERT (numeric_traits::precise()); + lp_assert (numeric_traits::precise()); if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search) return column_is_benefitial_for_entering_on_breakpoints(j); const T& dj = this->m_d[j]; @@ -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,12 +177,12 @@ 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; default: - SASSERT(false); + lp_unreachable(); break; } return false; @@ -190,7 +190,7 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis template int lp_primal_core_solver::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - SASSERT(numeric_traits::precise()); + lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { @@ -274,7 +274,7 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef template int lp_primal_core_solver::advance_on_sorted_breakpoints(unsigned entering, X &t) { T slope_at_entering = this->m_d[entering]; breakpoint * last_bp = nullptr; - SASSERT(m_breakpoint_indices_queue.is_empty()==false); + lp_assert(m_breakpoint_indices_queue.is_empty()==false); while (m_breakpoint_indices_queue.is_empty() == false) { unsigned bi = m_breakpoint_indices_queue.dequeue(); breakpoint *b = &m_breakpoints[bi]; @@ -289,7 +289,7 @@ template int lp_primal_core_solver::advance_on_so } } } - SASSERT (last_bp != nullptr); + lp_assert (last_bp != nullptr); t = last_bp->m_delta; return last_bp->m_j; } @@ -297,13 +297,13 @@ template int lp_primal_core_solver::advance_on_so template int lp_primal_core_solver::find_leaving_and_t_with_breakpoints(unsigned entering, X & t){ - SASSERT(this->precise() == false); + lp_assert(this->precise() == false); fill_breakpoints_array(entering); return advance_on_sorted_breakpoints(entering, t); } template bool lp_primal_core_solver::get_harris_theta(X & theta) { - SASSERT(this->m_ed.is_OK()); + lp_assert(this->m_ed.is_OK()); bool unlimited = true; for (unsigned i : this->m_ed.m_index) { if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; @@ -360,13 +360,13 @@ template bool lp_primal_core_solver::try_jump_to_ if (m_sign_of_entering_delta > 0) { t = this->m_upper_bounds[entering] - this->m_x[entering]; if (unlimited || t <= theta){ - SASSERT(t >= zero_of_type()); + lp_assert(t >= zero_of_type()); 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) { - SASSERT(t >= zero_of_type()); + lp_assert(t >= zero_of_type()); return true; } } @@ -375,16 +375,16 @@ template bool lp_primal_core_solver::try_jump_to_ if (m_sign_of_entering_delta > 0) { t = this->m_upper_bounds[entering] - this->m_x[entering]; if (unlimited || t <= theta){ - SASSERT(t >= zero_of_type()); + lp_assert(t >= zero_of_type()); return true; } } 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) { - SASSERT(t >= zero_of_type()); + 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; } @@ -420,7 +420,7 @@ template int lp_primal_core_solver::find_leaving_ do { unsigned i = this->m_ed.m_index[k]; const T & ed = this->m_ed[i]; - SASSERT(!numeric_traits::is_zero(ed)); + lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); if (!unlimited) { @@ -439,7 +439,7 @@ template int lp_primal_core_solver::find_leaving_ while (k != initial_k) { unsigned i = this->m_ed.m_index[k]; const T & ed = this->m_ed[i]; - SASSERT(!numeric_traits::is_zero(ed)); + lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; unlimited = true; limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); @@ -479,7 +479,7 @@ template int lp_primal_core_solver::find_leavi return find_leaving_and_t_with_breakpoints(entering, t); X theta; bool unlimited = get_harris_theta(theta); - SASSERT(unlimited || theta >= zero_of_type()); + lp_assert(unlimited || theta >= zero_of_type()); if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; if (unlimited) return -1; @@ -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; } @@ -548,7 +548,7 @@ template X lp_primal_core_solver::get_max_boun template void lp_primal_core_solver::check_Ax_equal_b() { dense_matrix d(this->m_A); T * ls = d.apply_from_left_with_different_dims(this->m_x); - SASSERT(vectors_are_equal(ls, this->m_b, this->m_m())); + lp_assert(vectors_are_equal(ls, this->m_b, this->m_m())); delete [] ls; } template void lp_primal_core_solver::check_the_bounds() { @@ -558,8 +558,8 @@ template void lp_primal_core_solver::check_the } template void lp_primal_core_solver::check_bound(unsigned i) { - SASSERT (!(this->column_has_low_bound(i) && (numeric_traits::zero() > this->m_x[i]))); - SASSERT (!(this->column_has_upper_bound(i) && (this->m_upper_bounds[i] < 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]))); } template void lp_primal_core_solver::check_correctness() { @@ -575,8 +575,8 @@ void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned e // the basis heading has changed already #ifdef Z3DEBUG auto & basis_heading = this->m_basis_heading; - SASSERT(basis_heading[entering] >= 0 && static_cast(basis_heading[entering]) < this->m_m()); - SASSERT(basis_heading[leaving] < 0); + lp_assert(basis_heading[entering] >= 0 && static_cast(basis_heading[entering]) < this->m_m()); + lp_assert(basis_heading[leaving] < 0); #endif T pivot = this->m_pivot_row[entering]; T dq = this->m_d[entering]/pivot; @@ -599,7 +599,7 @@ void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned e template int lp_primal_core_solver::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { if (numeric_traits::precise()) return 0; T reduced_at_entering_was = this->m_d[entering]; // can benefit from going over non-zeros of m_ed - SASSERT(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost); + lp_assert(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost); T refreshed_cost = this->m_costs[entering]; unsigned i = this->m_m(); while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i]; @@ -634,7 +634,7 @@ template void lp_primal_core_solver::backup_an m_costs_backup = this->m_costs; } else { T cost_max = std::max(max_abs_in_vector(this->m_costs), T(1)); - SASSERT(m_costs_backup.size() == 0); + lp_assert(m_costs_backup.size() == 0); for (unsigned j = 0; j < this->m_costs.size(); j++) m_costs_backup.push_back(this->m_costs[j] /= cost_max); } @@ -664,16 +664,16 @@ template void lp_primal_core_solver::init_run( template void lp_primal_core_solver::calc_working_vector_beta_for_column_norms(){ - SASSERT(numeric_traits::precise() == false); - SASSERT(this->m_ed.is_OK()); - SASSERT(m_beta.is_OK()); + lp_assert(numeric_traits::precise() == false); + lp_assert(this->m_ed.is_OK()); + lp_assert(m_beta.is_OK()); m_beta = this->m_ed; this->m_factorization->solve_yB_with_error_check_indexed(m_beta, this->m_basis_heading, this->m_basis, this->m_settings); } template void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering, X & t) { - SASSERT(!this->A_mult_x_is_off() ); + CASSERT("A_off", !this->A_mult_x_is_off() ); this->update_x(entering, t * m_sign_of_entering_delta); if (this->A_mult_x_is_off_on_index(this->m_ed.m_index) && !this->find_x_by_solving()) { this->init_lu(); @@ -685,7 +685,7 @@ void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering } } if (this->m_using_infeas_costs) { - SASSERT(is_zero(this->m_costs[entering])); + lp_assert(is_zero(this->m_costs[entering])); init_infeasibility_costs_for_changed_basis_only(); } if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) @@ -698,10 +698,10 @@ void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering } template void lp_primal_core_solver::advance_on_entering_and_leaving(int entering, int leaving, X & t) { - SASSERT(entering >= 0 && m_non_basis_list.back() == static_cast(entering)); - SASSERT(this->m_using_infeas_costs || t >= zero_of_type()); - SASSERT(leaving >= 0 && entering >= 0); - SASSERT(entering != leaving || !is_zero(t)); // otherwise nothing changes + lp_assert(entering >= 0 && m_non_basis_list.back() == static_cast(entering)); + lp_assert(this->m_using_infeas_costs || t >= zero_of_type()); + lp_assert(leaving >= 0 && entering >= 0); + lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes if (entering == leaving) { advance_on_entering_equal_leaving(entering, t); return; @@ -713,14 +713,14 @@ template void lp_primal_core_solver::advance_on_en int pivot_compare_result = this->pivots_in_column_and_row_are_different(entering, leaving); if (!pivot_compare_result){;} else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - this->set_status(UNSTABLE); + this->set_status(lp_status::UNSTABLE); this->iters_with_no_cost_growing()++; return; } else { - SASSERT(pivot_compare_result == 1); + lp_assert(pivot_compare_result == 1); this->init_lu(); if (this->m_factorization == nullptr || this->m_factorization->get_status() != LU_status::OK) { - this->set_status(UNSTABLE); + this->set_status(lp_status::UNSTABLE); this->iters_with_no_cost_growing()++; return; } @@ -732,10 +732,10 @@ template void lp_primal_core_solver::advance_on_en t = -t; } if (!this->update_basis_and_x(entering, leaving, t)) { - if (this->get_status() == FLOATING_POINT_ERROR) + if (this->get_status() == lp_status::FLOATING_POINT_ERROR) return; if (this->m_look_for_feasible_solution_only) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); return; } init_reduced_costs(); @@ -748,7 +748,7 @@ template void lp_primal_core_solver::advance_on_en } if (this->current_x_is_feasible()) { - this->set_status(FEASIBLE); + this->set_status(lp_status::FEASIBLE); if (this->m_look_for_feasible_solution_only) return; } @@ -761,7 +761,7 @@ template void lp_primal_core_solver::advance_on_en } else { update_reduced_costs_from_pivot_row(entering, leaving); } - SASSERT(!need_to_switch_costs()); + lp_assert(!need_to_switch_costs()); std::list::iterator it = m_non_basis_list.end(); it--; * it = static_cast(leaving); @@ -769,13 +769,13 @@ template void lp_primal_core_solver::advance_on_en template void lp_primal_core_solver::advance_on_entering_precise(int entering) { - SASSERT(numeric_traits::precise()); - SASSERT(entering > -1); + lp_assert(numeric_traits::precise()); + lp_assert(entering > -1); this->solve_Bd(entering); X t; int leaving = find_leaving_and_t_precise(entering, t); if (leaving == -1) { - this->set_status(UNBOUNDED); + this->set_status(lp_status::UNBOUNDED); return; } advance_on_entering_and_leaving(entering, leaving, t); @@ -786,12 +786,12 @@ template void lp_primal_core_solver::advance_on_e advance_on_entering_precise(entering); return; } - SASSERT(entering > -1); + lp_assert(entering > -1); this->solve_Bd(entering); int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering); if (refresh_result) { if (this->m_look_for_feasible_solution_only) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); return; } @@ -806,7 +806,7 @@ template void lp_primal_core_solver::advance_on_e int leaving = find_leaving_and_t(entering, t); if (leaving == -1){ if (!this->current_x_is_feasible()) { - SASSERT(!numeric_traits::precise()); // we cannot have unbounded with inf costs + lp_assert(!numeric_traits::precise()); // we cannot have unbounded with inf costs // if (m_look_for_feasible_solution_only) { // this->m_status = INFEASIBLE; @@ -814,19 +814,19 @@ template void lp_primal_core_solver::advance_on_e // } - if (this->get_status() == UNSTABLE) { - this->set_status(FLOATING_POINT_ERROR); + if (this->get_status() == lp_status::UNSTABLE) { + this->set_status(lp_status::FLOATING_POINT_ERROR); return; } init_infeasibility_costs(); - this->set_status(UNSTABLE); + this->set_status(lp_status::UNSTABLE); return; } - if (this->get_status() == TENTATIVE_UNBOUNDED) { - this->set_status(UNBOUNDED); + if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) { + this->set_status(lp_status::UNBOUNDED); } else { - this->set_status(TENTATIVE_UNBOUNDED); + this->set_status(lp_status::TENTATIVE_UNBOUNDED); } return; } @@ -840,7 +840,7 @@ template void lp_primal_core_solver::push_forw template unsigned lp_primal_core_solver::get_number_of_non_basic_column_to_try_for_enter() { unsigned ret = static_cast(this->m_nbasis.size()); - if (this->get_status() == TENTATIVE_UNBOUNDED) + if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) return ret; // we really need to find entering with a large reduced cost if (ret > 300) { ret = (unsigned)(ret * this->m_settings.percent_of_entering_to_check / 100); @@ -864,15 +864,15 @@ template void lp_primal_core_solver::print_column template unsigned lp_primal_core_solver::solve() { if (numeric_traits::precise() && this->m_settings.use_tableau()) return solve_with_tableau(); - + init_run(); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { - this->set_status(FEASIBLE); + this->set_status(lp_status::FEASIBLE); return 0; } if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); return 0; } do { @@ -880,10 +880,11 @@ template unsigned lp_primal_core_solver::solve() return this->total_iterations(); } one_iteration(); - SASSERT(!this->m_using_infeas_costs || this->costs_on_nbasis_are_zeros()); + + lp_assert(!this->m_using_infeas_costs || this->costs_on_nbasis_are_zeros()); switch (this->get_status()) { - case OPTIMAL: // double check that we are at optimum - case INFEASIBLE: + case lp_status::OPTIMAL: // double check that we are at optimum + case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; if (!numeric_traits::precise()) { @@ -892,7 +893,7 @@ template unsigned lp_primal_core_solver::solve() this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status (FLOATING_POINT_ERROR); + this->set_status (lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); @@ -900,7 +901,7 @@ template unsigned lp_primal_core_solver::solve() decide_on_status_when_cannot_find_entering(); break; } - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } else { // precise case if (this->m_look_for_feasible_solution_only) { // todo: keep the reduced costs correct all the time! init_reduced_costs(); @@ -908,31 +909,31 @@ template unsigned lp_primal_core_solver::solve() decide_on_status_when_cannot_find_entering(); break; } - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } } break; - case TENTATIVE_UNBOUNDED: + case lp_status::TENTATIVE_UNBOUNDED: this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); break; - case UNBOUNDED: + case lp_status::UNBOUNDED: if (this->current_x_is_infeasible()) { init_reduced_costs(); - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } break; - case UNSTABLE: - SASSERT(! (numeric_traits::precise())); + case lp_status::UNSTABLE: + lp_assert(! (numeric_traits::precise())); this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); @@ -941,13 +942,13 @@ template unsigned lp_primal_core_solver::solve() 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 && @@ -955,7 +956,7 @@ template unsigned lp_primal_core_solver::solve() && !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); - SASSERT(this->get_status() == FLOATING_POINT_ERROR + lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR || this->current_x_is_feasible() == false || @@ -972,7 +973,7 @@ template void lp_primal_core_solver::delete_fa // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" template void lp_primal_core_solver::init_column_norms() { - SASSERT(numeric_traits::precise() == false); + lp_assert(numeric_traits::precise() == false); for (unsigned j = 0; j < this->m_n(); j++) { this->m_column_norms[j] = T(static_cast(this->m_A.m_columns[j].size() + 1)) @@ -982,7 +983,7 @@ template void lp_primal_core_solver::init_column_ // debug only template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { - SASSERT(numeric_traits::precise() == false); + lp_assert(numeric_traits::precise() == false); indexed_vector w(this->m_m()); this->m_A.copy_column_to_vector(j, w); vector d(this->m_m()); @@ -994,8 +995,8 @@ template T lp_primal_core_solver::calculate_colum } template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { - SASSERT(numeric_traits::precise() == false); - SASSERT(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); + lp_assert(numeric_traits::precise() == false); + lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { m_column_norm_update_counter = 0; init_column_norms(); @@ -1007,7 +1008,7 @@ template void lp_primal_core_solver::update_or // following Swietanowski - A new steepest ... template void lp_primal_core_solver::update_column_norms(unsigned entering, unsigned leaving) { - SASSERT(numeric_traits::precise() == false); + lp_assert(numeric_traits::precise() == false); T pivot = this->m_pivot_row[entering]; T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; if (!numeric_traits::precise()) { @@ -1042,8 +1043,8 @@ template T lp_primal_core_solver::calculate_no // calling it stage1 is too cryptic template void lp_primal_core_solver::find_feasible_solution() { this->m_look_for_feasible_solution_only = true; - SASSERT(this->non_basic_columns_are_set_correctly()); - this->set_status(UNKNOWN); + lp_assert(this->non_basic_columns_are_set_correctly()); + this->set_status(lp_status::UNKNOWN); solve(); } @@ -1087,15 +1088,15 @@ template void lp_primal_core_solver::fill_breakpo template bool lp_primal_core_solver::done() { - if (this->get_status() == OPTIMAL || this->get_status() == FLOATING_POINT_ERROR) return true; - if (this->get_status() == INFEASIBLE) { + if (this->get_status() == lp_status::OPTIMAL || this->get_status() == lp_status::FLOATING_POINT_ERROR) return true; + if (this->get_status() == lp_status::INFEASIBLE) { return true; } if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) { - this->get_status() = ITERATIONS_EXHAUSTED; return true; + this->get_status() = lp_status::ITERATIONS_EXHAUSTED; return true; } if (this->total_iterations() >= this->m_settings.max_total_number_of_iterations) { - this->get_status() = ITERATIONS_EXHAUSTED; return true; + this->get_status() = lp_status::ITERATIONS_EXHAUSTED; return true; } return false; } @@ -1110,8 +1111,8 @@ void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_onl template void lp_primal_core_solver::init_infeasibility_costs() { - SASSERT(this->m_x.size() >= this->m_n()); - SASSERT(this->m_column_types.size() >= this->m_n()); + lp_assert(this->m_x.size() >= this->m_n()); + lp_assert(this->m_column_types.size() >= this->m_n()); for (unsigned j = this->m_n(); j--;) init_infeasibility_cost_for_column(j); this->m_using_infeas_costs = true; @@ -1135,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 { @@ -1153,7 +1154,7 @@ lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const ret = numeric_traits::zero(); break; default: - SASSERT(false); + lp_assert(false); ret = numeric_traits::zero(); // does not matter break; } @@ -1189,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 { @@ -1207,14 +1208,14 @@ lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { this->m_costs[j] = numeric_traits::zero(); break; default: - SASSERT(false); + lp_assert(false); break; } if (numeric_traits::is_zero(this->m_costs[j])) { - this->m_inf_set.erase(j); + this->remove_column_from_inf_set(j); } else { - this->m_inf_set.insert(j); + this->insert_column_into_inf_set(j); } if (!this->m_settings.use_breakpoints_in_feasibility_search) { this->m_costs[j] = - this->m_costs[j]; @@ -1227,18 +1228,18 @@ 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; default: - SASSERT(false); + lp_unreachable(); } } @@ -1277,7 +1278,7 @@ template std::string lp_primal_core_solver::break case upper_break: return "upper_break"; case fixed_break: return "fixed_break"; default: - SASSERT(false); + lp_assert(false); break; } return "type is not found"; @@ -1290,7 +1291,7 @@ template void lp_primal_core_solver::print_breakp template void lp_primal_core_solver::init_reduced_costs() { - SASSERT(!this->use_tableau()); + lp_assert(!this->use_tableau()); if (this->current_x_is_infeasible() && !this->m_using_infeas_costs) { init_infeasibility_costs(); } else if (this->current_x_is_feasible() && this->m_using_infeas_costs) { @@ -1305,12 +1306,12 @@ void lp_primal_core_solver::init_reduced_costs() { template void lp_primal_core_solver::change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering) { if (b->m_j == entering) { - SASSERT(b->m_type != fixed_break && (!is_zero(b->m_delta))); + lp_assert(b->m_type != fixed_break && (!is_zero(b->m_delta))); slope_at_entering += m_sign_of_entering_delta; return; } - SASSERT(this->m_basis_heading[b->m_j] >= 0); + lp_assert(this->m_basis_heading[b->m_j] >= 0); unsigned i_row = this->m_basis_heading[b->m_j]; const T & d = - this->m_ed[i_row]; if (numeric_traits::is_zero(d)) return; @@ -1329,27 +1330,27 @@ template void lp_primal_core_solver::change_sl slope_at_entering += delta; break; default: - SASSERT(false); + lp_assert(false); } } template void lp_primal_core_solver::try_add_breakpoint_in_row(unsigned i) { - SASSERT(i < this->m_m()); + lp_assert(i < this->m_m()); const T & d = this->m_ed[i]; // the coefficient before m_entering in the i-th row if (d == 0) return; // the change of x[m_entering] will not change the corresponding basis x unsigned j = this->m_basis[i]; 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]); @@ -1357,7 +1358,7 @@ template void lp_primal_core_solver::try_add_b case column_type::free_column: break; default: - SASSERT(false); + lp_assert(false); break; } } @@ -1369,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; @@ -1381,7 +1382,7 @@ template void lp_primal_core_solver::print_bound_ out << "inf, inf" << std::endl; break; default: - SASSERT(false); + lp_assert(false); break; } } 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 81% rename from src/util/lp/lp_primal_core_solver_tableau.h rename to src/util/lp/lp_primal_core_solver_tableau_def.h index 0c56f0ab9..f85c131a3 100644 --- a/src/util/lp/lp_primal_core_solver_tableau.h +++ b/src/util/lp/lp_primal_core_solver_tableau_def.h @@ -28,14 +28,14 @@ template void lp_primal_core_solver::one_iteratio else { advance_on_entering_tableau(entering); } - SASSERT(this->inf_set_is_correct()); + lp_assert(this->inf_set_is_correct()); } template void lp_primal_core_solver::advance_on_entering_tableau(int entering) { X t; int leaving = find_leaving_and_t_tableau(entering, t); if (leaving == -1) { - this->set_status(UNBOUNDED); + this->set_status(lp_status::UNBOUNDED); return; } advance_on_entering_and_leaving_tableau(entering, leaving, t); @@ -52,7 +52,7 @@ template int lp_primal_core_solver::choose_enteri //this moment m_y = cB * B(-1) unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); - SASSERT(numeric_traits::precise()); + lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { @@ -100,25 +100,26 @@ template unsigned lp_primal_core_solver::solve_with_tableau() { init_run_tableau(); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { - this->set_status(FEASIBLE); + this->set_status(lp_status::FEASIBLE); return 0; } if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); return 0; } do { if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->m_using_infeas_costs? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) { return this->total_iterations(); } - if (this->m_settings.use_tableau_rows()) + if (this->m_settings.use_tableau_rows()) { one_iteration_tableau_rows(); + } else one_iteration_tableau(); switch (this->get_status()) { - case OPTIMAL: // double check that we are at optimum - case INFEASIBLE: + case lp_status::OPTIMAL: // double check that we are at optimum + case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; if (!numeric_traits::precise()) { @@ -127,7 +128,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); @@ -135,7 +136,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { decide_on_status_when_cannot_find_entering(); break; } - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } else { // precise case if ((!this->infeasibility_costs_are_correct())) { init_reduced_costs_tableau(); // forcing recalc @@ -143,31 +144,31 @@ unsigned lp_primal_core_solver::solve_with_tableau() { decide_on_status_when_cannot_find_entering(); break; } - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } } break; - case TENTATIVE_UNBOUNDED: + case lp_status::TENTATIVE_UNBOUNDED: this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); break; - case UNBOUNDED: + case lp_status::UNBOUNDED: if (this->current_x_is_infeasible()) { init_reduced_costs(); - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } break; - case UNSTABLE: - SASSERT(! (numeric_traits::precise())); + case lp_status::UNSTABLE: + lp_assert(! (numeric_traits::precise())); this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); @@ -176,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 && @@ -193,13 +194,13 @@ 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); } - SASSERT( - this->get_status() == FLOATING_POINT_ERROR + lp_assert( + this->get_status() == lp_status::FLOATING_POINT_ERROR || - this->get_status() == CANCELLED + this->get_status() == lp_status::CANCELLED || this->current_x_is_feasible() == false || @@ -208,13 +209,13 @@ unsigned lp_primal_core_solver::solve_with_tableau() { } template void lp_primal_core_solver::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) { - SASSERT(this->A_mult_x_is_off() == false); - SASSERT(leaving >= 0 && entering >= 0); - SASSERT((this->m_settings.simplex_strategy() == + CASSERT("A_off", this->A_mult_x_is_off() == false); + lp_assert(leaving >= 0 && entering >= 0); + lp_assert((this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) || m_non_basis_list.back() == static_cast(entering)); - SASSERT(this->m_using_infeas_costs || !is_neg(t)); - SASSERT(entering != leaving || !is_zero(t)); // otherwise nothing changes + lp_assert(this->m_using_infeas_costs || !is_neg(t)); + lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes if (entering == leaving) { advance_on_entering_equal_leaving_tableau(entering, t); return; @@ -225,7 +226,7 @@ template void lp_primal_core_solver::advance_on_en t = -t; } this->update_basis_and_x_tableau(entering, leaving, t); - SASSERT(this->A_mult_x_is_off() == false); + CASSERT("A_off", this->A_mult_x_is_off() == false); this->iters_with_no_cost_growing() = 0; } else { this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); @@ -240,7 +241,7 @@ template void lp_primal_core_solver::advance_on_en this->init_reduced_costs_tableau(); } - SASSERT(!need_to_switch_costs()); + lp_assert(!need_to_switch_costs()); std::list::iterator it = m_non_basis_list.end(); it--; * it = static_cast(leaving); @@ -249,7 +250,7 @@ template void lp_primal_core_solver::advance_on_en template void lp_primal_core_solver::advance_on_entering_equal_leaving_tableau(int entering, X & t) { - SASSERT(!this->A_mult_x_is_off() ); + CASSERT("A_off", !this->A_mult_x_is_off() ); this->update_x_tableau(entering, t * m_sign_of_entering_delta); if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) return; @@ -270,7 +271,7 @@ template int lp_primal_core_solver::find_leaving_ const column_cell & c = col[k]; unsigned i = c.m_i; const T & ed = this->m_A.get_val(c); - SASSERT(!numeric_traits::is_zero(ed)); + lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); if (!unlimited) { @@ -289,7 +290,7 @@ template int lp_primal_core_solver::find_leaving_ const column_cell & c = col[k]; unsigned i = c.m_i; const T & ed = this->m_A.get_val(c); - SASSERT(!numeric_traits::is_zero(ed)); + lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; unlimited = true; limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); @@ -321,13 +322,12 @@ template int lp_primal_core_solver::find_leaving_ return m_leaving_candidates[k]; } template void lp_primal_core_solver::init_run_tableau() { - // print_matrix(&(this->m_A), std::cout); - SASSERT(this->A_mult_x_is_off() == false); - SASSERT(basis_columns_are_set_correctly()); + CASSERT("A_off", this->A_mult_x_is_off() == false); + lp_assert(basis_columns_are_set_correctly()); this->m_basis_sort_counter = 0; // to initiate the sort of the basis this->set_total_iterations(0); this->iters_with_no_cost_growing() = 0; - SASSERT(this->inf_set_is_correct()); + lp_assert(this->inf_set_is_correct()); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) return; if (this->m_settings.backup_costs) @@ -341,13 +341,13 @@ template void lp_primal_core_solver::init_run_tab } if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); - SASSERT(this->reduced_costs_are_correct_tableau()); - SASSERT(!this->need_to_pivot_to_basis_tableau()); + lp_assert(this->reduced_costs_are_correct_tableau()); + lp_assert(!this->need_to_pivot_to_basis_tableau()); } template bool lp_primal_core_solver:: update_basis_and_x_tableau(int entering, int leaving, X const & tt) { - SASSERT(this->use_tableau()); + lp_assert(this->use_tableau()); update_x_tableau(entering, tt); this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); this->change_basis(entering, leaving); @@ -355,36 +355,34 @@ update_basis_and_x_tableau(int entering, int leaving, X const & tt) { } template void lp_primal_core_solver:: update_x_tableau(unsigned entering, const X& delta) { + this->add_delta_to_x_and_call_tracker(entering, delta); if (!this->m_using_infeas_costs) { - this->m_x[entering] += delta; for (const auto & c : this->m_A.m_columns[entering]) { unsigned i = c.m_i; - this->m_x[this->m_basis[i]] -= delta * this->m_A.get_val(c); - this->update_column_in_inf_set(this->m_basis[i]); + this->update_x_with_delta_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c)); } } else { // m_using_infeas_costs == true - this->m_x[entering] += delta; - SASSERT(this->column_is_feasible(entering)); - SASSERT(this->m_costs[entering] == zero_of_type()); + lp_assert(this->column_is_feasible(entering)); + lp_assert(this->m_costs[entering] == zero_of_type()); // m_d[entering] can change because of the cost change for basic columns. for (const auto & c : this->m_A.m_columns[entering]) { unsigned i = c.m_i; unsigned j = this->m_basis[i]; - this->m_x[j] -= delta * this->m_A.get_val(c); + this->add_delta_to_x_and_call_tracker(j, -delta * this->m_A.get_val(c)); update_inf_cost_for_column_tableau(j); if (is_zero(this->m_costs[j])) - this->m_inf_set.erase(j); + this->remove_column_from_inf_set(j); else - this->m_inf_set.insert(j); + this->insert_column_into_inf_set(j); } } - SASSERT(this->A_mult_x_is_off() == false); + CASSERT("A_off", this->A_mult_x_is_off() == false); } template void lp_primal_core_solver:: update_inf_cost_for_column_tableau(unsigned j) { - SASSERT(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows); - SASSERT(this->m_using_infeas_costs); + lp_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows); + lp_assert(this->m_using_infeas_costs); T new_cost = get_infeasibility_cost_for_column(j); T delta = this->m_costs[j] - new_cost; if (is_zero(delta)) 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..d0f5d7d7d 100644 --- a/src/util/lp/lp_primal_simplex.h +++ b/src/util/lp/lp_primal_simplex.h @@ -26,12 +26,11 @@ Revision History: #include "util/lp/column_info.h" #include "util/lp/lp_primal_core_solver.h" #include "util/lp/lp_solver.h" -#include "util/lp/iterator_on_row.h" 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 89% rename from src/util/lp/lp_primal_simplex.hpp rename to src/util/lp/lp_primal_simplex_def.h index fd717ec7f..722e4bd79 100644 --- a/src/util/lp/lp_primal_simplex.hpp +++ b/src/util/lp/lp_primal_simplex_def.h @@ -76,14 +76,14 @@ template void lp_primal_simplex::fill_costs_and_x int row, unsigned & slack_var, unsigned & artificial) { - SASSERT(row >= 0 && row < this->row_count()); + lp_assert(row >= 0 && row < this->row_count()); auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; // we need to bring the program to the form Ax = b T rs = this->m_b[row]; 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) { - SASSERT(numeric_traits::is_zero(this->m_x[slack_var])); + 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 - SASSERT(numeric_traits::is_zero(this->m_x[slack_var])); - this->m_column_types[artificial] = column_type::low_bound; + lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); + 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; @@ -192,12 +192,12 @@ template void lp_primal_simplex::fill_A_x_and_bas } template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { - SASSERT(row < this->row_count()); + lp_assert(row < this->row_count()); auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); - SASSERT(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); + lp_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); unsigned ext_row = ext_row_it->second; auto constr_it = this->m_constraints.find(ext_row); - SASSERT(constr_it != this->m_constraints.end()); + lp_assert(constr_it != this->m_constraints.end()); auto & constraint = constr_it->second; unsigned j = this->m_A->column_count(); // j is a slack variable this->m_A->add_column(); @@ -208,34 +208,34 @@ 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: - SASSERT(false); + lp_unreachable(); } } template void lp_primal_simplex::solve_with_total_inf() { int total_vars = this->m_A->column_count() + this->row_count(); if (total_vars == 0) { - this->m_status = OPTIMAL; + 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(); @@ -278,7 +278,6 @@ template bool lp_primal_simplex::bounds_hold(std: } if (!it.second->bounds_hold(sol_it->second)) { - // std::cout << "bounds do not hold for " << it.second->get_name() << std::endl; it.second->bounds_hold(sol_it->second); return false; } @@ -296,10 +295,10 @@ template T lp_primal_simplex::get_row_value(unsig T ret = numeric_traits::zero(); for (auto & pair : it->second) { auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); - SASSERT(cit != this->m_map_from_var_index_to_column_info.end()); + lp_assert(cit != this->m_map_from_var_index_to_column_info.end()); column_info * ci = cit->second; auto sol_it = solution.find(ci->get_name()); - SASSERT(sol_it != solution.end()); + lp_assert(sol_it != solution.end()); T column_val = sol_it->second; if (out != nullptr) { (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; @@ -344,7 +343,7 @@ template bool lp_primal_simplex::row_constraint_h } return true;; } - SASSERT(false); + lp_unreachable(); return false; // it is unreachable } 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 cd9e78321..b2e785064 100644 --- a/src/util/lp/lp_settings.h +++ b/src/util/lp/lp_settings.h @@ -31,13 +31,16 @@ namespace lp { typedef unsigned var_index; typedef unsigned constraint_index; typedef unsigned row_index; + +typedef vector> explanation_t; + enum class column_type { free_column = 0, - low_bound = 1, - upper_bound = 2, - boxed = 3, - fixed = 4 - }; + lower_bound = 1, + upper_bound = 2, + boxed = 3, + fixed = 4 +}; enum class simplex_strategy_enum { undecided = 3, @@ -48,7 +51,7 @@ enum class simplex_strategy_enum { std::string column_type_to_string(column_type t); -enum lp_status { +enum class lp_status { UNKNOWN, INFEASIBLE, TENTATIVE_UNBOUNDED, @@ -81,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 @@ -91,11 +94,22 @@ public: }; struct stats { + unsigned m_make_feasible; unsigned m_total_iterations; unsigned m_iters_with_no_cost_growing; unsigned m_num_factorizations; unsigned m_num_of_implied_bounds; unsigned m_need_to_solve_inf; + unsigned m_max_cols; + unsigned m_max_rows; + unsigned m_gcd_calls; + unsigned m_gcd_conflicts; + unsigned m_cube_calls; + unsigned m_cube_success; + unsigned m_patches; + unsigned m_patches_success; + unsigned m_hnf_cutter_calls; + unsigned m_hnf_cuts; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; @@ -115,53 +129,76 @@ private: }; default_lp_resource_limit m_default_resource_limit; - lp_resource_limit* m_resource_limit; + lp_resource_limit* m_resource_limit; // used for debug output - std::ostream* m_debug_out; + std::ostream* m_debug_out; // used for messages, for example, the computation progress messages - std::ostream* m_message_out; + std::ostream* m_message_out; - stats m_stats; - random_gen m_rand; + stats m_stats; + random_gen m_rand; public: - unsigned reps_in_scaler; + unsigned reps_in_scaler; // when the absolute value of an element is less than pivot_epsilon // in pivoting, we treat it as a zero - double pivot_epsilon; + double pivot_epsilon; // see Chatal, page 115 - double positive_price_epsilon; + double positive_price_epsilon; // a quatation "if some choice of the entering vairable leads to an eta matrix // whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ... - double entering_diag_epsilon; - int c_partial_pivoting; // this is the constant c from page 410 - unsigned depth_of_rook_search; - bool using_partial_pivoting; + double entering_diag_epsilon; + int c_partial_pivoting; // this is the constant c from page 410 + unsigned depth_of_rook_search; + bool using_partial_pivoting; // dissertation of Achim Koberstein // if Bx - b is different at any component more that refactor_epsilon then we refactor - double refactor_tolerance; - double pivot_tolerance; - double zero_tolerance; - double drop_tolerance; - double tolerance_for_artificials; - double can_be_taken_to_basis_tolerance; + double refactor_tolerance; + double pivot_tolerance; + double zero_tolerance; + double drop_tolerance; + double tolerance_for_artificials; + double can_be_taken_to_basis_tolerance; - unsigned percent_of_entering_to_check; // we try to find a profitable column in a percentage of the columns - bool use_scaling; - double scaling_maximum; - double scaling_minimum; - double harris_feasibility_tolerance; // page 179 of Istvan Maros - double ignore_epsilon_of_harris; - unsigned max_number_of_iterations_with_no_improvements; - unsigned max_total_number_of_iterations; - double time_limit; // the maximum time limit of the total run time in seconds + unsigned percent_of_entering_to_check; // we try to find a profitable column in a percentage of the columns + bool use_scaling; + double scaling_maximum; + double scaling_minimum; + double harris_feasibility_tolerance; // page 179 of Istvan Maros + double ignore_epsilon_of_harris; + unsigned max_number_of_iterations_with_no_improvements; + unsigned max_total_number_of_iterations; + double time_limit; // the maximum time limit of the total run time in seconds // dual section - double dual_feasibility_tolerance; // // page 71 of the PhD thesis of Achim Koberstein - double primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein - double relative_primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein - - bool m_bound_propagation; + double dual_feasibility_tolerance; // // page 71 of the PhD thesis of Achim Koberstein + double primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein + double relative_primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein + // end of dual section + bool m_bound_propagation; + bool presolve_with_double_solver_for_lar; + simplex_strategy_enum m_simplex_strategy; + int report_frequency; + bool print_statistics; + unsigned column_norms_update_frequency; + bool scale_with_ratio; + double density_threshold; + bool use_breakpoints_in_feasibility_search; + unsigned max_row_length_for_bound_propagation; + bool backup_costs; + unsigned column_number_threshold_for_using_lu_in_lar_solver; + unsigned m_int_gomory_cut_period; + unsigned m_int_find_cube_period; + unsigned m_hnf_cut_period; + bool m_int_run_gcd_test; + bool m_int_pivot_fixed_vars_from_basis; + bool m_int_patch_only_integer_values; + unsigned limit_on_rows_for_hnf_cutter; + unsigned limit_on_columns_for_hnf_cutter; + + unsigned random_next() { return m_rand(); } + void set_random_seed(unsigned s) { m_rand.set_seed(s); } + bool bound_progation() const { return m_bound_propagation; } @@ -172,15 +209,15 @@ public: lp_settings() : m_default_resource_limit(*this), m_resource_limit(&m_default_resource_limit), - m_debug_out( &std::cout), + m_debug_out(&std::cout), m_message_out(&std::cout), reps_in_scaler(20), pivot_epsilon(0.00000001), positive_price_epsilon(1e-7), - entering_diag_epsilon ( 1e-8), - c_partial_pivoting ( 10), // this is the constant c from page 410 - depth_of_rook_search ( 4), - using_partial_pivoting ( true), + entering_diag_epsilon (1e-8), + c_partial_pivoting (10), // this is the constant c from page 410 + depth_of_rook_search (4), + using_partial_pivoting (true), // dissertation of Achim Koberstein // if Bx - b is different at any component more that refactor_epsilon then we refactor refactor_tolerance ( 1e-4), @@ -189,7 +226,6 @@ public: drop_tolerance ( 1e-14), tolerance_for_artificials ( 1e-4), can_be_taken_to_basis_tolerance ( 0.00001), - percent_of_entering_to_check ( 5),// we try to find a profitable column in a percentage of the columns use_scaling ( true), scaling_maximum ( 1), @@ -214,7 +250,15 @@ public: use_breakpoints_in_feasibility_search(false), max_row_length_for_bound_propagation(300), backup_costs(true), - column_number_threshold_for_using_lu_in_lar_solver(4000) + column_number_threshold_for_using_lu_in_lar_solver(4000), + m_int_gomory_cut_period(4), + m_int_find_cube_period(4), + m_hnf_cut_period(4), + m_int_run_gcd_test(true), + m_int_pivot_fixed_vars_from_basis(false), + m_int_patch_only_integer_values(true), + limit_on_rows_for_hnf_cutter(75), + limit_on_columns_for_hnf_cutter(150) {} void set_resource_limit(lp_resource_limit& lim) { m_resource_limit = &lim; } @@ -284,8 +328,6 @@ public: return is_eps_small_general(t, tolerance_for_artificials); } // the method of lar solver to use - bool presolve_with_double_solver_for_lar; - simplex_strategy_enum m_simplex_strategy; simplex_strategy_enum simplex_strategy() const { return m_simplex_strategy; } @@ -307,20 +349,9 @@ public: return m_simplex_strategy == simplex_strategy_enum::tableau_rows; } - int report_frequency; - bool print_statistics; - unsigned column_norms_update_frequency; - bool scale_with_ratio; - double density_threshold; // need to tune it up, todo #ifdef Z3DEBUG static unsigned ddd; // used for debugging #endif - bool use_breakpoints_in_feasibility_search; - unsigned random_next() { return m_rand(); } - void random_seed(unsigned s) { m_rand.set_seed(s); } - unsigned max_row_length_for_bound_propagation; - bool backup_costs; - unsigned column_number_threshold_for_using_lu_in_lar_solver; }; // end of lp_settings class @@ -343,7 +374,7 @@ inline std::string T_to_string(const numeric_pair & t) { inline std::string T_to_string(const mpq & t) { std::ostringstream strs; - strs << t.get_double(); + strs << t; return strs.str(); } @@ -382,7 +413,7 @@ inline void print_blanks(int n, std::ostream & out) { // after a push of the last element we ensure that the vector increases // we also suppose that before the last push the vector was increasing inline void ensure_increasing(vector & v) { - SASSERT(v.size() > 0); + lp_assert(v.size() > 0); unsigned j = v.size() - 1; for (; j > 0; j-- ) if (v[j] <= v[j - 1]) { diff --git a/src/util/lp/lp_settings.hpp b/src/util/lp/lp_settings_def.h similarity index 71% rename from src/util/lp/lp_settings.hpp rename to src/util/lp/lp_settings_def.h index 659d47c62..02963718e 100644 --- a/src/util/lp/lp_settings.hpp +++ b/src/util/lp/lp_settings_def.h @@ -26,30 +26,30 @@ 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: SASSERT(false); + default: lp_unreachable(); } return "unknown"; // it is unreachable } const char* lp_status_to_string(lp_status status) { switch (status) { - case UNKNOWN: return "UNKNOWN"; - case INFEASIBLE: return "INFEASIBLE"; - case UNBOUNDED: return "UNBOUNDED"; - case TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED"; - case DUAL_UNBOUNDED: return "DUAL_UNBOUNDED"; - case OPTIMAL: return "OPTIMAL"; - case FEASIBLE: return "FEASIBLE"; - case FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR"; - case TIME_EXHAUSTED: return "TIME_EXHAUSTED"; - case ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED"; - case EMPTY: return "EMPTY"; - case UNSTABLE: return "UNSTABLE"; + case lp_status::UNKNOWN: return "UNKNOWN"; + case lp_status::INFEASIBLE: return "INFEASIBLE"; + case lp_status::UNBOUNDED: return "UNBOUNDED"; + case lp_status::TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED"; + case lp_status::DUAL_UNBOUNDED: return "DUAL_UNBOUNDED"; + case lp_status::OPTIMAL: return "OPTIMAL"; + case lp_status::FEASIBLE: return "FEASIBLE"; + case lp_status::FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR"; + case lp_status::TIME_EXHAUSTED: return "TIME_EXHAUSTED"; + case lp_status::ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED"; + case lp_status::EMPTY: return "EMPTY"; + case lp_status::UNSTABLE: return "UNSTABLE"; default: - SASSERT(false); + lp_unreachable(); } return "UNKNOWN"; // it is unreachable } @@ -64,7 +64,7 @@ lp_status lp_status_from_string(std::string status) { if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED; if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED; if (status == "EMPTY") return lp_status::EMPTY; - SASSERT(false); + lp_unreachable(); return lp_status::UNKNOWN; // it is unreachable } @@ -74,14 +74,12 @@ bool vectors_are_equal(T * a, vector &b, unsigned n) { if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { - // std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl; return false; } } } else { for (unsigned i = 0; i < n; i ++){ if (std::abs(numeric_traits::get_double(a[i] - b[i])) > 0.000001) { - // std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl; return false; } } @@ -97,7 +95,6 @@ bool vectors_are_equal(const vector & a, const vector &b) { if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { - // std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl; return false; } } @@ -112,7 +109,6 @@ bool vectors_are_equal(const vector & a, const vector &b) { } if (fabs(da - db) > 0.000001) { - // std::cout << "a[" << i <<"] = " << a[i] << ", but " << "b[" << i <<"] = " << b[i] << std::endl; return false; } } diff --git a/src/util/lp/lp_solver_instances.cpp b/src/util/lp/lp_solver.cpp similarity index 93% rename from src/util/lp/lp_solver_instances.cpp rename to src/util/lp/lp_solver.cpp index d7caf9a18..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(); @@ -34,7 +34,6 @@ template void lp::lp_solver::print_statistics_on_A(std::ostream template bool lp::lp_solver::problem_is_empty(); template void lp::lp_solver::scale(); template void lp::lp_solver::set_scaled_cost(unsigned int); -template std::string lp::lp_solver::get_column_name(unsigned int) const; template lp::lp_solver::~lp_solver(); template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); template void lp::lp_solver::cleanup(); @@ -54,4 +53,3 @@ template void lp::lp_solver::scale(); template void lp::lp_solver::set_scaled_cost(unsigned int); template lp::lp_solver::~lp_solver(); template double lp::lp_solver::get_column_value_by_name(std::string) const; -template std::string lp::lp_solver::get_column_name(unsigned int) const; diff --git a/src/util/lp/lp_solver.h b/src/util/lp/lp_solver.h index c447b1870..f1fa15e00 100644 --- a/src/util/lp/lp_solver.h +++ b/src/util/lp/lp_solver.h @@ -28,7 +28,6 @@ Revision History: #include "util/lp/static_matrix.h" #include "util/lp/lp_core_solver_base.h" #include "util/lp/scaler.h" -#include "util/lp/linear_combination_iterator.h" #include "util/lp/bound_analyzer_on_row.h" namespace lp { enum lp_relation { @@ -114,9 +113,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 +123,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 +193,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 +243,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 87% rename from src/util/lp/lp_solver.hpp rename to src/util/lp/lp_solver_def.h index 3bc83b316..10c7a6feb 100644 --- a/src/util/lp/lp_solver.hpp +++ b/src/util/lp/lp_solver_def.h @@ -47,7 +47,7 @@ template T lp_solver::get_column_cost_value(unsig return ci->get_cost() * get_column_value(j); } template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { - SASSERT(m_constraints.find(row_index) == m_constraints.end()); + lp_assert(m_constraints.find(row_index) == m_constraints.end()); lp_constraint cs(right_side, relation); m_constraints[row_index] = cs; } @@ -173,29 +173,29 @@ template void lp_solver::pin_vars_on_row_with_sig column_info * ci = m_map_from_var_index_to_column_info[j]; T a = t.second; if (a * sign > numeric_traits::zero()) { - SASSERT(ci->upper_bound_is_set()); + lp_assert(ci->upper_bound_is_set()); ci->set_fixed_value(ci->get_upper_bound()); } else { - SASSERT(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; } @@ -238,17 +238,17 @@ template bool lp_solver::row_e_is_obsolete(std T rs = m_constraints[row_index].m_rs; if (row_is_zero(row)) { if (!is_zero(rs)) - m_status = INFEASIBLE; + m_status = lp_status::INFEASIBLE; 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 - m_status = INFEASIBLE; + // lower_bound > rs + m_settings.refactor_epsilon + m_status = lp_status::INFEASIBLE; return true; } if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ @@ -263,7 +263,7 @@ template bool lp_solver::row_e_is_obsolete(std T diff = rs - upper_bound; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { // upper_bound < rs - m_settings.refactor_tolerance - m_status = INFEASIBLE; + m_status = lp_status::INFEASIBLE; return true; } if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ @@ -279,7 +279,7 @@ template bool lp_solver::row_ge_is_obsolete(std: T rs = m_constraints[row_index].m_rs; if (row_is_zero(row)) { if (rs > zero_of_type()) - m_status = INFEASIBLE; + m_status = lp_status::INFEASIBLE; return true; } @@ -288,7 +288,7 @@ template bool lp_solver::row_ge_is_obsolete(std: T diff = rs - upper_bound; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { // upper_bound < rs - m_settings.refactor_tolerance - m_status = INFEASIBLE; + m_status = lp_status::INFEASIBLE; return true; } if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ @@ -301,18 +301,18 @@ 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()) - m_status = INFEASIBLE; + m_status = lp_status::INFEASIBLE; 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; } @@ -343,7 +343,7 @@ template bool lp_solver::row_is_obsolete(std:: case lp_relation::Less_or_equal: return row_le_is_obsolete(row, row_index); } - SASSERT(false); + lp_unreachable(); return false; // it is unreachable } @@ -358,7 +358,7 @@ template void lp_solver::remove_fixed_or_zero_col vector removed; for (auto & col : row) { unsigned j = col.first; - SASSERT(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); + lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); column_info * ci = m_map_from_var_index_to_column_info[j]; if (ci->is_fixed()) { removed.push_back(j); @@ -427,7 +427,7 @@ template void lp_solver::map_external_columns_to_ } unsigned j = col.first; auto column_info_it = m_map_from_var_index_to_column_info.find(j); - SASSERT(column_info_it != m_map_from_var_index_to_column_info.end()); + lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); auto j_column = column_info_it->second->get_column_index(); if (!is_valid(j_column)) { // j is a newcomer @@ -450,14 +450,14 @@ template void lp_solver::fill_A_from_A_values() { m_A = new static_matrix(static_cast(m_A_values.size()), number_of_core_structurals()); for (auto & t : m_A_values) { auto row_it = m_external_rows_to_core_solver_rows.find(t.first); - SASSERT(row_it != m_external_rows_to_core_solver_rows.end()); + lp_assert(row_it != m_external_rows_to_core_solver_rows.end()); unsigned row = row_it->second; for (auto k : t.second) { auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); - SASSERT(column_info_it != m_map_from_var_index_to_column_info.end()); + lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); column_info *ci = column_info_it->second; unsigned col = ci->get_column_index(); - SASSERT(is_valid(col)); + lp_assert(is_valid(col)); bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); if (!col_is_flipped) { (*m_A)(row, col) = k.second; @@ -471,7 +471,7 @@ template void lp_solver::fill_A_from_A_values() { template void lp_solver::fill_matrix_A_and_init_right_side() { map_external_rows_to_core_solver_rows(); map_external_columns_to_core_solver_columns(); - SASSERT(m_A == nullptr); + lp_assert(m_A == nullptr); fill_A_from_A_values(); m_b.resize(m_A->row_count()); } @@ -483,7 +483,7 @@ template void lp_solver::count_slacks_and_artific } template void lp_solver::count_slacks_and_artificials_for_row(unsigned i) { - SASSERT(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); + lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; switch (constraint.m_relation) { case Equal: @@ -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); @@ -519,10 +519,10 @@ template T lp_solver::low_bound_shift_for_row( template void lp_solver::fill_m_b() { for (int i = this->row_count() - 1; i >= 0; i--) { - SASSERT(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); + 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 @@ -557,13 +557,13 @@ template T lp_solver::get_column_value_with_core_ template void lp_solver::set_scaled_cost(unsigned j) { // grab original costs but modify it with the column scales - SASSERT(j < this->m_column_scale.size()); + lp_assert(j < this->m_column_scale.size()); column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; T cost = ci->get_cost(); if (ci->is_flipped()){ cost *= -1; } - SASSERT(ci->is_fixed() == false); + lp_assert(ci->is_fixed() == false); this->m_costs[j] = cost * this->m_column_scale[j]; } } diff --git a/src/util/lp/lp_utils.cpp b/src/util/lp/lp_utils.cpp index 46a82e9ec..98ab022d1 100644 --- a/src/util/lp/lp_utils.cpp +++ b/src/util/lp/lp_utils.cpp @@ -18,9 +18,10 @@ Revision History: --*/ #include "util/lp/lp_utils.h" - +#ifdef lp_for_z3 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 fce9f4d02..aa804919f 100644 --- a/src/util/lp/lp_utils.h +++ b/src/util/lp/lp_utils.h @@ -22,8 +22,15 @@ Revision History: #include "util/lp/numeric_pair.h" #include "util/debug.h" #include +template +void print_vector(const C & t, std::ostream & out) { + for (const auto & p : t) + out << p << " "; + out << std::endl; +} + template -bool try_get_val(const std::unordered_map & map, const A& key, B & val) { +bool try_get_value(const std::unordered_map & map, const A& key, B & val) { const auto it = map.find(key); if (it == map.end()) return false; val = it->second; @@ -35,19 +42,32 @@ bool contains(const std::unordered_map & map, const A& key) { return map.find(key) != map.end(); } +#ifdef lp_for_z3 + +#ifdef Z3DEBUG +#define Z3DEBUG 1 +#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; - 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); } +#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); } +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<> @@ -84,3 +104,70 @@ struct hash> { }; } +#else // else of #if lp_for_z3 +#include +#include +//include "util/numerics/mpq.h" +//include "util/numerics/numeric_traits.h" +//include "util/numerics/double.h" + +#ifdef __CLANG__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +namespace std { +template<> +struct hash { + inline size_t operator()(const lp::mpq & v) const { + return v.hash(); + } +}; +} +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); } +template inline T from_string(std::string const & ) { lp_unreachable();} +template <> double inline from_string(std::string const & str) { return atof(str.c_str());} +template <> mpq inline from_string(std::string const & str) { + return mpq(atof(str.c_str())); +} + +} // closing lp +template +inline void hash_combine(std::size_t & seed, const T & v) { + seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +namespace std { +template struct hash> { + inline size_t operator()(const pair & v) const { + size_t seed = 0; + hash_combine(seed, v.first); + hash_combine(seed, v.second); + return seed; + } +}; +template<> +struct hash> { + inline size_t operator()(const lp::numeric_pair & v) const { + size_t seed = 0; + hash_combine(seed, v.x); + hash_combine(seed, v.y); + return seed; + } +}; +} // std +#ifdef __CLANG__ +#pragma clang diagnostic pop +#endif +#endif diff --git a/src/util/lp/lu.cpp b/src/util/lp/lu.cpp new file mode 100644 index 000000000..e6df10908 --- /dev/null +++ b/src/util/lp/lu.cpp @@ -0,0 +1,84 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include +#include +#include +#include "util/vector.h" +#include "util/debug.h" +#include "util/lp/lu_def.h" +namespace lp { +template double dot_product(vector const&, vector const&); +template lu>::lu(static_matrix const&, vector&, lp_settings&); +template void lu>::push_matrix_to_tail(tail_matrix*); +template void lu>::replace_column(double, indexed_vector&, unsigned); +template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); +template lu>::~lu(); +template void lu>::push_matrix_to_tail(tail_matrix*); +template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); +template lu>::~lu(); +template void lu>::push_matrix_to_tail(tail_matrix*); +template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); +template lu>::~lu(); +template mpq dot_product(vector const&, vector const&); +template void init_factorization> + (lu>*&, static_matrix&, vector&, lp_settings&); +template void init_factorization> + (lu>*&, static_matrix&, vector&, lp_settings&); +template void init_factorization>(lu >*&, static_matrix&, vector&, lp_settings&); +#ifdef Z3DEBUG +template void print_matrix>(square_sparse_matrix&, std::ostream & out); +template void print_matrix>(static_matrix&, std::ostream&); +template void print_matrix >(static_matrix&, std::ostream&); +template void print_matrix>(static_matrix&, std::ostream & out); +template bool lu>::is_correct(const vector& basis); +template bool lu>::is_correct( vector const &); +template dense_matrix get_B>(lu>&, const vector& basis); +template dense_matrix get_B>(lu>&, vector const&); + +#endif + +template bool lu>::pivot_the_row(int); // NOLINT +template void lu>::init_vector_w(unsigned int, indexed_vector&); +template void lu>::solve_By(vector&); +template void lu>::solve_By_when_y_is_ready_for_X(vector&); +template void lu>::solve_yB_with_error_check(vector&, const vector& basis); +template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector const&, const vector & basis, const lp_settings&); +template void lu>::replace_column(mpq, indexed_vector&, unsigned); +template void lu>::solve_By(vector&); +template void lu>::solve_By_when_y_is_ready_for_X(vector&); +template void lu>::solve_yB_with_error_check(vector&, const vector& basis); +template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); +template void lu >::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); +template void lu >::init_vector_w(unsigned int, indexed_vector&); +template void lu >::replace_column(mpq, indexed_vector&, unsigned); +template void lu >::solve_Bd_faster(unsigned int, indexed_vector&); +template void lu >::solve_By(vector&); +template void lu >::solve_By_when_y_is_ready_for_X(vector&); +template void lu >::solve_yB_with_error_check(vector&, const vector& basis); +template void lu>::solve_By(indexed_vector&); +template void lu>::solve_By(indexed_vector&); +template void lu>::solve_yB_indexed(indexed_vector&); +template void lu >::solve_yB_indexed(indexed_vector&); +template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); +template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); +#ifdef Z3DEBUG +template void print_matrix>(tail_matrix&, std::ostream&); +#endif +} diff --git a/src/util/lp/lu.h b/src/util/lp/lu.h index f2cae7961..820786d87 100644 --- a/src/util/lp/lu.h +++ b/src/util/lp/lu.h @@ -8,7 +8,12 @@ Module Name: Abstract: - + for matrix B we have + t0*...*tn-1*B = Q*U*R + here ti are matrices corresponding to pivot operations, + including columns and rows swaps, + or a multiplication matrix row by a number + Q, R - permutations and U is an upper triangular matrix Author: Lev Nachmanson (levnach) @@ -24,7 +29,7 @@ Revision History: #include "util/debug.h" #include #include -#include "util/lp/sparse_matrix.h" +#include "util/lp/square_sparse_matrix.h" #include "util/lp/static_matrix.h" #include #include "util/lp/numeric_pair.h" @@ -36,18 +41,16 @@ Revision History: namespace lp { #ifdef Z3DEBUG template // print the nr x nc submatrix at the top left corner -void print_submatrix(sparse_matrix & m, unsigned mr, unsigned nc); +void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc); -template -void print_matrix(static_matrix &m, std::ostream & out); +template +void print_matrix(M &m, std::ostream & out); -template -void print_matrix(sparse_matrix& m, std::ostream & out); #endif template X dot_product(const vector & a, const vector & b) { - SASSERT(a.size() == b.size()); + lp_assert(a.size() == b.size()); auto r = zero_of_type(); for (unsigned i = 0; i < a.size(); i++) { r += a[i] * b[i]; @@ -114,7 +117,7 @@ public: m_i = p.apply_reverse(m_i); #ifdef Z3DEBUG - // SASSERT(*this == deb); + // lp_assert(*this == deb); #endif } }; // end of one_elem_on_diag @@ -123,17 +126,20 @@ enum class LU_status { OK, Degenerated}; // This class supports updates of the columns of B, and solves systems Bx=b,and yB=c // Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 -template +template class lu { LU_status m_status; public: + typedef typename M::coefftype T; + typedef typename M::argtype X; + // the fields unsigned m_dim; - static_matrix const &m_A; + const M & m_A; permutation_matrix m_Q; permutation_matrix m_R; permutation_matrix m_r_wave; - sparse_matrix m_U; + square_sparse_matrix m_U; square_dense_submatrix* m_dense_LU; vector *> m_tail; @@ -147,10 +153,11 @@ public: // constructor // if A is an m by n matrix then basis has length m and values in [0,n); the values are all different // they represent the set of m columns - lu(static_matrix const & A, + lu(const M & A, vector& basis, lp_settings & settings); - void debug_test_of_basis(static_matrix const & A, vector & basis); + lu(const M & A, lp_settings&); + void debug_test_of_basis(const M & A, vector & basis); void solve_Bd_when_w_is_ready(vector & d, indexed_vector& w ); void solve_By(indexed_vector & y); @@ -222,7 +229,7 @@ public: eta_matrix * get_eta_matrix_for_pivot(unsigned j); // we're processing the column j now - eta_matrix * get_eta_matrix_for_pivot(unsigned j, sparse_matrix& copy_of_U); + eta_matrix * get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U); // see page 407 of Chvatal unsigned transform_U_to_V_by_replacing_column(indexed_vector & w, unsigned leaving_column_of_U); @@ -245,7 +252,7 @@ public: } T B_(unsigned i, unsigned j, const vector& basis) { - return m_A.get_elem(i, basis[j]); + return m_A[i][basis[j]]; } unsigned dimension() { return m_dim; } @@ -261,11 +268,13 @@ public: void process_column(int j); bool is_correct(const vector& basis); + bool is_correct(); #ifdef Z3DEBUG dense_matrix tail_product(); dense_matrix get_left_side(const vector& basis); + dense_matrix get_left_side(); dense_matrix get_right_side(); #endif @@ -306,7 +315,7 @@ public: bool need_to_refactor() { return m_refactor_counter >= 200; } void adjust_dimension_with_matrix_A() { - SASSERT(m_A.row_count() >= m_dim); + lp_assert(m_A.row_count() >= m_dim); m_dim = m_A.row_count(); m_U.resize(m_dim); m_Q.resize(m_dim); @@ -320,7 +329,7 @@ public: unsigned m = m_A.row_count(); unsigned m_prev = m_U.dimension(); - SASSERT(m_A.column_count() == heading.size()); + lp_assert(m_A.column_count() == heading.size()); for (unsigned i = m_prev; i < m; i++) { for (const row_cell & c : m_A.m_rows[i]) { @@ -336,14 +345,14 @@ public: void add_last_rows_to_B(const vector & heading, const std::unordered_set & columns_to_replace) { unsigned m = m_A.row_count(); - SASSERT(m_A.column_count() == heading.size()); + lp_assert(m_A.column_count() == heading.size()); adjust_dimension_with_matrix_A(); m_w_for_extension.resize(m); // At this moment the LU is correct // for B extended by only by ones at the diagonal in the lower right corner for (unsigned j :columns_to_replace) { - SASSERT(heading[j] >= 0); + lp_assert(heading[j] >= 0); replace_column_with_only_change_at_last_rows(j, heading[j]); if (get_status() == LU_status::Degenerated) break; @@ -364,11 +373,14 @@ public: }; // end of lu -template -void init_factorization(lu* & factorization, static_matrix & m_A, vector & m_basis, lp_settings &m_settings); +template +void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings); #ifdef Z3DEBUG -template -dense_matrix get_B(lu& f, const vector& basis); +template +dense_matrix get_B(lu& f, const vector& basis); + +template +dense_matrix get_B(lu& f); #endif } diff --git a/src/util/lp/lu.hpp b/src/util/lp/lu_def.h similarity index 69% rename from src/util/lp/lu.hpp rename to src/util/lp/lu_def.h index 9d1532ac9..be4cd724d 100644 --- a/src/util/lp/lu.hpp +++ b/src/util/lp/lu_def.h @@ -26,8 +26,8 @@ Revision History: #include "util/lp/lu.h" namespace lp { #ifdef Z3DEBUG -template // print the nr x nc submatrix at the top left corner -void print_submatrix(sparse_matrix & m, unsigned mr, unsigned nc, std::ostream & out) { +template // print the nr x nc submatrix at the top left corner +void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc, std::ostream & out) { vector> A; vector widths; for (unsigned i = 0; i < m.row_count() && i < mr ; i++) { @@ -44,15 +44,14 @@ void print_submatrix(sparse_matrix & m, unsigned mr, unsigned nc, std::ost print_matrix_with_widths(A, widths, out); } -template -void print_matrix(static_matrix &m, std::ostream & out) { +template +void print_matrix(M &m, std::ostream & out) { vector> A; vector widths; - std::set> domain = m.get_domain(); for (unsigned i = 0; i < m.row_count(); i++) { A.push_back(vector()); for (unsigned j = 0; j < m.column_count(); j++) { - A[i].push_back(T_to_string(static_cast(m(i, j)))); + A[i].push_back(T_to_string(m[i][j])); } } @@ -63,23 +62,6 @@ void print_matrix(static_matrix &m, std::ostream & out) { print_matrix_with_widths(A, widths, out); } -template -void print_matrix(sparse_matrix& m, std::ostream & out) { - vector> A; - vector widths; - for (unsigned i = 0; i < m.row_count(); i++) { - A.push_back(vector()); - for (unsigned j = 0; j < m.column_count(); j++) { - A[i].push_back(T_to_string(static_cast(m(i, j)))); - } - } - - for (unsigned j = 0; j < m.column_count(); j++) { - widths.push_back(get_width_of_column(j, A)); - } - - print_matrix_with_widths(A, widths, out); -} #endif @@ -122,10 +104,10 @@ void one_elem_on_diag::apply_from_left_to_T(indexed_vector & w, lp_sett // This class supports updates of the columns of B, and solves systems Bx=b,and yB=c // Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 -template -lu::lu(static_matrix const & A, - vector& basis, - lp_settings & settings): +template +lu::lu(const M& A, + vector& basis, + lp_settings & settings): m_status(LU_status::OK), m_dim(A.row_count()), m_A(A), @@ -137,42 +119,62 @@ lu::lu(static_matrix const & A, m_failure(false), m_row_eta_work_vector(A.row_count()), m_refactor_counter(0) { - SASSERT(!(numeric_traits::precise() && settings.use_tableau())); + lp_assert(!(numeric_traits::precise() && settings.use_tableau())); #ifdef Z3DEBUG debug_test_of_basis(A, basis); #endif ++m_settings.st().m_num_factorizations; create_initial_factorization(); #ifdef Z3DEBUG - // SASSERT(check_correctness()); + // lp_assert(check_correctness()); #endif } -template -void lu::debug_test_of_basis(static_matrix const & A, vector & basis) { +template +lu::lu(const M& A, + lp_settings & settings): + m_status(LU_status::OK), + m_dim(A.row_count()), + m_A(A), + m_Q(m_dim), + m_R(m_dim), + m_r_wave(m_dim), + m_U(A), // create the square matrix that eventually will be factorized + m_settings(settings), + m_failure(false), + m_row_eta_work_vector(A.row_count()), + m_refactor_counter(0) { + lp_assert(A.row_count() == A.column_count()); + create_initial_factorization(); +#ifdef Z3DEBUG + lp_assert(is_correct()); +#endif +} +template +void lu::debug_test_of_basis( M const & A, vector & basis) { std::set set; for (unsigned i = 0; i < A.row_count(); i++) { - SASSERT(basis[i]< A.column_count()); + lp_assert(basis[i]< A.column_count()); set.insert(basis[i]); } - SASSERT(set.size() == A.row_count()); + lp_assert(set.size() == A.row_count()); } - template - void lu::solve_By(indexed_vector & y) { - SASSERT(false); // not implemented +template +void lu::solve_By(indexed_vector & y) { + lp_assert(false); // not implemented // init_vector_y(y); // solve_By_when_y_is_ready(y); } -template -void lu::solve_By(vector & y) { +template +void lu::solve_By(vector & y) { init_vector_y(y); solve_By_when_y_is_ready_for_X(y); } -template -void lu::solve_By_when_y_is_ready_for_X(vector & y) { +template +void lu::solve_By_when_y_is_ready_for_X(vector & y) { if (numeric_traits::precise()) { m_U.solve_U_y(y); m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal @@ -189,8 +191,8 @@ void lu::solve_By_when_y_is_ready_for_X(vector & y) { } } -template -void lu::solve_By_when_y_is_ready_for_T(vector & y, vector & index) { +template +void lu::solve_By_when_y_is_ready_for_T(vector & y, vector & index) { if (numeric_traits::precise()) { m_U.solve_U_y(y); m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal @@ -214,8 +216,8 @@ void lu::solve_By_when_y_is_ready_for_T(vector & y, vector & } } -template -void lu::solve_By_for_T_indexed_only(indexed_vector & y, const lp_settings & settings) { +template +void lu::solve_By_for_T_indexed_only(indexed_vector & y, const lp_settings & settings) { if (numeric_traits::precise()) { vector active_rows; m_U.solve_U_y_indexed_only(y, settings, active_rows); @@ -226,8 +228,8 @@ void lu::solve_By_for_T_indexed_only(indexed_vector & y, const lp_setti m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal } -template -void lu::print_matrix_compact(std::ostream & f) { +template +void lu::print_matrix_compact(std::ostream & f) { f << "matrix_start" << std::endl; f << "nrows " << m_A.row_count() << std::endl; f << "ncolumns " << m_A.column_count() << std::endl; @@ -241,8 +243,8 @@ void lu::print_matrix_compact(std::ostream & f) { } f << "matrix_end" << std::endl; } -template -void lu::print(indexed_vector & w, const vector& basis) { +template +void lu< M>::print(indexed_vector & w, const vector& basis) { std::string dump_file_name("/tmp/lu"); remove(dump_file_name.c_str()); std::ofstream f(dump_file_name); @@ -256,8 +258,8 @@ void lu::print(indexed_vector & w, const vector& basis) { print_indexed_vector(w, f); f.close(); } -template -void lu::solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w) { +template +void lu< M>::solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w) { init_vector_w(a_column, w); if (w.m_index.size() * ratio_of_index_size_to_all_size() < d.m_data.size()) { // this const might need some tuning @@ -270,14 +272,14 @@ void lu::solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector } } -template -void lu::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts the a_column into d +template +void lu< M>::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts the a_column into d init_vector_w(a_column, d); solve_By_for_T_indexed_only(d, m_settings); } -template -void lu::solve_yB(vector& y) { +template +void lu< M>::solve_yB(vector& y) { // first solve yU = cb*R(-1) m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) m_U.solve_y_U(y); // got y*U=cb*R(-1) @@ -290,37 +292,37 @@ void lu::solve_yB(vector& y) { } } -template -void lu::solve_yB_indexed(indexed_vector& y) { - SASSERT(y.is_OK()); +template +void lu< M>::solve_yB_indexed(indexed_vector& y) { + lp_assert(y.is_OK()); // first solve yU = cb*R(-1) m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); m_U.solve_y_U_indexed(y, m_settings); // got y*U=cb*R(-1) - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); m_Q.apply_reverse_from_right_to_T(y); - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { #ifdef Z3DEBUG (*e)->set_number_of_columns(m_dim); #endif (*e)->apply_from_right(y); - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); } } -template -void lu::add_delta_to_solution(const vector& yc, vector& y){ +template +void lu< M>::add_delta_to_solution(const vector& yc, vector& y){ unsigned i = static_cast(y.size()); while (i--) y[i]+=yc[i]; } -template -void lu::add_delta_to_solution_indexed(indexed_vector& y) { +template +void lu< M>::add_delta_to_solution_indexed(indexed_vector& y) { // the delta sits in m_y_copy, put result into y - SASSERT(y.is_OK()); - SASSERT(m_y_copy.is_OK()); + lp_assert(y.is_OK()); + lp_assert(m_y_copy.is_OK()); m_ii.clear(); m_ii.resize(y.data_size()); for (unsigned i : y.m_index) @@ -330,7 +332,7 @@ void lu::add_delta_to_solution_indexed(indexed_vector& y) { if (m_ii[i] == 0) m_ii.set_value(1, i); } - SASSERT(m_ii.is_OK()); + lp_assert(m_ii.is_OK()); y.m_index.clear(); for (unsigned i : m_ii.m_index) { @@ -341,24 +343,24 @@ void lu::add_delta_to_solution_indexed(indexed_vector& y) { v = zero_of_type(); } - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); } -template -void lu::find_error_of_yB(vector& yc, const vector& y, const vector& m_basis) { +template +void lu< M>::find_error_of_yB(vector& yc, const vector& y, const vector& m_basis) { unsigned i = m_dim; while (i--) { yc[i] -= m_A.dot_product_with_column(y, m_basis[i]); } } -template -void lu::find_error_of_yB_indexed(const indexed_vector& y, const vector& heading, const lp_settings& settings) { +template +void lu< M>::find_error_of_yB_indexed(const indexed_vector& y, const vector& heading, const lp_settings& settings) { #if 0 == 1 // it is a non efficient version indexed_vector yc = m_y_copy; yc.m_index.clear(); - SASSERT(!numeric_traits::precise()); + lp_assert(!numeric_traits::precise()); { vector d_basis(y.m_data.size()); @@ -379,10 +381,10 @@ void lu::find_error_of_yB_indexed(const indexed_vector& y, const vector } } #endif - SASSERT(m_ii.is_OK()); + lp_assert(m_ii.is_OK()); m_ii.clear(); m_ii.resize(y.data_size()); - SASSERT(m_y_copy.is_OK()); + lp_assert(m_y_copy.is_OK()); // put the error into m_y_copy for (auto k : y.m_index) { auto & row = m_A.m_rows[k]; @@ -414,7 +416,7 @@ void lu::find_error_of_yB_indexed(const indexed_vector& y, const vector m_y_copy.set_value(v, k); } } - SASSERT(m_y_copy.is_OK()); + lp_assert(m_y_copy.is_OK()); } @@ -423,8 +425,8 @@ void lu::find_error_of_yB_indexed(const indexed_vector& y, const vector // solves y*B = y // y is the input -template -void lu::solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings & settings) { +template +void lu< M>::solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings & settings) { if (numeric_traits::precise()) { if (y.m_index.size() * ratio_of_index_size_to_all_size() * 3 < m_A.column_count()) { solve_yB_indexed(y); @@ -434,12 +436,12 @@ void lu::solve_yB_with_error_check_indexed(indexed_vector & y, const ve } return; } - SASSERT(m_y_copy.is_OK()); - SASSERT(y.is_OK()); + lp_assert(m_y_copy.is_OK()); + lp_assert(y.is_OK()); if (y.m_index.size() * ratio_of_index_size_to_all_size() < m_A.column_count()) { m_y_copy = y; solve_yB_indexed(y); - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); if (y.m_index.size() * ratio_of_index_size_to_all_size() >= m_A.column_count()) { find_error_of_yB(m_y_copy.m_data, y.m_data, basis); solve_yB(m_y_copy.m_data); @@ -451,7 +453,7 @@ void lu::solve_yB_with_error_check_indexed(indexed_vector & y, const ve solve_yB_indexed(m_y_copy); add_delta_to_solution_indexed(y); } - SASSERT(m_y_copy.is_OK()); + lp_assert(m_y_copy.is_OK()); } else { solve_yB_with_error_check(y.m_data, basis); y.restore_index_and_clean_from_data(); @@ -461,8 +463,8 @@ void lu::solve_yB_with_error_check_indexed(indexed_vector & y, const ve // solves y*B = y // y is the input -template -void lu::solve_yB_with_error_check(vector & y, const vector& basis) { +template +void lu< M>::solve_yB_with_error_check(vector & y, const vector& basis) { if (numeric_traits::precise()) { solve_yB(y); return; @@ -475,8 +477,8 @@ void lu::solve_yB_with_error_check(vector & y, const vector& add_delta_to_solution(yc, y); m_y_copy.clear_all(); } -template -void lu::apply_Q_R_to_U(permutation_matrix & r_wave) { +template +void lu< M>::apply_Q_R_to_U(permutation_matrix & r_wave) { m_U.multiply_from_right(r_wave); m_U.multiply_from_left_with_reverse(r_wave); } @@ -488,62 +490,62 @@ void lu::apply_Q_R_to_U(permutation_matrix & r_wave) { // solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering // variable -template -lu::~lu(){ +template +lu< M>::~lu(){ for (auto t : m_tail) { delete t; } } -template -void lu::init_vector_y(vector & y) { +template +void lu< M>::init_vector_y(vector & y) { apply_lp_list_to_y(y); m_Q.apply_reverse_from_left_to_X(y); } -template -void lu::perform_transformations_on_w(indexed_vector& w) { +template +void lu< M>::perform_transformations_on_w(indexed_vector& w) { apply_lp_list_to_w(w); m_Q.apply_reverse_from_left(w); - // TBD does not compile: SASSERT(numeric_traits::precise() || check_vector_for_small_values(w, m_settings)); + // TBD does not compile: lp_assert(numeric_traits::precise() || check_vector_for_small_values(w, m_settings)); } // see Chvatal 24.3 -template -void lu::init_vector_w(unsigned entering, indexed_vector & w) { +template +void lu< M>::init_vector_w(unsigned entering, indexed_vector & w) { w.clear(); m_A.copy_column_to_indexed_vector(entering, w); // w = a, the column perform_transformations_on_w(w); } -template -void lu::apply_lp_list_to_w(indexed_vector & w) { +template +void lu< M>::apply_lp_list_to_w(indexed_vector & w) { for (unsigned i = 0; i < m_tail.size(); i++) { m_tail[i]->apply_from_left_to_T(w, m_settings); - // TBD does not compile: SASSERT(check_vector_for_small_values(w, m_settings)); + // TBD does not compile: lp_assert(check_vector_for_small_values(w, m_settings)); } } -template -void lu::apply_lp_list_to_y(vector& y) { +template +void lu< M>::apply_lp_list_to_y(vector& y) { for (unsigned i = 0; i < m_tail.size(); i++) { m_tail[i]->apply_from_left(y, m_settings); } } -template -void lu::swap_rows(int j, int k) { +template +void lu< M>::swap_rows(int j, int k) { if (j != k) { m_Q.transpose_from_left(j, k); m_U.swap_rows(j, k); } } -template -void lu::swap_columns(int j, int pivot_column) { +template +void lu< M>::swap_columns(int j, int pivot_column) { if (j == pivot_column) return; m_R.transpose_from_right(j, pivot_column); m_U.swap_columns(j, pivot_column); } -template -bool lu::pivot_the_row(int row) { +template +bool lu< M>::pivot_the_row(int row) { eta_matrix * eta_matrix = get_eta_matrix_for_pivot(row); if (get_status() != LU_status::OK) { return false; @@ -560,8 +562,8 @@ bool lu::pivot_the_row(int row) { return true; } // we're processing the column j now -template -eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j) { +template +eta_matrix * lu< M>::get_eta_matrix_for_pivot(unsigned j) { eta_matrix *ret; if(!m_U.fill_eta_matrix(j, &ret)) { set_status(LU_status::Degenerated); @@ -569,16 +571,16 @@ eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j) { return ret; } // we're processing the column j now -template -eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j, sparse_matrix& copy_of_U) { +template +eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U) { eta_matrix *ret; copy_of_U.fill_eta_matrix(j, &ret); return ret; } // see page 407 of Chvatal -template -unsigned lu::transform_U_to_V_by_replacing_column(indexed_vector & w, +template +unsigned lu::transform_U_to_V_by_replacing_column(indexed_vector & w, unsigned leaving_column) { unsigned column_to_replace = m_R.apply_reverse(leaving_column); m_U.replace_column(column_to_replace, w, m_settings); @@ -586,15 +588,15 @@ unsigned lu::transform_U_to_V_by_replacing_column(indexed_vector & w, } #ifdef Z3DEBUG -template -void lu::check_vector_w(unsigned entering) { +template +void lu::check_vector_w(unsigned entering) { T * w = new T[m_dim]; m_A.copy_column_to_vector(entering, w); check_apply_lp_lists_to_w(w); delete [] w; } -template -void lu::check_apply_matrix_to_vector(matrix *lp, T *w) { +template +void lu::check_apply_matrix_to_vector(matrix *lp, T *w) { if (lp != nullptr) { lp -> set_number_of_rows(m_dim); lp -> set_number_of_columns(m_dim); @@ -602,21 +604,21 @@ void lu::check_apply_matrix_to_vector(matrix *lp, T *w) { } } -template -void lu::check_apply_lp_lists_to_w(T * w) { +template +void lu::check_apply_lp_lists_to_w(T * w) { for (unsigned i = 0; i < m_tail.size(); i++) { check_apply_matrix_to_vector(m_tail[i], w); } permutation_matrix qr = m_Q.get_reverse(); apply_to_vector(qr, w); for (int i = m_dim - 1; i >= 0; i--) { - SASSERT(abs(w[i] - w[i]) < 0.0000001); + lp_assert(abs(w[i] - w[i]) < 0.0000001); } } #endif -template -void lu::process_column(int j) { +template +void lu::process_column(int j) { unsigned pi, pj; bool success = m_U.get_pivot_for_column(pi, pj, m_settings.c_partial_pivoting, j); if (!success) { @@ -637,8 +639,8 @@ void lu::process_column(int j) { m_failure = true; } } -template -bool lu::is_correct(const vector& basis) { +template +bool lu::is_correct(const vector& basis) { #ifdef Z3DEBUG if (get_status() != LU_status::OK) { return false; @@ -651,11 +653,25 @@ bool lu::is_correct(const vector& basis) { #endif } +template +bool lu::is_correct() { +#ifdef Z3DEBUG + if (get_status() != LU_status::OK) { + return false; + } + dense_matrix left_side = get_left_side(); + dense_matrix right_side = get_right_side(); + return left_side == right_side; +#else + return true; +#endif +} + #ifdef Z3DEBUG -template -dense_matrix lu::tail_product() { - SASSERT(tail_size() > 0); +template +dense_matrix lu::tail_product() { + lp_assert(tail_size() > 0); dense_matrix left_side = permutation_matrix(m_dim); for (unsigned i = 0; i < tail_size(); i++) { matrix* lp = get_lp_matrix(i); @@ -665,8 +681,8 @@ dense_matrix lu::tail_product() { } return left_side; } -template -dense_matrix lu::get_left_side(const vector& basis) { +template +dense_matrix lu::get_left_side(const vector& basis) { dense_matrix left_side = get_B(*this, basis); for (unsigned i = 0; i < tail_size(); i++) { matrix* lp = get_lp_matrix(i); @@ -676,8 +692,19 @@ dense_matrix lu::get_left_side(const vector& basis) { } return left_side; } -template -dense_matrix lu::get_right_side() { +template +dense_matrix lu::get_left_side() { + dense_matrix left_side = get_B(*this); + for (unsigned i = 0; i < tail_size(); i++) { + matrix* lp = get_lp_matrix(i); + lp->set_number_of_rows(m_dim); + lp->set_number_of_columns(m_dim); + left_side = ((*lp) * left_side); + } + return left_side; +} +template +dense_matrix lu::get_right_side() { auto ret = U() * R(); ret = Q() * ret; return ret; @@ -685,8 +712,8 @@ dense_matrix lu::get_right_side() { #endif // needed for debugging purposes -template -void lu::copy_w(T *buffer, indexed_vector & w) { +template +void lu::copy_w(T *buffer, indexed_vector & w) { unsigned i = m_dim; while (i--) { buffer[i] = w[i]; @@ -694,24 +721,24 @@ void lu::copy_w(T *buffer, indexed_vector & w) { } // needed for debugging purposes -template -void lu::restore_w(T *buffer, indexed_vector & w) { +template +void lu::restore_w(T *buffer, indexed_vector & w) { unsigned i = m_dim; while (i--) { w[i] = buffer[i]; } } -template -bool lu::all_columns_and_rows_are_active() { +template +bool lu::all_columns_and_rows_are_active() { unsigned i = m_dim; while (i--) { - SASSERT(m_U.col_is_active(i)); - SASSERT(m_U.row_is_active(i)); + lp_assert(m_U.col_is_active(i)); + lp_assert(m_U.row_is_active(i)); } return true; } -template -bool lu::too_dense(unsigned j) const { +template +bool lu::too_dense(unsigned j) const { unsigned r = m_dim - j; if (r < 5) return false; @@ -720,8 +747,8 @@ bool lu::too_dense(unsigned j) const { // return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j); return r * r * m_settings.density_threshold <= m_U.get_n_of_active_elems(); } -template -void lu::pivot_in_dense_mode(unsigned i) { +template +void lu::pivot_in_dense_mode(unsigned i) { int j = m_dense_LU->find_pivot_column_in_row(i); if (j == -1) { m_failure = true; @@ -733,8 +760,8 @@ void lu::pivot_in_dense_mode(unsigned i) { } m_dense_LU->pivot(i, m_settings); } -template -void lu::create_initial_factorization(){ +template +void lu::create_initial_factorization(){ m_U.prepare_for_factorization(); unsigned j; for (j = 0; j < m_dim; j++) { @@ -748,9 +775,9 @@ void lu::create_initial_factorization(){ } } if (j == m_dim) { - // TBD does not compile: SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - // SASSERT(is_correct()); - // SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + // TBD does not compile: lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + // lp_assert(is_correct()); + // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); return; } j++; @@ -763,16 +790,16 @@ void lu::create_initial_factorization(){ } } m_dense_LU->update_parent_matrix(m_settings); - SASSERT(m_dense_LU->is_L_matrix()); + lp_assert(m_dense_LU->is_L_matrix()); m_dense_LU->conjugate_by_permutation(m_Q); push_matrix_to_tail(m_dense_LU); m_refactor_counter = 0; - // SASSERT(is_correct()); - // SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + // lp_assert(is_correct()); + // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); } -template -void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave) { +template +void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave) { if (bump_start > bump_end) { set_status(LU_status::Degenerated); return; @@ -790,12 +817,12 @@ void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_ m_U.multiply_from_right(r_wave); m_U.multiply_from_left_with_reverse(r_wave); } -template -void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { +template +void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { vector> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump)); for (auto & iv : last_row_vec) { if (is_zero(iv.m_value)) continue; - SASSERT(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value)); + lp_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value)); unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index); if (adjusted_col < lowest_row_of_the_bump) { m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col); @@ -805,8 +832,8 @@ void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { } } -template -void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) { +template +void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) { // we have the system right side at m_row_eta_work_vector now // solve the system column wise for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) { @@ -816,14 +843,14 @@ void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned low vector> & row = m_U.get_row_values(aj); for (auto & iv : row) { unsigned col = m_U.adjust_column_inverse(iv.m_index); - SASSERT(col >= j || numeric_traits::is_zero(iv.m_value)); + lp_assert(col >= j || numeric_traits::is_zero(iv.m_value)); if (col == j) continue; if (numeric_traits::is_zero(iv.m_value)) { continue; } // the -v is for solving the system ( to zero the last row), and +v is for pivoting T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value; - SASSERT(numeric_traits::is_zero(delta) == false); + lp_assert(numeric_traits::is_zero(delta) == false); @@ -846,8 +873,8 @@ void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned low } // see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last // row at the same time -template -row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) { +template +row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) { if (replaced_column == lowest_row_of_the_bump) return nullptr; scan_last_row_to_work_vector(lowest_row_of_the_bump); pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump); @@ -861,9 +888,9 @@ row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned r } } #ifdef Z3DEBUG - auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump, m_dim); + auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump, m_dim); #else - auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump); + auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump); #endif for (auto j : m_row_eta_work_vector.m_index) { @@ -880,8 +907,8 @@ row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned r return ret; } -template -void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, unsigned leaving_column_of_U){ +template +void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, unsigned leaving_column_of_U){ m_refactor_counter++; unsigned replaced_column = transform_U_to_V_by_replacing_column( w, leaving_column_of_U); unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column); @@ -900,15 +927,15 @@ void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, push_matrix_to_tail(row_eta); } calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump); - // SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - // SASSERT(w.is_OK() && m_row_eta_work_vector.is_OK()); + // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + // lp_assert(w.is_OK() && m_row_eta_work_vector.is_OK()); } -template -void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ +template +void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ T diagonal_elem; if (replaced_column < lowest_row_of_the_bump) { diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump]; - // SASSERT(m_row_eta_work_vector.is_OK()); + // lp_assert(m_row_eta_work_vector.is_OK()); m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings); } else { diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently @@ -919,11 +946,11 @@ void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned } calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem); - // SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); } -template -void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { +template +void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { auto l = new one_elem_on_diag(lowest_row_of_the_bump, diagonal_element); #ifdef Z3DEBUG l->set_number_of_columns(m_dim); @@ -933,26 +960,35 @@ void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bum l->conjugate_by_permutation(m_Q); } -template -void init_factorization(lu* & factorization, static_matrix & m_A, vector & m_basis, lp_settings &m_settings) { +template +void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings) { if (factorization != nullptr) delete factorization; - factorization = new lu(m_A, m_basis, m_settings); + factorization = new lu(m_A, m_basis, m_settings); // if (factorization->get_status() != LU_status::OK) // LP_OUT(m_settings, "failing in init_factorization" << std::endl); } #ifdef Z3DEBUG -template -dense_matrix get_B(lu& f, const vector& basis) { - SASSERT(basis.size() == f.dimension()); - SASSERT(basis.size() == f.m_U.dimension()); - dense_matrix B(f.dimension(), f.dimension()); +template +dense_matrix get_B(lu& f, const vector& basis) { + lp_assert(basis.size() == f.dimension()); + lp_assert(basis.size() == f.m_U.dimension()); + dense_matrix B(f.dimension(), f.dimension()); for (unsigned i = 0; i < f.dimension(); i++) for (unsigned j = 0; j < f.dimension(); j++) B.set_elem(i, j, f.B_(i, j, basis)); return B; } +template +dense_matrix get_B(lu& f) { + dense_matrix B(f.dimension(), f.dimension()); + for (unsigned i = 0; i < f.dimension(); i++) + for (unsigned j = 0; j < f.dimension(); j++) + B.set_elem(i, j, f.m_A[i][j]); + + return B; +} #endif } diff --git a/src/util/lp/lu_instances.cpp b/src/util/lp/lu_instances.cpp deleted file mode 100644 index 057895068..000000000 --- a/src/util/lp/lu_instances.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include "util/debug.h" -#include "util/lp/lu.hpp" -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*); -template void lp::lu::replace_column(double, lp::indexed_vector&, unsigned); -template void lp::lu::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&); -template lp::lu::~lu(); -template void lp::lu::push_matrix_to_tail(lp::tail_matrix*); -template void lp::lu::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&); -template lp::lu::~lu(); -template void lp::lu >::push_matrix_to_tail(lp::tail_matrix >*); -template void lp::lu >::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&); -template lp::lu >::~lu(); -template lp::mpq lp::dot_product(vector const&, vector const&); -template void lp::init_factorization(lp::lu*&, lp::static_matrix&, vector&, lp::lp_settings&); -template void lp::init_factorization(lp::lu*&, lp::static_matrix&, vector&, lp::lp_settings&); -template void lp::init_factorization >(lp::lu >*&, lp::static_matrix >&, vector&, lp::lp_settings&); -#ifdef Z3DEBUG -template void lp::print_matrix(lp::sparse_matrix&, std::ostream & out); -template void lp::print_matrix(lp::static_matrix&, std::ostream&); -template void lp::print_matrix >(lp::static_matrix >&, std::ostream&); -template void lp::print_matrix(lp::static_matrix&, std::ostream & out); -template bool lp::lu::is_correct(const vector& basis); -template bool lp::lu >::is_correct( vector const &); -template lp::dense_matrix lp::get_B(lp::lu&, const vector& basis); -template lp::dense_matrix lp::get_B(lp::lu&, vector const&); - -#endif - -template bool lp::lu::pivot_the_row(int); // NOLINT -template void lp::lu::init_vector_w(unsigned int, lp::indexed_vector&); -template void lp::lu::solve_By(vector&); -template void lp::lu::solve_By_when_y_is_ready_for_X(vector&); -template void lp::lu::solve_yB_with_error_check(vector&, const vector& basis); -template void lp::lu::solve_yB_with_error_check_indexed(lp::indexed_vector&, vector const&, const vector & basis, const lp_settings&); -template void lp::lu::replace_column(lp::mpq, lp::indexed_vector&, unsigned); -template void lp::lu::solve_By(vector&); -template void lp::lu::solve_By_when_y_is_ready_for_X(vector&); -template void lp::lu::solve_yB_with_error_check(vector&, const vector& basis); -template void lp::lu::solve_yB_with_error_check_indexed(lp::indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); -template void lp::lu >::solve_yB_with_error_check_indexed(lp::indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); -template void lp::lu >::init_vector_w(unsigned int, lp::indexed_vector&); -template void lp::lu >::replace_column(lp::mpq, lp::indexed_vector&, unsigned); -template void lp::lu >::solve_Bd_faster(unsigned int, lp::indexed_vector&); -template void lp::lu >::solve_By(vector >&); -template void lp::lu >::solve_By_when_y_is_ready_for_X(vector >&); -template void lp::lu >::solve_yB_with_error_check(vector&, const vector& basis); -template void lp::lu::solve_By(lp::indexed_vector&); -template void lp::lu::solve_By(lp::indexed_vector&); -template void lp::lu::solve_yB_indexed(lp::indexed_vector&); -template void lp::lu::solve_yB_indexed(lp::indexed_vector&); -template void lp::lu >::solve_yB_indexed(lp::indexed_vector&); -template void lp::lu::solve_By_for_T_indexed_only(lp::indexed_vector&, lp::lp_settings const&); -template void lp::lu::solve_By_for_T_indexed_only(lp::indexed_vector&, lp::lp_settings const&); 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.h b/src/util/lp/matrix.h index f6374756f..063513287 100644 --- a/src/util/lp/matrix.h +++ b/src/util/lp/matrix.h @@ -49,11 +49,25 @@ void apply_to_vector(matrix & m, T * w); unsigned get_width_of_column(unsigned j, vector> & A); -void print_matrix_with_widths(vector> & A, vector & ws, std::ostream & out); -void print_string_matrix(vector> & A); +void print_matrix_with_widths(vector> & A, vector & ws, std::ostream & out, unsigned blanks = 0); +void print_string_matrix(vector> & A, std::ostream &, unsigned blanks_in_front = 0); template void print_matrix(matrix const * m, std::ostream & out); + +template +void print_matrix(const vector> & A, std::ostream & out, unsigned blanks_in_front = 0) { + vector> s(A.size()); + for (unsigned i = 0; i < A.size(); i++) { + for (const auto & v : A[i]) { + s[i].push_back(T_to_string(v)); + } + } + + print_string_matrix(s, out, blanks_in_front); +} + + } #endif diff --git a/src/util/lp/matrix.hpp b/src/util/lp/matrix_def.h similarity index 82% rename from src/util/lp/matrix.hpp rename to src/util/lp/matrix_def.h index 6eb82a9cc..ae5f05ad1 100644 --- a/src/util/lp/matrix.hpp +++ b/src/util/lp/matrix_def.h @@ -82,9 +82,11 @@ unsigned get_width_of_column(unsigned j, vector> & A) { return r; } -void print_matrix_with_widths(vector> & A, vector & ws, std::ostream & out) { +void print_matrix_with_widths(vector> & A, vector & ws, std::ostream & out, unsigned blanks_in_front) { for (unsigned i = 0; i < A.size(); i++) { for (unsigned j = 0; j < static_cast(A[i].size()); j++) { + if (i != 0 && j == 0) + print_blanks(blanks_in_front, out); print_blanks(ws[j] - static_cast(A[i][j].size()), out); out << A[i][j] << " "; } @@ -92,7 +94,7 @@ void print_matrix_with_widths(vector> & A, vector } } -void print_string_matrix(vector> & A, std::ostream & out) { +void print_string_matrix(vector> & A, std::ostream & out, unsigned blanks_in_front) { vector widths; if (A.size() > 0) @@ -100,10 +102,23 @@ void print_string_matrix(vector> & A, std::ostream & out) { widths.push_back(get_width_of_column(j, A)); } - print_matrix_with_widths(A, widths, out); + print_matrix_with_widths(A, widths, out, blanks_in_front); out << std::endl; } +template +void print_matrix(vector> & A, std::ostream & out, unsigned blanks_in_front = 0) { + vector> s(A.size()); + for (unsigned i = 0; i < A.size(); i++) { + for (const auto & v : A[i]) { + s[i].push_back(T_to_string(v)); + } + } + + print_string_matrix(s, out, blanks_in_front); +} + + template void print_matrix(matrix const * m, std::ostream & out) { vector> A(m->row_count()); diff --git a/src/util/lp/monomial.h b/src/util/lp/monomial.h new file mode 100644 index 000000000..5aa1eeb0c --- /dev/null +++ b/src/util/lp/monomial.h @@ -0,0 +1,33 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +namespace lp { +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) {} + 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());} +}; +} diff --git a/src/util/lp/mps_reader.h b/src/util/lp/mps_reader.h index 916e56322..09762cd5e 100644 --- a/src/util/lp/mps_reader.h +++ b/src/util/lp/mps_reader.h @@ -175,15 +175,15 @@ class mps_reader { if (m_line[i] == ' ') break; } - SASSERT(m_line.size() >= offset); - SASSERT(m_line.size() >> i); - SASSERT(i >= offset); + lp_assert(m_line.size() >= offset); + lp_assert(m_line.size() >> i); + lp_assert(i >= offset); return m_line.substr(offset, i - offset); } 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); @@ -303,7 +303,6 @@ class mps_reader { do { read_line(); if (m_line.find("RHS") == 0) { - // cout << "found RHS" << std::endl; break; } if (m_line.size() < 22) { @@ -512,7 +511,7 @@ class mps_reader { void create_or_update_bound() { const unsigned name_offset = 14; - SASSERT(m_line.size() >= 14); + lp_assert(m_line.size() >= 14); vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); if (bound_string.size() == 0) { @@ -618,7 +617,7 @@ class mps_reader { } for (auto s : row_with_range->m_row_columns) { - SASSERT(m_columns.find(s.first) != m_columns.end()); + lp_assert(m_columns.find(s.first) != m_columns.end()); other_bound_range_row->m_row_columns[s.first] = s.second; } } @@ -694,7 +693,7 @@ class mps_reader { if (row->m_name != m_cost_row_name) { solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index); for (auto s : row->m_row_columns) { - SASSERT(m_columns.find(s.first) != m_columns.end()); + lp_assert(m_columns.find(s.first) != m_columns.end()); solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second); } } else { @@ -729,7 +728,7 @@ class mps_reader { void set_solver_cost(row * row, lp_solver *solver) { for (auto s : row->m_row_columns) { std::string name = s.first; - SASSERT(m_columns.find(name) != m_columns.end()); + lp_assert(m_columns.find(name) != m_columns.end()); mps_reader::column * col = m_columns[name]; solver->set_cost_for_column(col->m_index, s.second); } @@ -738,7 +737,7 @@ class mps_reader { public: void set_message_stream(std::ostream * o) { - SASSERT(o != nullptr); + lp_assert(o != nullptr); m_message_stream = o; } vector column_names() { @@ -825,7 +824,7 @@ public: auto kind = get_lar_relation_from_row(row->m_type); vector> ls; for (auto s : row->m_row_columns) { - var_index i = solver->add_var(get_var_index(s.first)); + var_index i = solver->add_var(get_var_index(s.first), false); ls.push_back(std::make_pair(s.second, i)); } solver->add_constraint(ls, kind, row->m_right_side); @@ -843,20 +842,20 @@ public: void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { vector> ls; - var_index i = solver->add_var(col->m_index); + var_index i = solver->add_var(col->m_index, false); ls.push_back(std::make_pair(numeric_traits::one(), i)); solver->add_constraint(ls, GE, b->m_low); } void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index); + var_index i = solver->add_var(col->m_index, false); vector> ls; ls.push_back(std::make_pair(numeric_traits::one(), i)); solver->add_constraint(ls, LE, b->m_upper); } void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index); + var_index i = solver->add_var(col->m_index, false); vector> ls; ls.push_back(std::make_pair(numeric_traits::one(), i)); solver->add_constraint(ls, EQ, b->m_fixed_value); @@ -865,7 +864,7 @@ public: void fill_lar_solver_on_columns(lar_solver * solver) { for (auto s : m_columns) { mps_reader::column * col = s.second; - solver->add_var(col->m_index); + solver->add_var(col->m_index, false); auto b = col->m_bound; if (b == nullptr) return; diff --git a/src/util/lp/nra_solver.cpp b/src/util/lp/nra_solver.cpp new file mode 100644 index 000000000..b1ca67274 --- /dev/null +++ b/src/util/lp/nra_solver.cpp @@ -0,0 +1,264 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Nikolaj Bjorner +*/ + +#include "util/lp/lar_solver.h" +#include "util/lp/nra_solver.h" +#include "nlsat/nlsat_solver.h" +#include "math/polynomial/polynomial.h" +#include "math/polynomial/algebraic_numbers.h" +#include "util/map.h" + + +namespace nra { + + struct mon_eq { + mon_eq(lp::var_index v, unsigned sz, lp::var_index const* vs): + m_v(v), m_vs(sz, vs) {} + lp::var_index m_v; + svector m_vs; + }; + + struct solver::imp { + lp::lar_solver& s; + reslimit& m_limit; + params_ref m_params; + u_map m_lp2nl; // map from lar_solver variables to nlsat::solver variables + scoped_ptr m_nlsat; + vector m_monomials; + unsigned_vector m_monomials_lim; + mutable std::unordered_map m_variable_values; // current model + + imp(lp::lar_solver& s, reslimit& lim, params_ref const& p): + s(s), + m_limit(lim), + m_params(p) { + } + + bool need_check() { + return !m_monomials.empty() && !check_assignments(); + } + + void add(lp::var_index v, unsigned sz, lp::var_index const* vs) { + m_monomials.push_back(mon_eq(v, sz, vs)); + } + + void push() { + m_monomials_lim.push_back(m_monomials.size()); + } + + void pop(unsigned n) { + if (n == 0) return; + m_monomials.shrink(m_monomials_lim[m_monomials_lim.size() - n]); + m_monomials_lim.shrink(m_monomials_lim.size() - n); + } + + /* + \brief Check if polynomials are well defined. + multiply values for vs and check if they are equal to value for v. + epsilon has been computed. + */ + bool check_assignment(mon_eq const& m) const { + rational r1 = m_variable_values[m.m_v]; + rational r2(1); + for (auto w : m.m_vs) { + r2 *= m_variable_values[w]; + } + return r1 == r2; + } + + bool check_assignments() const { + s.get_model(m_variable_values); + for (auto const& m : m_monomials) { + if (!check_assignment(m)) return false; + } + return true; + } + + /** + \brief one-shot nlsat check. + A one shot checker is the least functionality that can + enable non-linear reasoning. + In addition to checking satisfiability we would also need + to identify equalities in the model that should be assumed + with the remaining solver. + + TBD: use partial model from lra_solver to prime the state of nlsat_solver. + TBD: explore more incremental ways of applying nlsat (using assumptions) + */ + lbool check(lp::explanation_t& ex) { + SASSERT(need_check()); + m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); + m_lp2nl.reset(); + vector core; + + // add linear inequalities from lra_solver + for (unsigned i = 0; i < s.constraint_count(); ++i) { + add_constraint(i); + } + + // add polynomial definitions. + for (auto const& m : m_monomials) { + add_monomial_eq(m); + } + // TBD: add variable bounds? + + lbool r = m_nlsat->check(); + TRACE("arith", display(tout); m_nlsat->display(tout << r << "\n");); + switch (r) { + case l_true: + break; + case l_false: + ex.reset(); + m_nlsat->get_core(core); + for (auto c : core) { + unsigned idx = static_cast(static_cast(c) - this); + ex.push_back(std::pair(rational(1), idx)); + TRACE("arith", tout << "ex: " << idx << "\n";); + } + break; + + case l_undef: + break; + } + return r; + } + + void add_monomial_eq(mon_eq const& m) { + polynomial::manager& pm = m_nlsat->pm(); + svector vars; + for (auto v : m.m_vs) { + vars.push_back(lp2nl(v)); + } + polynomial::monomial_ref m1(pm.mk_monomial(vars.size(), vars.c_ptr()), pm); + polynomial::monomial_ref m2(pm.mk_monomial(lp2nl(m.m_v), 1), pm); + polynomial::monomial* mls[2] = { m1, m2 }; + polynomial::scoped_numeral_vector coeffs(pm.m()); + coeffs.push_back(mpz(1)); + coeffs.push_back(mpz(-1)); + polynomial::polynomial_ref p(pm.mk_polynomial(2, coeffs.c_ptr(), mls), pm); + polynomial::polynomial* ps[1] = { p }; + bool even[1] = { false }; + nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, even); + m_nlsat->mk_clause(1, &lit, 0); + } + + void add_constraint(unsigned idx) { + auto& c = s.get_constraint(idx); + auto& pm = m_nlsat->pm(); + auto k = c.m_kind; + auto rhs = c.m_right_side; + auto lhs = c.get_left_side_coefficients(); + auto sz = lhs.size(); + svector vars; + rational den = denominator(rhs); + for (auto kv : lhs) { + vars.push_back(lp2nl(kv.second)); + den = lcm(den, denominator(kv.first)); + } + vector coeffs; + for (auto kv : lhs) { + coeffs.push_back(den * kv.first); + } + rhs *= den; + polynomial::polynomial_ref p(pm.mk_linear(sz, coeffs.c_ptr(), vars.c_ptr(), -rhs), pm); + polynomial::polynomial* ps[1] = { p }; + bool is_even[1] = { false }; + nlsat::literal lit; + nlsat::assumption a = this + idx; + switch (k) { + case lp::lconstraint_kind::LE: + lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); + break; + case lp::lconstraint_kind::GE: + lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); + break; + case lp::lconstraint_kind::LT: + lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); + break; + case lp::lconstraint_kind::GT: + lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); + break; + case lp::lconstraint_kind::EQ: + lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); + break; + } + m_nlsat->mk_clause(1, &lit, a); + } + + bool is_int(lp::var_index v) { + return s.var_is_int(v); + } + + + polynomial::var lp2nl(lp::var_index v) { + polynomial::var r; + if (!m_lp2nl.find(v, r)) { + r = m_nlsat->mk_var(is_int(v)); + m_lp2nl.insert(v, r); + } + return r; + } + + nlsat::anum const& value(lp::var_index v) const { + return m_nlsat->value(m_lp2nl.find(v)); + } + + nlsat::anum_manager& am() { + return m_nlsat->am(); + } + + std::ostream& display(std::ostream& out) const { + for (auto m : m_monomials) { + out << "v" << m.m_v << " = "; + for (auto v : m.m_vs) { + out << "v" << v << " "; + } + out << "\n"; + } + return out; + } + }; + + solver::solver(lp::lar_solver& s, reslimit& lim, params_ref const& p) { + m_imp = alloc(imp, s, lim, p); + } + + solver::~solver() { + dealloc(m_imp); + } + + void solver::add_monomial(lp::var_index v, unsigned sz, lp::var_index const* vs) { + m_imp->add(v, sz, vs); + } + + lbool solver::check(lp::explanation_t& ex) { + return m_imp->check(ex); + } + + bool solver::need_check() { + return m_imp->need_check(); + } + + void solver::push() { + m_imp->push(); + } + + void solver::pop(unsigned n) { + m_imp->pop(n); + } + + std::ostream& solver::display(std::ostream& out) const { + return m_imp->display(out); + } + + nlsat::anum const& solver::value(lp::var_index v) const { + return m_imp->value(v); + } + + nlsat::anum_manager& solver::am() { + return m_imp->am(); + } + +} diff --git a/src/util/lp/nra_solver.h b/src/util/lp/nra_solver.h new file mode 100644 index 000000000..70e614e91 --- /dev/null +++ b/src/util/lp/nra_solver.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Nikolaj Bjorner +*/ + +#pragma once +#include "util/vector.h" +#include "util/lp/lp_settings.h" +#include "util/rlimit.h" +#include "util/params.h" +#include "nlsat/nlsat_solver.h" + +namespace lp { + class lar_solver; +} + + +namespace nra { + + + + class solver { + struct imp; + imp* m_imp; + + public: + + solver(lp::lar_solver& s, reslimit& lim, params_ref const& p = params_ref()); + + ~solver(); + + /* + \brief Add a definition v = vs[0]*vs[1]*...*vs[sz-1] + The variable v is equal to the product of variables vs. + */ + void add_monomial(lp::var_index v, unsigned sz, lp::var_index const* vs); + + /* + \brief Check feasiblity of linear constraints augmented by polynomial definitions + that are added. + */ + lbool check(lp::explanation_t& ex); + + /* + \brief determine whether nra check is needed. + */ + bool need_check(); + + /* + \brief Access model. + */ + nlsat::anum const& value(lp::var_index v) const; + + nlsat::anum_manager& am(); + + /* + \brief push and pop scope. + Monomial definitions are retraced when popping scope. + */ + void push(); + + void pop(unsigned n); + + /* + \brief display state + */ + std::ostream& display(std::ostream& out) const; + + }; +} diff --git a/src/util/lp/numeric_pair.h b/src/util/lp/numeric_pair.h index a8a743a71..e98d76cbb 100644 --- a/src/util/lp/numeric_pair.h +++ b/src/util/lp/numeric_pair.h @@ -18,21 +18,29 @@ 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" - +#else + // include "util/numerics/mpq.h" + // include "util/numerics/numeric_traits.h" +#endif namespace lp { - typedef rational mpq; // rename rationals +#ifdef lp_for_z3 // rename rationals + typedef rational mpq; +#else + typedef lp::mpq mpq; +#endif template std::string T_to_string(const T & t); // forward definition - +#ifdef lp_for_z3 template class numeric_traits {}; template <> class numeric_traits { @@ -42,8 +50,25 @@ public: static unsigned one() { return 1; } 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 { +public: + static bool precise() { return true; } + static int const zero() { return 0; } + static int const one() { return 1; } + static bool is_zero(int v) { return v == 0; } + 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());} +}; + + template <> class numeric_traits { public: static bool precise() { return false; } @@ -71,14 +96,23 @@ template <> class numeric_traits { static rational from_string(std::string const & str) { return rational(str.c_str()); } 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 { static X convert(const Y & y){ return X(y);} static bool is_epsilon_small(const X & x, const double & y) { return std::abs(numeric_traits::get_double(x)) < y; } - static bool below_bound_numeric(const X &, const X &, const Y &) { /*SASSERT(false);*/ return false;} - static bool above_bound_numeric(const X &, const X &, const Y &) { /*SASSERT(false);*/ return false; } + static bool below_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false;} + static bool above_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false; } }; @@ -148,7 +182,7 @@ struct numeric_pair { } numeric_pair operator/(const numeric_pair &) const { - // SASSERT(false); + // lp_unreachable(); } @@ -157,7 +191,7 @@ struct numeric_pair { } numeric_pair operator*(const numeric_pair & /*a*/) const { - // SASSERT(false); + // lp_unreachable(); } numeric_pair& operator+=(const numeric_pair & a) { @@ -203,6 +237,11 @@ struct numeric_pair { std::string to_string() const { return std::string("(") + T_to_string(x) + ", " + T_to_string(y) + ")"; } + + bool is_int() const { + return x.is_int() && y.is_zero(); + } + }; @@ -229,7 +268,7 @@ numeric_pair operator/(const numeric_pair & r, const X & a) { } // template bool precise() { return numeric_traits::precise();} -template double get_double(const lp::numeric_pair & ) { /* SASSERT(false); */ return 0;} +template double get_double(const lp::numeric_pair & ) { /* lp_unreachable(); */ return 0;} template class numeric_traits> { public: @@ -237,7 +276,7 @@ class numeric_traits> { static lp::numeric_pair zero() { return lp::numeric_pair(numeric_traits::zero(), numeric_traits::zero()); } static bool is_zero(const lp::numeric_pair & v) { return numeric_traits::is_zero(v.x) && numeric_traits::is_zero(v.y); } static double get_double(const lp::numeric_pair & v){ return numeric_traits::get_double(v.x); } // just return the double of the first coordinate - static double one() { /*SASSERT(false);*/ return 0;} + static double one() { /*lp_unreachable();*/ return 0;} static bool is_pos(const numeric_pair &p) { return numeric_traits::is_pos(p.x) || (numeric_traits::is_zero(p.x) && numeric_traits::is_pos(p.y)); @@ -246,7 +285,9 @@ class numeric_traits> { return numeric_traits::is_neg(p.x) || (numeric_traits::is_zero(p.x) && numeric_traits::is_neg(p.y)); } - + static bool is_int(const numeric_pair & p) { + return numeric_traits::is_int(p.x) && numeric_traits::is_zero(p.y); + } }; template <> @@ -267,11 +308,11 @@ struct convert_struct, double> { return convert_struct::is_epsilon_small(p.x, eps) && convert_struct::is_epsilon_small(p.y, eps); } static bool below_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { - // SASSERT(false); + // lp_unreachable(); return false; } static bool above_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { - // SASSERT(false); + // lp_unreachable(); return false; } }; @@ -328,4 +369,26 @@ struct convert_struct { template bool is_epsilon_small(const X & v, const double &eps) { return convert_struct::is_epsilon_small(v, eps);} template bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::below_bound_numeric(x, bound, eps);} template bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::above_bound_numeric(x, bound, eps);} +template T floor(const numeric_pair & r) { + if (r.x.is_int()) { + if (r.y.is_nonneg()) { + return r.x; + } + return r.x - mpq::one(); + } + + return floor(r.x); +} + +template T ceil(const numeric_pair & r) { + if (r.x.is_int()) { + if (r.y.is_nonpos()) { + return r.x; + } + return r.x + mpq::one(); + } + + return ceil(r.x); +} + } 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.h b/src/util/lp/permutation_matrix.h index 8e664bba0..d1422ef86 100644 --- a/src/util/lp/permutation_matrix.h +++ b/src/util/lp/permutation_matrix.h @@ -101,7 +101,7 @@ class permutation_matrix : public tail_matrix { void apply_reverse_from_right_to_X(vector & w); void set_val(unsigned i, unsigned pi) { - SASSERT(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; } + lp_assert(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; } void transpose_from_left(unsigned i, unsigned j); @@ -132,8 +132,6 @@ class permutation_matrix : public tail_matrix { unsigned size() const { return static_cast(m_rev.size()); } - unsigned * values() const { return m_permutation.c_ptr(); } - void resize(unsigned size) { unsigned old_size = m_permutation.size(); m_permutation.resize(size); diff --git a/src/util/lp/permutation_matrix.hpp b/src/util/lp/permutation_matrix_def.h similarity index 91% rename from src/util/lp/permutation_matrix.hpp rename to src/util/lp/permutation_matrix_def.h index be96ca99f..76af12ed8 100644 --- a/src/util/lp/permutation_matrix.hpp +++ b/src/util/lp/permutation_matrix_def.h @@ -64,8 +64,7 @@ void permutation_matrix::apply_from_left(vector & w, lp_settings & ) { // L * deb_w = clone_vector(w, row_count()); // deb.apply_from_left(deb_w); #endif - // std::cout << " apply_from_left " << std::endl; - SASSERT(m_X_buffer.size() == w.size()); + lp_assert(m_X_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_X_buffer[i] = w[m_permutation[i]]; @@ -75,7 +74,7 @@ void permutation_matrix::apply_from_left(vector & w, lp_settings & ) { w[i] = m_X_buffer[i]; } #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(deb_w, w, row_count())); + // lp_assert(vectors_are_equal(deb_w, w, row_count())); // delete [] deb_w; #endif } @@ -101,7 +100,7 @@ template void permutation_matrix::apply_from_righ // T * deb_w = clone_vector(w, row_count()); // deb.apply_from_right(deb_w); #endif - SASSERT(m_T_buffer.size() == w.size()); + lp_assert(m_T_buffer.size() == w.size()); for (unsigned i = 0; i < size(); i++) { m_T_buffer[i] = w[m_rev[i]]; } @@ -110,7 +109,7 @@ template void permutation_matrix::apply_from_righ w[i] = m_T_buffer[i]; } #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(deb_w, w, row_count())); + // lp_assert(vectors_are_equal(deb_w, w, row_count())); // delete [] deb_w; #endif } @@ -132,9 +131,9 @@ template void permutation_matrix::apply_from_righ unsigned pj = m_permutation[j]; w.set_value(buffer[i], pj); } - SASSERT(w.is_OK()); + lp_assert(w.is_OK()); #ifdef Z3DEBUG - SASSERT(vectors_are_equal(wcopy, w.m_data)); + lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif } @@ -181,7 +180,7 @@ void permutation_matrix::apply_reverse_from_left(indexed_vector & w) { w.m_index[i] = j; } #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(deb_w, w.m_data, row_count())); + // lp_assert(vectors_are_equal(deb_w, w.m_data, row_count())); // delete [] deb_w; #endif } @@ -189,7 +188,7 @@ void permutation_matrix::apply_reverse_from_left(indexed_vector & w) { template void permutation_matrix::apply_reverse_from_left_to_T(vector & w) { // the result will be w = p(-1) * w - SASSERT(m_T_buffer.size() == w.size()); + lp_assert(m_T_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_T_buffer[m_permutation[i]] = w[i]; @@ -202,7 +201,7 @@ void permutation_matrix::apply_reverse_from_left_to_T(vector & w) { template void permutation_matrix::apply_reverse_from_left_to_X(vector & w) { // the result will be w = p(-1) * w - SASSERT(m_X_buffer.size() == w.size()); + lp_assert(m_X_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_X_buffer[m_permutation[i]] = w[i]; @@ -216,7 +215,7 @@ void permutation_matrix::apply_reverse_from_left_to_X(vector & w) { template void permutation_matrix::apply_reverse_from_right_to_T(vector & w) { // the result will be w = w * p(-1) - SASSERT(m_T_buffer.size() == w.size()); + lp_assert(m_T_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_T_buffer[i] = w[m_permutation[i]]; @@ -234,7 +233,7 @@ void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & // vector wcopy(w.m_data); // apply_reverse_from_right_to_T(wcopy); #endif - SASSERT(w.is_OK()); + lp_assert(w.is_OK()); vector tmp; vector tmp_index(w.m_index); for (auto i : w.m_index) { @@ -247,15 +246,15 @@ void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & w.set_value(tmp[k], m_rev[j]); } - // SASSERT(w.is_OK()); - // SASSERT(vectors_are_equal(w.m_data, wcopy)); + // lp_assert(w.is_OK()); + // lp_assert(vectors_are_equal(w.m_data, wcopy)); } template void permutation_matrix::apply_reverse_from_right_to_X(vector & w) { // the result will be w = w * p(-1) - SASSERT(m_X_buffer.size() == w.size()); + lp_assert(m_X_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_X_buffer[i] = w[m_permutation[i]]; @@ -268,7 +267,7 @@ void permutation_matrix::apply_reverse_from_right_to_X(vector & w) { template void permutation_matrix::transpose_from_left(unsigned i, unsigned j) { // the result will be this = (i,j)*this - SASSERT(i < size() && j < size() && i != j); + lp_assert(i < size() && j < size() && i != j); auto pi = m_rev[i]; auto pj = m_rev[j]; set_val(pi, j); @@ -277,7 +276,7 @@ template void permutation_matrix::transpose_from_ template void permutation_matrix::transpose_from_right(unsigned i, unsigned j) { // the result will be this = this * (i,j) - SASSERT(i < size() && j < size() && i != j); + lp_assert(i < size() && j < size() && i != j); auto pi = m_permutation[i]; auto pj = m_permutation[j]; set_val(i, pj); @@ -286,7 +285,7 @@ template void permutation_matrix::transpose_from_ template void permutation_matrix::multiply_by_permutation_from_left(permutation_matrix & p) { m_work_array = m_permutation; - SASSERT(p.size() == size()); + lp_assert(p.size() == size()); unsigned i = size(); while (i-- > 0) { set_val(i, m_work_array[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation @@ -296,7 +295,7 @@ template void permutation_matrix::multiply_by_per // this is multiplication in the matrix sense template void permutation_matrix::multiply_by_permutation_from_right(permutation_matrix & p) { m_work_array = m_permutation; - SASSERT(p.size() == size()); + lp_assert(p.size() == size()); unsigned i = size(); while (i-- > 0) set_val(i, p[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation @@ -304,7 +303,7 @@ template void permutation_matrix::multiply_by_per } template void permutation_matrix::multiply_by_reverse_from_right(permutation_matrix & q){ // todo : condensed permutations ? - SASSERT(q.size() == size()); + lp_assert(q.size() == size()); m_work_array = m_permutation; // the result is this = this*q(-1) unsigned i = size(); diff --git a/src/util/lp/polynomial.h b/src/util/lp/polynomial.h new file mode 100644 index 000000000..f4f9615ea --- /dev/null +++ b/src/util/lp/polynomial.h @@ -0,0 +1,115 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +namespace lp { +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; + } + + template // c plays a role of a map from indices to impq + mpq value(const c& v) const { + mpq r = m_a; + for (auto & p : m_coeffs) + r += v[p.var()].x * p.coeff(); + return r; + } + + const vector & coeffs() const { return m_coeffs; } +}; +} diff --git a/src/util/lp/quick_xplain.cpp b/src/util/lp/quick_xplain.cpp index f9506c056..c4a89ae40 100644 --- a/src/util/lp/quick_xplain.cpp +++ b/src/util/lp/quick_xplain.cpp @@ -35,7 +35,7 @@ void quick_xplain::copy_constraint_and_add_constraint_vars(const lar_constraint& vector < std::pair> ls; for (auto & p : lar_c.get_left_side_coefficients()) { unsigned j = p.second; - unsigned lj = m_qsol.add_var(j); + unsigned lj = m_qsol.add_var(j, false); ls.push_back(std::make_pair(p.first, lj)); } m_constraints_in_local_vars.push_back(lar_constraint(ls, lar_c.m_kind, lar_c.m_right_side)); @@ -44,7 +44,7 @@ void quick_xplain::copy_constraint_and_add_constraint_vars(const lar_constraint& bool quick_xplain::infeasible() { m_qsol.solve(); - return m_qsol.get_status() == INFEASIBLE; + return m_qsol.get_status() == lp_status::INFEASIBLE; } // u - unexplored constraints @@ -71,7 +71,7 @@ void quick_xplain::minimize(const vector& u) { } } if (m > 0) { - SASSERT(m_qsol.constraint_stack_size() >= initial_stack_size); + lp_assert(m_qsol.constraint_stack_size() >= initial_stack_size); m_qsol.pop(m_qsol.constraint_stack_size() - initial_stack_size); for (auto j : m_x) add_constraint_to_qsol(j); @@ -88,7 +88,7 @@ void quick_xplain::minimize(const vector& u) { void quick_xplain::run(vector> & explanation, const lar_solver & ls){ if (explanation.size() <= 2) return; lar_solver qsol; - SASSERT(ls.explanation_is_correct(explanation)); + lp_assert(ls.explanation_is_correct(explanation)); quick_xplain q(explanation, ls, qsol); q.solve(); } @@ -109,13 +109,13 @@ bool quick_xplain::is_feasible(const vector & x, unsigned k) const { vector < std::pair> ls; const lar_constraint & c = m_constraints_in_local_vars[i]; for (auto & p : c.get_left_side_coefficients()) { - unsigned lj = l.add_var(p.second); + unsigned lj = l.add_var(p.second, false); ls.push_back(std::make_pair(p.first, lj)); } l.add_constraint(ls, c.m_kind, c.m_right_side); } l.solve(); - return l.get_status() != INFEASIBLE; + return l.get_status() != lp_status::INFEASIBLE; } bool quick_xplain::x_is_minimal() const { @@ -124,7 +124,7 @@ bool quick_xplain::x_is_minimal() const { x.push_back(j); for (unsigned k = 0; k < x.size(); k++) { - SASSERT(is_feasible(x, x[k])); + lp_assert(is_feasible(x, x[k])); } return true; } @@ -132,8 +132,8 @@ bool quick_xplain::x_is_minimal() const { void quick_xplain::solve() { copy_constraints_to_local_constraints(); m_qsol.push(); - SASSERT(m_qsol.constraint_count() == 0); - vector u; + lp_assert(m_qsol.constraint_count() == 0) + vector u; for (unsigned k = 0; k < m_constraints_in_local_vars.size(); k++) u.push_back(k); minimize(u); @@ -142,10 +142,10 @@ void quick_xplain::solve() { for (unsigned i : m_x) add_constraint_to_qsol(i); m_qsol.solve(); - SASSERT(m_qsol.get_status() == INFEASIBLE); + lp_assert(m_qsol.get_status() == lp_status::INFEASIBLE); m_qsol.get_infeasibility_explanation(m_explanation); - SASSERT(m_qsol.explanation_is_correct(m_explanation)); - SASSERT(x_is_minimal()); + lp_assert(m_qsol.explanation_is_correct(m_explanation)); + lp_assert(x_is_minimal()); for (auto & p : m_explanation) { p.second = this->m_local_constraint_offset_to_external_ci[m_local_ci_to_constraint_offsets[p.second]]; } 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.h b/src/util/lp/random_updater.h index 6b03ad941..e5067f2e5 100644 --- a/src/util/lp/random_updater.h +++ b/src/util/lp/random_updater.h @@ -25,72 +25,23 @@ Revision History: #include #include #include "util/lp/lp_settings.h" -#include "util/lp/linear_combination_iterator.h" // see http://research.microsoft.com/projects/z3/smt07.pdf // The class searches for a feasible solution with as many different values of variables as it can find namespace lp { template struct numeric_pair; // forward definition -class lar_core_solver; // forward definition +class lar_solver; // forward definition class random_updater { - struct interval { - bool upper_bound_is_set; - numeric_pair upper_bound; - bool low_bound_is_set; - numeric_pair low_bound; - interval() : upper_bound_is_set(false), - low_bound_is_set(false) {} - - void set_low_bound(const numeric_pair & v) { - if (low_bound_is_set) { - low_bound = std::max(v, low_bound); - } else { - low_bound = v; - low_bound_is_set = true; - } - } - void set_upper_bound(const numeric_pair & v) { - if (upper_bound_is_set) { - upper_bound = std::min(v, upper_bound); - } else { - upper_bound = v; - upper_bound_is_set = true; - } - } - bool is_empty() const {return - upper_bound_is_set && low_bound_is_set && low_bound >= upper_bound; - } - - bool low_bound_holds(const numeric_pair & a) const { - return low_bound_is_set == false || a >= low_bound; - } - bool upper_bound_holds(const numeric_pair & a) const { - return upper_bound_is_set == false || a <= upper_bound; - } - - bool contains(const numeric_pair & a) const { - return low_bound_holds(a) && upper_bound_holds(a); - } - std::string lbs() { return low_bound_is_set ? T_to_string(low_bound):std::string("inf");} - std::string rbs() { return upper_bound_is_set? T_to_string(upper_bound):std::string("inf");} - std::string to_str() { return std::string("[")+ lbs() + ", " + rbs() + "]";} - }; std::set m_var_set; - lar_core_solver & m_core_solver; - unsigned range; - linear_combination_iterator* m_column_j; // the actual column - interval find_shift_interval(unsigned j); - interval get_interval_of_non_basic_var(unsigned j); + lar_solver & m_lar_solver; + unsigned m_range; void add_column_to_sets(unsigned j); - void random_shift_var(unsigned j); + bool random_shift_var(unsigned j); std::unordered_map, unsigned> m_values; // it maps a value to the number of time it occurs - void diminish_interval_to_leave_basic_vars_feasible(numeric_pair &nb_x, interval & inter); - void shift_var(unsigned j, interval & r); - void diminish_interval_for_basic_var(numeric_pair &nb_x, unsigned j, mpq & a, interval & r); - numeric_pair get_random_from_interval(interval & r); - void add_value(numeric_pair& v); - void remove_value(numeric_pair & v); + bool shift_var(unsigned j); + void add_value(const numeric_pair& v); + void remove_value(const numeric_pair & v); public: - random_updater(lar_core_solver & core_solver, const vector & column_list); + random_updater(lar_solver & solver, const vector & column_list); void update(); }; } diff --git a/src/util/lp/random_updater.hpp b/src/util/lp/random_updater.hpp deleted file mode 100644 index 5bbcdf27c..000000000 --- a/src/util/lp/random_updater.hpp +++ /dev/null @@ -1,222 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "util/lp/random_updater.h" -#include "util/lp/static_matrix.h" -#include "util/lp/lar_solver.h" -#include "util/vector.h" -namespace lp { - - - -random_updater::random_updater( - lar_core_solver & lar_core_solver, - const vector & column_indices) : - m_core_solver(lar_core_solver), - range(100000) { - for (unsigned j : column_indices) - add_column_to_sets(j); -} - -random_updater::interval random_updater::get_interval_of_non_basic_var(unsigned j) { - interval ret; - switch (m_core_solver.get_column_type(j)) { - case column_type::free_column: - break; - case column_type::low_bound: - ret.set_low_bound(m_core_solver.m_r_low_bounds[j]); - break; - case column_type::upper_bound: - ret.set_upper_bound(m_core_solver.m_r_upper_bounds[j]); - break; - case column_type::boxed: - case column_type::fixed: - ret.set_low_bound(m_core_solver.m_r_low_bounds[j]); - ret.set_upper_bound(m_core_solver.m_r_upper_bounds[j]); - break; - default: - SASSERT(false); - } - return ret; -} - -void random_updater::diminish_interval_for_basic_var(numeric_pair& nb_x, unsigned j, - mpq & a, - interval & r) { - SASSERT(m_core_solver.m_r_heading[j] >= 0); - numeric_pair delta; - SASSERT(a != zero_of_type()); - switch (m_core_solver.get_column_type(j)) { - case column_type::free_column: - break; - case column_type::low_bound: - delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j]; - SASSERT(delta >= zero_of_type>()); - if (a > 0) { - r.set_upper_bound(nb_x + delta / a); - } else { - r.set_low_bound(nb_x + delta / a); - } - break; - case column_type::upper_bound: - delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j]; - SASSERT(delta >= zero_of_type>()); - if (a > 0) { - r.set_low_bound(nb_x - delta / a); - } else { - r.set_upper_bound(nb_x - delta / a); - } - break; - case column_type::boxed: - if (a > 0) { - delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j]; - SASSERT(delta >= zero_of_type>()); - r.set_upper_bound(nb_x + delta / a); - delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j]; - SASSERT(delta >= zero_of_type>()); - r.set_low_bound(nb_x - delta / a); - } else { // a < 0 - delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j]; - SASSERT(delta >= zero_of_type>()); - r.set_upper_bound(nb_x - delta / a); - delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j]; - SASSERT(delta >= zero_of_type>()); - r.set_low_bound(nb_x + delta / a); - } - break; - case column_type::fixed: - r.set_low_bound(nb_x); - r.set_upper_bound(nb_x); - break; - default: - SASSERT(false); - } -} - - -void random_updater::diminish_interval_to_leave_basic_vars_feasible(numeric_pair &nb_x, interval & r) { - m_column_j->reset(); - unsigned i; - mpq a; - while (m_column_j->next(a, i)) { - diminish_interval_for_basic_var(nb_x, m_core_solver.m_r_basis[i], a, r); - if (r.is_empty()) - break; - } -} - -random_updater::interval random_updater::find_shift_interval(unsigned j) { - interval ret = get_interval_of_non_basic_var(j); - diminish_interval_to_leave_basic_vars_feasible(m_core_solver.m_r_x[j], ret); - return ret; -} - -void random_updater::shift_var(unsigned j, interval & r) { - SASSERT(r.contains(m_core_solver.m_r_x[j])); - SASSERT(m_core_solver.m_r_solver.column_is_feasible(j)); - auto old_x = m_core_solver.m_r_x[j]; - remove_value(old_x); - auto new_val = m_core_solver.m_r_x[j] = get_random_from_interval(r); - add_value(new_val); - - SASSERT(r.contains(m_core_solver.m_r_x[j])); - SASSERT(m_core_solver.m_r_solver.column_is_feasible(j)); - auto delta = m_core_solver.m_r_x[j] - old_x; - - unsigned i; - m_column_j->reset(); - mpq a; - while(m_column_j->next(a, i)) { - unsigned bj = m_core_solver.m_r_basis[i]; - m_core_solver.m_r_x[bj] -= a * delta; - SASSERT(m_core_solver.m_r_solver.column_is_feasible(bj)); - } - SASSERT(m_core_solver.m_r_solver.A_mult_x_is_off() == false); -} - -numeric_pair random_updater::get_random_from_interval(interval & r) { - unsigned rand = m_core_solver.settings().random_next(); - if ((!r.low_bound_is_set) && (!r.upper_bound_is_set)) - return numeric_pair(rand % range, 0); - if (r.low_bound_is_set && (!r.upper_bound_is_set)) - return r.low_bound + numeric_pair(rand % range, 0); - if ((!r.low_bound_is_set) && r.upper_bound_is_set) - return r.upper_bound - numeric_pair(rand % range, 0); - SASSERT(r.low_bound_is_set && r.upper_bound_is_set); - return r.low_bound + (rand % range) * (r.upper_bound - r.low_bound)/ range; -} - -void random_updater::random_shift_var(unsigned j) { - m_column_j = m_core_solver.get_column_iterator(j); - if (m_column_j->size() >= 50) { - delete m_column_j; - return; - } - interval interv = find_shift_interval(j); - if (interv.is_empty()) { - delete m_column_j; - return; - } - - shift_var(j, interv); - delete m_column_j; -} - -void random_updater::update() { - for (auto j : m_var_set) { - if (m_var_set.size() <= m_values.size()) { - break; // we are done - } - random_shift_var(j); - } -} - -void random_updater::add_value(numeric_pair& v) { - auto it = m_values.find(v); - if (it == m_values.end()) { - m_values[v] = 1; - } else { - it->second++; - } -} - -void random_updater::remove_value(numeric_pair& v) { - std::unordered_map, unsigned>::iterator it = m_values.find(v); - SASSERT(it != m_values.end()); - it->second--; - if (it->second == 0) - m_values.erase((std::unordered_map, unsigned>::const_iterator)it); -} - -void random_updater::add_column_to_sets(unsigned j) { - if (m_core_solver.m_r_heading[j] < 0) { - m_var_set.insert(j); - add_value(m_core_solver.m_r_x[j]); - } else { - unsigned row = m_core_solver.m_r_heading[j]; - for (auto row_c : m_core_solver.m_r_A.m_rows[row]) { - unsigned cj = row_c.m_j; - if (m_core_solver.m_r_heading[cj] < 0) { - m_var_set.insert(cj); - add_value(m_core_solver.m_r_x[cj]); - } - } - } -} -} diff --git a/src/util/lp/random_updater_def.h b/src/util/lp/random_updater_def.h new file mode 100644 index 000000000..419335aa3 --- /dev/null +++ b/src/util/lp/random_updater_def.h @@ -0,0 +1,95 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include "util/lp/random_updater.h" +#include "util/lp/static_matrix.h" +#include "util/lp/lar_solver.h" +#include "util/vector.h" +namespace lp { + + + +random_updater::random_updater( + lar_solver & lar_solver, + const vector & column_indices) : + m_lar_solver(lar_solver), + m_range(100000) { + for (unsigned j : column_indices) + add_column_to_sets(j); +} + + +bool random_updater::shift_var(unsigned v) { + return m_lar_solver.get_int_solver()->shift_var(v, m_range); +} + +bool random_updater::random_shift_var(unsigned j) { + if (m_lar_solver.A_r().m_columns.size() >= 50) { + return false; + } + + return shift_var(j); +} + +void random_updater::update() { + for (auto j : m_var_set) { + if (m_var_set.size() <= m_values.size()) { + break; // we are done + } + auto old_x = m_lar_solver.get_column_value(j); + if (random_shift_var(j)) { + remove_value(old_x); + add_value(m_lar_solver.get_column_value(j)); + } + } +} + +void random_updater::add_value(const numeric_pair& v) { + auto it = m_values.find(v); + if (it == m_values.end()) { + m_values[v] = 1; + } else { + it->second++; + } +} + +void random_updater::remove_value(const numeric_pair& v) { + std::unordered_map, unsigned>::iterator it = m_values.find(v); + lp_assert(it != m_values.end()); + it->second--; + if (it->second == 0) + m_values.erase((std::unordered_map, unsigned>::const_iterator)it); +} + +void random_updater::add_column_to_sets(unsigned j) { + if (m_lar_solver.get_core_solver().m_r_heading[j] < 0) { + m_var_set.insert(j); + add_value(m_lar_solver.get_core_solver().m_r_x[j]); + } else { + unsigned row = m_lar_solver.get_core_solver().m_r_heading[j]; + for (auto & row_c : m_lar_solver.get_core_solver().m_r_A.m_rows[row]) { + unsigned cj = row_c.m_j; + if (m_lar_solver.get_core_solver().m_r_heading[cj] < 0) { + m_var_set.insert(cj); + add_value(m_lar_solver.get_core_solver().m_r_x[cj]); + } + } + } +} +} 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.h b/src/util/lp/row_eta_matrix.h index 2bc0ac24e..497e7935c 100644 --- a/src/util/lp/row_eta_matrix.h +++ b/src/util/lp/row_eta_matrix.h @@ -70,7 +70,7 @@ public: } void push_back(unsigned row_index, T val ) { - SASSERT(row_index != m_row); + lp_assert(row_index != m_row); m_row_vector.push_back(row_index, val); } diff --git a/src/util/lp/row_eta_matrix.hpp b/src/util/lp/row_eta_matrix_def.h similarity index 92% rename from src/util/lp/row_eta_matrix.hpp rename to src/util/lp/row_eta_matrix_def.h index 969b4af7d..46b187a24 100644 --- a/src/util/lp/row_eta_matrix.hpp +++ b/src/util/lp/row_eta_matrix_def.h @@ -34,7 +34,7 @@ void row_eta_matrix::apply_from_left(vector & w, lp_settings &) { } // w[m_row] = w_at_row; // #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(clone_w, w, m_dimension)); + // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); // delete [] clone_w; // #endif } @@ -58,7 +58,7 @@ void row_eta_matrix::apply_from_left_local_to_T(indexed_vector & w, lp_ auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); w.m_index.erase(it); } - // TBD: SASSERT(check_vector_for_small_values(w, settings)); + // TBD: lp_assert(check_vector_for_small_values(w, settings)); } template @@ -80,7 +80,7 @@ void row_eta_matrix::apply_from_left_local_to_X(indexed_vector & w, lp_ auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); w.m_index.erase(it); } - // TBD: does not compile SASSERT(check_vector_for_small_values(w, settings)); + // TBD: does not compile lp_assert(check_vector_for_small_values(w, settings)); } template @@ -96,14 +96,14 @@ void row_eta_matrix::apply_from_right(vector & w) { w[it.first] += w_row * it.second; } #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(clone_w, w, m_dimension)); + // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); // delete clone_w; #endif } template void row_eta_matrix::apply_from_right(indexed_vector & w) { - SASSERT(w.is_OK()); + lp_assert(w.is_OK()); const T & w_row = w[m_row]; if (numeric_traits::is_zero(w_row)) return; #ifdef Z3DEBUG @@ -145,7 +145,7 @@ void row_eta_matrix::apply_from_right(indexed_vector & w) { } } #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(wcopy, w.m_data)); + // lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif } @@ -166,7 +166,7 @@ void row_eta_matrix::conjugate_by_permutation(permutation_matrix & p for (unsigned i = static_cast(columns.size()); i-- > 0;) m_row_vector.m_data[i].first = p.get_rev(columns[i]); #ifdef Z3DEBUG - // SASSERT(deb == *this); + // lp_assert(deb == *this); #endif } #ifdef Z3DEBUG 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.h b/src/util/lp/scaler.h index 509671528..f98be83bd 100644 --- a/src/util/lp/scaler.h +++ b/src/util/lp/scaler.h @@ -46,7 +46,7 @@ public: m_scaling_maximum(scaling_maximum), m_column_scale(column_scale), m_settings(settings) { - SASSERT(m_column_scale.size() == 0); + lp_assert(m_column_scale.size() == 0); m_column_scale.resize(m_A.column_count(), numeric_traits::one()); } diff --git a/src/util/lp/scaler.hpp b/src/util/lp/scaler_def.h similarity index 90% rename from src/util/lp/scaler.hpp rename to src/util/lp/scaler_def.h index ea8dc98c4..2710f89bf 100644 --- a/src/util/lp/scaler.hpp +++ b/src/util/lp/scaler_def.h @@ -56,7 +56,7 @@ template T scaler::A_max() const { template T scaler::get_A_ratio() const { T min = A_min(); T max = A_max(); - SASSERT(!m_settings.abs_val_is_smaller_than_zero_tolerance(min)); + lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(min)); T ratio = max / min; return ratio; } @@ -66,7 +66,7 @@ template T scaler::get_max_ratio_on_rows() con unsigned i = m_A.row_count(); while (i--) { T den = m_A.get_min_abs_in_row(i); - SASSERT(!m_settings.abs_val_is_smaller_than_zero_tolerance(den)); + lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(den)); T t = m_A.get_max_abs_in_row(i)/ den; if (t > ret) ret = t; @@ -93,7 +93,7 @@ template void scaler::scale_rows_with_geometri while (i--) { T max = m_A.get_max_abs_in_row(i); T min = m_A.get_min_abs_in_row(i); - SASSERT(max > zero_of_type() && min > zero_of_type()); + lp_assert(max > zero_of_type() && min > zero_of_type()); if (is_zero(max) || is_zero(min)) continue; T gm = T(sqrt(numeric_traits::get_double(max*min))); @@ -219,18 +219,18 @@ template void scaler::scale_row(unsigned i) { if (numeric_traits::is_zero(row_max)) { return; } - if (numeric_traits::get_double(row_max) < m_scaling_minimum) { + if (row_max < m_scaling_minimum) { do { alpha *= 2; row_max *= 2; - } while (numeric_traits::get_double(row_max) < m_scaling_minimum); + } while (row_max < m_scaling_minimum); m_A.multiply_row(i, alpha); m_b[i] *= alpha; - } else if (numeric_traits::get_double(row_max) > m_scaling_maximum) { + } else if (row_max > m_scaling_maximum) { do { alpha /= 2; row_max /= 2; - } while (numeric_traits::get_double(row_max) > m_scaling_maximum); + } while (row_max > m_scaling_maximum); m_A.multiply_row(i, alpha); m_b[i] *= alpha; } @@ -243,17 +243,16 @@ template void scaler::scale_column(unsigned i) if (numeric_traits::is_zero(column_max)){ return; // the column has zeros only } - - if (numeric_traits::get_double(column_max) < m_scaling_minimum) { + if (column_max < m_scaling_minimum) { do { alpha *= 2; column_max *= 2; - } while (numeric_traits::get_double(column_max) < m_scaling_minimum); - } else if (numeric_traits::get_double(column_max) > m_scaling_maximum) { + } while (column_max < m_scaling_minimum); + } else if (column_max > m_scaling_maximum) { do { alpha /= 2; column_max /= 2; - } while (numeric_traits::get_double(column_max) > m_scaling_maximum); + } while (column_max > m_scaling_maximum); } else { return; } 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_instances.cpp deleted file mode 100644 index 64a555bb2..000000000 --- a/src/util/lp/sparse_matrix_instances.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "util/lp/lp_settings.h" -#include "util/lp/lu.h" -#include "util/lp/sparse_matrix.hpp" -#include "util/lp/dense_matrix.h" -namespace lp { -template double sparse_matrix::dot_product_with_row(unsigned int, vector const&) const; -template void sparse_matrix::add_new_element(unsigned int, unsigned int, const double&); -template void sparse_matrix::divide_row_by_constant(unsigned int, const double&, lp_settings&); -template bool sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); -template const double & sparse_matrix::get(unsigned int, unsigned int) const; -template unsigned sparse_matrix::get_number_of_nonzeroes() const; -template bool sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned sparse_matrix::lowest_row_in_column(unsigned int); -template bool sparse_matrix::pivot_row_to_row(unsigned int, const double&, unsigned int, lp_settings&); -template bool sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); -template void sparse_matrix::prepare_for_factorization(); -template void sparse_matrix::remove_element(vector >&, indexed_value&); -template void sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void sparse_matrix::set(unsigned int, unsigned int, double); -template void sparse_matrix::set_max_in_row(vector >&); -template bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); -template void sparse_matrix::solve_y_U(vector&) const; -template sparse_matrix::sparse_matrix(static_matrix const&, vector&); -template sparse_matrix::sparse_matrix(unsigned int); -template void sparse_matrix::add_new_element(unsigned int, unsigned int, const mpq&); -template void sparse_matrix::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); -template bool sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); -template mpq const & sparse_matrix::get(unsigned int, unsigned int) const; -template unsigned sparse_matrix::get_number_of_nonzeroes() const; -template bool sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned sparse_matrix::lowest_row_in_column(unsigned int); -template bool sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); -template void sparse_matrix::prepare_for_factorization(); -template void sparse_matrix::remove_element(vector> &, indexed_value&); -template void sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void sparse_matrix::set_max_in_row(vector>&); -template bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); -template void sparse_matrix::solve_y_U(vector&) const; -template sparse_matrix::sparse_matrix(static_matrix const&, vector&); -template void sparse_matrix>::add_new_element(unsigned int, unsigned int, const mpq&); -template void sparse_matrix>::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); -template bool sparse_matrix>::fill_eta_matrix(unsigned int, eta_matrix >**); -template const mpq & sparse_matrix>::get(unsigned int, unsigned int) const; -template unsigned sparse_matrix>::get_number_of_nonzeroes() const; -template bool sparse_matrix>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned sparse_matrix>::lowest_row_in_column(unsigned int); -template bool sparse_matrix>::pivot_with_eta(unsigned int, eta_matrix >*, lp_settings&); -template void sparse_matrix>::prepare_for_factorization(); -template void sparse_matrix>::remove_element(vector>&, indexed_value&); -template void sparse_matrix>::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void sparse_matrix>::set_max_in_row(vector>&); -template bool sparse_matrix>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool sparse_matrix>::shorten_active_matrix(unsigned int, eta_matrix >*); -template void sparse_matrix>::solve_y_U(vector&) const; -template sparse_matrix>::sparse_matrix(static_matrix > const&, vector&); -template void sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings &); -template void sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings&); -template void sparse_matrix>::double_solve_U_y(indexed_vector&, const lp_settings&); -template void sparse_matrix >::double_solve_U_y >(indexed_vector>&, const lp_settings&); -template void lp::sparse_matrix::solve_U_y_indexed_only(lp::indexed_vector&, const lp_settings&, vector &); -template void lp::sparse_matrix::solve_U_y_indexed_only(lp::indexed_vector&, const lp_settings &, vector &); -#ifdef Z3DEBUG -template bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -template bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -template bool sparse_matrix >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -#endif -} -template void lp::sparse_matrix >::solve_U_y_indexed_only(lp::indexed_vector&, const lp_settings &, vector &); -template void lp::sparse_matrix::solve_U_y(vector&); -template void lp::sparse_matrix::double_solve_U_y(vector&); -template void lp::sparse_matrix::solve_U_y(vector&); -template void lp::sparse_matrix::double_solve_U_y(vector&); -template void lp::sparse_matrix >::solve_U_y >(vector >&); -template void lp::sparse_matrix >::double_solve_U_y >(vector >&); -template void lp::sparse_matrix::find_error_in_solution_U_y_indexed(lp::indexed_vector&, lp::indexed_vector&, const vector &); -template double lp::sparse_matrix::dot_product_with_row(unsigned int, lp::indexed_vector const&) const; -template void lp::sparse_matrix::find_error_in_solution_U_y_indexed(lp::indexed_vector&, lp::indexed_vector&, const vector &); -template lp::mpq lp::sparse_matrix::dot_product_with_row(unsigned int, lp::indexed_vector const&) const; -template void lp::sparse_matrix >::find_error_in_solution_U_y_indexed(lp::indexed_vector&, lp::indexed_vector&, const vector &); -template lp::mpq lp::sparse_matrix >::dot_product_with_row(unsigned int, lp::indexed_vector const&) const; -template void lp::sparse_matrix >::find_error_in_solution_U_y_indexed >(lp::indexed_vector >&, lp::indexed_vector >&, const vector &); -template lp::numeric_pair lp::sparse_matrix >::dot_product_with_row >(unsigned int, lp::indexed_vector > const&) const; -template void lp::sparse_matrix::extend_and_sort_active_rows(vector const&, vector&); - -template void lp::sparse_matrix >::extend_and_sort_active_rows(vector const&, vector&); - -template void lp::sparse_matrix >::solve_U_y(vector&); -template void lp::sparse_matrix >::double_solve_U_y(vector&); -template void lp::sparse_matrix< lp::mpq,lp::numeric_pair< lp::mpq> >::set(unsigned int,unsigned int, lp::mpq); -template void lp::sparse_matrix::solve_y_U_indexed(lp::indexed_vector&, const lp_settings & ); -template void lp::sparse_matrix::solve_y_U_indexed(lp::indexed_vector&, const lp_settings &); -template void lp::sparse_matrix >::solve_y_U_indexed(lp::indexed_vector&, const lp_settings &); - diff --git a/src/util/lp/sparse_vector.h b/src/util/lp/sparse_vector.h index 51639674c..67ea97a9d 100644 --- a/src/util/lp/sparse_vector.h +++ b/src/util/lp/sparse_vector.h @@ -42,7 +42,7 @@ public: } #endif void divide(T const & a) { - SASSERT(!lp_settings::is_eps_small_general(a, 1e-12)); + lp_assert(!lp_settings::is_eps_small_general(a, 1e-12)); for (auto & t : m_data) { t.second /= a; } } diff --git a/src/util/lp/square_dense_submatrix_instances.cpp b/src/util/lp/square_dense_submatrix.cpp similarity index 87% rename from src/util/lp/square_dense_submatrix_instances.cpp rename to src/util/lp/square_dense_submatrix.cpp index e1df0036e..802f3d74a 100644 --- a/src/util/lp/square_dense_submatrix_instances.cpp +++ b/src/util/lp/square_dense_submatrix.cpp @@ -19,15 +19,15 @@ Revision History: --*/ #include #include "util/vector.h" -#include "util/lp/square_dense_submatrix.hpp" -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); +#include "util/lp/square_dense_submatrix_def.h" +template void lp::square_dense_submatrix::init(lp::square_sparse_matrix*, unsigned int); +template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); template bool lp::square_dense_submatrix::is_L_matrix() const; template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); template int lp::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; template void lp::square_dense_submatrix::pivot(unsigned int, lp::lp_settings&); -template lp::square_dense_submatrix >::square_dense_submatrix(lp::sparse_matrix >*, unsigned int); +template lp::square_dense_submatrix >::square_dense_submatrix(lp::square_sparse_matrix >*, unsigned int); template void lp::square_dense_submatrix >::update_parent_matrix(lp::lp_settings&); template bool lp::square_dense_submatrix >::is_L_matrix() const; template void lp::square_dense_submatrix >::conjugate_by_permutation(lp::permutation_matrix >&); @@ -40,7 +40,7 @@ template void lp::square_dense_submatrix::apply_from_right(vecto template void lp::square_dense_submatrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); template void lp::square_dense_submatrix::apply_from_left_to_vector(vector&); -template lp::square_dense_submatrix::square_dense_submatrix(lp::sparse_matrix*, unsigned int); +template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); template bool lp::square_dense_submatrix::is_L_matrix() const; template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); diff --git a/src/util/lp/square_dense_submatrix.h b/src/util/lp/square_dense_submatrix.h index d43b2eadd..0c6bbdec3 100644 --- a/src/util/lp/square_dense_submatrix.h +++ b/src/util/lp/square_dense_submatrix.h @@ -34,7 +34,7 @@ Revision History: #include "util/lp/lp_settings.h" #include "util/lp/eta_matrix.h" #include "util/lp/binary_heap_upair_queue.h" -#include "util/lp/sparse_matrix.h" +#include "util/lp/square_sparse_matrix.h" namespace lp { template class square_dense_submatrix : public tail_matrix { @@ -45,11 +45,11 @@ class square_dense_submatrix : public tail_matrix { ref(unsigned i, square_dense_submatrix & s) : m_i_offset((i - s.m_index_start) * s.m_dim), m_s(s){} T & operator[] (unsigned j) { - SASSERT(j >= m_s.m_index_start); + lp_assert(j >= m_s.m_index_start); return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; } const T & operator[] (unsigned j) const { - SASSERT(j >= m_s.m_index_start); + lp_assert(j >= m_s.m_index_start); return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; } }; @@ -57,7 +57,7 @@ public: unsigned m_index_start; unsigned m_dim; vector m_v; - sparse_matrix * m_parent; + square_sparse_matrix * m_parent; permutation_matrix m_row_permutation; indexed_vector m_work_vector; public: @@ -66,15 +66,15 @@ public: square_dense_submatrix() {} - square_dense_submatrix (sparse_matrix *parent_matrix, unsigned index_start); + square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start); - void init(sparse_matrix *parent_matrix, unsigned index_start); + void init(square_sparse_matrix *parent_matrix, unsigned index_start); bool is_dense() const override { return true; } ref operator[] (unsigned i) { - SASSERT(i >= m_index_start); - SASSERT(i < m_parent->dimension()); + lp_assert(i >= m_index_start); + lp_assert(i < m_parent->dimension()); return ref(i, *this); } @@ -163,7 +163,7 @@ public: } } } - SASSERT(wcopy.is_OK()); + lp_assert(wcopy.is_OK()); apply_from_right(w.m_data); w.m_index.clear(); if (numeric_traits::precise()) { @@ -182,11 +182,11 @@ public: } } #else - SASSERT(w.is_OK()); - SASSERT(m_work_vector.is_OK()); + lp_assert(w.is_OK()); + lp_assert(m_work_vector.is_OK()); m_work_vector.resize(w.data_size()); m_work_vector.clear(); - SASSERT(m_work_vector.is_OK()); + lp_assert(m_work_vector.is_OK()); unsigned end = m_index_start + m_dim; for (unsigned k : w.m_index) { // find j such that k = adjust_row_inverse(j) @@ -203,7 +203,7 @@ public: } } m_work_vector.clean_up(); - SASSERT(m_work_vector.is_OK()); + lp_assert(m_work_vector.is_OK()); w = m_work_vector; #endif } diff --git a/src/util/lp/square_dense_submatrix.hpp b/src/util/lp/square_dense_submatrix_def.h similarity index 93% rename from src/util/lp/square_dense_submatrix.hpp rename to src/util/lp/square_dense_submatrix_def.h index cbf69c5dd..54f6e5327 100644 --- a/src/util/lp/square_dense_submatrix.hpp +++ b/src/util/lp/square_dense_submatrix_def.h @@ -21,7 +21,7 @@ Revision History: #include "util/lp/square_dense_submatrix.h" namespace lp { template -square_dense_submatrix::square_dense_submatrix (sparse_matrix *parent_matrix, unsigned index_start) : +square_dense_submatrix::square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start) : m_index_start(index_start), m_dim(parent_matrix->dimension() - index_start), m_v(m_dim * m_dim), @@ -33,14 +33,14 @@ square_dense_submatrix::square_dense_submatrix (sparse_matrix *paren unsigned row = parent_matrix->adjust_row(i); for (auto & iv : parent_matrix->get_row_values(row)) { unsigned j = parent_matrix->adjust_column_inverse(iv.m_index); - SASSERT(j>= m_index_start); + lp_assert(j>= m_index_start); m_v[row_offset + j] = iv.m_value; } row_offset += m_dim; } } -template void square_dense_submatrix::init(sparse_matrix *parent_matrix, unsigned index_start) { +template void square_dense_submatrix::init(square_sparse_matrix *parent_matrix, unsigned index_start) { m_index_start = index_start; m_dim = parent_matrix->dimension() - index_start; m_v.resize(m_dim * m_dim); @@ -58,7 +58,7 @@ template void square_dense_submatrix::init(sparse template int square_dense_submatrix::find_pivot_column_in_row(unsigned i) const { int j = -1; T max = zero_of_type(); - SASSERT(i >= m_index_start); + lp_assert(i >= m_index_start); unsigned row_start = (i - m_index_start) * m_dim; for (unsigned k = i; k < m_parent->dimension(); k++) { unsigned col = adjust_column(k); // this is where the column is in the row @@ -79,14 +79,14 @@ template void square_dense_submatrix::pivot(un } template void square_dense_submatrix::pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings) { - SASSERT(i < row); + lp_assert(i < row); unsigned pj = adjust_column(i); // the pivot column unsigned pjd = pj - m_index_start; unsigned pivot_row_offset = (i-m_index_start)*m_dim; T pivot = m_v[pivot_row_offset + pjd]; unsigned row_offset= (row-m_index_start)*m_dim; T m = m_v[row_offset + pjd]; - SASSERT(!is_zero(pivot)); + lp_assert(!is_zero(pivot)); m_v[row_offset + pjd] = -m * pivot; // creating L matrix for (unsigned j = m_index_start; j < m_parent->dimension(); j++) { if (j == pj) { @@ -109,7 +109,7 @@ template void square_dense_submatrix::divide_r unsigned pj = adjust_column(i); // the pivot column unsigned irow_offset = (i - m_index_start) * m_dim; T pivot = m_v[irow_offset + pj - m_index_start]; - SASSERT(!is_zero(pivot)); + lp_assert(!is_zero(pivot)); for (unsigned k = m_index_start; k < m_parent->dimension(); k++) { if (k == pj){ m_v[irow_offset++] = one_of_type() / pivot; // creating the L matrix diagonal @@ -173,7 +173,7 @@ template void square_dense_submatrix::push_new template template L square_dense_submatrix::row_by_vector_product(unsigned i, const vector & v) { - SASSERT(i >= m_index_start); + lp_assert(i >= m_index_start); unsigned row_in_subm = i - m_index_start; unsigned row_offset = row_in_subm * m_dim; @@ -186,7 +186,7 @@ L square_dense_submatrix::row_by_vector_product(unsigned i, const vector template L square_dense_submatrix::column_by_vector_product(unsigned j, const vector & v) { - SASSERT(j >= m_index_start); + lp_assert(j >= m_index_start); unsigned offset = j - m_index_start; L r = zero_of_type(); @@ -197,7 +197,7 @@ L square_dense_submatrix::column_by_vector_product(unsigned j, const vecto template template L square_dense_submatrix::row_by_indexed_vector_product(unsigned i, const indexed_vector & v) { - SASSERT(i >= m_index_start); + lp_assert(i >= m_index_start); unsigned row_in_subm = i - m_index_start; unsigned row_offset = row_in_subm * m_dim; @@ -264,8 +264,8 @@ void square_dense_submatrix::apply_from_left_local(indexed_vector & w, #ifdef Z3DEBUG // cout << "w final" << endl; // print_vector(w.m_data); - // SASSERT(vectors_are_equal(deb_w, w.m_data)); - // SASSERT(w.is_OK()); + // lp_assert(vectors_are_equal(deb_w, w.m_data)); + // lp_assert(w.is_OK()); #endif } @@ -295,16 +295,16 @@ void square_dense_submatrix::apply_from_left_to_vector(vector & w) { #ifdef Z3DEBUG // cout << "w final" << endl; // print_vector(w.m_data); - // SASSERT(vectors_are_equal(deb_w, w)); + // lp_assert(vectors_are_equal(deb_w, w)); #endif } template bool square_dense_submatrix::is_L_matrix() const { #ifdef Z3DEBUG - SASSERT(m_row_permutation.is_identity()); + lp_assert(m_row_permutation.is_identity()); for (unsigned i = 0; i < m_parent->dimension(); i++) { if (i < m_index_start) { - SASSERT(m_column_permutation[i] == i); + lp_assert(m_column_permutation[i] == i); continue; } unsigned row_offs = (i-m_index_start)*m_dim; @@ -312,9 +312,9 @@ template bool square_dense_submatrix::is_L_mat unsigned j = m_index_start + k; unsigned jex = adjust_column_inverse(j); if (jex > i) { - SASSERT(is_zero(m_v[row_offs + k])); + lp_assert(is_zero(m_v[row_offs + k])); } else if (jex == i) { - SASSERT(!is_zero(m_v[row_offs + k])); + lp_assert(!is_zero(m_v[row_offs + k])); } } } @@ -342,7 +342,7 @@ template void square_dense_submatrix::apply_from_ } w = t; #ifdef Z3DEBUG - // SASSERT(vector_are_equal(deb_w, w)); + // lp_assert(vector_are_equal(deb_w, w)); #endif } diff --git a/src/util/lp/square_sparse_matrix.cpp b/src/util/lp/square_sparse_matrix.cpp new file mode 100644 index 000000000..6c65cd863 --- /dev/null +++ b/src/util/lp/square_sparse_matrix.cpp @@ -0,0 +1,119 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include +#include "util/vector.h" +#include "util/lp/lp_settings.h" +#include "util/lp/lu.h" +#include "util/lp/square_sparse_matrix_def.h" +#include "util/lp/dense_matrix.h" +namespace lp { +template double square_sparse_matrix::dot_product_with_row(unsigned int, vector const&) const; +template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const double&); +template void square_sparse_matrix::divide_row_by_constant(unsigned int, const double&, lp_settings&); +template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); +template const double & square_sparse_matrix::get(unsigned int, unsigned int) const; +template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; +template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); +template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); +template bool square_sparse_matrix::pivot_row_to_row(unsigned int, const double&, unsigned int, lp_settings&); +template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); +template void square_sparse_matrix::prepare_for_factorization(); +template void square_sparse_matrix::remove_element(vector >&, indexed_value&); +template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); +template void square_sparse_matrix::set(unsigned int, unsigned int, double); +template void square_sparse_matrix::set_max_in_row(vector >&); +template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); +template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); +template void square_sparse_matrix::solve_y_U(vector&) const; +template square_sparse_matrix::square_sparse_matrix(unsigned int, unsigned); +template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const mpq&); +template void square_sparse_matrix::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); +template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); +template mpq const & square_sparse_matrix::get(unsigned int, unsigned int) const; +template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; +template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); +template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); +template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); +template void square_sparse_matrix::prepare_for_factorization(); +template void square_sparse_matrix::remove_element(vector> &, indexed_value&); +template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); +template void square_sparse_matrix::set_max_in_row(vector>&); +template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); +template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); +template void square_sparse_matrix::solve_y_U(vector&) const; +template void square_sparse_matrix>::add_new_element(unsigned int, unsigned int, const mpq&); +template void square_sparse_matrix>::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); +template bool square_sparse_matrix>::fill_eta_matrix(unsigned int, eta_matrix >**); +template const mpq & square_sparse_matrix>::get(unsigned int, unsigned int) const; +template unsigned square_sparse_matrix>::get_number_of_nonzeroes() const; +template bool square_sparse_matrix>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); +template unsigned square_sparse_matrix>::lowest_row_in_column(unsigned int); +template bool square_sparse_matrix>::pivot_with_eta(unsigned int, eta_matrix >*, lp_settings&); +template void square_sparse_matrix>::prepare_for_factorization(); +template void square_sparse_matrix>::remove_element(vector>&, indexed_value&); +template void square_sparse_matrix>::replace_column(unsigned int, indexed_vector&, lp_settings&); +template void square_sparse_matrix>::set_max_in_row(vector>&); +template bool square_sparse_matrix>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); +template bool square_sparse_matrix>::shorten_active_matrix(unsigned int, eta_matrix >*); +template void square_sparse_matrix>::solve_y_U(vector&) const; +template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings &); +template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings&); +template void square_sparse_matrix>::double_solve_U_y(indexed_vector&, const lp_settings&); +template void square_sparse_matrix >::double_solve_U_y >(indexed_vector>&, const lp_settings&); +template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings&, vector &); +template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); +#ifdef Z3DEBUG +template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; +template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; +template bool square_sparse_matrix >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; +#endif + +template void square_sparse_matrix >::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); +template void square_sparse_matrix::solve_U_y(vector&); +template void square_sparse_matrix::double_solve_U_y(vector&); +template void square_sparse_matrix::solve_U_y(vector&); +template void square_sparse_matrix::double_solve_U_y(vector&); +template void square_sparse_matrix >::solve_U_y >(vector >&); +template void square_sparse_matrix >::double_solve_U_y >(vector >&); +template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); +template double square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; +template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); +template mpq square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; +template void square_sparse_matrix >::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); +template mpq square_sparse_matrix >::dot_product_with_row(unsigned int, indexed_vector const&) const; +template void square_sparse_matrix >::find_error_in_solution_U_y_indexed >(indexed_vector >&, indexed_vector >&, const vector &); +template numeric_pair square_sparse_matrix >::dot_product_with_row >(unsigned int, indexed_vector > const&) const; +template void square_sparse_matrix::extend_and_sort_active_rows(vector const&, vector&); + +template void square_sparse_matrix >::extend_and_sort_active_rows(vector const&, vector&); + +template void square_sparse_matrix >::solve_U_y(vector&); +template void square_sparse_matrix >::double_solve_U_y(vector&); +template void square_sparse_matrix< mpq,numeric_pair< mpq> >::set(unsigned int,unsigned int, mpq); +template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings & ); +template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings &); +template void square_sparse_matrix >::solve_y_U_indexed(indexed_vector&, const lp_settings &); + +template square_sparse_matrix::square_sparse_matrix(static_matrix const&, vector&); +template square_sparse_matrix::square_sparse_matrix (static_matrix const&, vector&); +template square_sparse_matrix >::square_sparse_matrix(static_matrix > const&, vector&); +} +template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); +template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); diff --git a/src/util/lp/sparse_matrix.h b/src/util/lp/square_sparse_matrix.h similarity index 91% rename from src/util/lp/sparse_matrix.h rename to src/util/lp/square_sparse_matrix.h index 44111a9fd..d84a8a289 100644 --- a/src/util/lp/sparse_matrix.h +++ b/src/util/lp/square_sparse_matrix.h @@ -39,7 +39,7 @@ Revision History: namespace lp { // it is a square matrix template -class sparse_matrix +class square_sparse_matrix #ifdef Z3DEBUG : public matrix #endif @@ -96,29 +96,46 @@ public: return m_column_permutation[col]; } - void copy_column_from_static_matrix(unsigned col, static_matrix const &A, unsigned col_index_in_the_new_matrix); - void copy_B(static_matrix const &A, vector & basis); + template + void copy_column_from_input(unsigned input_column, const M& A, unsigned j); + template + void copy_column_from_input_with_possible_zeros(const M& A, unsigned j); + + template + void copy_from_input(const M& A); + template + void copy_from_input_on_basis(const M& A, vector & basis); public: - // constructor that copies columns of the basis from A - sparse_matrix(static_matrix const &A, vector & basis); + + // constructors + template + square_sparse_matrix(const M &A, vector& basis); + template + square_sparse_matrix(const M &A); + + square_sparse_matrix(unsigned dim, unsigned); // the second parameter is needed to distinguish this + // constructor from the one above + + + class ref_matrix_element { - sparse_matrix & m_matrix; + square_sparse_matrix & m_matrix; unsigned m_row; unsigned m_col; public: - ref_matrix_element(sparse_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} + ref_matrix_element(square_sparse_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} ref_matrix_element & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; } ref_matrix_element & operator=(ref_matrix_element const & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } operator T () const { return m_matrix.get(m_row, m_col); } }; class ref_row { - sparse_matrix & m_matrix; + square_sparse_matrix & m_matrix; unsigned m_row; public: - ref_row(sparse_matrix & m, unsigned row) : m_matrix(m), m_row(row) {} + ref_row(square_sparse_matrix & m, unsigned row) : m_matrix(m), m_row(row) {} ref_matrix_element operator[](unsigned col) const { return ref_matrix_element(m_matrix, m_row, col); } }; @@ -154,11 +171,6 @@ public: return m_columns[col].m_values; } - // constructor creating a zero matrix of dim*dim - sparse_matrix(unsigned dim); - - - unsigned dimension() const {return static_cast(m_row_permutation.size());} #ifdef Z3DEBUG @@ -221,19 +233,19 @@ public: void multiply_from_right(permutation_matrix& p) { // m_dense = m_dense * p; m_column_permutation.multiply_by_permutation_from_right(p); - // SASSERT(*this == m_dense); + // lp_assert(*this == m_dense); } void multiply_from_left(permutation_matrix& p) { // m_dense = p * m_dense; m_row_permutation.multiply_by_permutation_from_left(p); - // SASSERT(*this == m_dense); + // lp_assert(*this == m_dense); } void multiply_from_left_with_reverse(permutation_matrix& p) { // m_dense = p * m_dense; m_row_permutation.multiply_by_permutation_reverse_from_left(p); - // SASSERT(*this == m_dense); + // lp_assert(*this == m_dense); } // adding delta columns at the end of the matrix @@ -242,17 +254,13 @@ public: void delete_column(int i); void swap_columns(unsigned a, unsigned b) { - // cout << "swaapoiiin" << std::endl; - // dense_matrix d(*this); m_column_permutation.transpose_from_left(a, b); - // d.swap_columns(a, b); - // SASSERT(*this == d); } void swap_rows(unsigned a, unsigned b) { m_row_permutation.transpose_from_right(a, b); // m_dense.swap_rows(a, b); - // SASSERT(*this == m_dense); + // lp_assert(*this == m_dense); } void divide_row_by_constant(unsigned i, const T & t, lp_settings & settings); @@ -408,7 +416,7 @@ public: void process_index_recursively_for_y_U(unsigned j, vector & sorted_rows); void resize(unsigned new_dim) { unsigned old_dim = dimension(); - SASSERT(new_dim >= old_dim); + lp_assert(new_dim >= old_dim); for (unsigned j = old_dim; j < new_dim; j++) { m_rows.push_back(vector>()); m_columns.push_back(col_header()); diff --git a/src/util/lp/sparse_matrix.hpp b/src/util/lp/square_sparse_matrix_def.h similarity index 74% rename from src/util/lp/sparse_matrix.hpp rename to src/util/lp/square_sparse_matrix_def.h index d2040d313..791bdb6ae 100644 --- a/src/util/lp/sparse_matrix.hpp +++ b/src/util/lp/square_sparse_matrix_def.h @@ -19,38 +19,61 @@ Revision History: --*/ #include "util/vector.h" -#include "util/lp/sparse_matrix.h" +#include "util/lp/square_sparse_matrix.h" #include #include namespace lp { template -void sparse_matrix::copy_column_from_static_matrix(unsigned col, static_matrix const &A, unsigned col_index_in_the_new_matrix) { - vector const & A_col_vector = A.m_columns[col]; - unsigned size = static_cast(A_col_vector.size()); - vector> & new_column_vector = m_columns[col_index_in_the_new_matrix].m_values; - for (unsigned l = 0; l < size; l++) { - column_cell const & col_cell = A_col_vector[l]; +template +void square_sparse_matrix::copy_column_from_input(unsigned input_column, const M& A, unsigned j) { + vector> & new_column_vector = m_columns[j].m_values; + for (const auto & c : A.column(input_column)) { unsigned col_offset = static_cast(new_column_vector.size()); - vector> & row_vector = m_rows[col_cell.m_i]; + vector> & row_vector = m_rows[c.var()]; unsigned row_offset = static_cast(row_vector.size()); - const T & val = A.get_val(col_cell); - new_column_vector.push_back(indexed_value(val, col_cell.m_i, row_offset)); - row_vector.push_back(indexed_value(val, col_index_in_the_new_matrix, col_offset)); + new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); + row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); m_n_of_active_elems++; } } template -void sparse_matrix::copy_B(static_matrix const &A, vector & basis) { - unsigned m = A.row_count(); // this should be the size of basis +template +void square_sparse_matrix::copy_column_from_input_with_possible_zeros(const M& A, unsigned j) { + vector> & new_column_vector = m_columns[j].m_values; + for (const auto & c : A.column(j)) { + if (is_zero(c.coeff())) + continue; + unsigned col_offset = static_cast(new_column_vector.size()); + vector> & row_vector = m_rows[c.var()]; + unsigned row_offset = static_cast(row_vector.size()); + new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); + row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); + m_n_of_active_elems++; + } +} + +template +template +void square_sparse_matrix::copy_from_input_on_basis(const M & A, vector & basis) { + unsigned m = A.row_count(); for (unsigned j = m; j-- > 0;) { - copy_column_from_static_matrix(basis[j], A, j); + copy_column_from_input(basis[j], A, j); + } +} +template +template +void square_sparse_matrix::copy_from_input(const M & A) { + unsigned m = A.row_count(); + for (unsigned j = m; j-- > 0;) { + copy_column_from_input_with_possible_zeros(A, j); } } // constructor that copies columns of the basis from A template -sparse_matrix::sparse_matrix(static_matrix const &A, vector & basis) : +template +square_sparse_matrix::square_sparse_matrix(const M &A, vector & basis) : m_n_of_active_elems(0), m_pivot_queue(A.row_count()), m_row_permutation(A.row_count()), @@ -59,11 +82,26 @@ sparse_matrix::sparse_matrix(static_matrix const &A, vector -void sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned col, T val) { // should not be used in efficient code +template +square_sparse_matrix::square_sparse_matrix(const M &A) : + m_n_of_active_elems(0), + m_pivot_queue(A.row_count()), + m_row_permutation(A.row_count()), + m_column_permutation(A.row_count()), + m_work_pivot_vector(A.row_count(), -1), + m_processed(A.row_count()) { + init_row_headers(); + init_column_headers(); + copy_from_input(A); +} + + +template +void square_sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned col, T val) { // should not be used in efficient code vector> & row_vec = m_rows[row]; for (auto & iv : row_vec) { if (iv.m_index == col) { @@ -76,7 +114,7 @@ void sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned c } template -void sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned col, T val) { // should not be used in efficient code +void square_sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned col, T val) { // should not be used in efficient code vector> & col_vec = m_columns[col].m_values; for (auto & iv : col_vec) { if (iv.m_index == row) { @@ -90,23 +128,23 @@ void sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned c template -void sparse_matrix::set_with_no_adjusting(unsigned row, unsigned col, T val) { // should not be used in efficient code +void square_sparse_matrix::set_with_no_adjusting(unsigned row, unsigned col, T val) { // should not be used in efficient code set_with_no_adjusting_for_row(row, col, val); set_with_no_adjusting_for_col(row, col, val); } template -void sparse_matrix::set(unsigned row, unsigned col, T val) { // should not be used in efficient code - SASSERT(row < dimension() && col < dimension()); +void square_sparse_matrix::set(unsigned row, unsigned col, T val) { // should not be used in efficient code + lp_assert(row < dimension() && col < dimension()); // m_dense.set_elem(row, col, val); row = adjust_row(row); col = adjust_column(col); set_with_no_adjusting(row, col, val); - // SASSERT(*this == m_dense); + // lp_assert(*this == m_dense); } template -T const & sparse_matrix::get_not_adjusted(unsigned row, unsigned col) const { +T const & square_sparse_matrix::get_not_adjusted(unsigned row, unsigned col) const { for (indexed_value const & iv : m_rows[row]) { if (iv.m_index == col) { return iv.m_value; @@ -116,7 +154,7 @@ T const & sparse_matrix::get_not_adjusted(unsigned row, unsigned col) cons } template -T const & sparse_matrix::get(unsigned row, unsigned col) const { // should not be used in efficient code +T const & square_sparse_matrix::get(unsigned row, unsigned col) const { // should not be used in efficient code row = adjust_row(row); auto & row_chunk = m_rows[row]; col = adjust_column(col); @@ -130,7 +168,7 @@ T const & sparse_matrix::get(unsigned row, unsigned col) const { // should // constructor creating a zero matrix of dim*dim template -sparse_matrix::sparse_matrix(unsigned dim) : +square_sparse_matrix::square_sparse_matrix(unsigned dim, unsigned ) : m_pivot_queue(dim), // dim will be the initial size of the queue m_row_permutation(dim), m_column_permutation(dim), @@ -141,21 +179,21 @@ sparse_matrix::sparse_matrix(unsigned dim) : } template -void sparse_matrix::init_row_headers() { +void square_sparse_matrix::init_row_headers() { for (unsigned l = 0; l < m_row_permutation.size(); l++) { m_rows.push_back(vector>()); } } template -void sparse_matrix::init_column_headers() { // we alway have only square sparse_matrix +void square_sparse_matrix::init_column_headers() { // we alway have only square square_sparse_matrix for (unsigned l = 0; l < m_row_permutation.size(); l++) { m_columns.push_back(col_header()); } } template -unsigned sparse_matrix::lowest_row_in_column(unsigned j) { +unsigned square_sparse_matrix::lowest_row_in_column(unsigned j) { auto & mc = get_column_values(adjust_column(j)); unsigned ret = 0; for (auto & iv : mc) { @@ -168,7 +206,7 @@ unsigned sparse_matrix::lowest_row_in_column(unsigned j) { } template -void sparse_matrix::remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset) { +void square_sparse_matrix::remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset) { if (column_offset != column_vals.size() - 1) { auto & column_iv = column_vals[column_offset] = column_vals.back(); // copy from the tail column_iv_other(column_iv).m_other = column_offset; @@ -187,14 +225,14 @@ void sparse_matrix::remove_element(vector> & row_vals, un } template -void sparse_matrix::remove_element(vector> & row_chunk, indexed_value & row_el_iv) { +void square_sparse_matrix::remove_element(vector> & row_chunk, indexed_value & row_el_iv) { auto & column_chunk = get_column_values(row_el_iv.m_index); indexed_value & col_el_iv = column_chunk[row_el_iv.m_other]; remove_element(row_chunk, col_el_iv.m_other, column_chunk, row_el_iv.m_other); } template -void sparse_matrix::put_max_index_to_0(vector> & row_vals, unsigned max_index) { +void square_sparse_matrix::put_max_index_to_0(vector> & row_vals, unsigned max_index) { if (max_index == 0) return; indexed_value * max_iv = & row_vals[max_index]; indexed_value * start_iv = & row_vals[0]; @@ -209,7 +247,7 @@ void sparse_matrix::put_max_index_to_0(vector> & row_vals } template -void sparse_matrix::set_max_in_row(vector> & row_vals) { +void square_sparse_matrix::set_max_in_row(vector> & row_vals) { if (row_vals.size() == 0) return; T max_val = abs(row_vals[0].m_value); @@ -225,7 +263,7 @@ void sparse_matrix::set_max_in_row(vector> & row_vals) { } template -bool sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings) { +bool square_sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings) { const T& pivot = eta_matrix->get_diagonal_element(); for (auto & it : eta_matrix->m_column_vector.m_data) { if (!pivot_row_to_row(i, it.second, it.first, settings)) { @@ -243,7 +281,7 @@ bool sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matri // returns the offset of the pivot column in the row template -void sparse_matrix::scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column) { +void square_sparse_matrix::scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column) { auto & rvals = m_rows[row]; unsigned size = rvals.size(); for (unsigned j = 0; j < size; j++) { @@ -260,7 +298,7 @@ void sparse_matrix::scan_row_to_work_vector_and_remove_pivot_column(unsign #ifdef Z3DEBUG template -vector sparse_matrix::get_full_row(unsigned i) const { +vector square_sparse_matrix::get_full_row(unsigned i) const { vector r; for (unsigned j = 0; j < column_count(); j++) r.push_back(get(i, j)); @@ -275,9 +313,9 @@ vector sparse_matrix::get_full_row(unsigned i) const { // After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, // Returns false if the resulting row is all zeroes, and true otherwise template -bool sparse_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ) { - SASSERT(i < dimension() && i0 < dimension()); - SASSERT(i != i0); +bool square_sparse_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ) { + lp_assert(i < dimension() && i0 < dimension()); + lp_assert(i != i0); unsigned pivot_col = adjust_column(i); i = adjust_row(i); i0 = adjust_row(i0); @@ -334,7 +372,7 @@ bool sparse_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned // set the max val as well // returns false if the resulting row is all zeroes, and true otherwise template -bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, +bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, lp_settings & settings) { remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(i0, work_vec, settings); // all non-zero elements in m_work_pivot_vector are new @@ -342,7 +380,7 @@ bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adj if (numeric_traits::is_zero(work_vec[j])) { continue; } - SASSERT(!settings.abs_val_is_smaller_than_drop_tolerance(work_vec[j])); + lp_assert(!settings.abs_val_is_smaller_than_drop_tolerance(work_vec[j])); add_new_element(i0, adjust_column(j), work_vec[j]); work_vec[j] = numeric_traits::zero(); } @@ -358,7 +396,7 @@ bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adj template -void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements(unsigned row) { +void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements(unsigned row) { auto & row_vals = m_rows[row]; for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing // elements from row_vals @@ -377,7 +415,7 @@ void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements // work_vec here has not adjusted column indices template -void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings) { +void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings) { auto & row_vals = m_rows[row]; for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing // elements from row_vals @@ -387,7 +425,7 @@ void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements T val = work_vec[rj]; if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { remove_element(row_vals, row_el_iv); - SASSERT(numeric_traits::is_zero(val)); + lp_assert(numeric_traits::is_zero(val)); } else { m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value = val); work_vec[rj] = numeric_traits::zero(); @@ -398,7 +436,7 @@ void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements // adding delta columns at the end of the matrix template -void sparse_matrix::add_columns_at_the_end(unsigned delta) { +void square_sparse_matrix::add_columns_at_the_end(unsigned delta) { for (unsigned i = 0; i < delta; i++) { col_header col_head; m_columns.push_back(col_head); @@ -407,8 +445,8 @@ void sparse_matrix::add_columns_at_the_end(unsigned delta) { } template -void sparse_matrix::delete_column(int i) { - SASSERT(i < dimension()); +void square_sparse_matrix::delete_column(int i) { + lp_assert(i < dimension()); for (auto cell = m_columns[i].m_head; cell != nullptr;) { auto next_cell = cell->m_down; kill_cell(cell); @@ -417,8 +455,8 @@ void sparse_matrix::delete_column(int i) { } template -void sparse_matrix::divide_row_by_constant(unsigned i, const T & t, lp_settings & settings) { - SASSERT(!settings.abs_val_is_smaller_than_zero_tolerance(t)); +void square_sparse_matrix::divide_row_by_constant(unsigned i, const T & t, lp_settings & settings) { + lp_assert(!settings.abs_val_is_smaller_than_zero_tolerance(t)); i = adjust_row(i); for (auto & iv : m_rows[i]) { T &v = iv.m_value; @@ -434,7 +472,7 @@ void sparse_matrix::divide_row_by_constant(unsigned i, const T & t, lp_set // solving x * this = y, and putting the answer into y // the matrix here has to be upper triangular template -void sparse_matrix::solve_y_U(vector & y) const { // works by rows +void square_sparse_matrix::solve_y_U(vector & y) const { // works by rows #ifdef Z3DEBUG // T * rs = clone_vector(y, dimension()); #endif @@ -455,7 +493,7 @@ void sparse_matrix::solve_y_U(vector & y) const { // works by rows // dense_matrix deb(*this); // T * clone_y = clone_vector(y, dimension()); // deb.apply_from_right(clone_y); - // SASSERT(vectors_are_equal(rs, clone_y, dimension())); + // lp_assert(vectors_are_equal(rs, clone_y, dimension())); // delete [] clone_y; // delete [] rs; #endif @@ -464,7 +502,7 @@ void sparse_matrix::solve_y_U(vector & y) const { // works by rows // solving x * this = y, and putting the answer into y // the matrix here has to be upper triangular template -void sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_settings & settings) { +void square_sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_settings & settings) { #if 0 && Z3DEBUG vector ycopy(y.m_data); if (numeric_traits::precise() == false) @@ -489,10 +527,10 @@ void sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_sett y.m_data[j] = zero_of_type(); } - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); #if 0 && Z3DEBUG if (numeric_traits::precise() == false) - SASSERT(vectors_are_equal(ycopy, y.m_data)); + lp_assert(vectors_are_equal(ycopy, y.m_data)); #endif } @@ -525,7 +563,7 @@ void sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_sett template template -void sparse_matrix::find_error_in_solution_U_y(vector& y_orig, vector & y) { +void square_sparse_matrix::find_error_in_solution_U_y(vector& y_orig, vector & y) { unsigned i = dimension(); while (i--) { y_orig[i] -= dot_product_with_row(i, y); @@ -534,7 +572,7 @@ void sparse_matrix::find_error_in_solution_U_y(vector& y_orig, vector template -void sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows) { +void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows) { for (unsigned i: sorted_active_rows) y_orig.add_value_at_index(i, -dot_product_with_row(i, y)); // cannot round up here!!! // y_orig can contain very small values @@ -543,7 +581,7 @@ void sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector& template template -void sparse_matrix::add_delta_to_solution(const vector& del, vector & y) { +void square_sparse_matrix::add_delta_to_solution(const vector& del, vector & y) { unsigned i = dimension(); while (i--) { y[i] += del[i]; @@ -551,21 +589,21 @@ void sparse_matrix::add_delta_to_solution(const vector& del, vector } template template -void sparse_matrix::add_delta_to_solution(const indexed_vector& del, indexed_vector & y) { -// SASSERT(del.is_OK()); - // SASSERT(y.is_OK()); +void square_sparse_matrix::add_delta_to_solution(const indexed_vector& del, indexed_vector & y) { +// lp_assert(del.is_OK()); + // lp_assert(y.is_OK()); for (auto i : del.m_index) { y.add_value_at_index(i, del[i]); } } template template -void sparse_matrix::double_solve_U_y(indexed_vector& y, const lp_settings & settings){ - SASSERT(y.is_OK()); +void square_sparse_matrix::double_solve_U_y(indexed_vector& y, const lp_settings & settings){ + lp_assert(y.is_OK()); indexed_vector y_orig(y); // copy y aside vector active_rows; solve_U_y_indexed_only(y, settings, active_rows); - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); find_error_in_solution_U_y_indexed(y_orig, y, active_rows); // y_orig contains the error now if (y_orig.m_index.size() * ratio_of_index_size_to_all_size() < 32 * dimension()) { @@ -578,11 +616,11 @@ void sparse_matrix::double_solve_U_y(indexed_vector& y, const lp_settin add_delta_to_solution(y_orig.m_data, y.m_data); y.restore_index_and_clean_from_data(); } - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); } template template -void sparse_matrix::double_solve_U_y(vector& y){ +void square_sparse_matrix::double_solve_U_y(vector& y){ vector y_orig(y); // copy y aside solve_U_y(y); find_error_in_solution_U_y(y_orig, y); @@ -595,7 +633,7 @@ void sparse_matrix::double_solve_U_y(vector& y){ // the matrix here has to be upper triangular template template -void sparse_matrix::solve_U_y(vector & y) { // it is a column wise version +void square_sparse_matrix::solve_U_y(vector & y) { // it is a column wise version #ifdef Z3DEBUG // T * rs = clone_vector(y, dimension()); #endif @@ -614,12 +652,12 @@ void sparse_matrix::solve_U_y(vector & y) { // it is a column wise vers // dense_matrix deb(*this); // T * clone_y = clone_vector(y, dimension()); // deb.apply_from_left(clone_y); - // SASSERT(vectors_are_equal(rs, clone_y, dimension())); + // lp_assert(vectors_are_equal(rs, clone_y, dimension())); #endif } template -void sparse_matrix::process_index_recursively_for_y_U(unsigned j, vector & sorted_active_rows) { - SASSERT(m_processed[j] == false); +void square_sparse_matrix::process_index_recursively_for_y_U(unsigned j, vector & sorted_active_rows) { + lp_assert(m_processed[j] == false); m_processed[j]=true; auto & row = m_rows[adjust_row(j)]; for (auto & c : row) { @@ -633,8 +671,8 @@ void sparse_matrix::process_index_recursively_for_y_U(unsigned j, vector -void sparse_matrix::process_column_recursively(unsigned j, vector & sorted_active_rows) { - SASSERT(m_processed[j] == false); +void square_sparse_matrix::process_column_recursively(unsigned j, vector & sorted_active_rows) { + lp_assert(m_processed[j] == false); auto & mc = m_columns[adjust_column(j)].m_values; for (auto & iv : mc) { unsigned i = adjust_row_inverse(iv.m_index); @@ -649,7 +687,7 @@ void sparse_matrix::process_column_recursively(unsigned j, vector -void sparse_matrix::create_graph_G(const vector & index_or_right_side, vector & sorted_active_rows) { +void square_sparse_matrix::create_graph_G(const vector & index_or_right_side, vector & sorted_active_rows) { for (auto i : index_or_right_side) { if (m_processed[i]) continue; process_column_recursively(i, sorted_active_rows); @@ -662,7 +700,7 @@ void sparse_matrix::create_graph_G(const vector & index_or_right template -void sparse_matrix::extend_and_sort_active_rows(const vector & index_or_right_side, vector & sorted_active_rows) { +void square_sparse_matrix::extend_and_sort_active_rows(const vector & index_or_right_side, vector & sorted_active_rows) { for (auto i : index_or_right_side) { if (m_processed[i]) continue; process_index_recursively_for_y_U(i, sorted_active_rows); @@ -676,7 +714,7 @@ void sparse_matrix::extend_and_sort_active_rows(const vector & i template template -void sparse_matrix::solve_U_y_indexed_only(indexed_vector & y, const lp_settings & settings, vector & sorted_active_rows) { // it is a column wise version +void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector & y, const lp_settings & settings, vector & sorted_active_rows) { // it is a column wise version create_graph_G(y.m_index, sorted_active_rows); for (auto k = sorted_active_rows.size(); k-- > 0;) { @@ -699,18 +737,18 @@ void sparse_matrix::solve_U_y_indexed_only(indexed_vector & y, const lp y[j] = zero_of_type(); } - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); #ifdef Z3DEBUG // dense_matrix deb(this); // vector clone_y(y.m_data); // deb.apply_from_left(clone_y); - // SASSERT(vectors_are_equal(rs, clone_y)); + // lp_assert(vectors_are_equal(rs, clone_y)); #endif } template template -L sparse_matrix::dot_product_with_row (unsigned row, const vector & y) const { +L square_sparse_matrix::dot_product_with_row (unsigned row, const vector & y) const { L ret = zero_of_type(); auto & mc = get_row_values(adjust_row(row)); for (auto & c : mc) { @@ -722,7 +760,7 @@ L sparse_matrix::dot_product_with_row (unsigned row, const vector & y) template template -L sparse_matrix::dot_product_with_row (unsigned row, const indexed_vector & y) const { +L square_sparse_matrix::dot_product_with_row (unsigned row, const indexed_vector & y) const { L ret = zero_of_type(); auto & mc = get_row_values(adjust_row(row)); for (auto & c : mc) { @@ -734,7 +772,7 @@ L sparse_matrix::dot_product_with_row (unsigned row, const indexed_vector< template -unsigned sparse_matrix::get_number_of_nonzeroes() const { +unsigned square_sparse_matrix::get_number_of_nonzeroes() const { unsigned ret = 0; for (unsigned i = dimension(); i--; ) { ret += number_of_non_zeroes_in_row(i); @@ -743,7 +781,7 @@ unsigned sparse_matrix::get_number_of_nonzeroes() const { } template -bool sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) const { +bool square_sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) const { // go over the i-th row auto & mc = get_row_values(adjust_row(i)); if (mc.size() > 0) { @@ -754,7 +792,7 @@ bool sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) co } template -void sparse_matrix::remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv) { +void square_sparse_matrix::remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv) { auto & row_chunk = m_rows[col_el_iv.m_index]; indexed_value & row_el_iv = row_chunk[col_el_iv.m_other]; unsigned index_in_row = col_el_iv.m_other; @@ -767,7 +805,7 @@ void sparse_matrix::remove_element_that_is_not_in_w(vector -void sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w) { +void square_sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w) { // -------------------------------- // column_vals represents the old column auto & column_vals = m_columns[column_to_replace].m_values; @@ -796,7 +834,7 @@ void sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_el } template -void sparse_matrix::add_new_element(unsigned row, unsigned col, const T& val) { +void square_sparse_matrix::add_new_element(unsigned row, unsigned col, const T& val) { auto & row_vals = m_rows[row]; auto & col_vals = m_columns[col].m_values; unsigned row_el_offs = static_cast(row_vals.size()); @@ -809,7 +847,7 @@ void sparse_matrix::add_new_element(unsigned row, unsigned col, const T& v // w contains the "rest" of the new column; all common elements of w and the old column has been zeroed // the old column inside of the matrix has not been changed yet template -void sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings) { +void square_sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings) { for (unsigned i : w.m_index) { T w_at_i = w[i]; if (numeric_traits::is_zero(w_at_i)) continue; // was dealt with already @@ -817,7 +855,7 @@ void sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_r unsigned ai = adjust_row(i); add_new_element(ai, column_to_replace, w_at_i); auto & row_chunk = m_rows[ai]; - SASSERT(row_chunk.size() > 0); + lp_assert(row_chunk.size() > 0); if (abs(w_at_i) > abs(row_chunk[0].m_value)) put_max_index_to_0(row_chunk, static_cast(row_chunk.size()) - 1); } @@ -827,14 +865,14 @@ void sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_r } template -void sparse_matrix::replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings) { +void square_sparse_matrix::replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings) { column_to_replace = adjust_column(column_to_replace); remove_elements_that_are_not_in_w_and_update_common_elements(column_to_replace, w); add_new_elements_of_w_and_clear_w(column_to_replace, w, settings); } template -unsigned sparse_matrix::pivot_score(unsigned i, unsigned j) { +unsigned square_sparse_matrix::pivot_score(unsigned i, unsigned j) { // It goes like this (rnz-1)(cnz-1) is the Markovitz number, that is the max number of // new non zeroes we can obtain after the pivoting. // In addition we will get another cnz - 1 elements in the eta matrix created for this pivot, @@ -847,8 +885,8 @@ unsigned sparse_matrix::pivot_score(unsigned i, unsigned j) { } template -void sparse_matrix::enqueue_domain_into_pivot_queue() { - SASSERT(m_pivot_queue.size() == 0); +void square_sparse_matrix::enqueue_domain_into_pivot_queue() { + lp_assert(m_pivot_queue.size() == 0); for (unsigned i = 0; i < dimension(); i++) { auto & rh = m_rows[i]; unsigned rnz = static_cast(rh.size()); @@ -860,7 +898,7 @@ void sparse_matrix::enqueue_domain_into_pivot_queue() { } template -void sparse_matrix::set_max_in_rows() { +void square_sparse_matrix::set_max_in_rows() { unsigned i = dimension(); while (i--) set_max_in_row(i); @@ -868,27 +906,27 @@ void sparse_matrix::set_max_in_rows() { template -void sparse_matrix::zero_shortened_markovitz_numbers() { +void square_sparse_matrix::zero_shortened_markovitz_numbers() { for (auto & ch : m_columns) ch.zero_shortened_markovitz(); } template -void sparse_matrix::prepare_for_factorization() { +void square_sparse_matrix::prepare_for_factorization() { zero_shortened_markovitz_numbers(); set_max_in_rows(); enqueue_domain_into_pivot_queue(); } template -void sparse_matrix::recover_pivot_queue(vector & rejected_pivots) { +void square_sparse_matrix::recover_pivot_queue(vector & rejected_pivots) { for (auto p : rejected_pivots) { m_pivot_queue.enqueue(p.first, p.second, pivot_score(p.first, p.second)); } } template -int sparse_matrix::elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting) { +int square_sparse_matrix::elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting) { auto & row_chunk = m_rows[i]; if (j == row_chunk[0].m_index) { @@ -904,7 +942,7 @@ int sparse_matrix::elem_is_too_small(unsigned i, unsigned j, int c_partial } template -bool sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsigned row) { +bool square_sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsigned row) { unsigned arow = adjust_row(row); for (auto & iv : m_rows[arow]) { m_pivot_queue.remove(arow, iv.m_index); @@ -921,7 +959,7 @@ bool sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsi } template -void sparse_matrix::remove_pivot_column(unsigned row) { +void square_sparse_matrix::remove_pivot_column(unsigned row) { unsigned acol = adjust_column(row); for (const auto & iv : m_columns[acol].m_values) if (adjust_row_inverse(iv.m_index) >= row) @@ -929,12 +967,12 @@ void sparse_matrix::remove_pivot_column(unsigned row) { } template -void sparse_matrix::update_active_pivots(unsigned row) { +void square_sparse_matrix::update_active_pivots(unsigned row) { unsigned arow = adjust_row(row); for (const auto & iv : m_rows[arow]) { col_header & ch = m_columns[iv.m_index]; int cols = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; - SASSERT(cols >= 0); + lp_assert(cols >= 0); for (const auto &ivc : ch.m_values) { unsigned i = ivc.m_index; if (adjust_row_inverse(i) <= row) continue; // the i is not an active row @@ -944,7 +982,7 @@ void sparse_matrix::update_active_pivots(unsigned row) { } template -bool sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix *eta_matrix) { +bool square_sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix *eta_matrix) { if (!remove_row_from_active_pivots_and_shorten_columns(row)) return false; remove_pivot_column(row); @@ -960,7 +998,7 @@ bool sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix * for (auto & iv : row_values) { const col_header& ch = m_columns[iv.m_index]; int cnz = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; - SASSERT(cnz >= 0); + lp_assert(cnz >= 0); m_pivot_queue.enqueue(row, iv.m_index, rnz * cnz); } } @@ -969,32 +1007,32 @@ bool sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix * } template -unsigned sparse_matrix::pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k) { +unsigned square_sparse_matrix::pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k) { auto &cols = m_columns[j].m_values; unsigned cnz = cols.size(); for (auto & iv : cols) { if (adjust_row_inverse(iv.m_index) < k) cnz--; } - SASSERT(cnz > 0); + lp_assert(cnz > 0); return m_rows[i].m_values.size() * (cnz - 1); } #ifdef Z3DEBUG template -bool sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k) { +bool square_sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k) { unsigned arow = adjust_row(row); auto & row_vals = m_rows[arow].m_values; auto & begin_iv = row_vals[0]; T row_max = abs(begin_iv.m_value); - SASSERT(adjust_column_inverse(begin_iv.m_index) >= k); + lp_assert(adjust_column_inverse(begin_iv.m_index) >= k); if (pivot_score_without_shortened_counters(arow, begin_iv.m_index, k) < score) { print_active_matrix(k); return true; } for (unsigned jj = 1; jj < row_vals.size(); jj++) { auto & iv = row_vals[jj]; - SASSERT(adjust_column_inverse(iv.m_index) >= k); - SASSERT(abs(iv.m_value) <= row_max); + lp_assert(adjust_column_inverse(iv.m_index) >= k); + lp_assert(abs(iv.m_value) <= row_max); if (c_partial_pivoting * abs(iv.m_value) < row_max) continue; if (pivot_score_without_shortened_counters(arow, iv.m_index, k) < score) { print_active_matrix(k); @@ -1005,15 +1043,15 @@ bool sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score } template -bool sparse_matrix::really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k) { +bool square_sparse_matrix::really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k) { unsigned queue_pivot_score = pivot_score_without_shortened_counters(i, j, k); for (unsigned ii = k; ii < dimension(); ii++) { - SASSERT(!can_improve_score_for_row(ii, queue_pivot_score, c_partial_pivoting, k)); + lp_assert(!can_improve_score_for_row(ii, queue_pivot_score, c_partial_pivoting, k)); } return true; } template -void sparse_matrix::print_active_matrix(unsigned k, std::ostream & out) { +void square_sparse_matrix::print_active_matrix(unsigned k, std::ostream & out) { out << "active matrix for k = " << k << std::endl; if (k >= dimension()) { out << "empty" << std::endl; @@ -1038,26 +1076,26 @@ void sparse_matrix::print_active_matrix(unsigned k, std::ostream & out) { } template -bool sparse_matrix::pivot_queue_is_correct_for_row(unsigned i, unsigned k) { +bool square_sparse_matrix::pivot_queue_is_correct_for_row(unsigned i, unsigned k) { unsigned arow = adjust_row(i); for (auto & iv : m_rows[arow].m_values) { - SASSERT(pivot_score_without_shortened_counters(arow, iv.m_index, k + 1) == + lp_assert(pivot_score_without_shortened_counters(arow, iv.m_index, k + 1) == m_pivot_queue.get_priority(arow, iv.m_index)); } return true; } template -bool sparse_matrix::pivot_queue_is_correct_after_pivoting(int k) { +bool square_sparse_matrix::pivot_queue_is_correct_after_pivoting(int k) { for (unsigned i = k + 1; i < dimension(); i++ ) - SASSERT(pivot_queue_is_correct_for_row(i, k)); - SASSERT(m_pivot_queue.is_correct()); + lp_assert(pivot_queue_is_correct_for_row(i, k)); + lp_assert(m_pivot_queue.is_correct()); return true; } #endif template -bool sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k) { +bool square_sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k) { vector pivots_candidates_that_are_too_small; while (!m_pivot_queue.is_empty()) { m_pivot_queue.dequeue(i, j); @@ -1070,7 +1108,7 @@ bool sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_p #ifdef Z3DEBUG // if (!really_best_pivot(i, j, c_partial_pivoting, k)) { // print_active_matrix(k); - // SASSERT(false); + // lp_assert(false); // } #endif recover_pivot_queue(pivots_candidates_that_are_too_small); @@ -1087,7 +1125,7 @@ bool sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_p } template -bool sparse_matrix::elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting) { +bool square_sparse_matrix::elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting) { if (&iv == &row_chunk[0]) { return false; // the max element is at the head } @@ -1097,13 +1135,13 @@ bool sparse_matrix::elem_is_too_small(vector> & row_chunk } template -bool sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column) { +bool square_sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column) { vector> & row_chunk = get_row_values(i); for (indexed_value & iv : row_chunk) { unsigned j = iv.m_index; if (j == pivot_column) { - SASSERT(!col_is_active(j)); + lp_assert(!col_is_active(j)); continue; } m_columns[j].shorten_markovich_by_one(); @@ -1116,7 +1154,7 @@ bool sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivo } template -bool sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { +bool square_sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { const vector> & col_chunk = get_column_values(adjust_column(j)); bool is_unit = true; for (const auto & iv : col_chunk) { @@ -1163,20 +1201,19 @@ bool sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { } #ifdef Z3DEBUG template -bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const { +bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const { for (unsigned i = 0; i < dimension(); i++) { vector> const & row_chunk = get_row_values(i); - SASSERT(row_chunk.size()); + lp_assert(row_chunk.size()); T const & max = abs(row_chunk[0].m_value); unsigned ai = adjust_row_inverse(i); for (auto & iv : row_chunk) { - SASSERT(abs(iv.m_value) <= max); + lp_assert(abs(iv.m_value) <= max); unsigned aj = adjust_column_inverse(iv.m_index); if (!(ai <= aj || numeric_traits::is_zero(iv.m_value))) return false; if (aj == ai) { if (iv.m_value != 1) { - // std::cout << "value at diagonal = " << iv.m_value << std::endl; return false; } } @@ -1188,7 +1225,7 @@ bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_ } template -bool sparse_matrix::is_upper_triangular_until(unsigned k) const { +bool square_sparse_matrix::is_upper_triangular_until(unsigned k) const { for (unsigned j = 0; j < dimension() && j < k; j++) { unsigned aj = adjust_column(j); auto & col = get_column_values(aj); @@ -1202,67 +1239,59 @@ bool sparse_matrix::is_upper_triangular_until(unsigned k) const { } template -void sparse_matrix::check_column_vs_rows(unsigned col) { +void square_sparse_matrix::check_column_vs_rows(unsigned col) { auto & mc = get_column_values(col); for (indexed_value & column_iv : mc) { indexed_value & row_iv = column_iv_other(column_iv); if (row_iv.m_index != col) { - // std::cout << "m_other in row does not belong to column " << col << ", but to column " << row_iv.m_index << std::endl; - SASSERT(false); + lp_assert(false); } if (& row_iv_other(row_iv) != &column_iv) { - // std::cout << "row and col do not point to each other" << std::endl; - SASSERT(false); + lp_assert(false); } if (row_iv.m_value != column_iv.m_value) { - // std::cout << "the data from col " << col << " for row " << column_iv.m_index << " is different in the column " << std::endl; - // std::cout << "in the col it is " << column_iv.m_value << ", but in the row it is " << row_iv.m_value << std::endl; - SASSERT(false); + lp_assert(false); } } } template -void sparse_matrix::check_row_vs_columns(unsigned row) { +void square_sparse_matrix::check_row_vs_columns(unsigned row) { auto & mc = get_row_values(row); for (indexed_value & row_iv : mc) { indexed_value & column_iv = row_iv_other(row_iv); if (column_iv.m_index != row) { - // std::cout << "col_iv does not point to correct row " << row << " but to " << column_iv.m_index << std::endl; - SASSERT(false); + lp_assert(false); } if (& row_iv != & column_iv_other(column_iv)) { - // std::cout << "row and col do not point to each other" << std::endl; - SASSERT(false); + lp_assert(false); } if (row_iv.m_value != column_iv.m_value) { - // std::cout << "the data from col " << column_iv.m_index << " for row " << row << " is different in the column " << std::endl; - // std::cout << "in the col it is " << column_iv.m_value << ", but in the row it is " << row_iv.m_value << std::endl; - SASSERT(false); + lp_assert(false); } } } template -void sparse_matrix::check_rows_vs_columns() { +void square_sparse_matrix::check_rows_vs_columns() { for (unsigned i = 0; i < dimension(); i++) { check_row_vs_columns(i); } } template -void sparse_matrix::check_columns_vs_rows() { +void square_sparse_matrix::check_columns_vs_rows() { for (unsigned i = 0; i < dimension(); i++) { check_column_vs_rows(i); } } template -void sparse_matrix::check_matrix() { +void square_sparse_matrix::check_matrix() { check_rows_vs_columns(); check_columns_vs_rows(); } diff --git a/src/util/lp/stacked_map.h b/src/util/lp/stacked_map.h deleted file mode 100644 index 1bcad5649..000000000 --- a/src/util/lp/stacked_map.h +++ /dev/null @@ -1,194 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -// this class implements a map with some stack functionality -#include -#include -#include -namespace lp { - - -template , - typename KeyEqual = std::equal_to, - typename Allocator = std::allocator< std::pair > > -class stacked_map { - struct delta { - std::unordered_set m_new; - std::unordered_map m_original_changed; - // std::unordered_map m_deb_copy; - }; - std::unordered_map m_map; - std::stack m_stack; -public: - class ref { - stacked_map & m_map; - const A & m_key; - public: - ref(stacked_map & m, const A & key) :m_map(m), m_key(key) {} - ref & operator=(const B & b) { - m_map.emplace_replace(m_key, b); - return *this; - } - ref & operator=(const ref & b) { SASSERT(false); return *this; } - operator const B&() const { - auto it = m_map.m_map.find(m_key); - SASSERT(it != m_map.m_map.end()); - return it->second; - } - }; -private: - void emplace_replace(const A & a, const B & b) { - if (!m_stack.empty()) { - delta & d = m_stack.top(); - auto it = m_map.find(a); - if (it == m_map.end()) { - d.m_new.insert(a); - m_map.emplace(a, b); - } else if (it->second != b) { - auto nit = d.m_new.find(a); - if (nit == d.m_new.end()) { // we do not have the old key - auto & orig_changed= d.m_original_changed; - auto itt = orig_changed.find(a); - if (itt == orig_changed.end()) { - orig_changed.emplace(a, it->second); - } else if (itt->second == b) { - orig_changed.erase(itt); - } - } - it->second = b; - } - } else { // there is no stack: just emplace or replace - m_map[a] = b; - } - } -public: - ref operator[] (const A & a) { - return ref(*this, a); - } - - const B & operator[]( const A & a) const { - auto it = m_map.find(a); - if (it == m_map.end()) { - SASSERT(false); - } - - return it->second; - } - - bool try_get_value(const A& key, B& val) const { - auto it = m_map.find(key); - if (it == m_map.end()) - return false; - - val = it->second; - return true; - } - bool try_get_value(const A&& key, B& val) const { - auto it = m_map.find(std::move(key)); - if (it == m_map.end()) - return false; - - val = it->second; - return true; - } - - unsigned size() const { - return m_map.size(); - } - - bool contains(const A & key) const { - return m_map.find(key) != m_map.end(); - } - - bool contains(const A && key) const { - return m_map.find(std::move(key)) != m_map.end(); - } - - void push() { - delta d; - // d.m_deb_copy = m_map; - m_stack.push(d); - } - - void pop() { - pop(1); - } - void pop(unsigned k) { - while (k-- > 0) { - 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; - } - // SASSERT(d.m_deb_copy == m_map); - m_stack.pop(); - } - } - - void erase(const A & key) { - if (m_stack.empty()) { - m_map.erase(key); - return; - } - - delta & d = m_stack.top(); - auto it = m_map.find(key); - if (it == m_map.end()) { - SASSERT(d.m_new.find(key) == d.m_new.end()); - return; - } - auto &orig_changed = d.m_original_changed; - auto nit = d.m_new.find(key); - if (nit == d.m_new.end()) { // key is old - if (orig_changed.find(key) == orig_changed.end()) - orig_changed.emplace(it->first, it->second); // need to restore - } else { // k is new - SASSERT(orig_changed.find(key) == orig_changed.end()); - d.m_new.erase(nit); - } - - m_map.erase(it); - } - - void clear() { - if (m_stack.empty()) { - m_map.clear(); - return; - } - - delta & d = m_stack.top(); - auto & oc = d.m_original_changed; - for (auto & p : m_map) { - const auto & it = oc.find(p.first); - if (it == oc.end() && d.m_new.find(p.first) == d.m_new.end()) - oc.emplace(p.first, p.second); - } - m_map.clear(); - } - - const std::unordered_map& operator()() const { return m_map;} -}; -} diff --git a/src/util/lp/stacked_unordered_set.h b/src/util/lp/stacked_unordered_set.h deleted file mode 100644 index 6e313e6c0..000000000 --- a/src/util/lp/stacked_unordered_set.h +++ /dev/null @@ -1,107 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -// this class implements an unordered_set with some stack functionality -#include -#include -#include -namespace lp { - -template , - typename KeyEqual = std::equal_to, - typename Allocator = std::allocator - > class stacked_unordered_set { - struct delta { - std::unordered_set m_inserted; - std::unordered_set m_erased; - std::unordered_set m_deb_copy; - }; - std::unordered_set m_set; - std::stack m_stack; -public: - void insert(const A & a) { - if (m_stack.empty()) { - m_set.insert(a); - } else if (m_set.find(a) == m_set.end()) { - m_set.insert(a); - size_t in_erased = m_stack.top().m_erased.erase(a); - if (in_erased == 0) { - m_stack.top().m_inserted.insert(a); - } - } - } - - void erase(const A &a) { - if (m_stack.empty()) { - m_set.erase(a); - return; - } - auto erased = m_set.erase(a); - if (erased == 1) { - auto was_new = m_stack.top().m_inserted.erase(a); - if (was_new == 0) { - m_stack.top().m_erased.insert(a); - } - } - } - - unsigned size() const { - return m_set.size(); - } - - bool contains(A & key) const { - return m_set.find(key) != m_set.end(); - } - - bool contains(A && key) const { - return m_set.find(std::move(key)) != m_set.end(); - } - - void push() { - delta d; - d.m_deb_copy = m_set; - m_stack.push(d); - } - - void pop() { - pop(1); - } - void pop(unsigned k) { - while (k-- > 0) { - if (m_stack.empty()) - return; - delta & d = m_stack.top(); - for (auto & t : d.m_inserted) { - m_set.erase(t); - } - for (auto & t : d.m_erased) { - m_set.insert(t); - } - SASSERT(d.m_deb_copy == m_set); - m_stack.pop(); - } - } - - const std::unordered_set& operator()() const { return m_set;} -}; -} - 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 e8234d1e4..539e932b2 100644 --- a/src/util/lp/stacked_vector.h +++ b/src/util/lp/stacked_vector.h @@ -35,7 +35,7 @@ public: unsigned m_i; public: ref(stacked_vector &m, unsigned key) :m_vec(m), m_i(key) { - SASSERT(key < m.size()); + lp_assert(key < m.size()); } ref & operator=(const B & b) { m_vec.emplace_replace(m_i, b); @@ -48,7 +48,18 @@ public: operator const B&() const { return m_vec.m_vector[m_i]; } - + + bool operator==(B const& other) const { + return m_vec.m_vector[m_i] == other; + } + B& operator+=(B const &delta) { + // not tracking the change here! + return m_vec.m_vector[m_i] += delta; + } + B& operator-=(B const &delta) { + // not tracking the change here! + return m_vec.m_vector[m_i] -= delta; + } }; class ref_const { @@ -56,7 +67,7 @@ public: unsigned m_i; public: ref_const(const stacked_vector &m, unsigned key) :m_vec(m), m_i(key) { - SASSERT(key < m.size()); + lp_assert(key < m.size()); } operator const B&() const { @@ -83,10 +94,10 @@ public: } /* - const B & operator[](unsigned a) const { - SASSERT(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(); @@ -104,7 +115,7 @@ public: template void pop_tail(vector & v, unsigned k) { - SASSERT(v.size() >= k); + lp_assert(v.size() >= k); v.resize(v.size() - k); } @@ -112,10 +123,16 @@ public: 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) { - SASSERT(m_stack_of_vector_sizes.size() >= k); - SASSERT(k > 0); + lp_assert(m_stack_of_vector_sizes.size() >= k); + lp_assert(k > 0); resize(m_vector, m_stack_of_vector_sizes[m_stack_of_vector_sizes.size() - k]); pop_tail(m_stack_of_vector_sizes, k); unsigned first_change = m_stack_of_change_sizes[m_stack_of_change_sizes.size() - k]; @@ -129,22 +146,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(); - SASSERT(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) { - SASSERT(t.first < m_vector.size()); - m_vector[t.first] = t.second; - } - // SASSERT(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();*/ } @@ -173,7 +190,7 @@ public: } unsigned peek_size(unsigned k) const { - SASSERT(k > 0 && k <= m_stack_of_vector_sizes.size()); + lp_assert(k > 0 && k <= m_stack_of_vector_sizes.size()); return m_stack_of_vector_sizes[m_stack_of_vector_sizes.size() - k]; } diff --git a/src/util/lp/static_matrix_instances.cpp b/src/util/lp/static_matrix.cpp similarity index 99% rename from src/util/lp/static_matrix_instances.cpp rename to src/util/lp/static_matrix.cpp index c57f31177..9d12087bb 100644 --- a/src/util/lp/static_matrix_instances.cpp +++ b/src/util/lp/static_matrix.cpp @@ -18,10 +18,10 @@ Revision History: --*/ #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.h b/src/util/lp/static_matrix.h index 29b7ed646..e4bf257f4 100644 --- a/src/util/lp/static_matrix.h +++ b/src/util/lp/static_matrix.h @@ -26,7 +26,6 @@ Revision History: #include "util/lp/sparse_vector.h" #include "util/lp/indexed_vector.h" #include "util/lp/permutation_matrix.h" -#include "util/lp/linear_combination_iterator.h" #include namespace lp { @@ -35,20 +34,26 @@ struct column_cell { unsigned m_offset; // the offset of the element in the matrix row column_cell(unsigned i, unsigned offset) : m_i(i), m_offset(offset) { } + }; template struct row_cell { unsigned m_j; // points to the column unsigned m_offset; // offset in column + T m_value; row_cell(unsigned j, unsigned offset, T const & val) : m_j(j), m_offset(offset), m_value(val) { } const T & get_val() const { return m_value;} T & get_val() { return m_value;} void set_val(const T& v) { m_value = v; } - T m_value; + unsigned var() const { return m_j;} + const T & coeff() const { return m_value;} }; +template +using row_strip = vector>; + // each assignment for this matrix should be issued only once!!! template class static_matrix @@ -62,13 +67,11 @@ class static_matrix dim(unsigned m, unsigned n) :m_m(m), m_n(n) {} }; std::stack m_stack; - vector m_became_zeros; // the row indices that became zeroes during the pivoting public: - typedef vector> row_strip; typedef vector column_strip; vector m_vector_of_row_offsets; indexed_vector m_work_vector; - vector m_rows; + vector> m_rows; vector m_columns; // starting inner classes class ref { @@ -79,17 +82,17 @@ public: ref(static_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} ref & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; } - ref & operator=(ref const & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } + ref operator=(ref & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } operator T () const { return m_matrix.get_elem(m_row, m_col); } }; class ref_row { - static_matrix & m_matrix; + const static_matrix & m_matrix; unsigned m_row; public: - ref_row(static_matrix & m, unsigned row):m_matrix(m), m_row(row) {} - ref operator[](unsigned col) const { return ref(m_matrix, m_row, col); } + ref_row(const static_matrix & m, unsigned row): m_matrix(m), m_row(row) {} + const T operator[](unsigned col) const { return m_matrix.get_elem(m_row, col); } }; public: @@ -97,6 +100,10 @@ public: const T & get_val(const column_cell & c) const { return m_rows[c.m_i][c.m_offset].get_val(); } + + column_cell & get_column_cell(const row_cell &rc) { + return m_columns[rc.m_j][rc.m_offset]; + } void init_row_columns(unsigned m, unsigned n); @@ -125,7 +132,7 @@ public: void add_columns_at_the_end(unsigned delta); void add_new_element(unsigned i, unsigned j, const T & v); - void add_row() {m_rows.push_back(row_strip());} + void add_row() {m_rows.push_back(row_strip());} void add_column() { m_columns.push_back(column_strip()); m_vector_of_row_offsets.push_back(-1); @@ -188,7 +195,7 @@ public: // void fix_row_indices_in_each_column_for_crossed_row(unsigned k); - void cross_out_row_from_columns(unsigned k, row_strip & row); + void cross_out_row_from_columns(unsigned k, row_strip & row); void cross_out_row_from_column(unsigned col, unsigned k); @@ -218,7 +225,7 @@ public: virtual void set_number_of_columns(unsigned /*n*/) { } #endif - T get_max_val_in_row(unsigned /* i */) const { SASSERT(false); } + T get_max_val_in_row(unsigned /* i */) const { lp_unreachable(); } T get_balance() const; @@ -234,7 +241,7 @@ public: for (auto & c : row) { unsigned j = c.m_j; auto & col = m_columns[j]; - SASSERT(col[col.size() - 1].m_i == m_rows.size() -1 ); // todo : start here!!!! + lp_assert(col[col.size() - 1].m_i == m_rows.size() -1 ); // todo : start here!!!! col.pop_back(); } } @@ -261,7 +268,7 @@ public: m_columns.pop_back(); // delete the last column m_stack.pop(); } - SASSERT(is_correct()); + lp_assert(is_correct()); } void multiply_row(unsigned row, T const & alpha) { @@ -277,7 +284,7 @@ public: } T dot_product_with_column(const vector & y, unsigned j) const { - SASSERT(j < column_count()); + lp_assert(j < column_count()); T ret = numeric_traits::zero(); for (auto & it : m_columns[j]) { ret += y[it.m_i] * get_val(it); // get_value_of_column_cell(it); @@ -287,7 +294,7 @@ public: // pivot row i to row ii bool pivot_row_to_row_given_cell(unsigned i, column_cell& c, unsigned); - void scan_row_ii_to_offset_vector(unsigned ii); + void scan_row_ii_to_offset_vector(const row_strip & rvals); void transpose_rows(unsigned i, unsigned ii) { auto t = m_rows[i]; @@ -296,64 +303,72 @@ public: // now fix the columns for (auto & rc : m_rows[i]) { column_cell & cc = m_columns[rc.m_j][rc.m_offset]; - SASSERT(cc.m_i == ii); + lp_assert(cc.m_i == ii); cc.m_i = i; } for (auto & rc : m_rows[ii]) { column_cell & cc = m_columns[rc.m_j][rc.m_offset]; - SASSERT(cc.m_i == i); + lp_assert(cc.m_i == i); cc.m_i = ii; } } + void fill_last_row_with_pivoting_loop_block(unsigned j, const vector & basis_heading) { + int row_index = basis_heading[j]; + if (row_index < 0) + return; + T & alpha = m_work_vector[j]; // the pivot alpha + if (is_zero(alpha)) + return; + + for (const auto & c : m_rows[row_index]) { + if (c.m_j == j) { + continue; + } + T & wv = m_work_vector.m_data[c.m_j]; + bool was_zero = is_zero(wv); + wv -= alpha * c.m_value; + if (was_zero) + m_work_vector.m_index.push_back(c.m_j); + else { + if (is_zero(wv)) { + m_work_vector.erase_from_index(c.m_j); + } + } + } + alpha = zero_of_type(); + m_work_vector.erase_from_index(j); + } - void fill_last_row_with_pivoting(linear_combination_iterator & it, const vector & basis_heading) { - SASSERT(numeric_traits::precise()); - SASSERT(row_count() > 0); + + + template + void fill_last_row_with_pivoting(const term& row, + unsigned bj, // the index of the basis column + const vector & basis_heading) { + lp_assert(numeric_traits::precise()); + lp_assert(row_count() > 0); m_work_vector.resize(column_count()); T a; - unsigned j; - while (it.next(a, j)) { - m_work_vector.set_value(-a, j); // we use the form -it + 1 = 0 + // we use the form -it + 1 = 0 + m_work_vector.set_value(one_of_type(), bj); + for (auto p : row) { + m_work_vector.set_value(-p.coeff(), p.var()); // but take care of the basis 1 later } - it.reset(); - // not iterate with pivoting - while (it.next(j)) { - int row_index = basis_heading[j]; - if (row_index < 0) - continue; - - T & alpha = m_work_vector[j]; // the pivot alpha - if (is_zero(alpha)) - continue; - - for (const auto & c : m_rows[row_index]) { - if (c.m_j == j) { - continue; - } - T & wv = m_work_vector.m_data[c.m_j]; - bool was_zero = is_zero(wv); - wv -= alpha * c.m_value; - if (was_zero) - m_work_vector.m_index.push_back(c.m_j); - else { - if (is_zero(wv)) { - m_work_vector.erase_from_index(c.m_j); - } - } - } - alpha = zero_of_type(); - m_work_vector.erase_from_index(j); + // now iterate with pivoting + fill_last_row_with_pivoting_loop_block(bj, basis_heading); + for (auto p : row) { + fill_last_row_with_pivoting_loop_block(p.var(), basis_heading); } - SASSERT(m_work_vector.is_OK()); + lp_assert(m_work_vector.is_OK()); unsigned last_row = row_count() - 1; for (unsigned j : m_work_vector.m_index) { set (last_row, j, m_work_vector.m_data[j]); } - SASSERT(column_count() > 0); + lp_assert(column_count() > 0); set(last_row, column_count() - 1, one_of_type()); } @@ -369,11 +384,76 @@ public: template L dot_product_with_row(unsigned row, const vector & w) const { L ret = zero_of_type(); - SASSERT(row < m_rows.size()); + lp_assert(row < m_rows.size()); for (auto & it : m_rows[row]) { ret += w[it.m_j] * it.get_val(); } return ret; } + + struct column_cell_plus { + const column_cell & m_c; + const static_matrix& m_A; + // constructor + column_cell_plus(const column_cell & c, const static_matrix& A) : + m_c(c), m_A(A) {} + unsigned var() const { return m_c.m_i; } + const T & coeff() const { return m_A.m_rows[var()][m_c.m_offset].get_val(); } + + }; + + struct column_container { + unsigned m_j; // the column index + const static_matrix & m_A; + column_container(unsigned j, const static_matrix& A) : m_j(j), m_A(A) { + } + struct const_iterator { + // fields + const column_cell *m_c; + const static_matrix& m_A; + + //typedefs + + + typedef const_iterator self_type; + typedef column_cell_plus value_type; + typedef const column_cell_plus reference; + // typedef const column_cell* pointer; + typedef int difference_type; + typedef std::forward_iterator_tag iterator_category; + + reference operator*() const { + return column_cell_plus(*m_c, m_A); + } + self_type operator++() { self_type i = *this; m_c++; return i; } + self_type operator++(int) { m_c++; return *this; } + + const_iterator(const column_cell* it, const static_matrix& A) : + m_c(it), + m_A(A) + {} + bool operator==(const self_type &other) const { + return m_c == other.m_c; + } + bool operator!=(const self_type &other) const { return !(*this == other); } + }; + + const_iterator begin() const { + return const_iterator(m_A.m_columns[m_j].begin(), m_A); + } + + const_iterator end() const { + return const_iterator(m_A.m_columns[m_j].end(), m_A); + } + }; + + column_container column(unsigned j) const { + return column_container(j, *this); + } + + ref_row operator[](unsigned i) const { return ref_row(*this, i);} + typedef T coefftype; + typedef X argtype; }; + } diff --git a/src/util/lp/static_matrix.hpp b/src/util/lp/static_matrix_def.h similarity index 84% rename from src/util/lp/static_matrix.hpp rename to src/util/lp/static_matrix_def.h index 846c2a19f..42249b4d8 100644 --- a/src/util/lp/static_matrix.hpp +++ b/src/util/lp/static_matrix_def.h @@ -25,9 +25,9 @@ namespace lp { // each assignment for this matrix should be issued only once!!! template void static_matrix::init_row_columns(unsigned m, unsigned n) { - SASSERT(m_rows.size() == 0 && m_columns.size() == 0); + lp_assert(m_rows.size() == 0 && m_columns.size() == 0); for (unsigned i = 0; i < m; i++){ - m_rows.push_back(row_strip()); + m_rows.push_back(row_strip()); } for (unsigned j = 0; j < n; j++){ m_columns.push_back(column_strip()); @@ -35,57 +35,46 @@ void static_matrix::init_row_columns(unsigned m, unsigned n) { } -template void static_matrix::scan_row_ii_to_offset_vector(unsigned ii) { - auto & rvals = m_rows[ii]; - unsigned size = rvals.size(); - for (unsigned j = 0; j < size; j++) +template void static_matrix::scan_row_ii_to_offset_vector(const row_strip & rvals) { + for (unsigned j = 0; j < rvals.size(); j++) m_vector_of_row_offsets[rvals[j].m_j] = j; } template bool static_matrix::pivot_row_to_row_given_cell(unsigned i, column_cell & c, unsigned pivot_col) { unsigned ii = c.m_i; - SASSERT(i < row_count() && ii < column_count()); - SASSERT(i != ii); - - m_became_zeros.reset(); + lp_assert(i < row_count() && ii < column_count() && i != ii); T alpha = -get_val(c); - SASSERT(!is_zero(alpha)); - auto & ii_row_vals = m_rows[ii]; - remove_element(ii_row_vals, ii_row_vals[c.m_offset]); - scan_row_ii_to_offset_vector(ii); - SASSERT(!is_zero(alpha)); - unsigned prev_size_ii = ii_row_vals.size(); + lp_assert(!is_zero(alpha)); + auto & rowii = m_rows[ii]; + remove_element(rowii, rowii[c.m_offset]); + scan_row_ii_to_offset_vector(rowii); + unsigned prev_size_ii = rowii.size(); // run over the pivot row and update row ii for (const auto & iv : m_rows[i]) { unsigned j = iv.m_j; if (j == pivot_col) continue; T alv = alpha * iv.m_value; - SASSERT(!is_zero(iv.m_value)); + lp_assert(!is_zero(iv.m_value)); int j_offs = m_vector_of_row_offsets[j]; if (j_offs == -1) { // it is a new element add_new_element(ii, j, alv); } else { - auto & row_el_iv = ii_row_vals[j_offs]; - row_el_iv.m_value += alv; - if (is_zero(row_el_iv.m_value)) { - m_became_zeros.push_back(j_offs); - ensure_increasing(m_became_zeros); - } + rowii[j_offs].m_value += alv; } } - // clean the work vector for (unsigned k = 0; k < prev_size_ii; k++) { - m_vector_of_row_offsets[ii_row_vals[k].m_j] = -1; + m_vector_of_row_offsets[rowii[k].m_j] = -1; } - for (unsigned k = m_became_zeros.size(); k-- > 0; ) { - unsigned j = m_became_zeros[k]; - remove_element(ii_row_vals, ii_row_vals[j]); + // remove zeroes + for (unsigned k = rowii.size(); k-- > 0; ) { + if (is_zero(rowii[k].m_value)) + remove_element(rowii, rowii[k]); } - return !ii_row_vals.empty(); + return !rowii.empty(); } @@ -119,9 +108,9 @@ template void static_matrix::init_empty_matrix } template unsigned static_matrix::lowest_row_in_column(unsigned col) { - SASSERT(col < column_count()); + lp_assert(col < column_count()); column_strip & colstrip = m_columns[col]; - SASSERT(colstrip.size() > 0); + lp_assert(colstrip.size() > 0); unsigned ret = 0; for (auto & t : colstrip) { if (t.m_i > ret) { @@ -137,7 +126,7 @@ template void static_matrix::add_columns_at_th } template void static_matrix::forget_last_columns(unsigned how_many_to_forget) { - SASSERT(m_columns.size() >= how_many_to_forget); + lp_assert(m_columns.size() >= how_many_to_forget); unsigned j = column_count() - 1; for (; how_many_to_forget > 0; how_many_to_forget--) { remove_last_column(j --); @@ -166,7 +155,7 @@ template void static_matrix::remove_last_column(u template void static_matrix::set(unsigned row, unsigned col, T const & val) { if (numeric_traits::is_zero(val)) return; - SASSERT(row < row_count() && col < column_count()); + lp_assert(row < row_count() && col < column_count()); auto & r = m_rows[row]; unsigned offs_in_cols = static_cast(m_columns[col].size()); m_columns[col].push_back(make_column_cell(row, static_cast(r.size()))); @@ -177,7 +166,7 @@ template std::set> static_matrix::get_domain() { std::set> ret; for (unsigned i = 0; i < m_rows.size(); i++) { - for (auto it : m_rows[i]) { + for (auto &it : m_rows[i]) { ret.insert(std::make_pair(i, it.m_j)); } } @@ -186,7 +175,7 @@ std::set> static_matrix::get_domain() { template void static_matrix::copy_column_to_indexed_vector (unsigned j, indexed_vector & v) const { - SASSERT(j < m_columns.size()); + lp_assert(j < m_columns.size()); for (auto & it : m_columns[j]) { const T& val = get_val(it); if (!is_zero(val)) @@ -255,7 +244,7 @@ template void static_matrix::check_consistency for (int i = 0; i < m_rows.size(); i++){ for (auto & t : m_rows[i]) { std::pair p(i, t.m_j); - SASSERT(by_rows.find(p) == by_rows.end()); + lp_assert(by_rows.find(p) == by_rows.end()); by_rows[p] = t.get_val(); } } @@ -263,20 +252,16 @@ template void static_matrix::check_consistency for (int i = 0; i < m_columns.size(); i++){ for (auto & t : m_columns[i]) { std::pair p(t.m_i, i); - SASSERT(by_cols.find(p) == by_cols.end()); + lp_assert(by_cols.find(p) == by_cols.end()); by_cols[p] = get_val(t); } } - SASSERT(by_rows.size() == by_cols.size()); + lp_assert(by_rows.size() == by_cols.size()); for (auto & t : by_rows) { auto ic = by_cols.find(t.first); - if (ic == by_cols.end()){ - //std::cout << "rows have pair (" << t.first.first <<"," << t.first.second - // << "), but columns don't " << std::endl; - } - SASSERT(ic != by_cols.end()); - SASSERT(t.second == ic->second); + lp_assert(ic != by_cols.end()); + lp_assert(t.second == ic->second); } } #endif @@ -307,7 +292,7 @@ template void static_matrix::fix_row_indices_i } } -template void static_matrix::cross_out_row_from_columns(unsigned k, row_strip & row) { +template void static_matrix::cross_out_row_from_columns(unsigned k, row_strip & row) { for (auto & t : row) { cross_out_row_from_column(t.m_j, k); } @@ -353,11 +338,11 @@ template T static_matrix::get_row_balance(unsi } template bool static_matrix::is_correct() const { - for (auto & r : m_rows) { + for (unsigned i = 0; i < m_rows.size(); i++) { + auto &r = m_rows[i]; std::unordered_set s; for (auto & rc : r) { if (s.find(rc.m_j) != s.end()) { - std::cout << "found column " << rc.m_j << " twice in a row" << std::endl; return false; } s.insert(rc.m_j); @@ -367,13 +352,18 @@ template bool static_matrix::is_correct() const { return false; if (rc.get_val() != get_val(m_columns[rc.m_j][rc.m_offset])) return false; + if (is_zero(rc.get_val())) { + return false; + } + } } - for (auto & c : m_columns) { + + for (unsigned j = 0; j < m_columns.size(); j++) { + auto & c = m_columns[j]; std::unordered_set s; for (auto & cc : c) { if (s.find(cc.m_i) != s.end()) { - std::cout << "found row " << cc.m_i << " twice in a column" << std::endl; return false; } s.insert(cc.m_i); diff --git a/src/util/lp/tail_matrix.h b/src/util/lp/tail_matrix.h index 37b217205..2a851bc16 100644 --- a/src/util/lp/tail_matrix.h +++ b/src/util/lp/tail_matrix.h @@ -39,5 +39,12 @@ public: virtual void apply_from_right(indexed_vector & w) = 0; virtual ~tail_matrix() {} virtual bool is_dense() const = 0; + struct ref_row { + const tail_matrix & m_A; + unsigned m_row; + ref_row(const tail_matrix& m, unsigned row): m_A(m), m_row(row) {} + T operator[](unsigned j) const { return m_A.get_elem(m_row, j);} + }; + ref_row operator[](unsigned i) const { return ref_row(*this, i);} }; } diff --git a/src/util/lp/test_bound_analyzer.h b/src/util/lp/test_bound_analyzer.h index 30f2dd16a..7cee0cf56 100644 --- a/src/util/lp/test_bound_analyzer.h +++ b/src/util/lp/test_bound_analyzer.h @@ -17,9 +17,9 @@ Revision History: --*/ +#if 0 #pragma once #include "util/vector.h" -#include "util/lp/linear_combination_iterator.h" #include "util/lp/implied_bound.h" #include "util/lp/lp_settings.h" #include @@ -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), @@ -89,7 +89,7 @@ public : void analyze_i_for_upper(unsigned i) { mpq l; bool strict = false; - SASSERT(is_zero(l)); + lp_assert(is_zero(l)); for (unsigned k = 0; k < m_index.size(); k++) { if (k == i) continue; @@ -115,22 +115,21 @@ public : } } default: - // std::cout << " got an upper bound with " << T_to_string(l) << "\n"; m_implied_bounds.push_back(implied_bound(l, j, false, is_pos(m_coeffs[i]), m_row_or_term_index, strict)); } } - 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 +168,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; @@ -180,14 +179,14 @@ public : void analyze_i_for_lower(unsigned i) { mpq l; - SASSERT(is_zero(l)); + lp_assert(is_zero(l)); bool strict = false; for (unsigned k = 0; k < m_index.size(); k++) { if (k == i) 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,24 +198,23 @@ 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 } } default: - // std::cout << " got a lower bound with " << T_to_string(l) << "\n"; m_implied_bounds.push_back(implied_bound(l, j, true, is_pos(m_coeffs[i]), m_row_or_term_index, strict)); } } - 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 +237,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; } } @@ -273,3 +271,4 @@ public : }; } +#endif diff --git a/src/util/lp/ul_pair.h b/src/util/lp/ul_pair.h index cbf511d90..7fac6b3ae 100644 --- a/src/util/lp/ul_pair.h +++ b/src/util/lp/ul_pair.h @@ -27,31 +27,34 @@ Revision History: namespace lp { - enum lconstraint_kind { - LE = -2, LT = -1 , GE = 2, GT = 1, EQ = 0 - }; - - inline std::ostream& operator<<(std::ostream& out, lconstraint_kind k) { - switch (k) { - case LE: return out << "<="; - case LT: return out << "<"; - case GE: return out << ">="; - case GT: return out << ">"; - case EQ: return out << "="; - } - return out << "??"; +enum lconstraint_kind { + LE = -2, LT = -1 , GE = 2, GT = 1, EQ = 0 +}; + + +inline bool kind_is_strict(lconstraint_kind kind) { return kind == LT || kind == GT;} + +inline std::ostream& operator<<(std::ostream& out, lconstraint_kind k) { + switch (k) { + case LE: return out << "<="; + case LT: return out << "<"; + case GE: return out << ">="; + case GT: return out << ">"; + case EQ: return out << "="; } + return out << "??"; +} inline bool compare(const std::pair & a, const std::pair & b) { return a.second < b.second; } class ul_pair { - constraint_index m_low_bound_witness; + constraint_index m_lower_bound_witness; constraint_index m_upper_bound_witness; public: - constraint_index& low_bound_witness() {return m_low_bound_witness;} - constraint_index low_bound_witness() const {return m_low_bound_witness;} + constraint_index& lower_bound_witness() {return m_lower_bound_witness;} + constraint_index lower_bound_witness() const {return m_lower_bound_witness;} constraint_index& upper_bound_witness() { return m_upper_bound_witness;} constraint_index upper_bound_witness() const {return m_upper_bound_witness;} row_index m_i; @@ -60,21 +63,21 @@ public: } bool operator==(const ul_pair & p) const { - return m_low_bound_witness == p.m_low_bound_witness + return m_lower_bound_witness == p.m_lower_bound_witness && m_upper_bound_witness == p.m_upper_bound_witness && m_i == p.m_i; } // empty constructor ul_pair() : - m_low_bound_witness(static_cast(-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/lp/var_register.h b/src/util/lp/var_register.h new file mode 100644 index 000000000..86c238e12 --- /dev/null +++ b/src/util/lp/var_register.h @@ -0,0 +1,111 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +namespace lp { +class ext_var_info { + unsigned m_internal_j; // the internal index + bool m_is_integer; +public: + ext_var_info() {} + ext_var_info(unsigned j): ext_var_info(j, true) {} + 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;} +}; + +class var_register { + svector m_local_to_external; + std::unordered_map m_external_to_local; +public: + unsigned add_var(unsigned user_var) { + return add_var(user_var, true); + } + unsigned add_var(unsigned user_var, bool is_int) { + auto t = m_external_to_local.find(user_var); + if (t != m_external_to_local.end()) { + return t->second.internal_j(); + } + + unsigned j = size(); + m_external_to_local[user_var] = ext_var_info(j, is_int); + m_local_to_external.push_back(user_var); + return j; + } + + const svector & vars() const { return m_local_to_external; } + + unsigned local_to_external(unsigned local_var) const { + return m_local_to_external[local_var]; + } + unsigned size() const { + return m_local_to_external.size(); + } + void clear() { + m_local_to_external.clear(); + m_external_to_local.clear(); + } + + unsigned external_to_local(unsigned j) const { + auto it = m_external_to_local.find(j); + lp_assert(it != m_external_to_local.end()); + return it->second.internal_j(); + } + + bool external_is_int(unsigned j) const { + auto it = m_external_to_local.find(j); + lp_assert(it != m_external_to_local.end()); + return it->second.is_integer(); + } + + bool external_is_used(unsigned ext_j) const { + auto it = m_external_to_local.find(ext_j); + return it != m_external_to_local.end(); + } + + bool external_is_used(unsigned ext_j, unsigned & local_j ) const { + auto it = m_external_to_local.find(ext_j); + if ( it == m_external_to_local.end()) + return false; + local_j = it->second.internal_j(); + return true; + } + + bool external_is_used(unsigned ext_j, unsigned & local_j, bool & is_int ) const { + auto it = m_external_to_local.find(ext_j); + if ( it == m_external_to_local.end()) + return false; + local_j = it->second.internal_j(); + is_int = it->second.is_integer(); + return true; + } + + + bool local_is_int(unsigned j) const { + return external_is_int(m_local_to_external[j]); + } + + void shrink(unsigned shrunk_size) { + for (unsigned j = size(); j-- > shrunk_size;) { + m_external_to_local.erase(m_local_to_external[j]); + } + m_local_to_external.resize(shrunk_size); + } + +}; +} diff --git a/src/util/mpff.cpp b/src/util/mpff.cpp index 64e07f9f0..e00a25b1b 100644 --- a/src/util/mpff.cpp +++ b/src/util/mpff.cpp @@ -1070,7 +1070,7 @@ bool mpff_manager::is_power_of_two(mpff const & a) const { template void mpff_manager::significand_core(mpff const & n, mpz_manager & m, mpz & t) { - m.set(t, m_precision, sig(n)); + m.set_digits(t, m_precision, sig(n)); } void mpff_manager::significand(mpff const & n, unsynch_mpz_manager & m, mpz & t) { @@ -1090,10 +1090,10 @@ void mpff_manager::to_mpz_core(mpff const & n, mpz_manager & m, mpz & t) to_buffer(0, n); unsigned * b = m_buffers[0].c_ptr(); shr(m_precision, b, -exp, m_precision, b); - m.set(t, m_precision, b); + m.set_digits(t, m_precision, b); } else { - m.set(t, m_precision, sig(n)); + m.set_digits(t, m_precision, sig(n)); if (exp > 0) { _scoped_numeral > p(m); m.set(p, 2); diff --git a/src/util/mpfx.cpp b/src/util/mpfx.cpp index e17a5e766..2ebc54840 100644 --- a/src/util/mpfx.cpp +++ b/src/util/mpfx.cpp @@ -705,7 +705,7 @@ template void mpfx_manager::to_mpz_core(mpfx const & n, mpz_manager & m, mpz & t) { SASSERT(is_int(n)); unsigned * w = words(n); - m.set(t, m_int_part_sz, w+m_frac_part_sz); + m.set_digits(t, m_int_part_sz, w+m_frac_part_sz); if (is_neg(n)) m.neg(t); } diff --git a/src/util/mpq.cpp b/src/util/mpq.cpp index bc6f99213..4c636f1af 100644 --- a/src/util/mpq.cpp +++ b/src/util/mpq.cpp @@ -315,6 +315,112 @@ unsigned mpq_manager::prev_power_of_two(mpq const & a) { return prev_power_of_two(_tmp); } + +template +template +void mpq_manager::lin_arith_op(mpq const& a, mpq const& b, mpq& c, mpz& g, mpz& tmp1, mpz& tmp2, mpz& tmp3) { + gcd(a.m_den, b.m_den, g); + if (is_one(g)) { + mul(a.m_num, b.m_den, tmp1); + mul(b.m_num, a.m_den, tmp2); + if (SUB) sub(tmp1, tmp2, c.m_num); else add(tmp1, tmp2, c.m_num); + mul(a.m_den, b.m_den, c.m_den); + } + else { + div(a.m_den, g, tmp3); + mul(tmp3, b.m_den, c.m_den); + mul(tmp3, b.m_num, tmp2); + div(b.m_den, g, tmp3); + mul(tmp3, a.m_num, tmp1); + if (SUB) sub(tmp1, tmp2, tmp3); else add(tmp1, tmp2, tmp3); + gcd(tmp3, g, tmp1); + if (is_one(tmp1)) { + set(c.m_num, tmp3); + } + else { + div(tmp3, tmp1, c.m_num); + div(c.m_den, tmp1, c.m_den); + } + } +} + +template +void mpq_manager::rat_mul(mpq const & a, mpq const & b, mpq & c, mpz& g1, mpz& g2, mpz& tmp1, mpz& tmp2) { + gcd(a.m_den, b.m_num, g1); + gcd(a.m_num, b.m_den, g2); + div(a.m_num, g2, tmp1); + div(b.m_num, g1, tmp2); + mul(tmp1, tmp2, c.m_num); + div(b.m_den, g2, tmp1); + div(a.m_den, g1, tmp2); + mul(tmp1, tmp2, c.m_den); +} + +template +void mpq_manager::rat_mul(mpz const & a, mpq const & b, mpq & c) { + STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); + mul(a, b.m_num, c.m_num); + set(c.m_den, b.m_den); + normalize(c); + STRACE("rat_mpq", tout << to_string(c) << "\n";); +} + + +template +void mpq_manager::rat_mul(mpq const & a, mpq const & b, mpq & c) { + STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); + if (SYNCH) { + mpz g1, g2, tmp1, tmp2; + rat_mul(a, b, c, g1, g2, tmp1, tmp2); + del(g1); + del(g2); + del(tmp1); + del(tmp2); + } + else { + mpz& g1 = m_n_tmp, &g2 = m_addmul_tmp.m_num, &tmp1 = m_add_tmp1, &tmp2 = m_add_tmp2; + rat_mul(a, b, c, g1, g2, tmp1, tmp2); + } + STRACE("rat_mpq", tout << to_string(c) << "\n";); +} + +template +void mpq_manager::rat_add(mpq const & a, mpq const & b, mpq & c) { + STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); + if (SYNCH) { + mpz_stack tmp1, tmp2, tmp3, g; + lin_arith_op(a, b, c, g, tmp1, tmp2, tmp3); + del(tmp1); + del(tmp2); + del(tmp3); + del(g); + } + else { + mpz& g = m_n_tmp, &tmp1 = m_add_tmp1, &tmp2 = m_add_tmp2, &tmp3 = m_addmul_tmp.m_num; + lin_arith_op(a, b, c, g, tmp1, tmp2, tmp3); + } + STRACE("rat_mpq", tout << to_string(c) << "\n";); +} + +template +void mpq_manager::rat_sub(mpq const & a, mpq const & b, mpq & c) { + STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " - " << to_string(b) << " == ";); + if (SYNCH) { + mpz tmp1, tmp2, tmp3, g; + lin_arith_op(a, b, c, g, tmp1, tmp2, tmp3); + del(tmp1); + del(tmp2); + del(tmp3); + del(g); + } + else { + mpz& g = m_n_tmp, &tmp1 = m_add_tmp1, &tmp2 = m_add_tmp2, &tmp3 = m_addmul_tmp.m_num; + lin_arith_op(a, b, c, g, tmp1, tmp2, tmp3); + } + STRACE("rat_mpq", tout << to_string(c) << "\n";); +} + + template class mpq_manager; template class mpq_manager; diff --git a/src/util/mpq.h b/src/util/mpq.h index 010bb2c8a..1bccabc74 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -74,27 +74,7 @@ class mpq_manager : public mpz_manager { } } - void rat_add(mpq const & a, mpq const & b, mpq & c) { - STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); - if (SYNCH) { - mpz tmp1, tmp2; - mul(a.m_num, b.m_den, tmp1); - mul(b.m_num, a.m_den, tmp2); - mul(a.m_den, b.m_den, c.m_den); - add(tmp1, tmp2, c.m_num); - normalize(c); - del(tmp1); - del(tmp2); - } - else { - mul(a.m_num, b.m_den, m_add_tmp1); - mul(b.m_num, a.m_den, m_add_tmp2); - mul(a.m_den, b.m_den, c.m_den); - add(m_add_tmp1, m_add_tmp2, c.m_num); - normalize(c); - } - STRACE("rat_mpq", tout << to_string(c) << "\n";); - } + void rat_add(mpq const & a, mpq const & b, mpq & c); void rat_add(mpq const & a, mpz const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); @@ -115,46 +95,19 @@ class mpq_manager : public mpz_manager { STRACE("rat_mpq", tout << to_string(c) << "\n";); } - void rat_sub(mpq const & a, mpq const & b, mpq & c) { - STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " - " << to_string(b) << " == ";); - if (SYNCH) { - mpz tmp1, tmp2; - mul(a.m_num, b.m_den, tmp1); - mul(b.m_num, a.m_den, tmp2); - mul(a.m_den, b.m_den, c.m_den); - sub(tmp1, tmp2, c.m_num); - normalize(c); - del(tmp1); - del(tmp2); - } - else { - mul(a.m_num, b.m_den, m_add_tmp1); - mul(b.m_num, a.m_den, m_add_tmp2); - mul(a.m_den, b.m_den, c.m_den); - sub(m_add_tmp1, m_add_tmp2, c.m_num); - normalize(c); - } - STRACE("rat_mpq", tout << to_string(c) << "\n";); - } + void rat_sub(mpq const & a, mpq const & b, mpq & c); - void rat_mul(mpq const & a, mpq const & b, mpq & c) { - STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); - mul(a.m_num, b.m_num, c.m_num); - mul(a.m_den, b.m_den, c.m_den); - normalize(c); - STRACE("rat_mpq", tout << to_string(c) << "\n";); - } + void rat_mul(mpq const & a, mpq const & b, mpq & c); - void rat_mul(mpz const & a, mpq const & b, mpq & c) { - STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); - mul(a, b.m_num, c.m_num); - set(c.m_den, b.m_den); - normalize(c); - STRACE("rat_mpq", tout << to_string(c) << "\n";); - } + void rat_mul(mpz const & a, mpq const & b, mpq & c); bool rat_lt(mpq const & a, mpq const & b); + template + void lin_arith_op(mpq const& a, mpq const& b, mpq& c, mpz& g, mpz& tmp1, mpz& tmp2, mpz& tmp3); + + void rat_mul(mpq const & a, mpq const & b, mpq & c, mpz& g1, mpz& g2, mpz& tmp1, mpz& tmp2); + public: typedef mpq numeral; typedef mpq rational; @@ -501,7 +454,7 @@ public: void machine_div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::machine_div(a, b, c); } - void machine_div_rem(mpz const & a, mpz const & b, mpz & c, mpz& d) { mpz_manager::machine_div_rem(a, b, c, d); } + void machine_div_rem(mpz const & a, mpz const & b, mpz & c, mpz & d) { mpz_manager::machine_div_rem(a, b, c, d); } void div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::div(a, b, c); } @@ -746,10 +699,12 @@ public: reset_denominator(a); } - void set(mpz & a, unsigned sz, digit_t const * digits) { mpz_manager::set(a, sz, digits); } + void set(mpz & a, unsigned sz, digit_t const * digits) { + mpz_manager::set_digits(a, sz, digits); + } void set(mpq & a, unsigned sz, digit_t const * digits) { - mpz_manager::set(a.m_num, sz, digits); + mpz_manager::set_digits(a.m_num, sz, digits); reset_denominator(a); } diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 861a31cfb..55e9ebf25 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -30,6 +30,7 @@ Revision History: #else #error No multi-precision library selected. #endif +#include // Available GCD algorithms // #define EUCLID_GCD @@ -45,9 +46,6 @@ Revision History: #define LEHMER_GCD #endif - -#include - #if defined(__GNUC__) #define _trailing_zeros32(X) __builtin_ctz(X) #else @@ -146,11 +144,6 @@ mpz_manager::mpz_manager(): else { m_init_cell_capacity = 6; } - for (unsigned i = 0; i < 2; i++) { - m_tmp[i] = allocate(m_init_cell_capacity); - m_arg[i] = allocate(m_init_cell_capacity); - m_arg[i]->m_size = 1; - } set(m_int_min, -static_cast(INT_MIN)); #else // GMP @@ -160,8 +153,6 @@ mpz_manager::mpz_manager(): mpz_set_ui(m_two32, UINT_MAX); mpz_set_ui(m_tmp, 1); mpz_add(m_two32, m_two32, m_tmp); - m_arg[0] = allocate(); - m_arg[1] = allocate(); mpz_init(m_uint64_max); unsigned max_l = static_cast(UINT64_MAX); unsigned max_h = static_cast(UINT64_MAX >> 32); @@ -193,16 +184,10 @@ mpz_manager::~mpz_manager() { del(m_two64); #ifndef _MP_GMP del(m_int_min); - for (unsigned i = 0; i < 2; i++) { - deallocate(m_tmp[i]); - deallocate(m_arg[i]); - } #else mpz_clear(m_tmp); mpz_clear(m_tmp2); mpz_clear(m_two32); - deallocate(m_arg[0]); - deallocate(m_arg[1]); mpz_clear(m_uint64_max); mpz_clear(m_int64_max); mpz_clear(m_int64_min); @@ -211,12 +196,48 @@ mpz_manager::~mpz_manager() { omp_destroy_nest_lock(&m_lock); } +template +void mpz_manager::del(mpz & a) { + if (a.m_ptr) { + deallocate(a.m_owner == mpz_self, a.m_ptr); + a.m_ptr = nullptr; + a.m_kind = mpz_small; + a.m_owner = mpz_self; + } +} + +template +void mpz_manager::add(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz] " << to_string(a) << " + " << to_string(b) << " == ";); + if (is_small(a) && is_small(b)) { + set_i64(c, i64(a) + i64(b)); + } + else { + big_add(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +template +void mpz_manager::sub(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz] " << to_string(a) << " - " << to_string(b) << " == ";); + if (is_small(a) && is_small(b)) { + set_i64(c, i64(a) - i64(b)); + } + else { + big_sub(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + template void mpz_manager::set_big_i64(mpz & c, int64_t v) { #ifndef _MP_GMP - if (is_small(c)) { + if (c.m_ptr == nullptr) { c.m_ptr = allocate(m_init_cell_capacity); + c.m_owner = mpz_self; } + c.m_kind = mpz_ptr; SASSERT(capacity(c) >= m_init_cell_capacity); uint64_t _v; if (v < 0) { @@ -239,9 +260,11 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2; } #else - if (is_small(c)) { + if (c.m_ptr == nullptr) { c.m_ptr = allocate(); + c.m_owner = mpz_self; } + c.m_kind = mpz_ptr; uint64_t _v; bool sign; if (v < 0) { @@ -253,9 +276,11 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { sign = false; } mpz_set_ui(*c.m_ptr, static_cast(_v)); + MPZ_BEGIN_CRITICAL(); mpz_set_ui(m_tmp, static_cast(_v >> 32)); mpz_mul(m_tmp, m_tmp, m_two32); mpz_add(*c.m_ptr, *c.m_ptr, m_tmp); + MPZ_END_CRITICAL(); if (sign) mpz_neg(*c.m_ptr, *c.m_ptr); #endif @@ -264,9 +289,11 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { template void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { #ifndef _MP_GMP - if (is_small(c)) { + if (c.m_ptr == nullptr) { c.m_ptr = allocate(m_init_cell_capacity); + c.m_owner = mpz_self; } + c.m_kind = mpz_ptr; SASSERT(capacity(c) >= m_init_cell_capacity); c.m_val = 1; if (sizeof(digit_t) == sizeof(uint64_t)) { @@ -281,54 +308,71 @@ void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2; } #else - if (is_small(c)) { + if (c.m_ptr == nullptr) { c.m_ptr = allocate(); + c.m_owner = mpz_self; } + c.m_kind = mpz_ptr; mpz_set_ui(*c.m_ptr, static_cast(v)); + MPZ_BEGIN_CRITICAL(); mpz_set_ui(m_tmp, static_cast(v >> 32)); mpz_mul(m_tmp, m_tmp, m_two32); mpz_add(*c.m_ptr, *c.m_ptr, m_tmp); + MPZ_END_CRITICAL(); #endif } +#ifdef _MP_GMP + +template +mpz_manager::ensure_mpz_t(mpz_const& a) { + if (is_small(a)) { + m_result = &m_local; + mpz_init(m_local); + mpz_set_si(m_local, a.m_val); + } + else { + m_result = a.m_ptr; + } +} + +template +mpz_manager::ensure_mpz_t::~ensure_mpz_t() { + if (m_result == &m_local) { + mpz_clear(m_local); + } +} + +#endif + #ifndef _MP_GMP template -template -void mpz_manager::set(mpz & a, int sign, unsigned sz) { -#if 0 - static unsigned max_sz = 0; - if (sz > max_sz) { - max_sz = sz; - verbose_stream() << "max_sz: " << max_sz << "\n"; - } -#endif +void mpz_manager::set(mpz_cell& src, mpz & a, int sign, unsigned sz) { unsigned i = sz; - for (; i > 0; --i) { - if (m_tmp[IDX]->m_digits[i-1] != 0) - break; - } + for (; i > 0 && src.m_digits[i-1] == 0; --i) ; if (i == 0) { - // m_tmp[IDX] is zero + // src is zero reset(a); return; } - if (i == 1 && m_tmp[IDX]->m_digits[0] <= INT_MAX) { - // m_tmp[IDX] fits is a fixnum - del(a); - a.m_val = sign < 0 ? -static_cast(m_tmp[IDX]->m_digits[0]) : static_cast(m_tmp[IDX]->m_digits[0]); + unsigned d = src.m_digits[0]; + if (i == 1 && d <= INT_MAX) { + // src fits is a fixnum + a.m_val = sign < 0 ? -static_cast(d) : static_cast(d); + a.m_kind = mpz_small; return; } + set_digits(a, i, src.m_digits); a.m_val = sign; - std::swap(a.m_ptr, m_tmp[IDX]); - a.m_ptr->m_size = i; - if (!m_tmp[IDX]) // 'a' was a small number - m_tmp[IDX] = allocate(m_init_cell_capacity); + + SASSERT(a.m_kind == mpz_ptr); } #endif + template void mpz_manager::set(mpz & a, char const * val) { reset(a); @@ -353,7 +397,7 @@ void mpz_manager::set(mpz & a, char const * val) { } template -void mpz_manager::set(mpz & target, unsigned sz, digit_t const * digits) { +void mpz_manager::set_digits(mpz & target, unsigned sz, digit_t const * digits) { // remove zero digits while (sz > 0 && digits[sz - 1] == 0) sz--; @@ -364,114 +408,299 @@ void mpz_manager::set(mpz & target, unsigned sz, digit_t const * digits) else { #ifndef _MP_GMP target.m_val = 1; // number is positive. - if (is_small(target)) { + if (target.m_ptr == nullptr) { unsigned c = sz < m_init_cell_capacity ? m_init_cell_capacity : sz; target.m_ptr = allocate(c); target.m_ptr->m_size = sz; target.m_ptr->m_capacity = c; + target.m_kind = mpz_ptr; + target.m_owner = mpz_self; memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); } + else if (capacity(target) < sz) { + SASSERT(sz > m_init_cell_capacity); + mpz_cell* ptr = allocate(sz); + memcpy(ptr->m_digits, digits, sizeof(digit_t) * sz); + ptr->m_size = sz; + ptr->m_capacity = sz; + deallocate(target); + target.m_val = 1; + target.m_ptr = ptr; + target.m_kind = mpz_ptr; + target.m_owner = mpz_self; + } else { - if (capacity(target) < sz) { - SASSERT(sz > m_init_cell_capacity); - deallocate(target.m_ptr); - target.m_ptr = allocate(sz); - target.m_ptr->m_size = sz; - target.m_ptr->m_capacity = sz; + target.m_ptr->m_size = sz; + if (target.m_ptr->m_digits != digits) memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); - } - else { - target.m_ptr->m_size = sz; - memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); - } + target.m_kind = mpz_ptr; } #else - mk_big(target); - // reset - mpz_set_ui(*target.m_ptr, digits[sz - 1]); - SASSERT(sz > 0); - unsigned i = sz - 1; - while (i > 0) { - --i; - mpz_mul_2exp(*target.m_ptr, *target.m_ptr, 32); - mpz_set_ui(m_tmp, digits[i]); - mpz_add(*target.m_ptr, *target.m_ptr, m_tmp); - } + mk_big(target); + // reset + mpz_set_ui(*target.m_ptr, digits[sz - 1]); + SASSERT(sz > 0); + unsigned i = sz - 1; + MPZ_BEGIN_CRITICAL(); + while (i > 0) { + --i; + mpz_mul_2exp(*target.m_ptr, *target.m_ptr, 32); + mpz_set_ui(m_tmp, digits[i]); + mpz_add(*target.m_ptr, *target.m_ptr, m_tmp); + } + MPZ_END_CRITICAL(); #endif } } +template +void mpz_manager::mul(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz] " << to_string(a) << " * " << to_string(b) << " == ";); + if (is_small(a) && is_small(b)) { + set_i64(c, i64(a) * i64(b)); + } + else { + big_mul(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +// d <- a + b*c +template +void mpz_manager::addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { + if (is_one(b)) { + add(a, c, d); + } + else if (is_minus_one(b)) { + sub(a, c, d); + } + else { + mpz tmp; + mul(b,c,tmp); + add(a,tmp,d); + del(tmp); + } +} + + +// d <- a - b*c +template +void mpz_manager::submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { + if (is_one(b)) { + sub(a, c, d); + } + else if (is_minus_one(b)) { + add(a, c, d); + } + else { + mpz tmp; + mul(b,c,tmp); + sub(a,tmp,d); + del(tmp); + } +} + + +template +void mpz_manager::machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { + STRACE("mpz", tout << "[mpz-ext] divrem(" << to_string(a) << ", " << to_string(b) << ") == ";); + if (is_small(a) && is_small(b)) { + int64_t _a = i64(a); + int64_t _b = i64(b); + set_i64(q, _a / _b); + set_i64(r, _a % _b); + } + else { + big_div_rem(a, b, q, r); + } + STRACE("mpz", tout << "(" << to_string(q) << ", " << to_string(r) << ")\n";); +} + +template +void mpz_manager::machine_div(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz-ext] machine-div(" << to_string(a) << ", " << to_string(b) << ") == ";); + if (is_small(a) && is_small(b)) { + set_i64(c, i64(a) / i64(b)); + } + else { + big_div(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +template +void mpz_manager::rem(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz-ext] rem(" << to_string(a) << ", " << to_string(b) << ") == ";); + if (is_small(a) && is_small(b)) { + set_i64(c, i64(a) % i64(b)); + } + else { + big_rem(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + + +template +void mpz_manager::div_gcd(mpz const& a, mpz const& b, mpz & c) { + STRACE("mpz", tout << "[mpz-ext] div(" << to_string(a) << ", " << to_string(b) << ") == ";); + if (is_one(b)) { + set(c, a); + } + else { + machine_div(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +template +void mpz_manager::div(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz-ext] div(" << to_string(a) << ", " << to_string(b) << ") == ";); + if (is_one(b)) { + set(c, a); + } + else if (is_neg(a)) { + mpz tmp; + machine_div_rem(a, b, c, tmp); + if (!is_zero(tmp)) { + if (is_neg(b)) + add(c, mk_z(1), c); + else + sub(c, mk_z(1), c); + } + del(tmp); + } + else { + machine_div(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +template +void mpz_manager::mod(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz-ext] mod(" << to_string(a) << ", " << to_string(b) << ") == ";); + rem(a, b, c); + if (is_neg(c)) { + if (is_pos(b)) + add(c, b, c); + else + sub(c, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +template +void mpz_manager::neg(mpz & a) { + STRACE("mpz", tout << "[mpz] 0 - " << to_string(a) << " == ";); + if (is_small(a) && a.m_val == INT_MIN) { + // neg(INT_MIN) is not a small int + set_big_i64(a, - static_cast(INT_MIN)); + return; + } +#ifndef _MP_GMP + a.m_val = -a.m_val; +#else + if (is_small(a)) { + a.m_val = -a.m_val; + } + else { + mpz_neg(*a.m_ptr, *a.m_ptr); + } +#endif + STRACE("mpz", tout << to_string(a) << "\n";); +} + +template +void mpz_manager::abs(mpz & a) { + if (is_small(a)) { + if (a.m_val < 0) { + if (a.m_val == INT_MIN) { + // abs(INT_MIN) is not a small int + set_big_i64(a, - static_cast(INT_MIN)); + } + else + a.m_val = -a.m_val; + } + } + else { +#ifndef _MP_GMP + a.m_val = 1; +#else + mpz_abs(*a.m_ptr, *a.m_ptr); +#endif + } +} + + +// TBD: replace use of 'tmp' by 'c'. #ifndef _MP_GMP template template void mpz_manager::big_add_sub(mpz const & a, mpz const & b, mpz & c) { - int sign_a; - int sign_b; - mpz_cell * cell_a; - mpz_cell * cell_b; - get_sign_cell<0>(a, sign_a, cell_a); - get_sign_cell<1>(b, sign_b, cell_b); + sign_cell ca(*this, a), cb(*this, b); + int sign_b = cb.sign(); + mpz_stack tmp; if (SUB) sign_b = -sign_b; size_t real_sz; - if (sign_a == sign_b) { - unsigned sz = std::max(cell_a->m_size, cell_b->m_size)+1; - ensure_tmp_capacity<0>(sz); - m_mpn_manager.add(cell_a->m_digits, cell_a->m_size, - cell_b->m_digits, cell_b->m_size, - m_tmp[0]->m_digits, sz, &real_sz); + if (ca.sign() == sign_b) { + unsigned sz = std::max(ca.cell()->m_size, cb.cell()->m_size)+1; + allocate_if_needed(tmp, sz); + m_mpn_manager.add(ca.cell()->m_digits, ca.cell()->m_size, + cb.cell()->m_digits, cb.cell()->m_size, + tmp.m_ptr->m_digits, sz, &real_sz); SASSERT(real_sz <= sz); - set<0>(c, sign_a, static_cast(real_sz)); + set(*tmp.m_ptr, c, ca.sign(), static_cast(real_sz)); } else { digit_t borrow; - int r = m_mpn_manager.compare(cell_a->m_digits, cell_a->m_size, - cell_b->m_digits, cell_b->m_size); + int r = m_mpn_manager.compare(ca.cell()->m_digits, ca.cell()->m_size, + cb.cell()->m_digits, cb.cell()->m_size); if (r == 0) { reset(c); } else if (r < 0) { // a < b - unsigned sz = cell_b->m_size; - ensure_tmp_capacity<0>(sz); - m_mpn_manager.sub(cell_b->m_digits, - cell_b->m_size, - cell_a->m_digits, - cell_a->m_size, - m_tmp[0]->m_digits, + unsigned sz = cb.cell()->m_size; + allocate_if_needed(tmp, sz); + m_mpn_manager.sub(cb.cell()->m_digits, + cb.cell()->m_size, + ca.cell()->m_digits, + ca.cell()->m_size, + tmp.m_ptr->m_digits, &borrow); SASSERT(borrow == 0); - set<0>(c, sign_b, sz); + set(*tmp.m_ptr, c, sign_b, sz); } else { // a > b - unsigned sz = cell_a->m_size; - ensure_tmp_capacity<0>(sz); - m_mpn_manager.sub(cell_a->m_digits, - cell_a->m_size, - cell_b->m_digits, - cell_b->m_size, - m_tmp[0]->m_digits, + unsigned sz = ca.cell()->m_size; + allocate_if_needed(tmp, sz); + m_mpn_manager.sub(ca.cell()->m_digits, + ca.cell()->m_size, + cb.cell()->m_digits, + cb.cell()->m_size, + tmp.m_ptr->m_digits, &borrow); SASSERT(borrow == 0); - set<0>(c, sign_a, sz); + set(*tmp.m_ptr, c, ca.sign(), sz); } } + del(tmp); } #endif + + template void mpz_manager::big_add(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP big_add_sub(a, b, c); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz a1(a), b1(b); mk_big(c); - mpz_add(*c.m_ptr, *arg0, *arg1); + mpz_add(*c.m_ptr, a1(), b1()); #endif } @@ -481,80 +710,35 @@ void mpz_manager::big_sub(mpz const & a, mpz const & b, mpz & c) { big_add_sub(a, b, c); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz a1(a), b1(b); mk_big(c); - mpz_sub(*c.m_ptr, *arg0, *arg1); + mpz_sub(*c.m_ptr, a1(), b1()); #endif } template void mpz_manager::big_mul(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP - int sign_a; - int sign_b; - mpz_cell * cell_a; - mpz_cell * cell_b; - get_sign_cell<0>(a, sign_a, cell_a); - get_sign_cell<1>(b, sign_b, cell_b); - unsigned sz = cell_a->m_size + cell_b->m_size; - ensure_tmp_capacity<0>(sz); - m_mpn_manager.mul(cell_a->m_digits, - cell_a->m_size, - cell_b->m_digits, - cell_b->m_size, - m_tmp[0]->m_digits); - set<0>(c, sign_a == sign_b ? 1 : -1, sz); + // TBD replace tmp by c. + mpz_stack tmp; + sign_cell ca(*this, a), cb(*this, b); + unsigned sz = ca.cell()->m_size + cb.cell()->m_size; + allocate_if_needed(tmp, sz); + m_mpn_manager.mul(ca.cell()->m_digits, + ca.cell()->m_size, + cb.cell()->m_digits, + cb.cell()->m_size, + tmp.m_ptr->m_digits); + set(*tmp.m_ptr, c, ca.sign() == cb.sign() ? 1 : -1, sz); + del(tmp); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz a1(a), b1(b); mk_big(c); - mpz_mul(*c.m_ptr, *arg0, *arg1); + mpz_mul(*c.m_ptr, a1(), b1()); #endif } -#ifndef _MP_GMP -template -template -void mpz_manager::quot_rem_core(mpz const & a, mpz const & b, mpz & q, mpz & r) { - /* - +26 / +7 = +3, remainder is +5 - -26 / +7 = -3, remainder is -5 - +26 / -7 = -3, remainder is +5 - -26 / -7 = +3, remainder is -5 - */ - int sign_a; - int sign_b; - mpz_cell * cell_a; - mpz_cell * cell_b; - get_sign_cell<0>(a, sign_a, cell_a); - get_sign_cell<1>(b, sign_b, cell_b); - if (cell_b->m_size > cell_a->m_size) { - if (MODE == REM_ONLY || MODE == QUOT_AND_REM) - set(r, a); - if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) - reset(q); - return; - } - unsigned q_sz = cell_a->m_size - cell_b->m_size + 1; - unsigned r_sz = cell_b->m_size; - ensure_tmp_capacity<0>(q_sz); - ensure_tmp_capacity<1>(r_sz); - m_mpn_manager.div(cell_a->m_digits, cell_a->m_size, - cell_b->m_digits, cell_b->m_size, - m_tmp[0]->m_digits, - m_tmp[1]->m_digits); - if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) - set<0>(q, sign_a == sign_b ? 1 : -1, q_sz); - if (MODE == REM_ONLY || MODE == QUOT_AND_REM) - set<1>(r, sign_a, r_sz); -} -#endif template void mpz_manager::big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { @@ -562,16 +746,51 @@ void mpz_manager::big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz quot_rem_core(a, b, q, r); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz a1(a), b1(b); mk_big(q); mk_big(r); - mpz_tdiv_qr(*q.m_ptr, *r.m_ptr, *arg0, *arg1); + mpz_tdiv_qr(*q.m_ptr, *r.m_ptr, a1(), b1()); #endif } +#ifndef _MP_GMP +template +template +void mpz_manager::quot_rem_core(mpz const & a, mpz const & b, mpz & q, mpz & r) +{ + /* + +26 / +7 = +3, remainder is +5 + -26 / +7 = -3, remainder is -5 + +26 / -7 = -3, remainder is +5 + -26 / -7 = +3, remainder is -5 + */ + + mpz_stack q1, r1; + sign_cell ca(*this, a), cb(*this, b); + if (cb.cell()->m_size > ca.cell()->m_size) { + if (MODE == REM_ONLY || MODE == QUOT_AND_REM) + set(r, a); + if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) + reset(q); + return; + } + unsigned q_sz = ca.cell()->m_size - cb.cell()->m_size + 1; + unsigned r_sz = cb.cell()->m_size; + allocate_if_needed(q1, q_sz); + allocate_if_needed(r1, r_sz); + m_mpn_manager.div(ca.cell()->m_digits, ca.cell()->m_size, + cb.cell()->m_digits, cb.cell()->m_size, + q1.m_ptr->m_digits, + r1.m_ptr->m_digits); + if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) + set(*q1.m_ptr, q, ca.sign() == cb.sign() ? 1 : -1, q_sz); + if (MODE == REM_ONLY || MODE == QUOT_AND_REM) + set(*r1.m_ptr, r, ca.sign(), r_sz); + del(q1); + del(r1); +} +#endif + template void mpz_manager::big_div(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP @@ -580,12 +799,9 @@ void mpz_manager::big_div(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_zero(dummy)); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz a1(a), b1(b); mk_big(c); - mpz_tdiv_q(*c.m_ptr, *arg0, *arg1); + mpz_tdiv_q(*c.m_ptr, a1(), b1()); #endif } @@ -597,18 +813,16 @@ void mpz_manager::big_rem(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_zero(dummy)); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz a1(a), b1(b); mk_big(c); - mpz_tdiv_r(*c.m_ptr, *arg0, *arg1); + mpz_tdiv_r(*c.m_ptr, a1(), b1()); #endif } template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { static_assert(sizeof(a.m_val) == sizeof(int), "size mismatch"); + static_assert(sizeof(mpz) <= 16, "mpz size overflow"); if (is_small(a) && is_small(b) && a.m_val != INT_MIN && b.m_val != INT_MIN) { int _a = a.m_val; int _b = b.m_val; @@ -619,12 +833,9 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { } else { #ifdef _MP_GMP - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz a1(a), b1(b); mk_big(c); - mpz_gcd(*c.m_ptr, *arg0, *arg1); + mpz_gcd(*c.m_ptr, a1(), b1()); return; #endif if (is_zero(a)) { @@ -879,6 +1090,14 @@ unsigned mpz_manager::size_info(mpz const & a) { #endif } +template +mpz_manager::sign_cell::sign_cell(mpz_manager& m, mpz const& a): + m_local(reinterpret_cast(m_bytes)), m_a(a) { + m_local.m_ptr->m_capacity = capacity; + m.get_sign_cell(a, m_sign, m_cell, m_local.m_ptr); +} + + template struct mpz_manager::sz_lt { mpz_manager & m; @@ -891,6 +1110,7 @@ struct mpz_manager::sz_lt { } }; + template void mpz_manager::gcd(unsigned sz, mpz const * as, mpz & g) { #if 0 @@ -1070,8 +1290,8 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(b)); TRACE("mpz", tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";); if (is_small(a) && is_small(b)) { - del(c); c.m_val = a.m_val | b.m_val; + c.m_kind = mpz_small; } else { #ifndef _MP_GMP @@ -1104,12 +1324,9 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz a1(a), b1(b); mk_big(c); - mpz_ior(*c.m_ptr, *arg0, *arg1); + mpz_ior(*c.m_ptr, a1(), b1()); #endif } } @@ -1119,8 +1336,8 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { - del(c); c.m_val = a.m_val & b.m_val; + c.m_kind = mpz_small; } else { #ifndef _MP_GMP @@ -1142,12 +1359,9 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz a1(a), b1(b); mk_big(c); - mpz_and(*c.m_ptr, *arg0, *arg1); + mpz_and(*c.m_ptr, a1(), b1()); #endif } } @@ -1187,12 +1401,9 @@ void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz a1(a), b1(b); mk_big(c); - mpz_xor(*c.m_ptr, *arg0, *arg1); + mpz_xor(*c.m_ptr, a1(), b1()); #endif } } @@ -1238,24 +1449,27 @@ void mpz_manager::big_set(mpz & target, mpz const & source) { if (&target == &source) return; target.m_val = source.m_val; - if (is_small(target)) { + if (target.m_ptr == nullptr) { target.m_ptr = allocate(capacity(source)); target.m_ptr->m_size = size(source); target.m_ptr->m_capacity = capacity(source); + target.m_kind = mpz_ptr; + target.m_owner = mpz_self; + memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); + } + else if (capacity(target) < size(source)) { + deallocate(target); + target.m_ptr = allocate(capacity(source)); + target.m_ptr->m_size = size(source); + target.m_ptr->m_capacity = capacity(source); + target.m_kind = mpz_ptr; + target.m_owner = mpz_self; memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); } else { - if (capacity(target) < size(source)) { - deallocate(target.m_ptr); - target.m_ptr = allocate(capacity(source)); - target.m_ptr->m_size = size(source); - target.m_ptr->m_capacity = capacity(source); - memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); - } - else { - target.m_ptr->m_size = size(source); - memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); - } + target.m_ptr->m_size = size(source); + memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); + target.m_kind = mpz_ptr; } #else // GMP version @@ -1267,19 +1481,14 @@ void mpz_manager::big_set(mpz & target, mpz const & source) { template int mpz_manager::big_compare(mpz const & a, mpz const & b) { #ifndef _MP_GMP - int sign_a; - int sign_b; - mpz_cell * cell_a; - mpz_cell * cell_b; - get_sign_cell<0>(a, sign_a, cell_a); - get_sign_cell<1>(b, sign_b, cell_b); + sign_cell ca(*this, a), cb(*this, b); - if (sign_a > 0) { + if (ca.sign() > 0) { // a is positive - if (sign_b > 0) { + if (cb.sign() > 0) { // a & b are positive - return m_mpn_manager.compare(cell_a->m_digits, cell_a->m_size, - cell_b->m_digits, cell_b->m_size); + return m_mpn_manager.compare(ca.cell()->m_digits, ca.cell()->m_size, + cb.cell()->m_digits, cb.cell()->m_size); } else { // b is negative @@ -1288,23 +1497,20 @@ int mpz_manager::big_compare(mpz const & a, mpz const & b) { } else { // a is negative - if (sign_b > 0) { + if (cb.sign() > 0) { // b is positive return -1; // a < b } else { // a & b are negative - return m_mpn_manager.compare(cell_b->m_digits, cell_b->m_size, - cell_a->m_digits, cell_a->m_size); + return m_mpn_manager.compare(cb.cell()->m_digits, cb.cell()->m_size, + ca.cell()->m_digits, ca.cell()->m_size); } } #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); - return mpz_cmp(*arg0, *arg1); + ensure_mpz a1(a), b1(b); + return mpz_cmp(a1(), b1()); #endif } @@ -1369,6 +1575,7 @@ uint64_t mpz_manager::get_uint64(mpz const & a) const { return mpz_get_ui(*a.m_ptr); } else { + MPZ_BEGIN_CRITICAL(); mpz_manager * _this = const_cast(this); mpz_set(_this->m_tmp, *a.m_ptr); mpz_mod(_this->m_tmp, m_tmp, m_two32); @@ -1376,6 +1583,7 @@ uint64_t mpz_manager::get_uint64(mpz const & a) const { mpz_set(_this->m_tmp, *a.m_ptr); mpz_div(_this->m_tmp, m_tmp, m_two32); r += static_cast(mpz_get_ui(m_tmp)) << static_cast(32); + MPZ_END_CRITICAL(); return r; } #endif @@ -1400,11 +1608,13 @@ int64_t mpz_manager::get_int64(mpz const & a) const { return mpz_get_si(*a.m_ptr); } else { + MPZ_BEGIN_CRITICAL(); mpz_manager * _this = const_cast(this); mpz_mod(_this->m_tmp, *a.m_ptr, m_two32); int64_t r = static_cast(mpz_get_ui(m_tmp)); mpz_div(_this->m_tmp, *a.m_ptr, m_two32); r += static_cast(mpz_get_si(m_tmp)) << static_cast(32); + MPZ_END_CRITICAL(); return r; } #endif @@ -1512,8 +1722,8 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { if (is_small(a)) { if (a.m_val == 2) { if (p < 8 * sizeof(int) - 1) { - del(b); b.m_val = 1 << p; + b.m_kind = mpz_small; } else { unsigned sz = p/(8 * sizeof(digit_t)) + 1; @@ -1526,6 +1736,7 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { b.m_ptr->m_digits[i] = 0; b.m_ptr->m_digits[sz-1] = 1 << shift; b.m_val = 1; + b.m_kind = mpz_ptr; } return; } @@ -1608,38 +1819,41 @@ void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { return; if (capacity < m_init_cell_capacity) capacity = m_init_cell_capacity; + if (is_small(a)) { - a.m_ptr = allocate(capacity); - SASSERT(a.m_ptr->m_capacity == capacity); - if (a.m_val == INT_MIN) { + int val = a.m_val; + allocate_if_needed(a, capacity); + a.m_kind = mpz_ptr; + SASSERT(a.m_ptr->m_capacity >= capacity); + if (val == INT_MIN) { unsigned intmin_sz = m_int_min.m_ptr->m_size; for (unsigned i = 0; i < intmin_sz; i++) a.m_ptr->m_digits[i] = m_int_min.m_ptr->m_digits[i]; a.m_val = -1; a.m_ptr->m_size = m_int_min.m_ptr->m_size; } - else if (a.m_val < 0) { - a.m_ptr->m_digits[0] = -a.m_val; + else if (val < 0) { + a.m_ptr->m_digits[0] = -val; a.m_val = -1; a.m_ptr->m_size = 1; } else { - a.m_ptr->m_digits[0] = a.m_val; + a.m_ptr->m_digits[0] = val; a.m_val = 1; a.m_ptr->m_size = 1; } } - else { - if (a.m_ptr->m_capacity >= capacity) - return; + else if (a.m_ptr->m_capacity < capacity) { mpz_cell * new_cell = allocate(capacity); SASSERT(new_cell->m_capacity == capacity); unsigned old_sz = a.m_ptr->m_size; new_cell->m_size = old_sz; for (unsigned i = 0; i < old_sz; i++) new_cell->m_digits[i] = a.m_ptr->m_digits[i]; - deallocate(a.m_ptr); + deallocate(a); a.m_ptr = new_cell; + a.m_owner = mpz_self; + a.m_kind = mpz_ptr; } } @@ -1662,8 +1876,8 @@ void mpz_manager::normalize(mpz & a) { if (i == 1 && ds[0] <= INT_MAX) { // a is small int val = a.m_val < 0 ? -static_cast(ds[0]) : static_cast(ds[0]); - del(a); a.m_val = val; + a.m_kind = mpz_small; return; } // adjust size @@ -1731,10 +1945,9 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { c->m_size = new_sz; normalize(a); #else + ensure_mpz a1(a); MPZ_BEGIN_CRITICAL(); - mpz_t * arg0; - get_arg<0>(a, arg0); - mpz_tdiv_q_2exp(m_tmp, *arg0, k); + mpz_tdiv_q_2exp(m_tmp, a1(), k); mk_big(a); mpz_swap(*a.m_ptr, m_tmp); MPZ_END_CRITICAL(); @@ -1757,7 +1970,8 @@ void mpz_manager::mul2k(mpz & a, unsigned k) { unsigned new_sz = old_sz + word_shift + 1; ensure_capacity(a, new_sz); TRACE("mpz_mul2k", tout << "word_shift: " << word_shift << "\nbit_shift: " << bit_shift << "\nold_sz: " << old_sz << "\nnew_sz: " << new_sz - << "\na after ensure capacity:\n" << to_string(a) << "\n";); + << "\na after ensure capacity:\n" << to_string(a) << "\n"; + tout << a.m_kind << "\n";); SASSERT(!is_small(a)); mpz_cell * cell_a = a.m_ptr; old_sz = cell_a->m_size; @@ -1796,10 +2010,9 @@ void mpz_manager::mul2k(mpz & a, unsigned k) { normalize(a); TRACE("mpz_mul2k", tout << "mul2k result:\n" << to_string(a) << "\n";); #else - mpz_t * arg0; - get_arg<0>(a, arg0); + ensure_mpz a1(a); mk_big(a); - mpz_mul_2exp(*a.m_ptr, *arg0, k); + mpz_mul_2exp(*a.m_ptr, a1(), k); #endif } @@ -1902,8 +2115,10 @@ unsigned mpz_manager::mlog2(mpz const & a) { else return (sz - 1)*32 + ::log2(static_cast(ds[sz-1])); #else + MPZ_BEGIN_CRITICAL(); mpz_neg(m_tmp, *a.m_ptr); unsigned r = mpz_sizeinbase(m_tmp, 2); + MPZ_END_CRITICAL(); SASSERT(r > 0); return r - 1; #endif @@ -2087,6 +2302,7 @@ bool mpz_manager::decompose(mpz const & a, svector & digits) { return a.m_val < 0; #else bool r = is_neg(a); + MPZ_BEGIN_CRITICAL(); mpz_set(m_tmp, *a.m_ptr); mpz_abs(m_tmp, m_tmp); while (mpz_sgn(m_tmp) != 0) { @@ -2095,6 +2311,7 @@ bool mpz_manager::decompose(mpz const & a, svector & digits) { digits.push_back(v); mpz_tdiv_q_2exp(m_tmp, m_tmp, 32); } + MPZ_END_CRITICAL(); return r; #endif } diff --git a/src/util/mpz.h b/src/util/mpz.h index 92c6d0d10..9ded83190 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -64,6 +64,7 @@ class mpz_cell { digit_t m_digits[0]; friend class mpz_manager; friend class mpz_manager; + friend class mpz_stack; }; #else #include @@ -72,17 +73,25 @@ class mpz_cell { /** \brief Multi-precision integer. - If m_ptr == 0, the it is a small number and the value is stored at m_val. - Otherwise, m_val contains the sign (-1 negative, 1 positive), and m_ptr points to a mpz_cell that - store the value. <<< This last statement is true only in Windows. + If m_kind == mpz_small, it is a small number and the value is stored in m_val. + If m_kind == mpz_ptr, the value is stored in m_ptr and m_ptr != nullptr. + m_val contains the sign (-1 negative, 1 positive) + under winodws, m_ptr points to a mpz_cell that store the value. */ + +enum mpz_kind { mpz_small = 0, mpz_ptr = 1}; +enum mpz_owner { mpz_self = 0, mpz_ext = 1}; + class mpz { - int m_val; #ifndef _MP_GMP - mpz_cell * m_ptr; + typedef mpz_cell mpz_type; #else - mpz_t * m_ptr; + typedef mpz_t mpz_type; #endif + int m_val; + unsigned m_kind:1; + unsigned m_owner:1; + mpz_type * m_ptr; friend class mpz_manager; friend class mpz_manager; friend class mpq_manager; @@ -90,19 +99,38 @@ class mpz { friend class mpq; friend class mpbq; friend class mpbq_manager; + friend class mpz_stack; mpz & operator=(mpz const & other) { UNREACHABLE(); return *this; } public: - mpz(int v):m_val(v), m_ptr(nullptr) {} - mpz():m_val(0), m_ptr(nullptr) {} - mpz(mpz && other) : m_val(other.m_val), m_ptr(nullptr) { + mpz(int v):m_val(v), m_kind(mpz_small), m_owner(mpz_self), m_ptr(nullptr) {} + mpz():m_val(0), m_kind(mpz_small), m_owner(mpz_self), m_ptr(nullptr) {} + mpz(mpz_type* ptr): m_val(0), m_kind(mpz_small), m_owner(mpz_ext), m_ptr(ptr) { SASSERT(ptr);} + mpz(mpz && other) : m_val(other.m_val), m_kind(mpz_small), m_owner(mpz_self), m_ptr(nullptr) { std::swap(m_ptr, other.m_ptr); + unsigned o = m_owner; m_owner = other.m_owner; other.m_owner = o; + unsigned k = m_kind; m_kind = other.m_kind; other.m_kind = k; } void swap(mpz & other) { std::swap(m_val, other.m_val); std::swap(m_ptr, other.m_ptr); + unsigned o = m_owner; m_owner = other.m_owner; other.m_owner = o; + unsigned k = m_kind; m_kind = other.m_kind; other.m_kind = k; } }; +#ifndef _MP_GMP +class mpz_stack : public mpz { + static const unsigned capacity = 8; + unsigned char m_bytes[sizeof(mpz_cell) + sizeof(digit_t) * capacity]; +public: + mpz_stack():mpz(reinterpret_cast(m_bytes)) { + m_ptr->m_capacity = capacity; + } +}; +#else +class mpz_stack : public mpz {}; +#endif + inline void swap(mpz & m1, mpz & m2) { m1.swap(m2); } template @@ -115,57 +143,59 @@ class mpz_manager { #ifndef _MP_GMP unsigned m_init_cell_capacity; - mpz_cell * m_tmp[2]; - mpz_cell * m_arg[2]; mpz m_int_min; - static unsigned cell_size(unsigned capacity) { return sizeof(mpz_cell) + sizeof(digit_t) * capacity; } + static unsigned cell_size(unsigned capacity) { + return sizeof(mpz_cell) + sizeof(digit_t) * capacity; + } mpz_cell * allocate(unsigned capacity) { SASSERT(capacity >= m_init_cell_capacity); + MPZ_BEGIN_CRITICAL(); mpz_cell * cell = reinterpret_cast(m_allocator.allocate(cell_size(capacity))); + MPZ_END_CRITICAL(); cell->m_capacity = capacity; return cell; } + + void deallocate(mpz& n) { + if (n.m_ptr) { + deallocate(n.m_owner == mpz_self, n.m_ptr); + n.m_ptr = nullptr; + n.m_kind = mpz_small; + } + } // make sure that n is a big number and has capacity equal to at least c. void allocate_if_needed(mpz & n, unsigned c) { - if (c < m_init_cell_capacity) - c = m_init_cell_capacity; - if (is_small(n)) { + c = std::max(c, m_init_cell_capacity); + if (n.m_ptr == nullptr || capacity(n) < c) { + deallocate(n); n.m_val = 1; + n.m_kind = mpz_ptr; + n.m_owner = mpz_self; n.m_ptr = allocate(c); - n.m_ptr->m_capacity = c; } - else if (capacity(n) < c) { - deallocate(n.m_ptr); - n.m_val = 1; - n.m_ptr = allocate(c); - n.m_ptr->m_capacity = c; + else { + n.m_kind = mpz_ptr; } } - void deallocate(mpz_cell * ptr) { - m_allocator.deallocate(cell_size(ptr->m_capacity), ptr); - } - - /** - \brief Make sure that m_tmp[IDX] can hold the given number of digits - */ - template - void ensure_tmp_capacity(unsigned capacity) { - if (m_tmp[IDX]->m_capacity >= capacity) - return; - deallocate(m_tmp[IDX]); - unsigned new_capacity = (3 * capacity + 1) >> 1; - m_tmp[IDX] = allocate(new_capacity); - SASSERT(m_tmp[IDX]->m_capacity >= capacity); + void deallocate(bool is_heap, mpz_cell * ptr) { + if (is_heap) { + MPZ_BEGIN_CRITICAL(); + m_allocator.deallocate(cell_size(ptr->m_capacity), ptr); + MPZ_END_CRITICAL(); + } } // Expand capacity of a while preserving its content. void ensure_capacity(mpz & a, unsigned sz); void normalize(mpz & a); + + void clear(mpz& n) { } + #else // GMP code mpz_t m_tmp, m_tmp2; @@ -175,22 +205,34 @@ class mpz_manager { mpz_t m_int64_max; mpz_t m_int64_min; - mpz_t * allocate() { + mpz_t * allocate() { + MPZ_BEGIN_CRITICAL(); mpz_t * cell = reinterpret_cast(m_allocator.allocate(sizeof(mpz_t))); + MPZ_END_CRITICAL(); mpz_init(*cell); return cell; } - void deallocate(mpz_t * ptr) { mpz_clear(*ptr); m_allocator.deallocate(sizeof(mpz_t), ptr); } + void deallocate(bool is_heap, mpz_t * ptr) { + mpz_clear(*ptr); + if (is_heap) { + MPZ_BEGIN_CRITICAL(); + m_allocator.deallocate(sizeof(mpz_t), ptr); + MPZ_END_CRITICAL(); + } + } + + void clear(mpz& n) { if (n.m_ptr) mpz_clear(n.m_ptr); } #endif + + mpz m_two64; /** - \brief Set \c a with the value stored at m_tmp[IDX], and the given sign. - \c sz is an overapproximation of the size of the number stored at \c tmp. + \brief Set \c a with the value stored at src, and the given sign. + \c sz is an overapproximation of the size of the number stored at \c src. */ - template - void set(mpz & a, int sign, unsigned sz); + void set(mpz_cell& src, mpz & a, int sign, unsigned sz); static int64_t i64(mpz const & a) { return static_cast(a.m_val); } @@ -198,19 +240,19 @@ class mpz_manager { void set_i64(mpz & c, int64_t v) { if (v >= INT_MIN && v <= INT_MAX) { - del(c); c.m_val = static_cast(v); + c.m_kind = mpz_small; } else { - MPZ_BEGIN_CRITICAL(); set_big_i64(c, v); - MPZ_END_CRITICAL(); } } void set_big_ui64(mpz & c, uint64_t v); + #ifndef _MP_GMP + static unsigned capacity(mpz const & c) { return c.m_ptr->m_capacity; } static unsigned size(mpz const & c) { return c.m_ptr->m_size; } @@ -241,16 +283,28 @@ class mpz_manager { return ((static_cast(digits(a)[1]) << 32) | (static_cast(digits(a)[0]))); } - template - void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell) { + class sign_cell { + static const unsigned capacity = 2; + unsigned char m_bytes[sizeof(mpz_cell) + sizeof(digit_t) * capacity]; + mpz m_local; + mpz const& m_a; + int m_sign; + mpz_cell* m_cell; + public: + sign_cell(mpz_manager& m, mpz const& a); + int sign() { return m_sign; } + mpz_cell const* cell() { return m_cell; } + }; + + void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell, mpz_cell* reserve) { if (is_small(a)) { if (a.m_val == INT_MIN) { sign = -1; cell = m_int_min.m_ptr; } else { - cell = m_arg[IDX]; - SASSERT(cell->m_size == 1); + cell = reserve; + cell->m_size = 1; if (a.m_val < 0) { sign = -1; cell->m_digits[0] = -a.m_val; @@ -266,26 +320,29 @@ class mpz_manager { cell = a.m_ptr; } } + #else // GMP code - template - void get_arg(mpz const & a, mpz_t * & result) { - if (is_small(a)) { - result = m_arg[IDX]; - mpz_set_si(*result, a.m_val); - } - else { - result = a.m_ptr; - } - } + class ensure_mpz_t { + mpz_t m_local; + mpz_t* m_result; + public: + ensure_mpz_t(mpz const& a); + ~ensure_mpz_t(); + mpz_t& operator()() { return *m_result; } + }; void mk_big(mpz & a) { - if (a.m_ptr == 0) { + if (a.m_ptr == null) { a.m_val = 0; a.m_ptr = allocate(); + a.m_owner = mpz_self; } + a.m_kind = mpz_ptr; } + + #endif #ifndef _MP_GMP @@ -333,245 +390,49 @@ public: ~mpz_manager(); - static bool is_small(mpz const & a) { return a.m_ptr == nullptr; } + static bool is_small(mpz const & a) { return a.m_kind == mpz_small; } static mpz mk_z(int val) { return mpz(val); } - void del(mpz & a) { - if (a.m_ptr != nullptr) { - MPZ_BEGIN_CRITICAL(); - deallocate(a.m_ptr); - MPZ_END_CRITICAL(); - a.m_ptr = nullptr; - } - } + void del(mpz & a); - void add(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz] " << to_string(a) << " + " << to_string(b) << " == ";); - if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) + i64(b)); - } - else { - MPZ_BEGIN_CRITICAL(); - big_add(a, b, c); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void add(mpz const & a, mpz const & b, mpz & c); - void sub(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz] " << to_string(a) << " - " << to_string(b) << " == ";); - if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) - i64(b)); - } - else { - MPZ_BEGIN_CRITICAL(); - big_sub(a, b, c); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void sub(mpz const & a, mpz const & b, mpz & c); void inc(mpz & a) { add(a, mpz(1), a); } void dec(mpz & a) { add(a, mpz(-1), a); } - void mul(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz] " << to_string(a) << " * " << to_string(b) << " == ";); - if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) * i64(b)); - } - else { - MPZ_BEGIN_CRITICAL(); - big_mul(a, b, c); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void mul(mpz const & a, mpz const & b, mpz & c); // d <- a + b*c - void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { - if (is_one(b)) { - add(a, c, d); - } - else if (is_minus_one(b)) { - sub(a, c, d); - } - else { - mpz tmp; - mul(b,c,tmp); - add(a,tmp,d); - del(tmp); - } - } - + void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d); // d <- a - b*c - void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { - if (is_one(b)) { - sub(a, c, d); - } - else if (is_minus_one(b)) { - add(a, c, d); - } - else { - mpz tmp; - mul(b,c,tmp); - sub(a,tmp,d); - del(tmp); - } - } + void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d); + void machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r); - void machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { - STRACE("mpz", tout << "[mpz-ext] divrem(" << to_string(a) << ", " << to_string(b) << ") == ";); - if (is_small(a) && is_small(b)) { - int64_t _a = i64(a); - int64_t _b = i64(b); - set_i64(q, _a / _b); - set_i64(r, _a % _b); - } - else { - MPZ_BEGIN_CRITICAL(); - big_div_rem(a, b, q, r); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << "(" << to_string(q) << ", " << to_string(r) << ")\n";); - } + void machine_div(mpz const & a, mpz const & b, mpz & c); - void machine_div(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz-ext] machine-div(" << to_string(a) << ", " << to_string(b) << ") == ";); - if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) / i64(b)); - } - else { - MPZ_BEGIN_CRITICAL(); - big_div(a, b, c); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void rem(mpz const & a, mpz const & b, mpz & c); - void rem(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz-ext] rem(" << to_string(a) << ", " << to_string(b) << ") == ";); - if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) % i64(b)); - } - else { - MPZ_BEGIN_CRITICAL(); - big_rem(a, b, c); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void div_gcd(mpz const & a, mpz const & b, mpz & c); - void div(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz-ext] div(" << to_string(a) << ", " << to_string(b) << ") == ";); - if (is_neg(a)) { - mpz tmp; - machine_div_rem(a, b, c, tmp); - if (!is_zero(tmp)) { - if (is_neg(b)) - add(c, mk_z(1), c); - else - sub(c, mk_z(1), c); - } - del(tmp); - } - else { - machine_div(a, b, c); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void div(mpz const & a, mpz const & b, mpz & c); - void mod(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz-ext] mod(" << to_string(a) << ", " << to_string(b) << ") == ";); - rem(a, b, c); - if (is_neg(c)) { - if (is_pos(b)) - add(c, b, c); - else - sub(c, b, c); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void mod(mpz const & a, mpz const & b, mpz & c); - void neg(mpz & a) { - STRACE("mpz", tout << "[mpz] 0 - " << to_string(a) << " == ";); - if (is_small(a) && a.m_val == INT_MIN) { - // neg(INT_MIN) is not a small int - MPZ_BEGIN_CRITICAL(); - set_big_i64(a, - static_cast(INT_MIN)); - MPZ_END_CRITICAL(); - return; - } -#ifndef _MP_GMP - a.m_val = -a.m_val; -#else - if (is_small(a)) { - a.m_val = -a.m_val; - } - else { - mpz_neg(*a.m_ptr, *a.m_ptr); - } -#endif - STRACE("mpz", tout << to_string(a) << "\n";); - } + void neg(mpz & a); - void abs(mpz & a) { - if (is_small(a)) { - if (a.m_val < 0) { - if (a.m_val == INT_MIN) { - // abs(INT_MIN) is not a small int - MPZ_BEGIN_CRITICAL(); - set_big_i64(a, - static_cast(INT_MIN)); - MPZ_END_CRITICAL(); - } - else - a.m_val = -a.m_val; - } - } - else { -#ifndef _MP_GMP - a.m_val = 1; -#else - mpz_abs(*a.m_ptr, *a.m_ptr); -#endif - } - } + void abs(mpz & a); - static bool is_pos(mpz const & a) { -#ifndef _MP_GMP - return a.m_val > 0; -#else - if (is_small(a)) - return a.m_val > 0; - else - return mpz_sgn(*a.m_ptr) > 0; -#endif - } + static bool is_pos(mpz const & a) { return sign(a) > 0; } - static bool is_neg(mpz const & a) { -#ifndef _MP_GMP - return a.m_val < 0; -#else - if (is_small(a)) - return a.m_val < 0; - else - return mpz_sgn(*a.m_ptr) < 0; -#endif - } + static bool is_neg(mpz const & a) { return sign(a) < 0; } - static bool is_zero(mpz const & a) { -#ifndef _MP_GMP - return a.m_val == 0; -#else - if (is_small(a)) - return a.m_val == 0; - else - return mpz_sgn(*a.m_ptr) == 0; -#endif - } + static bool is_zero(mpz const & a) { return sign(a) == 0; } static int sign(mpz const & a) { #ifndef _MP_GMP @@ -593,10 +454,7 @@ public: return a.m_val == b.m_val; } else { - MPZ_BEGIN_CRITICAL(); - bool res = big_compare(a, b) == 0; - MPZ_END_CRITICAL(); - return res; + return big_compare(a, b) == 0; } } @@ -614,10 +472,7 @@ public: return a.m_val < b.m_val; } else { - MPZ_BEGIN_CRITICAL(); - bool res = big_compare(a, b) < 0; - MPZ_END_CRITICAL(); - return res; + return big_compare(a, b) < 0; } } @@ -661,25 +516,24 @@ public: void set(mpz & target, mpz const & source) { if (is_small(source)) { - del(target); target.m_val = source.m_val; + target.m_kind = mpz_small; } else { - MPZ_BEGIN_CRITICAL(); big_set(target, source); - MPZ_END_CRITICAL(); } } void set(mpz & target, mpz && source) { - del(target); target.m_val = source.m_val; std::swap(target.m_ptr, source.m_ptr); + auto o = target.m_owner; target.m_owner = source.m_owner; source.m_owner = o; + auto k = target.m_kind; target.m_kind = source.m_kind; source.m_kind = k; } void set(mpz & a, int val) { - del(a); a.m_val = val; + a.m_kind = mpz_small; } void set(mpz & a, unsigned val) { @@ -697,17 +551,15 @@ public: void set(mpz & a, uint64_t val) { if (val < INT_MAX) { - del(a); a.m_val = static_cast(val); + a.m_kind = mpz_small; } else { - MPZ_BEGIN_CRITICAL(); set_big_ui64(a, val); - MPZ_END_CRITICAL(); } } - void set(mpz & target, unsigned sz, digit_t const * digits); + void set_digits(mpz & target, unsigned sz, digit_t const * digits); mpz dup(const mpz & source) { mpz temp; @@ -716,13 +568,15 @@ public: } void reset(mpz & a) { - del(a); a.m_val = 0; + a.m_kind = mpz_small; } void swap(mpz & a, mpz & b) { std::swap(a.m_val, b.m_val); std::swap(a.m_ptr, b.m_ptr); + auto o = a.m_owner; a.m_owner = b.m_owner; b.m_owner = o; + auto k = a.m_kind; a.m_kind = b.m_kind; b.m_kind = k; } bool is_uint64(mpz const & a) const; diff --git a/src/util/rational.h b/src/util/rational.h index dc7b79419..3403848c4 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -54,7 +54,6 @@ public: rational(mpz const & z) { m().set(m_val, z); } rational(double z) { UNREACHABLE(); } - explicit rational(char const * v) { m().set(m_val, v); } @@ -189,6 +188,12 @@ public: return r; } + friend inline rational machine_div_rem(rational const & r1, rational const & r2, rational & rem) { + rational r; + rational::m().machine_idiv_rem(r1.m_val, r2.m_val, r.m_val, rem.m_val); + return r; + } + friend inline rational mod(rational const & r1, rational const & r2) { rational r; rational::m().mod(r1.m_val, r2.m_val, r.m_val); @@ -410,8 +415,6 @@ public: } return num_bits; } - - }; inline bool operator!=(rational const & r1, rational const & r2) { @@ -422,6 +425,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); } @@ -430,6 +437,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); } @@ -438,6 +450,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)); } diff --git a/src/util/vector.h b/src/util/vector.h index c4443255a..edcee7449 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -188,6 +188,24 @@ public: m_data = nullptr; } + bool operator==(vector const & other) const { + if (this == &other) { + return true; + } + if (size() != other.size()) + return false; + for (unsigned i = 0; i < size(); i++) { + if ((*this)[i] != other[i]) + return false; + } + return true; + } + + bool operator!=(vector const & other) const { + return !(*this == other); + } + + vector & operator=(vector const & source) { if (this == &source) { return *this;