3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-15 21:38:44 +00:00

Merge pull request #1715 from levnach/master

merge lar_solver/int_solver
This commit is contained in:
Nikolaj Bjorner 2018-07-01 12:20:02 -07:00 committed by GitHub
commit 5a2a8d7d5c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
159 changed files with 14144 additions and 9250 deletions

View file

@ -534,3 +534,4 @@ inline app_ref operator>(app_ref const& x, app_ref const& y) {
} }
#endif /* ARITH_DECL_PLUGIN_H_ */ #endif /* ARITH_DECL_PLUGIN_H_ */

View file

@ -801,106 +801,104 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu
return BR_DONE; return BR_DONE;
} }
if (m_util.is_numeral(arg2, v2, is_int) && v2.is_one()) { if (m_util.is_numeral(arg2, v2, is_int) && v2.is_one()) {
result = arg1; result = arg1;
return BR_DONE; return BR_DONE;
} }
if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) {
return BR_FAILED; return BR_FAILED;
} }
if (arg1 == arg2) { if (arg1 == arg2) {
expr_ref zero(m_util.mk_int(0), m()); 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)); result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1));
return BR_REWRITE3; return BR_REWRITE3;
} }
if (divides(arg1, arg2, result)) { if (divides(arg1, arg2, result)) {
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
} }
return BR_FAILED; 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) { bool arith_rewriter::divides(expr* num, expr* den, expr_ref& result) {
expr_fast_mark1 mark; expr_fast_mark1 mark;
rational num_r(1), den_r(1); rational num_r(1), den_r(1);
expr* num_e = nullptr, *den_e = nullptr; expr* num_e = nullptr, *den_e = nullptr;
ptr_buffer<expr> args1, args2; ptr_buffer<expr> args1, args2;
flat_mul(num, args1); flat_mul(num, args1);
flat_mul(den, args2); flat_mul(den, args2);
for (expr * arg : args1) { for (expr * arg : args1) {
mark.mark(arg); mark.mark(arg);
if (m_util.is_numeral(arg, num_r)) num_e = arg; if (m_util.is_numeral(arg, num_r)) num_e = arg;
} }
for (expr* arg : args2) { for (expr* arg : args2) {
if (mark.is_marked(arg)) { if (mark.is_marked(arg)) {
result = remove_divisor(arg, num, den); result = remove_divisor(arg, num, den);
return true; return true;
} }
if (m_util.is_numeral(arg, den_r)) den_e = arg; if (m_util.is_numeral(arg, den_r)) den_e = arg;
} }
rational g = gcd(num_r, den_r); rational g = gcd(num_r, den_r);
if (!g.is_one()) { if (!g.is_one()) {
SASSERT(g.is_pos()); SASSERT(g.is_pos());
// replace num_e, den_e by their gcd reduction. // replace num_e, den_e by their gcd reduction.
for (unsigned i = 0; i < args1.size(); ++i) { for (unsigned i = 0; i < args1.size(); ++i) {
if (args1[i] == num_e) { if (args1[i] == num_e) {
args1[i] = m_util.mk_numeral(num_r / g, true); args1[i] = m_util.mk_numeral(num_r / g, true);
break; break;
} }
} }
for (unsigned i = 0; i < args2.size(); ++i) { for (unsigned i = 0; i < args2.size(); ++i) {
if (args2[i] == den_e) { if (args2[i] == den_e) {
args2[i] = m_util.mk_numeral(den_r / g, true); args2[i] = m_util.mk_numeral(den_r / g, true);
break; break;
} }
} }
num = m_util.mk_mul(args1.size(), args1.c_ptr());
num = m_util.mk_mul(args1.size(), args1.c_ptr()); den = m_util.mk_mul(args2.size(), args2.c_ptr());
den = m_util.mk_mul(args2.size(), args2.c_ptr()); result = m_util.mk_idiv(num, den);
result = m_util.mk_idiv(num, den); return true;
return true; }
} return false;
return false; }
}
expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) {
ptr_buffer<expr> 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<expr>& 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<expr>& args) {
for (unsigned i = 0; i < args.size(); ++i) {
if (args[i] == d) {
args[i] = args.back();
args.shrink(args.size()-1);
return;
}
}
UNREACHABLE();
}
expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) {
ptr_buffer<expr> 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<expr>& 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<expr>& 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) { br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & result) {
set_curr_sort(m().get_sort(arg1)); set_curr_sort(m().get_sort(arg1));
numeral v1, v2; numeral v1, v2;

View file

@ -96,10 +96,9 @@ class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
expr * mk_sin_value(rational const & k); expr * mk_sin_value(rational const & k);
app * mk_sqrt(rational const & k); app * mk_sqrt(rational const & k);
bool divides(expr* d, expr* n, expr_ref& result); bool divides(expr* d, expr* n, expr_ref& result);
expr_ref remove_divisor(expr* arg, expr* num, expr* den); expr_ref remove_divisor(expr* arg, expr* num, expr* den);
void flat_mul(expr* e, ptr_buffer<expr>& args); void flat_mul(expr* e, ptr_buffer<expr>& args);
void remove_divisor(expr* d, ptr_buffer<expr>& args); void remove_divisor(expr* d, ptr_buffer<expr>& args);
public: public:
arith_rewriter(ast_manager & m, params_ref const & p = params_ref()): arith_rewriter(ast_manager & m, params_ref const & p = params_ref()):
poly_rewriter<arith_rewriter_core>(m, p) { poly_rewriter<arith_rewriter_core>(m, p) {

View file

@ -85,7 +85,7 @@ void run_solver(lp_params & params, char const * mps_file_name) {
solver->find_maximal_solution(); solver->find_maximal_solution();
*(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; *(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()) { if (params.min()) {
solver->flip_costs(); solver->flip_costs();
} }

View file

@ -40,7 +40,7 @@ def_module_params(module_name='smt',
('bv.reflect', BOOL, True, 'create enode for every bit-vector term'), ('bv.reflect', BOOL, True, 'create enode for every bit-vector term'),
('bv.enable_int2bv', BOOL, True, 'enable support for int2bv and bv2int operators'), ('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.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', 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.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'), ('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters'),

View file

@ -25,11 +25,11 @@ Revision History:
enum arith_solver_id { enum arith_solver_id {
AS_NO_ARITH, // 0 AS_NO_ARITH, // 0
AS_DIFF_LOGIC, // 1 AS_DIFF_LOGIC, // 1
AS_ARITH, // 2 AS_OLD_ARITH, // 2
AS_DENSE_DIFF_LOGIC, // 3 AS_DENSE_DIFF_LOGIC, // 3
AS_UTVPI, // 4 AS_UTVPI, // 4
AS_OPTINF, // 5 AS_OPTINF, // 5
AS_LRA // 6 AS_NEW_ARITH // 6
}; };
enum bound_prop_mode { enum bound_prop_mode {
@ -113,7 +113,7 @@ struct theory_arith_params {
theory_arith_params(params_ref const & p = params_ref()): theory_arith_params(params_ref const & p = params_ref()):
m_arith_eq2ineq(false), m_arith_eq2ineq(false),
m_arith_process_all_eqs(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_auto_config_simplex(false),
m_arith_blands_rule_threshold(1000), m_arith_blands_rule_threshold(1000),
m_arith_propagate_eqs(true), m_arith_propagate_eqs(true),

View file

@ -3763,6 +3763,7 @@ namespace smt {
} }
m_stats.m_num_final_checks++; 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); final_check_status ok = m_qmanager->final_check_eh(false);
if (ok != FC_DONE) if (ok != FC_DONE)

View file

@ -849,7 +849,7 @@ namespace smt {
std::cerr << v << " ::=\n" << mk_ll_pp(n, m_manager) << "<END-OF-FORMULA>\n"; std::cerr << v << " ::=\n" << mk_ll_pp(n, m_manager) << "<END-OF-FORMULA>\n";
} }
#endif #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";); TRACE("mk_var_bug", tout << "mk_bool: " << v << "\n";);
set_bool_var(id, v); set_bool_var(id, v);
m_bdata.reserve(v+1); m_bdata.reserve(v+1);

View file

@ -740,8 +740,12 @@ namespace smt {
} }
void setup::setup_i_arith() { void setup::setup_i_arith() {
// m_context.register_plugin(alloc(smt::theory_lra, 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)); m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params));
}
else {
setup_r_arith();
}
} }
void setup::setup_r_arith() { void setup::setup_r_arith() {
@ -749,14 +753,21 @@ namespace smt {
} }
void setup::setup_mi_arith() { 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)); m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params));
} break;
else { case AS_NEW_ARITH:
setup_r_arith();
break;
default:
m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params));
break;
} }
} }
void setup::setup_arith() { void setup::setup_arith() {
static_features st(m_manager); static_features st(m_manager);
IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";); IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";);
@ -810,15 +821,15 @@ namespace smt {
case AS_OPTINF: case AS_OPTINF:
m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params));
break; break;
case AS_LRA: case AS_OLD_ARITH:
setup_r_arith();
break;
default:
if (m_params.m_arith_int_only && int_only) if (m_params.m_arith_int_only && int_only)
m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params));
else else
m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params));
break; 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.num_theories() == 2 && st.has_uf() && is_arith(st)) {
if (!st.m_has_real) if (!st.m_has_real)
setup_QF_UFLIA(st); 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(); setup_QF_UFLRA();
else else
setup_unknown(); setup_unknown();

View file

@ -42,7 +42,7 @@ Revision History:
namespace smt { namespace smt {
struct theory_arith_stats { 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_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_th2core_eqs, m_th2core_diseqs, m_bound_props, m_offset_eqs, m_fixed_eqs, m_offline_eqs;
unsigned m_max_min; unsigned m_max_min;

View file

@ -1335,7 +1335,7 @@ namespace smt {
} }
} }
}); });
m_stats.m_patches++;
patch_int_infeasible_vars(); patch_int_infeasible_vars();
fix_non_base_vars(); fix_non_base_vars();
@ -1368,6 +1368,7 @@ namespace smt {
theory_var int_var = find_infeasible_int_base_var(); theory_var int_var = find_infeasible_int_base_var();
if (int_var == null_theory_var) { if (int_var == null_theory_var) {
m_stats.m_patches_succ++;
TRACE("arith_int_incomp", tout << "FC_DONE 2...\n"; display(tout);); TRACE("arith_int_incomp", tout << "FC_DONE 2...\n"; display(tout););
return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE;
} }
@ -1385,6 +1386,7 @@ namespace smt {
m_branch_cut_counter++; m_branch_cut_counter++;
// TODO: add giveup code // 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) { if (m_branch_cut_counter % m_params.m_arith_branch_cut_ratio == 0) {
TRACE("opt_verbose", display(tout);); TRACE("opt_verbose", display(tout););
move_non_base_vars_to_bounds(); move_non_base_vars_to_bounds();
@ -1399,7 +1401,7 @@ namespace smt {
SASSERT(is_base(int_var)); SASSERT(is_base(int_var));
row const & r = m_rows[get_var_row(int_var)]; row const & r = m_rows[get_var_row(int_var)];
if (!mk_gomory_cut(r)) { if (!mk_gomory_cut(r)) {
// silent failure TRACE("gomory_cut", tout << "silent failure\n";);
} }
return FC_CONTINUE; return FC_CONTINUE;
} }

View file

@ -38,6 +38,8 @@ namespace smt {
st.update("arith gcd tests", m_stats.m_gcd_tests); st.update("arith gcd tests", m_stats.m_gcd_tests);
st.update("arith ineq splits", m_stats.m_branches); st.update("arith ineq splits", m_stats.m_branches);
st.update("arith gomory cuts", m_stats.m_gomory_cuts); 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 max-min", m_stats.m_max_min);
st.update("arith grobner", m_stats.m_gb_compute_basis); st.update("arith grobner", m_stats.m_gb_compute_basis);
st.update("arith pseudo nonlinear", m_stats.m_nl_linear); st.update("arith pseudo nonlinear", m_stats.m_nl_linear);
@ -389,8 +391,19 @@ namespace smt {
void theory_arith<Ext>::display_vars(std::ostream & out) const { void theory_arith<Ext>::display_vars(std::ostream & out) const {
out << "vars:\n"; out << "vars:\n";
int n = get_num_vars(); int n = get_num_vars();
for (theory_var v = 0; v < n; v++) int inf_vars = 0;
display_var(out, v); 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<typename Ext> template<typename Ext>

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,7 @@ namespace smt {
theory_lra(ast_manager& m, theory_arith_params& ap); theory_lra(ast_manager& m, theory_arith_params& ap);
~theory_lra() override; ~theory_lra() override;
theory* mk_fresh(context* new_ctx) 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; void init(context * ctx) override;
@ -78,6 +78,8 @@ namespace smt {
model_value_proc * mk_value(enode * n, model_generator & mg) override; model_value_proc * mk_value(enode * n, model_generator & mg) override;
bool get_value(enode* n, expr_ref& r) 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; bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const override;

View file

@ -26,6 +26,7 @@ Revision History:
#include "smt/smt_model_generator.h" #include "smt/smt_model_generator.h"
#include "smt/theory_seq.h" #include "smt/theory_seq.h"
#include "smt/theory_arith.h" #include "smt/theory_arith.h"
#include "smt/theory_lra.h"
#include "smt/smt_kernel.h" #include "smt/smt_kernel.h"
using namespace smt; 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); if (tha) return tha->get_value(ctx.get_enode(e), v);
theory_i_arith* thi = get_th_arith<theory_i_arith>(ctx, afid, e); theory_i_arith* thi = get_th_arith<theory_i_arith>(ctx, afid, e);
if (thi) return thi->get_value(ctx.get_enode(e), v); if (thi) return thi->get_value(ctx.get_enode(e), v);
theory_lra* thr = get_th_arith<theory_lra>(ctx, afid, e);
if (thr) return thr->get_value(ctx.get_enode(e), v);
TRACE("seq", tout << "no arithmetic theory\n";); TRACE("seq", tout << "no arithmetic theory\n";);
return false; return false;
} }
@ -4576,12 +4579,18 @@ bool theory_seq::lower_bound(expr* _e, rational& lo) const {
context& ctx = get_context(); context& ctx = get_context();
expr_ref e(m_util.str.mk_length(_e), m); expr_ref e(m_util.str.mk_length(_e), m);
expr_ref _lo(m); expr_ref _lo(m);
theory_mi_arith* tha = get_th_arith<theory_mi_arith>(ctx, m_autil.get_family_id(), e); family_id afid = m_autil.get_family_id();
if (tha && !tha->get_lower(ctx.get_enode(e), _lo)) return false; do {
if (!tha) { theory_mi_arith* tha = get_th_arith<theory_mi_arith>(ctx, afid, e);
theory_i_arith* thi = get_th_arith<theory_i_arith>(ctx, m_autil.get_family_id(), e); if (tha && tha->get_lower(ctx.get_enode(e), _lo)) break;
if (!thi || !thi->get_lower(ctx.get_enode(e), _lo)) return false; theory_i_arith* thi = get_th_arith<theory_i_arith>(ctx, afid, e);
if (thi && thi->get_lower(ctx.get_enode(e), _lo)) break;
theory_lra* thr = get_th_arith<theory_lra>(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(); 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 { bool theory_seq::upper_bound(expr* _e, rational& hi) const {
context& ctx = get_context(); context& ctx = get_context();
expr_ref e(m_util.str.mk_length(_e), m); expr_ref e(m_util.str.mk_length(_e), m);
theory_mi_arith* tha = get_th_arith<theory_mi_arith>(ctx, m_autil.get_family_id(), e); family_id afid = m_autil.get_family_id();
expr_ref _hi(m); expr_ref _hi(m);
if (tha && !tha->get_upper(ctx.get_enode(e), _hi)) return false; do {
if (!tha) { theory_mi_arith* tha = get_th_arith<theory_mi_arith>(ctx, afid, e);
theory_i_arith* thi = get_th_arith<theory_i_arith>(ctx, m_autil.get_family_id(), e); if (tha && tha->get_upper(ctx.get_enode(e), _hi)) break;
if (!thi || !thi->get_upper(ctx.get_enode(e), _hi)) return false; theory_i_arith* thi = get_th_arith<theory_i_arith>(ctx, afid, e);
if (thi && thi->get_upper(ctx.get_enode(e), _hi)) break;
theory_lra* thr = get_th_arith<theory_lra>(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(); return m_autil.is_numeral(_hi, hi) && hi.is_int();
} }

View file

@ -211,17 +211,23 @@ tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p) {
params_ref no_cut_p; params_ref no_cut_p;
no_cut_p.set_uint("arith.branch_cut_ratio", 10000000); no_cut_p.set_uint("arith.branch_cut_ratio", 10000000);
tactic * st = using_params(and_then(preamble_st, tactic * st = using_params(and_then(preamble_st,
#if 0
mk_smt_tactic()),
#else
or_else(mk_ilp_model_finder_tactic(m), or_else(mk_ilp_model_finder_tactic(m),
mk_pb_tactic(m), mk_pb_tactic(m),
and_then(fail_if_not(mk_is_quasi_pb_probe()), and_then(fail_if_not(mk_is_quasi_pb_probe()),
using_params(mk_lia2sat_tactic(m), quasi_pb_p), using_params(mk_lia2sat_tactic(m), quasi_pb_p),
mk_fail_if_undecided_tactic()), mk_fail_if_undecided_tactic()),
mk_bounded_tactic(m), mk_bounded_tactic(m),
mk_psmt_tactic(m, p))), mk_smt_tactic())),
#endif
main_p); main_p);
st->updt_params(p); st->updt_params(p);
return st; return st;

View file

@ -1,4 +1,5 @@
add_subdirectory(fuzzing) add_subdirectory(fuzzing)
add_subdirectory(lp)
################################################################################ ################################################################################
# z3-test executable # z3-test executable
################################################################################ ################################################################################
@ -120,6 +121,7 @@ add_executable(test-z3
upolynomial.cpp upolynomial.cpp
var_subst.cpp var_subst.cpp
vector.cpp vector.cpp
lp/lp.cpp
${z3_test_extra_object_files} ${z3_test_extra_object_files}
) )
z3_add_install_tactic_rule(${z3_test_deps}) z3_add_install_tactic_rule(${z3_test_deps})

View file

@ -0,0 +1,6 @@
add_executable(lp_tst lp_main.cpp lp.cpp $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:polynomial> $<TARGET_OBJECTS:nlsat> $<TARGET_OBJECTS:lp> )
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})

236
src/test/lp/gomory_test.h Normal file
View file

@ -0,0 +1,236 @@
namespace lp {
struct gomory_test {
gomory_test(
std::function<std::string (unsigned)> name_function_p,
std::function<mpq (unsigned)> get_value_p,
std::function<bool (unsigned)> at_low_p,
std::function<bool (unsigned)> at_upper_p,
std::function<impq (unsigned) > lower_bound_p,
std::function<impq (unsigned) > upper_bound_p,
std::function<unsigned (unsigned) > column_lower_bound_constraint_p,
std::function<unsigned (unsigned) > 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<std::string (unsigned)> m_name_function;
std::function<mpq (unsigned)> get_value;
std::function<bool (unsigned)> at_low;
std::function<bool (unsigned)> at_upper;
std::function<impq (unsigned) > lower_bound;
std::function<impq (unsigned) > upper_bound;
std::function<unsigned (unsigned) > column_lower_bound_constraint;
std::function<unsigned (unsigned) > column_upper_bound_constraint;
bool is_real(unsigned) { return false; } // todo: test real case
void print_row(std::ostream& out, vector<std::pair<mpq, unsigned>> & row ) {
bool first = true;
for (const auto & it : row) {
auto val = it.first;
if (first) {
first = false;
} else {
if (numeric_traits<mpq>::is_pos(val)) {
out << " + ";
} else {
out << " - ";
val = -val;
}
}
if (val == -numeric_traits<mpq>::one())
out << " - ";
else if (val != numeric_traits<mpq>::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<mpq>() && f_0 < one_of_type<mpq>());
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<std::pair<mpq, unsigned>> 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<std::pair<mpq, unsigned>> & 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;);
}
};
}

File diff suppressed because it is too large Load diff

14
src/test/lp/lp_main.cpp Normal file
View file

@ -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;
}

View file

@ -121,13 +121,13 @@ namespace lp {
void fill_simple_elem(lisp_elem & lm) { void fill_simple_elem(lisp_elem & lm) {
int separator = first_separator(); int separator = first_separator();
SASSERT(-1 != separator && separator != 0); lp_assert(-1 != separator && separator != 0);
lm.m_head = m_line.substr(0, separator); lm.m_head = m_line.substr(0, separator);
m_line = m_line.substr(separator); m_line = m_line.substr(separator);
} }
void fill_nested_elem(lisp_elem & lm) { void fill_nested_elem(lisp_elem & lm) {
SASSERT(m_line[0] == '('); lp_assert(m_line[0] == '(');
m_line = m_line.substr(1); m_line = m_line.substr(1);
int separator = first_separator(); int separator = first_separator();
lm.m_head = m_line.substr(0, 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*/) { 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) { 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]); set_constraint_coeffs_on_coeff_element(c, el.m_elems[0]);
adjust_rigth_side(c, el.m_elems[1]); adjust_rigth_side(c, el.m_elems[1]);
} }
@ -214,7 +214,7 @@ namespace lp {
add_mult_elem(c, el.m_elems); add_mult_elem(c, el.m_elems);
} else if (el.m_head == "~") { } else if (el.m_head == "~") {
lisp_elem & minel = el.m_elems[0]; 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)); c.m_right_side += mpq(str_to_int(minel.m_head));
} else { } else {
std::cout << "unexpected input " << el.m_head << std::endl; std::cout << "unexpected input " << el.m_head << std::endl;
@ -224,14 +224,14 @@ namespace lp {
} }
std::string get_name(lisp_elem & name) { std::string get_name(lisp_elem & name) {
SASSERT(name.is_simple()); lp_assert(name.is_simple());
SASSERT(!is_integer(name.m_head)); lp_assert(!is_integer(name.m_head));
return name.m_head; return name.m_head;
} }
void add_mult_elem(formula_constraint & c, std::vector<lisp_elem> & els) { void add_mult_elem(formula_constraint & c, std::vector<lisp_elem> & els) {
SASSERT(els.size() == 2); lp_assert(els.size() == 2);
mpq coeff = get_coeff(els[0]); mpq coeff = get_coeff(els[0]);
std::string col_name = get_name(els[1]); std::string col_name = get_name(els[1]);
c.add_pair(coeff, col_name); c.add_pair(coeff, col_name);
@ -241,16 +241,16 @@ namespace lp {
if (le.is_simple()) { if (le.is_simple()) {
return mpq(str_to_int(le.m_head)); return mpq(str_to_int(le.m_head));
} else { } else {
SASSERT(le.m_head == "~"); lp_assert(le.m_head == "~");
SASSERT(le.size() == 1); lp_assert(le.size() == 1);
lisp_elem & el = le.m_elems[0]; lisp_elem & el = le.m_elems[0];
SASSERT(el.is_simple()); lp_assert(el.is_simple());
return -mpq(str_to_int(el.m_head)); return -mpq(str_to_int(el.m_head));
} }
} }
int str_to_int(std::string & s) { int str_to_int(std::string & s) {
SASSERT(is_integer(s)); lp_assert(is_integer(s));
return atoi(s.c_str()); return atoi(s.c_str());
} }
@ -258,7 +258,7 @@ namespace lp {
if (el.size()) { if (el.size()) {
add_complex_sum_elem(c, el); add_complex_sum_elem(c, el);
} else { } else {
SASSERT(is_integer(el.m_head)); lp_assert(is_integer(el.m_head));
int v = atoi(el.m_head.c_str()); int v = atoi(el.m_head.c_str());
mpq vr(v); mpq vr(v);
c.m_right_side -= vr; c.m_right_side -= vr;
@ -276,7 +276,7 @@ namespace lp {
} else if (el.m_head == "+") { } else if (el.m_head == "+") {
add_sum(c, el.m_elems); add_sum(c, el.m_elems);
} else { } 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) { void add_constraint_to_solver(lar_solver * solver, formula_constraint & fc) {
vector<std::pair<mpq, var_index>> ls; vector<std::pair<mpq, var_index>> ls;
for (auto & it : fc.m_coeffs) { 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); solver->add_constraint(ls, fc.m_kind, fc.m_right_side);
} }

View file

@ -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/sparse_matrix_def.h"
#include "math/simplex/simplex.h" #include "math/simplex/simplex.h"
#include "math/simplex/simplex_def.h" #include "math/simplex/simplex_def.h"

View file

@ -1,33 +1,37 @@
z3_add_component(lp z3_add_component(lp
SOURCES SOURCES
lp_utils.cpp lp_utils.cpp
binary_heap_priority_queue_instances.cpp binary_heap_priority_queue.cpp
binary_heap_upair_queue_instances.cpp binary_heap_upair_queue.cpp
lp_bound_propagator.cpp bound_propagator.cpp
core_solver_pretty_printer_instances.cpp core_solver_pretty_printer.cpp
dense_matrix_instances.cpp dense_matrix.cpp
eta_matrix_instances.cpp eta_matrix.cpp
indexed_vector_instances.cpp indexed_vector.cpp
lar_core_solver_instances.cpp int_solver.cpp
lp_core_solver_base_instances.cpp lar_solver.cpp
lp_dual_core_solver_instances.cpp lar_core_solver.cpp
lp_dual_simplex_instances.cpp lp_core_solver_base.cpp
lp_primal_core_solver_instances.cpp lp_dual_core_solver.cpp
lp_primal_simplex_instances.cpp lp_dual_simplex.cpp
lp_settings_instances.cpp lp_primal_core_solver.cpp
lp_solver_instances.cpp lp_primal_simplex.cpp
lu_instances.cpp lp_settings.cpp
matrix_instances.cpp lp_solver.cpp
permutation_matrix_instances.cpp lu.cpp
quick_xplain.cpp matrix.cpp
row_eta_matrix_instances.cpp nra_solver.cpp
scaler_instances.cpp permutation_matrix.cpp
sparse_matrix_instances.cpp row_eta_matrix.cpp
square_dense_submatrix_instances.cpp scaler.cpp
static_matrix_instances.cpp square_sparse_matrix.cpp
random_updater_instances.cpp square_dense_submatrix.cpp
static_matrix.cpp
random_updater.cpp
COMPONENT_DEPENDENCIES COMPONENT_DEPENDENCIES
util util
polynomial
nlsat
PYG_FILES PYG_FILES
lp_params.pyg lp_params.pyg
) )

76
src/util/lp/active_set.h Normal file
View file

@ -0,0 +1,76 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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<constraint*, constraint_hash, constraint_equal> m_cs;
binary_heap_priority_queue<int> m_q;
std::unordered_map<unsigned, constraint *> m_id_to_constraint;
public:
std::unordered_set<constraint*, constraint_hash, constraint_equal> 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<unsigned>(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());
}
};
}

View file

@ -18,7 +18,7 @@ Revision History:
--*/ --*/
#include "util/lp/numeric_pair.h" #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 { namespace lp {
template binary_heap_priority_queue<int>::binary_heap_priority_queue(unsigned int); template binary_heap_priority_queue<int>::binary_heap_priority_queue(unsigned int);
template unsigned binary_heap_priority_queue<int>::dequeue(); template unsigned binary_heap_priority_queue<int>::dequeue();

View file

@ -45,7 +45,7 @@ public:
unsigned size() const { return m_heap_size; } unsigned size() const { return m_heap_size; }
binary_heap_priority_queue(): m_heap(1), m_heap_size(0) {} // the empty constructror binary_heap_priority_queue(): m_heap(1), m_heap_size(0) {} // the empty constructror
// n is the initial queue capacity. // 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); binary_heap_priority_queue(unsigned n);
void clear() { void clear() {
@ -75,7 +75,7 @@ public:
/// return the first element of the queue and removes it from the queue /// return the first element of the queue and removes it from the queue
unsigned dequeue(); unsigned dequeue();
unsigned peek() const { unsigned peek() const {
SASSERT(m_heap_size > 0); lp_assert(m_heap_size > 0);
return m_heap[1]; return m_heap[1];
} }
#ifdef Z3DEBUG #ifdef Z3DEBUG

View file

@ -20,7 +20,7 @@ Revision History:
#include "util/vector.h" #include "util/vector.h"
#include "util/lp/binary_heap_priority_queue.h" #include "util/lp/binary_heap_priority_queue.h"
namespace lp { namespace lp {
// this is the child place in the heap // "i" is the child's place in the heap
template <typename T> void binary_heap_priority_queue<T>::swap_with_parent(unsigned i) { template <typename T> void binary_heap_priority_queue<T>::swap_with_parent(unsigned i) {
unsigned parent = m_heap[i >> 1]; unsigned parent = m_heap[i >> 1];
put_at(i >> 1, m_heap[i]); put_at(i >> 1, m_heap[i]);
@ -48,8 +48,8 @@ template <typename T> void binary_heap_priority_queue<T>::decrease_priority(unsi
template <typename T> bool binary_heap_priority_queue<T>::is_consistent() const { template <typename T> bool binary_heap_priority_queue<T>::is_consistent() const {
for (int i = 0; i < m_heap_inverse.size(); i++) { for (int i = 0; i < m_heap_inverse.size(); i++) {
int i_index = m_heap_inverse[i]; int i_index = m_heap_inverse[i];
SASSERT(i_index <= static_cast<int>(m_heap_size)); lp_assert(i_index <= static_cast<int>(m_heap_size));
SASSERT(i_index == -1 || m_heap[i_index] == i); lp_assert(i_index == -1 || m_heap[i_index] == i);
} }
for (unsigned i = 1; i < m_heap_size; i++) { for (unsigned i = 1; i < m_heap_size; i++) {
unsigned ch = i << 1; unsigned ch = i << 1;
@ -71,7 +71,7 @@ template <typename T> void binary_heap_priority_queue<T>::remove(unsigned o) {
if (o_in_heap == -1) { if (o_in_heap == -1) {
return; // nothing to do return; // nothing to do
} }
SASSERT(static_cast<unsigned>(o_in_heap) <= m_heap_size); lp_assert(static_cast<unsigned>(o_in_heap) <= m_heap_size);
if (static_cast<unsigned>(o_in_heap) < m_heap_size) { if (static_cast<unsigned>(o_in_heap) < m_heap_size) {
put_at(o_in_heap, m_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) { if (m_priorities[m_heap[o_in_heap]] > priority_of_o) {
@ -88,11 +88,11 @@ template <typename T> void binary_heap_priority_queue<T>::remove(unsigned o) {
} }
} }
} else { } else {
SASSERT(static_cast<unsigned>(o_in_heap) == m_heap_size); lp_assert(static_cast<unsigned>(o_in_heap) == m_heap_size);
m_heap_size--; m_heap_size--;
} }
m_heap_inverse[o] = -1; m_heap_inverse[o] = -1;
// SASSERT(is_consistent()); // lp_assert(is_consistent());
} }
// n is the initial queue capacity. // n is the initial queue capacity.
// The capacity will be enlarged two times automatically if needed // The capacity will be enlarged two times automatically if needed
@ -118,7 +118,7 @@ template <typename T> void binary_heap_priority_queue<T>::put_to_heap(unsigned i
template <typename T> void binary_heap_priority_queue<T>::enqueue_new(unsigned o, const T& priority) { template <typename T> void binary_heap_priority_queue<T>::enqueue_new(unsigned o, const T& priority) {
m_heap_size++; m_heap_size++;
int i = m_heap_size; int i = m_heap_size;
SASSERT(o < m_priorities.size()); lp_assert(o < m_priorities.size());
m_priorities[o] = priority; m_priorities[o] = priority;
put_at(i, o); put_at(i, o);
while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) { while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) {
@ -130,8 +130,12 @@ template <typename T> void binary_heap_priority_queue<T>::enqueue_new(unsigned o
// In this case the priority will be changed and the queue adjusted. // In this case the priority will be changed and the queue adjusted.
template <typename T> void binary_heap_priority_queue<T>::enqueue(unsigned o, const T & priority) { template <typename T> void binary_heap_priority_queue<T>::enqueue(unsigned o, const T & priority) {
if (o >= m_priorities.size()) { 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) if (m_heap_inverse[o] == -1)
enqueue_new(o, priority); enqueue_new(o, priority);
else else
@ -150,7 +154,7 @@ template <typename T> void binary_heap_priority_queue<T>::change_priority_for_ex
/// return the first element of the queue and removes it from the queue /// return the first element of the queue and removes it from the queue
template <typename T> unsigned binary_heap_priority_queue<T>::dequeue_and_get_priority(T & priority) { template <typename T> unsigned binary_heap_priority_queue<T>::dequeue_and_get_priority(T & priority) {
SASSERT(m_heap_size != 0); lp_assert(m_heap_size != 0);
int ret = m_heap[1]; int ret = m_heap[1];
priority = m_priorities[ret]; priority = m_priorities[ret];
put_the_last_at_the_top_and_fix_the_heap(); put_the_last_at_the_top_and_fix_the_heap();
@ -184,7 +188,7 @@ template <typename T> void binary_heap_priority_queue<T>::put_the_last_at_the_to
} }
/// return the first element of the queue and removes it from the queue /// return the first element of the queue and removes it from the queue
template <typename T> unsigned binary_heap_priority_queue<T>::dequeue() { template <typename T> unsigned binary_heap_priority_queue<T>::dequeue() {
SASSERT(m_heap_size > 0); lp_assert(m_heap_size > 0);
int ret = m_heap[1]; int ret = m_heap[1];
put_the_last_at_the_top_and_fix_the_heap(); put_the_last_at_the_top_and_fix_the_heap();
m_heap_inverse[ret] = -1; m_heap_inverse[ret] = -1;

View file

@ -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 { namespace lp {
template binary_heap_upair_queue<int>::binary_heap_upair_queue(unsigned int); template binary_heap_upair_queue<int>::binary_heap_upair_queue(unsigned int);
template binary_heap_upair_queue<unsigned int>::binary_heap_upair_queue(unsigned int); template binary_heap_upair_queue<unsigned int>::binary_heap_upair_queue(unsigned int);

View file

@ -29,7 +29,7 @@ template <typename T> binary_heap_upair_queue<T>::binary_heap_upair_queue(unsign
template <typename T> unsigned template <typename T> unsigned
binary_heap_upair_queue<T>::dequeue_available_spot() { binary_heap_upair_queue<T>::dequeue_available_spot() {
SASSERT(m_available_spots.empty() == false); lp_assert(m_available_spots.empty() == false);
unsigned ret = m_available_spots.back(); unsigned ret = m_available_spots.back();
m_available_spots.pop_back(); m_available_spots.pop_back();
return ret; return ret;
@ -69,7 +69,7 @@ template <typename T> void binary_heap_upair_queue<T>::enqueue(unsigned i, unsig
m_pairs.resize(new_size); m_pairs.resize(new_size);
} }
ij_index = dequeue_available_spot(); ij_index = dequeue_available_spot();
// SASSERT(ij_index<m_pairs.size() && ij_index_is_new(ij_index)); // lp_assert(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
m_pairs[ij_index] = p; m_pairs[ij_index] = p;
m_pairs_to_index[p] = ij_index; m_pairs_to_index[p] = ij_index;
} else { } else {
@ -79,7 +79,7 @@ template <typename T> void binary_heap_upair_queue<T>::enqueue(unsigned i, unsig
} }
template <typename T> void binary_heap_upair_queue<T>::dequeue(unsigned & i, unsigned &j) { template <typename T> void binary_heap_upair_queue<T>::dequeue(unsigned & i, unsigned &j) {
SASSERT(!m_q.is_empty()); lp_assert(!m_q.is_empty());
unsigned ij_index = m_q.dequeue(); unsigned ij_index = m_q.dequeue();
upair & p = m_pairs[ij_index]; upair & p = m_pairs[ij_index];
i = p.first; i = p.first;

View file

@ -19,37 +19,93 @@ Revision History:
--*/ --*/
#pragma once #pragma once
#include "util/vector.h" #include "util/vector.h"
#include "util/lp/linear_combination_iterator.h" #include "implied_bound.h"
#include "util/lp/implied_bound.h" #include "test_bound_analyzer.h"
#include "util/lp/test_bound_analyzer.h" #include "util/lp/bound_propagator.h"
#include <functional>
#include "util/lp/lp_bound_propagator.h"
// We have an equality : sum by j of row[j]*x[j] = rs // 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 // 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 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 // In the same loop trying to pin variables by pushing the partial sum up, denoting the variable related to it by _l
namespace lp { namespace lp {
template <typename C> // C plays a role of a container
class bound_analyzer_on_row { 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<mpq> & m_it; //typedefs
lp_bound_propagator & m_bp;
unsigned m_row_or_term_index;
int m_column_of_u; // index of an unlimited from above monoid typedef const_iterator self_type;
// -1 means that such a value is not found, -2 means that at least two of such monoids were found typedef ival value_type;
int m_column_of_l; // index of an unlimited from below monoid typedef ival reference;
impq m_rs; typedef int difference_type;
typedef std::forward_iterator_tag iterator_category;
reference operator*() const {
if (m_bj == static_cast<unsigned>(-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<unsigned>(-1))
m_it++;
else
m_bj = static_cast<unsigned>(-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 : public :
// constructor // constructor
bound_analyzer_on_row( bound_analyzer_on_row(
linear_combination_iterator<mpq> &it, const C & it,
const numeric_pair<mpq>& rs, unsigned bj, // basis column for the row
unsigned row_or_term_index, const numeric_pair<mpq>& rs,
lp_bound_propagator & bp unsigned row_or_term_index,
bound_propagator & bp
) )
: :
m_it(it), m_row(it, bj),
m_bp(bp), m_bp(bp),
m_row_or_term_index(row_or_term_index), m_row_or_term_index(row_or_term_index),
m_column_of_u(-1), m_column_of_u(-1),
@ -60,11 +116,11 @@ public :
unsigned j; unsigned j;
void analyze() { void analyze() {
for (const auto & c : m_row) {
mpq a; unsigned j; if ((m_column_of_l == -2) && (m_column_of_u == -2))
while (((m_column_of_l != -2) || (m_column_of_u != -2)) && m_it.next(a, j)) break;
analyze_bound_on_var_on_coeff(j, a); analyze_bound_on_var_on_coeff(c.var(), c.coeff());
}
if (m_column_of_u >= 0) if (m_column_of_u >= 0)
limit_monoid_u_from_below(); limit_monoid_u_from_below();
else if (m_column_of_u == -1) else if (m_column_of_u == -1)
@ -76,42 +132,42 @@ public :
limit_all_monoids_from_above(); limit_all_monoids_from_above();
} }
bool bound_is_available(unsigned j, bool low_bound) { bool bound_is_available(unsigned j, bool lower_bound) {
return (low_bound && low_bound_is_available(j)) || return (lower_bound && lower_bound_is_available(j)) ||
(!low_bound && upper_bound_is_available(j)); (!lower_bound && upper_bound_is_available(j));
} }
bool upper_bound_is_available(unsigned j) const { bool upper_bound_is_available(unsigned j) const {
switch (m_bp.get_column_type(j)) switch (m_bp.get_column_type(j))
{ {
case column_type::fixed: case column_type::fixed:
case column_type::boxed: case column_type::boxed:
case column_type::upper_bound: case column_type::upper_bound:
return true; return true;
default: default:
return false; 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)) switch (m_bp.get_column_type(j))
{ {
case column_type::fixed: case column_type::fixed:
case column_type::boxed: case column_type::boxed:
case column_type::low_bound: case column_type::lower_bound:
return true; return true;
default: default:
return false; return false;
} }
} }
const impq & ub(unsigned j) const { 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); return m_bp.get_upper_bound(j);
} }
const impq & lb(unsigned j) const { const impq & lb(unsigned j) const {
SASSERT(low_bound_is_available(j)); lp_assert(lower_bound_is_available(j));
return m_bp.get_low_bound(j); return m_bp.get_lower_bound(j);
} }
@ -151,7 +207,7 @@ public :
strict = !is_zero(ub(j).y); strict = !is_zero(ub(j).y);
return a * ub(j).x; return a * ub(j).x;
} }
strict = !is_zero(lb(j).y); strict = !is_zero(lb(j).y);
return a * lb(j).x; return a * lb(j).x;
} }
@ -160,34 +216,32 @@ public :
if (is_neg(a)) { if (is_neg(a)) {
return a * ub(j).x; return a * ub(j).x;
} }
return a * lb(j).x; return a * lb(j).x;
} }
void limit_all_monoids_from_above() { void limit_all_monoids_from_above() {
int strict = 0; int strict = 0;
mpq total; mpq total;
SASSERT(is_zero(total)); lp_assert(is_zero(total));
m_it.reset(); for (const auto& p : m_row) {
mpq a; unsigned j;
while (m_it.next(a, j)) {
bool str; bool str;
total -= monoid_min(a, j, str); total -= monoid_min(p.coeff(), p.var(), str);
if (str) if (str)
strict++; strict++;
} }
m_it.reset();
while (m_it.next(a, j)) { for (const auto &p : m_row) {
bool str; bool str;
bool a_is_pos = is_pos(a); bool a_is_pos = is_pos(p.coeff());
mpq bound = total / a + monoid_min_no_mult(a_is_pos, j, str); mpq bound = total / p.coeff() + monoid_min_no_mult(a_is_pos, p.var(), str);
if (a_is_pos) { if (a_is_pos) {
limit_j(j, bound, true, false, strict - static_cast<int>(str) > 0); limit_j(p.var(), bound, true, false, strict - static_cast<int>(str) > 0);
} }
else { else {
limit_j(j, bound, false, true, strict - static_cast<int>(str) > 0); limit_j(p.var(), bound, false, true, strict - static_cast<int>(str) > 0);
} }
} }
} }
@ -195,52 +249,50 @@ public :
void limit_all_monoids_from_below() { void limit_all_monoids_from_below() {
int strict = 0; int strict = 0;
mpq total; mpq total;
SASSERT(is_zero(total)); lp_assert(is_zero(total));
m_it.reset(); for (const auto &p : m_row) {
mpq a; unsigned j;
while (m_it.next(a, j)) {
bool str; bool str;
total -= monoid_max(a, j, str); total -= monoid_max(p.coeff(), p.var(), str);
if (str) if (str)
strict++; strict++;
} }
m_it.reset();
while (m_it.next(a, j)) { for (const auto& p : m_row) {
bool str; bool str;
bool a_is_pos = is_pos(a); bool a_is_pos = is_pos(p.coeff());
mpq bound = total / a + monoid_max_no_mult(a_is_pos, j, str); mpq bound = total / p.coeff() + monoid_max_no_mult(a_is_pos, p.var(), str);
bool astrict = strict - static_cast<int>(str) > 0; bool astrict = strict - static_cast<int>(str) > 0;
if (a_is_pos) { if (a_is_pos) {
limit_j(j, bound, true, true, astrict); limit_j(p.var(), bound, true, true, astrict);
} }
else { else {
limit_j(j, bound, false, false, astrict); limit_j(p.var(), bound, false, false, astrict);
} }
} }
} }
void limit_monoid_u_from_below() { void limit_monoid_u_from_below() {
// we are going to limit from below the monoid m_column_of_u, // we are going to limit from below the monoid m_column_of_u,
// every other monoid is impossible to limit from below // every other monoid is impossible to limit from below
mpq u_coeff, a; mpq u_coeff;
unsigned j; unsigned j;
mpq bound = -m_rs.x; mpq bound = -m_rs.x;
m_it.reset();
bool strict = false; bool strict = false;
while (m_it.next(a, j)) { for (const auto& p : m_row) {
j = p.var();
if (j == static_cast<unsigned>(m_column_of_u)) { if (j == static_cast<unsigned>(m_column_of_u)) {
u_coeff = a; u_coeff = p.coeff();
continue; continue;
} }
bool str; bool str;
bound -= monoid_max(a, j, str); bound -= monoid_max(p.coeff(), j, str);
if (str) if (str)
strict = true; strict = true;
} }
bound /= u_coeff; bound /= u_coeff;
if (numeric_traits<impq>::is_pos(u_coeff)) { if (numeric_traits<impq>::is_pos(u_coeff)) {
limit_j(m_column_of_u, bound, true, true, strict); limit_j(m_column_of_u, bound, true, true, strict);
} else { } else {
@ -252,19 +304,19 @@ public :
void limit_monoid_l_from_above() { void limit_monoid_l_from_above() {
// we are going to limit from above the monoid m_column_of_l, // we are going to limit from above the monoid m_column_of_l,
// every other monoid is impossible to limit from above // every other monoid is impossible to limit from above
mpq l_coeff, a; mpq l_coeff;
unsigned j; unsigned j;
mpq bound = -m_rs.x; mpq bound = -m_rs.x;
bool strict = false; bool strict = false;
m_it.reset(); for (const auto &p : m_row) {
while (m_it.next(a, j)) { j = p.var();
if (j == static_cast<unsigned>(m_column_of_l)) { if (j == static_cast<unsigned>(m_column_of_l)) {
l_coeff = a; l_coeff = p.coeff();
continue; continue;
} }
bool str; bool str;
bound -= monoid_min(a, j, str); bound -= monoid_min(p.coeff(), j, str);
if (str) if (str)
strict = true; strict = true;
} }
@ -275,51 +327,51 @@ public :
limit_j(m_column_of_l, bound, false, true, strict); limit_j(m_column_of_l, bound, false, true, strict);
} }
} }
// // it is the coefficent before the bounded column // // it is the coefficent before the bounded column
// void provide_evidence(bool coeff_is_pos) { // void provide_evidence(bool coeff_is_pos) {
// /* // /*
// auto & be = m_ibounds.back(); // auto & be = m_ibounds.back();
// bool low_bound = be.m_low_bound; // bool lower_bound = be.m_lower_bound;
// if (!coeff_is_pos) // if (!coeff_is_pos)
// low_bound = !low_bound; // lower_bound = !lower_bound;
// auto it = m_it.clone(); // auto it = m_row.clone();
// mpq a; unsigned j; // mpq a; unsigned j;
// while (it->next(a, j)) { // while (it->next(a, j)) {
// if (be.m_j == j) continue; // 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<impq>:: // be.m_vector_of_bound_signatures.emplace_back(a, j, numeric_traits<impq>::
// is_neg(a)? low_bound: !low_bound); // is_neg(a)? lower_bound: !lower_bound);
// } // }
// delete it; // delete it;
// */ // */
// } // }
void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_low_bound, bool 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_low_bound, coeff_before_j_is_pos, m_row_or_term_index, 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) { void advance_u(unsigned j) {
if (m_column_of_u == -1) if (m_column_of_u == -1)
m_column_of_u = j; m_column_of_u = j;
else else
m_column_of_u = -2; m_column_of_u = -2;
} }
void advance_l(unsigned j) { void advance_l(unsigned j) {
if (m_column_of_l == -1) if (m_column_of_l == -1)
m_column_of_l = j; m_column_of_l = j;
else else
m_column_of_l = -2; m_column_of_l = -2;
} }
void analyze_bound_on_var_on_coeff(int j, const mpq &a) { void analyze_bound_on_var_on_coeff(int j, const mpq &a) {
switch (m_bp.get_column_type(j)) { switch (m_bp.get_column_type(j)) {
case column_type::low_bound: case column_type::lower_bound:
if (numeric_traits<mpq>::is_pos(a)) if (numeric_traits<mpq>::is_pos(a))
advance_u(j); advance_u(j);
else else
advance_l(j); advance_l(j);
break; break;
case column_type::upper_bound: case column_type::upper_bound:
@ -337,14 +389,16 @@ public :
} }
} }
static void analyze_row(linear_combination_iterator<mpq> &it, static void analyze_row(const C & row,
unsigned bj, // basis column for the row
const numeric_pair<mpq>& rs, const numeric_pair<mpq>& rs,
unsigned row_or_term_index, 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(); a.analyze();
} }
}; };
} }

View file

@ -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<lconstraint_kind>(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););
}
}
}
}

View file

@ -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<unsigned, unsigned> m_improved_lower_bounds; // these maps map a column index to the corresponding index in ibounds
std::unordered_map<unsigned, unsigned> m_improved_upper_bounds;
lar_solver & m_lar_solver;
public:
vector<implied_bound> 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;
};
}

View file

@ -30,11 +30,11 @@ inline bool is_valid(unsigned j) { return static_cast<int>(j) >= 0;}
template <typename T> template <typename T>
class column_info { class column_info {
std::string m_name; std::string m_name;
bool m_low_bound_is_set; bool m_lower_bound_is_set;
bool m_low_bound_is_strict; bool m_lower_bound_is_strict;
bool m_upper_bound_is_set; bool m_upper_bound_is_set;
bool m_upper_bound_is_strict; bool m_upper_bound_is_strict;
T m_low_bound; T m_lower_bound;
T m_upper_bound; T m_upper_bound;
T m_fixed_value; T m_fixed_value;
bool m_is_fixed; bool m_is_fixed;
@ -43,11 +43,11 @@ class column_info {
public: public:
bool operator==(const column_info & c) const { bool operator==(const column_info & c) const {
return m_name == c.m_name && return m_name == c.m_name &&
m_low_bound_is_set == c.m_low_bound_is_set && m_lower_bound_is_set == c.m_lower_bound_is_set &&
m_low_bound_is_strict == c.m_low_bound_is_strict && 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_set == c.m_upper_bound_is_set&&
m_upper_bound_is_strict == c.m_upper_bound_is_strict&& 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_upper_bound_is_set || m_upper_bound == c.m_upper_bound) &&
m_cost == c.m_cost && m_cost == c.m_cost &&
m_is_fixed == c.m_is_fixed && m_is_fixed == c.m_is_fixed &&
@ -60,8 +60,8 @@ public:
} }
// the default constructor // the default constructor
column_info(): column_info():
m_low_bound_is_set(false), m_lower_bound_is_set(false),
m_low_bound_is_strict(false), m_lower_bound_is_strict(false),
m_upper_bound_is_set (false), m_upper_bound_is_set (false),
m_upper_bound_is_strict (false), m_upper_bound_is_strict (false),
m_is_fixed(false), m_is_fixed(false),
@ -70,8 +70,8 @@ public:
{} {}
column_info(unsigned column_index) : column_info(unsigned column_index) :
m_low_bound_is_set(false), m_lower_bound_is_set(false),
m_low_bound_is_strict(false), m_lower_bound_is_strict(false),
m_upper_bound_is_set (false), m_upper_bound_is_set (false),
m_upper_bound_is_strict (false), m_upper_bound_is_strict (false),
m_is_fixed(false), m_is_fixed(false),
@ -81,11 +81,11 @@ public:
column_info(const column_info & ci) { column_info(const column_info & ci) {
m_name = ci.m_name; m_name = ci.m_name;
m_low_bound_is_set = ci.m_low_bound_is_set; m_lower_bound_is_set = ci.m_lower_bound_is_set;
m_low_bound_is_strict = ci.m_low_bound_is_strict; 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_set = ci.m_upper_bound_is_set;
m_upper_bound_is_strict = ci.m_upper_bound_is_strict; 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_upper_bound = ci.m_upper_bound;
m_cost = ci.m_cost; m_cost = ci.m_cost;
m_fixed_value = ci.m_fixed_value; m_fixed_value = ci.m_fixed_value;
@ -98,7 +98,7 @@ public:
} }
column_type get_column_type() const { 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 { column_type get_column_type_no_flipping() const {
@ -106,25 +106,25 @@ public:
return column_type::fixed; return column_type::fixed;
} }
if (m_low_bound_is_set) { if (m_lower_bound_is_set) {
return m_upper_bound_is_set? column_type::boxed: column_type::low_bound; return m_upper_bound_is_set? column_type::boxed: column_type::lower_bound;
} }
// we are flipping the bounds! // we are flipping the bounds!
return m_upper_bound_is_set? column_type::upper_bound return m_upper_bound_is_set? column_type::upper_bound
: column_type::free_column; : column_type::free_column;
} }
T get_low_bound() const { T get_lower_bound() const {
SASSERT(m_low_bound_is_set); lp_assert(m_lower_bound_is_set);
return m_low_bound; return m_lower_bound;
} }
T get_upper_bound() const { T get_upper_bound() const {
SASSERT(m_upper_bound_is_set); lp_assert(m_upper_bound_is_set);
return m_upper_bound; return m_upper_bound;
} }
bool low_bound_is_set() const { bool lower_bound_is_set() const {
return m_low_bound_is_set; return m_lower_bound_is_set;
} }
bool upper_bound_is_set() const { bool upper_bound_is_set() const {
@ -138,23 +138,23 @@ public:
if (is_flipped()){ if (is_flipped()){
return m_upper_bound; return m_upper_bound;
} }
return m_low_bound_is_set? m_low_bound : numeric_traits<T>::zero(); return m_lower_bound_is_set? m_lower_bound : numeric_traits<T>::zero();
} }
bool is_flipped() { 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() { bool adjusted_lower_bound_is_set() {
return !is_flipped()? low_bound_is_set(): upper_bound_is_set(); return !is_flipped()? lower_bound_is_set(): upper_bound_is_set();
} }
bool adjusted_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() { T get_adjusted_upper_bound() {
return get_upper_bound() - get_low_bound(); return get_upper_bound() - get_lower_bound();
} }
bool is_fixed() const { bool is_fixed() const {
@ -162,7 +162,7 @@ public:
} }
bool is_free() { 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) { void set_fixed_value(T v) {
@ -171,7 +171,7 @@ public:
} }
T get_fixed_value() const { T get_fixed_value() const {
SASSERT(m_is_fixed); lp_assert(m_is_fixed);
return m_fixed_value; return m_fixed_value;
} }
@ -191,9 +191,9 @@ public:
return m_name; return m_name;
} }
void set_low_bound(T const & l) { void set_lower_bound(T const & l) {
m_low_bound = l; m_lower_bound = l;
m_low_bound_is_set = true; m_lower_bound_is_set = true;
} }
void set_upper_bound(T const & l) { void set_upper_bound(T const & l) {
@ -201,8 +201,8 @@ public:
m_upper_bound_is_set = true; m_upper_bound_is_set = true;
} }
void unset_low_bound() { void unset_lower_bound() {
m_low_bound_is_set = false; m_lower_bound_is_set = false;
} }
void unset_upper_bound() { void unset_upper_bound() {
@ -213,8 +213,8 @@ public:
m_is_fixed = false; m_is_fixed = false;
} }
bool low_bound_holds(T v) { bool lower_bound_holds(T v) {
return !low_bound_is_set() || v >= m_low_bound -T(0.0000001); return !lower_bound_is_set() || v >= m_lower_bound -T(0.0000001);
} }
bool upper_bound_holds(T v) { bool upper_bound_holds(T v) {
@ -222,36 +222,36 @@ public:
} }
bool bounds_hold(T v) { 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) { 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) { bool adjusted_lower_bound_holds(T v) {
return !adjusted_low_bound_is_set() || v >= -T(0.0000001); return !adjusted_lower_bound_is_set() || v >= -T(0.0000001);
} }
bool adjusted_upper_bound_holds(T v) { bool adjusted_upper_bound_holds(T v) {
return !adjusted_upper_bound_is_set() || v <= get_adjusted_upper_bound() + T(0.000001); return !adjusted_upper_bound_is_set() || v <= get_adjusted_upper_bound() + T(0.000001);
} }
bool is_infeasible() { bool is_infeasible() {
if ((!upper_bound_is_set()) || (!low_bound_is_set())) if ((!upper_bound_is_set()) || (!lower_bound_is_set()))
return false; return false;
// ok, both bounds are set // 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) 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 // 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 { bool lower_bound_is_strict() const {
return m_low_bound_is_strict; return m_lower_bound_is_strict;
} }
void set_low_bound_strict(bool val) { void set_lower_bound_strict(bool val) {
m_low_bound_is_strict = val; m_lower_bound_is_strict = val;
} }
bool upper_bound_is_strict() const { bool upper_bound_is_strict() const {

View file

@ -19,31 +19,19 @@ Revision History:
--*/ --*/
#include <string> #include <string>
#include "util/lp/linear_combination_iterator.h" #include "util/lp/static_matrix.h"
namespace lp { namespace lp {
class column_namer { class column_namer {
public: public:
virtual std::string get_column_name(unsigned j) const = 0; virtual std::string get_column_name(unsigned j) const = 0;
template <typename T> template <typename T>
void print_linear_iterator(linear_combination_iterator<T>* it, std::ostream & out) const { void print_row(const row_strip<T> & row, std::ostream & out) const {
vector<std::pair<T, unsigned>> coeff; vector<std::pair<T, unsigned>> coeff;
T a; for (auto & p : row) {
unsigned i; coeff.push_back(std::make_pair(p.coeff(), p.var()));
while (it->next(a, i)) {
coeff.push_back(std::make_pair(a, i));
} }
print_linear_combination_of_column_indices(coeff, out); print_linear_combination_of_column_indices(coeff, out);
} }
template <typename T>
void print_linear_iterator_indices_only(linear_combination_iterator<T>* it, std::ostream & out) const {
vector<std::pair<T, unsigned>> 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 <typename T> template <typename T>
void print_linear_combination_of_column_indices_only(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const { void print_linear_combination_of_column_indices_only(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const {
@ -65,10 +53,34 @@ public:
else if (val != numeric_traits<T>::one()) else if (val != numeric_traits<T>::one())
out << T_to_string(val); out << T_to_string(val);
out << "_" << it.second; out << "v" << it.second;
} }
} }
template <typename T>
void print_linear_combination_of_column_indices_std(const vector<std::pair<T, unsigned>> & 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<T>::is_pos(val)) {
out << " + ";
} else {
out << " - ";
val = -val;
}
}
if (val == -numeric_traits<T>::one())
out << " - ";
else if (val != numeric_traits<T>::one())
out << val;
out << get_column_name(it.second);
}
}
template <typename T> template <typename T>
void print_linear_combination_of_column_indices(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const { void print_linear_combination_of_column_indices(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const {
bool first = true; bool first = true;

99
src/util/lp/constraint.h Normal file
View file

@ -0,0 +1,99 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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<constraint_index> 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<constraint_index> & assert_origins() { return m_assert_origins;}
const std::unordered_set<constraint_index> & 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<constraint_index>& 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<monomial>& 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); }
};
}

View file

@ -22,8 +22,8 @@ Revision History:
namespace lp { namespace lp {
template <typename V> template <typename V>
struct conversion_helper { struct conversion_helper {
static V get_low_bound(const column_info<mpq> & ci) { static V get_lower_bound(const column_info<mpq> & ci) {
return V(ci.get_low_bound(), ci.low_bound_is_strict()? 1 : 0); return V(ci.get_lower_bound(), ci.lower_bound_is_strict()? 1 : 0);
} }
static V get_upper_bound(const column_info<mpq> & ci) { static V get_upper_bound(const column_info<mpq> & ci) {
@ -37,20 +37,20 @@ struct conversion_helper <double> {
if (!ci.upper_bound_is_strict()) if (!ci.upper_bound_is_strict())
return ci.get_upper_bound().get_double(); return ci.get_upper_bound().get_double();
double eps = 0.00001; double eps = 0.00001;
if (!ci.low_bound_is_set()) if (!ci.lower_bound_is_set())
return ci.get_upper_bound().get_double() - eps; 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; return ci.get_upper_bound().get_double() - eps;
} }
static double get_low_bound(const column_info<mpq> & ci) { static double get_lower_bound(const column_info<mpq> & ci) {
if (!ci.low_bound_is_strict()) if (!ci.lower_bound_is_strict())
return ci.get_low_bound().get_double(); return ci.get_lower_bound().get_double();
double eps = 0.00001; double eps = 0.00001;
if (!ci.upper_bound_is_set()) if (!ci.upper_bound_is_set())
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_low_bound()).get_double() / 1000, eps); eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps);
return ci.get_low_bound().get_double() + eps; return ci.get_lower_bound().get_double() + eps;
} }
}; };

View file

@ -18,7 +18,7 @@ Revision History:
--*/ --*/
#include "util/lp/numeric_pair.h" #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<double, double>::core_solver_pretty_printer(lp::lp_core_solver_base<double, double> &, std::ostream & out); template lp::core_solver_pretty_printer<double, double>::core_solver_pretty_printer(lp::lp_core_solver_base<double, double> &, std::ostream & out);
template void lp::core_solver_pretty_printer<double, double>::print(); template void lp::core_solver_pretty_printer<double, double>::print();
template lp::core_solver_pretty_printer<double, double>::~core_solver_pretty_printer(); template lp::core_solver_pretty_printer<double, double>::~core_solver_pretty_printer();

View file

@ -48,7 +48,7 @@ class core_solver_pretty_printer {
std::string m_cost_title; std::string m_cost_title;
std::string m_basis_heading_title; std::string m_basis_heading_title;
std::string m_x_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_upp_bounds_title;
std::string m_exact_norm_title; std::string m_exact_norm_title;
std::string m_approx_norm_title; std::string m_approx_norm_title;
@ -75,7 +75,7 @@ public:
void init_column_widths(); 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_upper_bound(unsigned column, unsigned & w);
void adjust_width_with_bounds(unsigned column, unsigned & w); void adjust_width_with_bounds(unsigned column, unsigned & w);
@ -97,7 +97,7 @@ public:
void print_x(); 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); std::string get_upp_bound_string(unsigned j);

View file

@ -38,7 +38,7 @@ core_solver_pretty_printer<T, X>::core_solver_pretty_printer(lp_core_solver_base
m_rs(ncols(), zero_of_type<X>()), m_rs(ncols(), zero_of_type<X>()),
m_w_buff(core_solver.m_w), m_w_buff(core_solver.m_w),
m_ed_buff(core_solver.m_ed) { m_ed_buff(core_solver.m_ed) {
m_low_bounds_title = "low"; m_lower_bounds_title = "low";
m_upp_bounds_title = "upp"; m_upp_bounds_title = "upp";
m_exact_norm_title = "exact cn"; m_exact_norm_title = "exact cn";
m_approx_norm_title = "approx cn"; m_approx_norm_title = "approx cn";
@ -105,6 +105,8 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_m_
string name = m_core_solver.column_name(column); string name = m_core_solver.column_name(column);
for (unsigned row = 0; row < nrows(); row ++) { for (unsigned row = 0; row < nrows(); row ++) {
m_A[row].resize(ncols(), "");
m_signs[row].resize(ncols(),"");
set_coeff( set_coeff(
m_A[row], m_A[row],
m_signs[row], m_signs[row],
@ -139,9 +141,9 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_co
} }
} }
template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_low_bound(unsigned column, unsigned & w) { template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_lower_bound(unsigned column, unsigned & w) {
if (!m_core_solver.low_bounds_are_set()) return; if (!m_core_solver.lower_bounds_are_set()) return;
w = std::max(w, (unsigned)T_to_string(m_core_solver.low_bound_value(column)).size()); w = std::max(w, (unsigned)T_to_string(m_core_solver.lower_bound_value(column)).size());
} }
template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_upper_bound(unsigned column, unsigned & w) { template <typename T, typename X> void core_solver_pretty_printer<T, X>::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()); w = std::max(w, (unsigned)T_to_string(m_core_solver.upper_bound_value(column)).size());
@ -151,11 +153,11 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_
switch (m_core_solver.get_column_type(column)) { switch (m_core_solver.get_column_type(column)) {
case column_type::fixed: case column_type::fixed:
case column_type::boxed: 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); adjust_width_with_upper_bound(column, w);
break; break;
case column_type::low_bound: case column_type::lower_bound:
adjust_width_with_low_bound(column, w); adjust_width_with_lower_bound(column, w);
break; break;
case column_type::upper_bound: case column_type::upper_bound:
adjust_width_with_upper_bound(column, w); adjust_width_with_upper_bound(column, w);
@ -163,7 +165,7 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_
case column_type::free_column: case column_type::free_column:
break; break;
default: default:
SASSERT(false); lp_assert(false);
break; break;
} }
} }
@ -236,13 +238,13 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_x
m_out << std::endl; m_out << std::endl;
} }
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_low_bound_string(unsigned j) { template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_lower_bound_string(unsigned j) {
switch (m_core_solver.get_column_type(j)){ switch (m_core_solver.get_column_type(j)){
case column_type::boxed: case column_type::boxed:
case column_type::low_bound: case column_type::lower_bound:
case column_type::fixed: case column_type::fixed:
if (m_core_solver.low_bounds_are_set()) if (m_core_solver.lower_bounds_are_set())
return T_to_string(m_core_solver.low_bound_value(j)); return T_to_string(m_core_solver.lower_bound_value(j));
else else
return std::string("0"); return std::string("0");
break; break;
@ -268,12 +270,12 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_l
if (ncols() == 0) { if (ncols() == 0) {
return; return;
} }
int blanks = m_title_width + 1 - static_cast<unsigned>(m_low_bounds_title.size()); int blanks = m_title_width + 1 - static_cast<unsigned>(m_lower_bounds_title.size());
m_out << m_low_bounds_title; m_out << m_lower_bounds_title;
print_blanks(blanks, m_out); print_blanks(blanks, m_out);
for (unsigned i = 0; i < ncols(); i++) { 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<unsigned>(s.size()); int blanks = m_column_widths[i] - static_cast<unsigned>(s.size());
print_blanks(blanks, m_out); print_blanks(blanks, m_out);
m_out << s << " "; // the column interval m_out << s << " "; // the column interval
@ -372,7 +374,7 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_g
unsigned width = m_column_widths[col]; unsigned width = m_column_widths[col];
string s = row[col]; string s = row[col];
int number_of_blanks = width - static_cast<unsigned>(s.size()); int number_of_blanks = width - static_cast<unsigned>(s.size());
SASSERT(number_of_blanks >= 0); lp_assert(number_of_blanks >= 0);
print_blanks(number_of_blanks, m_out); print_blanks(number_of_blanks, m_out);
m_out << s << ' '; m_out << s << ' ';
if (col < row.size() - 1) { if (col < row.size() - 1) {
@ -383,7 +385,7 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_g
string rs = T_to_string(rst); string rs = T_to_string(rst);
int nb = m_rs_width - static_cast<int>(rs.size()); int nb = m_rs_width - static_cast<int>(rs.size());
SASSERT(nb >= 0); lp_assert(nb >= 0);
print_blanks(nb + 1, m_out); print_blanks(nb + 1, m_out);
m_out << rs << std::endl; m_out << rs << std::endl;
} }

View file

@ -18,7 +18,7 @@ Revision History:
--*/ --*/
#include "util/lp/lp_settings.h" #include "util/lp/lp_settings.h"
#include "util/lp/dense_matrix.hpp" #include "util/lp/dense_matrix_def.h"
#ifdef Z3DEBUG #ifdef Z3DEBUG
#include "util/vector.h" #include "util/vector.h"
template lp::dense_matrix<double, double> lp::operator*<double, double>(lp::matrix<double, double>&, lp::matrix<double, double>&); template lp::dense_matrix<double, double> lp::operator*<double, double>(lp::matrix<double, double>&, lp::matrix<double, double>&);

View file

@ -46,7 +46,7 @@ public:
dense_matrix(unsigned m, unsigned n); dense_matrix(unsigned m, unsigned n);
dense_matrix operator*=(matrix<T, X> const & a) { dense_matrix operator*=(matrix<T, X> const & a) {
SASSERT(column_count() == a.row_count()); lp_assert(column_count() == a.row_count());
dense_matrix c(row_count(), a.column_count()); dense_matrix c(row_count(), a.column_count());
for (unsigned i = 0; i < row_count(); i++) { for (unsigned i = 0; i < row_count(); i++) {
for (unsigned j = 0; j < a.column_count(); j++) { for (unsigned j = 0; j < a.column_count(); j++) {
@ -100,7 +100,7 @@ public:
void swap_rows(unsigned a, unsigned b); void swap_rows(unsigned a, unsigned b);
void multiply_row_by_constant(unsigned row, T & t); void multiply_row_by_constant(unsigned row, T & t);
}; };
template <typename T, typename X> template <typename T, typename X>
dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b); dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b);

View file

@ -23,7 +23,6 @@ Revision History:
#include "util/lp/numeric_pair.h" #include "util/lp/numeric_pair.h"
#include "util/lp/dense_matrix.h" #include "util/lp/dense_matrix.h"
namespace lp { namespace lp {
template <typename T> void print_vector(const vector<T> & t, std::ostream & out);
template <typename T, typename X> dense_matrix<T, X>::dense_matrix(unsigned m, unsigned n) : m_m(m), m_n(n), m_values(m * n, numeric_traits<T>::zero()) { template <typename T, typename X> dense_matrix<T, X>::dense_matrix(unsigned m, unsigned n) : m_m(m), m_n(n), m_values(m * n, numeric_traits<T>::zero()) {
} }
@ -185,7 +184,7 @@ template <typename T, typename X> void dense_matrix<T, X>::multiply_row_by_const
template <typename T, typename X> template <typename T, typename X>
dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b){ dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b){
SASSERT(a.column_count() == b.row_count()); lp_assert(a.column_count() == b.row_count());
dense_matrix<T, X> ret(a.row_count(), b.column_count()); dense_matrix<T, X> ret(a.row_count(), b.column_count());
for (unsigned i = 0; i < ret.m_m; i++) for (unsigned i = 0; i < ret.m_m; i++)
for (unsigned j = 0; j< ret.m_n; j++) { for (unsigned j = 0; j< ret.m_n; j++) {

View file

@ -1,334 +0,0 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include <map>
namespace lp {
// represents the set of disjoint intervals of integer number
struct disjoint_intervals {
std::map<int, short> 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<int, short>::iterator & it) const {
return is_start(it->second);
}
bool is_start(const std::map<int, short>::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<int, short>::iterator & it) const {
return is_end(it->second);
}
bool is_end(const std::map<int, short>::reverse_iterator & it) const {
return is_end(it->second);
}
int pos(const std::map<int, short>::iterator & it) const {
return it->first;
}
int pos(const std::map<int, short>::reverse_iterator & it) const {
return it->first;
}
int bound_kind(const std::map<int, short>::iterator & it) const {
return it->second;
}
int bound_kind(const std::map<int, short>::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<int, short>::iterator & it) const {
return is_proper_end(it->second);
}
bool is_proper_end(const std::map<int, short>::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<int, short>::iterator & it) const {
return is_one_point_interval(it->second);
}
bool is_one_point_interval(const std::map<int, short>::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";
}
};
}

View file

@ -20,7 +20,7 @@ Revision History:
#include <memory> #include <memory>
#include "util/vector.h" #include "util/vector.h"
#include "util/lp/numeric_pair.h" #include "util/lp/numeric_pair.h"
#include "util/lp/eta_matrix.hpp" #include "util/lp/eta_matrix_def.h"
#ifdef Z3DEBUG #ifdef Z3DEBUG
template double lp::eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const; template double lp::eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
template lp::mpq lp::eta_matrix<lp::mpq, lp::mpq>::get_elem(unsigned int, unsigned int) const; template lp::mpq lp::eta_matrix<lp::mpq, lp::mpq>::get_elem(unsigned int, unsigned int) const;

View file

@ -76,7 +76,7 @@ public:
void push_back(unsigned row_index, T val ) { 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); m_column_vector.push_back(row_index, val);
} }

View file

@ -75,7 +75,7 @@ void eta_matrix<T, X>::apply_from_right(vector<T> & w) {
} }
w[m_column_index] = t; w[m_column_index] = t;
#ifdef Z3DEBUG #ifdef Z3DEBUG
// SASSERT(vectors_are_equal<T>(clone_w, w, get_number_of_rows())); // lp_assert(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
// delete clone_w; // delete clone_w;
#endif #endif
} }
@ -115,8 +115,8 @@ void eta_matrix<T, X>::apply_from_right(indexed_vector<T> & w) {
} }
#ifdef Z3DEBUG #ifdef Z3DEBUG
// SASSERT(w.is_OK()); // lp_assert(w.is_OK());
// SASSERT(vectors_are_equal<T>(wcopy, w.m_data)); // lp_assert(vectors_are_equal<T>(wcopy, w.m_data));
#endif #endif
} }
#ifdef Z3DEBUG #ifdef Z3DEBUG
@ -145,7 +145,7 @@ void eta_matrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & p) {
pair.first = p.get_rev(pair.first); pair.first = p.get_rev(pair.first);
} }
#ifdef Z3DEBUG #ifdef Z3DEBUG
// SASSERT(deb == *this); // lp_assert(deb == *this);
#endif #endif
} }
} }

32
src/util/lp/explanation.h Normal file
View file

@ -0,0 +1,32 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
struct explanation {
void clear() { m_explanation.clear(); }
vector<std::pair<mpq, constraint_index>> 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<mpq>(), j));
}
};
}

View file

@ -0,0 +1,259 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include <functional>
namespace lp {
class general_matrix {
// fields
permutation_matrix<mpq, mpq> m_row_permutation;
permutation_matrix<mpq, mpq> m_column_permutation;
vector<vector<mpq>> m_data;
public:
unsigned adjust_row(unsigned row) const{
return m_row_permutation[row];
}
void push_row(vector<mpq> & 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<mpq>& m_row_data;
public:
ref_row(general_matrix& m, vector<mpq>& 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<mpq>& m_row_data;
public:
ref_row_const(const general_matrix& m, const vector<mpq>& 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<mpq>(g.m_data, out, blanks);
}
void print(std::ostream & out, const char * ss) const {
std::string s(ss);
out << s;
print(out, static_cast<unsigned>(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<mpq>(m.m_data, out, blanks);
}
#endif
void clear() { m_data.clear(); }
bool row_is_initialized_correctly(const vector<mpq>& row) {
lp_assert(row.size() == column_count());
for (unsigned j = 0; j < row.size(); j ++)
lp_assert(is_zero(row[j]));
return true;
}
template <typename T>
void init_row_from_container(int i, const T & c, std::function<unsigned (unsigned)> 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<mpq> &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<mpq> operator*(const vector<mpq> & x) const {
vector<mpq> 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<mpq>& 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<mpq>& x, const vector<mpq>& 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<unsigned>& basis_rows) {
if (basis_rows.size() == row_count()) return;
vector<vector<mpq>> 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<mpq> operator*(const vector<mpq> & f, const general_matrix& a) {
vector<mpq> r(a.column_count());
for (unsigned j = 0; j < a.column_count(); j ++) {
mpq t = zero_of_type<mpq>();
for (unsigned i = 0; i < a.row_count(); i++) {
t += f[i] * a[i][j];
}
r[j] = t;
}
return r;
}
};
}

View file

@ -1,54 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include <utility>
#include <functional>
#include "util/numerics/mpq.h"
#ifdef __CLANG__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif
namespace std {
template<>
struct hash<lp::mpq> {
inline size_t operator()(const lp::mpq & v) const {
return v.hash();
}
};
}
template <class T>
inline void hash_combine(std::size_t & seed, const T & v) {
seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
namespace std {
template<typename S, typename T> struct hash<pair<S, T>> {
inline size_t operator()(const pair<S, T> & 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

623
src/util/lp/hnf.h Normal file
View file

@ -0,0 +1,623 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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<mpq>();
v = one_of_type<mpq>();
d = b;
return;
}
if (is_zero(b)) {
u = one_of_type<mpq>();
v = zero_of_type<mpq>();
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<mpq>();
v = zero_of_type<mpq>();
return;
}
if (d == -a) {
u = - one_of_type<mpq>();
v = zero_of_type<mpq>();
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<mpq>();
}
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<mpq>());
if (is_pos(a)) {
u += (k + 1) * (b / d);
lp_assert( one_of_type<mpq>() <= u && u <= abs(b)/d);
} else {
u -= (k + 1) * (b / d);
lp_assert( one_of_type<mpq>() <= -u && -u <= abs(b)/d);
}
} else {
v = r; // v -= k * a_over_d;
lp_assert(- a_over_d < -v && -v <= zero_of_type<mpq>());
if (is_pos(a)) {
u += k * (b / d);
lp_assert( one_of_type<mpq>() <= u && u <= abs(b)/d);
} else {
u -= k * (b / d);
lp_assert( one_of_type<mpq>() <= -u && -u <= abs(b)/d);
}
}
lp_assert(d == u * a + v * b);
}
template <typename M>
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 <typename M>
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 <typename M>
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 <typename M>
mpq gcd_of_row_starting_from_diagonal(const M& m, unsigned i) {
mpq g = zero_of_type<mpq>();
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 <typename M>
mpq determinant_of_rectangular_matrix(const M& m, svector<unsigned> & 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<mpq>();
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 <typename M> // 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<mpq> 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<mpq>();
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<mpq>();
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<mpq>() != 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<mpq>();
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; }
};
}

232
src/util/lp/hnf_cutter.h Normal file
View file

@ -0,0 +1,232 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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<const lar_term*> m_terms;
svector<constraint_index> m_constraints_for_explanation;
vector<mpq> 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<mpq>()) {}
unsigned terms_count() const {
return m_terms.size();
}
const vector<const lar_term*>& terms() const { return m_terms; }
const svector<unsigned>& constraints_for_explanation() const {
return m_constraints_for_explanation;
}
const vector<mpq> & 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<mpq>();
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<mpq> & 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<mpq> create_b(const svector<unsigned> & basis_rows) {
if (basis_rows.size() == m_right_sides.size())
return m_right_sides;
vector<mpq> b;
for (unsigned i : basis_rows) {
b.push_back(m_right_sides[i]);
}
return b;
}
int find_cut_row_index(const vector<mpq> & b) {
int ret = -1;
int n = 0;
for (int i = 0; i < static_cast<int>(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<mpq> & 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<mpq>();
}
row[i] = one_of_type<mpq>() / H[i][i];
for(int k = i - 1; k >= 0; k--) {
mpq t = zero_of_type<mpq>();
for (unsigned l = k + 1; l <= i; l++) {
t += H[l][k]*row[l];
}
row[k] = -t / H[k][k];
}
// // test region
// vector<mpq> ei(H.row_count(), zero_of_type<mpq>());
// ei[i] = one_of_type<mpq>();
// vector<mpq> pr = row * H;
// pr.shrink(ei.size());
// lp_assert(ei == pr);
// // end test region
}
void fill_term(const vector<mpq> & 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<mpq> transform_to_local_columns(const vector<impq> & x) const {
vector<mpq> 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<unsigned>& basis_rows) {
svector<unsigned> 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<mpq> & x0
#endif
) {
// we suppose that x0 has at least one non integer element
init_matrix_A();
svector<unsigned> 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<general_matrix> h(m_A, d);
// general_matrix A_orig = m_A;
vector<mpq> b = create_b(basis_rows);
lp_assert(m_A * x0 == b);
// vector<mpq> 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<mpq> row(m_A.column_count());
get_ei_H_minus_1(cut_row, h.W(), row);
vector<mpq> f = row * m_A;
fill_term(f, t);
k = floor(b[cut_row]);
upper = true;
return lia_move::cut;
}
svector<unsigned> vars() const { return m_var_register.vars(); }
};
}

View file

@ -24,34 +24,34 @@ namespace lp {
struct implied_bound { struct implied_bound {
mpq m_bound; mpq m_bound;
unsigned m_j; // the column for which the bound has been found 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; bool m_coeff_before_j_is_pos;
unsigned m_row_or_term_index; unsigned m_row_or_term_index;
bool m_strict; bool m_strict;
lconstraint_kind kind() const { 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) if (m_strict)
k = static_cast<lconstraint_kind>(k / 2); k = static_cast<lconstraint_kind>(k / 2);
return k; return k;
} }
bool operator==(const implied_bound & o) const { 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_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; m_row_or_term_index == o.m_row_or_term_index && m_strict == o.m_strict;
} }
implied_bound(){} implied_bound(){}
implied_bound(const mpq & a, implied_bound(const mpq & a,
unsigned j, unsigned j,
bool low_bound, bool lower_bound,
bool coeff_before_j_is_pos, bool coeff_before_j_is_pos,
unsigned row_or_term_index, unsigned row_or_term_index,
bool strict): bool strict):
m_bound(a), m_bound(a),
m_j(j), 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_coeff_before_j_is_pos(coeff_before_j_is_pos),
m_row_or_term_index(row_or_term_index), m_row_or_term_index(row_or_term_index),
m_strict(strict) {} m_strict(strict) {
}
}; };
} }

View file

@ -18,7 +18,7 @@ Revision History:
--*/ --*/
#include "util/vector.h" #include "util/vector.h"
#include "util/lp/indexed_vector.hpp" #include "util/lp/indexed_vector_def.h"
namespace lp { namespace lp {
template void indexed_vector<double>::clear(); template void indexed_vector<double>::clear();
template void indexed_vector<double>::clear_all(); template void indexed_vector<double>::clear_all();
@ -42,11 +42,12 @@ template void lp::indexed_vector<double>::print(std::basic_ostream<char,struct s
template void lp::indexed_vector<lp::numeric_pair<lp::mpq> >::print(std::ostream&); template void lp::indexed_vector<lp::numeric_pair<lp::mpq> >::print(std::ostream&);
#endif #endif
} }
template void lp::print_vector<double>(vector<double> const&, std::ostream&); // template void lp::print_vector<double, vectro>(vector<double> const&, std::ostream&);
template void lp::print_vector<unsigned int>(vector<unsigned int> const&, std::ostream&); // template void lp::print_vector<unsigned int>(vector<unsigned int> const&, std::ostream&);
template void lp::print_vector<std::string>(vector<std::string> const&, std::ostream&); // template void lp::print_vector<std::string>(vector<std::string> const&, std::ostream&);
template void lp::print_vector<lp::numeric_pair<lp::mpq> >(vector<lp::numeric_pair<lp::mpq>> const&, std::ostream&); // template void lp::print_vector<lp::numeric_pair<lp::mpq> >(vector<lp::numeric_pair<lp::mpq>> const&, std::ostream&);
template void lp::indexed_vector<double>::resize(unsigned int); template void lp::indexed_vector<double>::resize(unsigned int);
template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream<char, std::char_traits<char> > &); // template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream<char, std::char_traits<char> > &);
template void lp::print_vector<std::pair<lp::mpq, unsigned int> >(vector<std::pair<lp::mpq, unsigned int>> const&, std::ostream&); // template void lp::print_vector<std::pair<lp::mpq, unsigned int> >(vector<std::pair<lp::mpq, unsigned int>> const&, std::ostream&);
template void lp::indexed_vector<lp::numeric_pair<lp::mpq> >::erase_from_index(unsigned int); template void lp::indexed_vector<lp::numeric_pair<lp::mpq> >::erase_from_index(unsigned int);

View file

@ -28,11 +28,9 @@ Revision History:
#include <unordered_set> #include <unordered_set>
namespace lp { namespace lp {
template <typename T> void print_vector(const vector<T> & t, std::ostream & out);
template <typename T> void print_vector(const buffer<T> & t, std::ostream & out);
template <typename T> void print_sparse_vector(const vector<T> & t, std::ostream & out); template <typename T> void print_sparse_vector(const vector<T> & t, std::ostream & out);
void print_vector(const vector<mpq> & t, std::ostream & out); void print_vector_as_doubles(const vector<mpq> & t, std::ostream & out);
template <typename T> template <typename T>
class indexed_vector { class indexed_vector {
public: public:
@ -90,16 +88,7 @@ public:
} }
void set_value(const T& value, unsigned index); 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<T>(); // use as a characteristic function
}
}
void clear(); void clear();
void clear_all(); void clear_all();
const T& operator[] (unsigned i) const { 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 #ifdef Z3DEBUG
bool is_OK() const; bool is_OK() const;

View file

@ -22,21 +22,6 @@ Revision History:
#include "util/lp/lp_settings.h" #include "util/lp/lp_settings.h"
namespace lp { namespace lp {
template <typename T>
void print_vector(const vector<T> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++)
out << t[i] << " ";
out << std::endl;
}
template <typename T>
void print_vector(const buffer<T> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++)
out << t[i] << " ";
out << std::endl;
}
template <typename T> template <typename T>
void print_sparse_vector(const vector<T> & t, std::ostream & out) { void print_sparse_vector(const vector<T> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++) { for (unsigned i = 0; i < t.size(); i++) {
@ -46,7 +31,7 @@ void print_sparse_vector(const vector<T> & t, std::ostream & out) {
out << std::endl; out << std::endl;
} }
void print_vector(const vector<mpq> & t, std::ostream & out) { void print_vector_as_doubles(const vector<mpq> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++) for (unsigned i = 0; i < t.size(); i++)
out << t[i].get_double() << std::setprecision(3) << " "; out << t[i].get_double() << std::setprecision(3) << " ";
out << std::endl; out << std::endl;
@ -56,13 +41,13 @@ template <typename T>
void indexed_vector<T>::resize(unsigned data_size) { void indexed_vector<T>::resize(unsigned data_size) {
clear(); clear();
m_data.resize(data_size, numeric_traits<T>::zero()); m_data.resize(data_size, numeric_traits<T>::zero());
SASSERT(is_OK()); lp_assert(is_OK());
} }
template <typename T> template <typename T>
void indexed_vector<T>::set_value(const T& value, unsigned index) { void indexed_vector<T>::set_value(const T& value, unsigned index) {
m_data[index] = value; 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); m_index.push_back(index);
} }

View file

@ -0,0 +1,45 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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<unsigned> 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; }
};
}

View file

@ -1,591 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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<unsigned>(-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<unsigned>(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<int>(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<int>(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<std::pair<mpq, var_index>> & 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<std::pair<mpq, var_index>> & 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<mpq>());
} 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<int>(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<double, double> & 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<mpq>(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<mpq>(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<mpq>(right_side, zero_of_type<mpq>());
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<mpq>(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<mpq>(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<mpq>(right_side, zero_of_type<mpq>());
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<mpq>(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<mpq>(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<mpq>(right_side, zero_of_type<mpq>());
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<mpq>(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<mpq>(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<mpq>(right_side, zero_of_type<mpq>());
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<mpq>(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);
}
}

View file

@ -35,7 +35,7 @@ public:
return m_data[j] >= 0; return m_data[j] >= 0;
} }
void insert(unsigned j) { void insert(unsigned j) {
SASSERT(j < m_data.size()); lp_assert(j < m_data.size());
if (contains(j)) return; if (contains(j)) return;
m_data[j] = m_index.size(); m_data[j] = m_index.size();
m_index.push_back(j); m_index.push_back(j);

1294
src/util/lp/int_solver.cpp Normal file

File diff suppressed because it is too large Load diff

166
src/util/lp/int_solver.h Normal file
View file

@ -0,0 +1,166 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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 <typename T, typename X>
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<mpq, numeric_pair<mpq>> & 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<mpq, mpq> & new_inequality);
bool ext_gcd_test(const row_strip<mpq>& row,
mpq const & least_coeff,
mpq const & lcm_den,
mpq const & consts);
void fill_explanation_from_fixed_columns(const row_strip<mpq> & 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<mpq>& 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<mpq>& );
bool is_gomory_cut_target(const row_strip<mpq>&);
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<std::pair<mpq, unsigned>> & 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);
};
}

View file

@ -1,65 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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 <typename T, typename X>
struct iterator_on_column:linear_combination_iterator<T> {
const vector<column_cell>& m_column; // the offset in term coeffs
const static_matrix<T, X> & 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_cell>& column, const static_matrix<T,X> & 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<int>(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<int>(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<mpq> * clone() override {
iterator_on_column * r = new iterator_on_column(m_column, m_A);
return r;
}
};
}

View file

@ -1,53 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/linear_combination_iterator.h"
namespace lp {
template <typename T>
struct iterator_on_indexed_vector:linear_combination_iterator<T> {
const indexed_vector<T> & m_v;
unsigned m_offset;
iterator_on_indexed_vector(const indexed_vector<T> & 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<T>* clone() override {
return new iterator_on_indexed_vector(m_v);
}
};
}

View file

@ -1,59 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/iterator_on_indexed_vector.h"
namespace lp {
template <typename T>
struct iterator_on_pivot_row:linear_combination_iterator<T> {
bool m_basis_returned;
const indexed_vector<T> & m_v;
unsigned m_basis_j;
iterator_on_indexed_vector<T> m_it;
unsigned size() const override { return m_it.size(); }
iterator_on_pivot_row(const indexed_vector<T> & 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<T>();
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<T> * clone() override {
iterator_on_pivot_row * r = new iterator_on_pivot_row(m_v, m_basis_j);
return r;
}
};
}

View file

@ -1,52 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/linear_combination_iterator.h"
namespace lp {
template <typename T>
struct iterator_on_row:linear_combination_iterator<T> {
const vector<row_cell<T>> & m_row;
unsigned m_i; // offset
iterator_on_row(const vector<row_cell<T>> & 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<T>* clone() override {
return new iterator_on_row(m_row);
}
};
}

View file

@ -1,72 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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<mpq> {
const lar_term & m_term;
std::unordered_map<unsigned, mpq>::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<unsigned>(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<mpq>();
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<mpq> * clone() override {
iterator_on_term_with_basis_var * r = new iterator_on_term_with_basis_var(m_term, m_term_j);
return r;
}
};
}

View file

@ -40,12 +40,11 @@ inline std::string lconstraint_kind_string(lconstraint_kind t) {
case GT: return std::string(">"); case GT: return std::string(">");
case EQ: return std::string("="); case EQ: return std::string("=");
} }
SASSERT(false); lp_unreachable();
return std::string(); // it is unreachable return std::string(); // it is unreachable
} }
class lar_base_constraint { struct lar_base_constraint {
public:
lconstraint_kind m_kind; lconstraint_kind m_kind;
mpq m_right_side; mpq m_right_side;
virtual vector<std::pair<mpq, var_index>> get_left_side_coefficients() const = 0; virtual vector<std::pair<mpq, var_index>> get_left_side_coefficients() const = 0;
@ -88,7 +87,7 @@ public:
: lar_base_constraint(kind, right_side), m_coeffs(left_side) {} : lar_base_constraint(kind, right_side), m_coeffs(left_side) {}
lar_constraint(const lar_base_constraint & c) { 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 { unsigned size() const override {

View file

@ -22,4 +22,4 @@ Revision History:
#include <string> #include <string>
#include "util/vector.h" #include "util/vector.h"
#include <functional> #include <functional>
#include "util/lp/lar_core_solver.hpp" #include "util/lp/lar_core_solver_def.h"

View file

@ -26,12 +26,9 @@ Revision History:
#include "util/lp/indexed_vector.h" #include "util/lp/indexed_vector.h"
#include "util/lp/binary_heap_priority_queue.h" #include "util/lp/binary_heap_priority_queue.h"
#include "util/lp/breakpoint.h" #include "util/lp/breakpoint.h"
#include "util/lp/stacked_unordered_set.h"
#include "util/lp/lp_primal_core_solver.h" #include "util/lp/lp_primal_core_solver.h"
#include "util/lp/stacked_vector.h" #include "util/lp/stacked_vector.h"
#include "util/lp/lar_solution_signature.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" #include "util/lp/stacked_value.h"
namespace lp { namespace lp {
@ -50,7 +47,7 @@ public:
stacked_vector<column_type> m_column_types; stacked_vector<column_type> m_column_types;
// r - solver fields, for rational numbers // r - solver fields, for rational numbers
vector<numeric_pair<mpq>> m_r_x; // the solution vector<numeric_pair<mpq>> m_r_x; // the solution
stacked_vector<numeric_pair<mpq>> m_r_low_bounds; stacked_vector<numeric_pair<mpq>> m_r_lower_bounds;
stacked_vector<numeric_pair<mpq>> m_r_upper_bounds; stacked_vector<numeric_pair<mpq>> m_r_upper_bounds;
static_matrix<mpq, numeric_pair<mpq>> m_r_A; static_matrix<mpq, numeric_pair<mpq>> m_r_A;
stacked_vector<unsigned> m_r_pushed_basis; stacked_vector<unsigned> m_r_pushed_basis;
@ -62,7 +59,7 @@ public:
// d - solver fields, for doubles // d - solver fields, for doubles
vector<double> m_d_x; // the solution in doubles vector<double> m_d_x; // the solution in doubles
vector<double> m_d_low_bounds; vector<double> m_d_lower_bounds;
vector<double> m_d_upper_bounds; vector<double> m_d_upper_bounds;
static_matrix<double, double> m_d_A; static_matrix<double, double> m_d_A;
stacked_vector<unsigned> m_d_pushed_basis; stacked_vector<unsigned> m_d_pushed_basis;
@ -155,11 +152,11 @@ public:
void fill_evidence(unsigned row); void fill_evidence(unsigned row);
unsigned get_number_of_non_ints() const;
void solve(); void solve();
bool low_bounds_are_set() const { return true; } bool lower_bounds_are_set() const { return true; }
const indexed_vector<mpq> & get_pivot_row() const { const indexed_vector<mpq> & get_pivot_row() const {
return m_r_solver.m_pivot_row; return m_r_solver.m_pivot_row;
@ -183,16 +180,16 @@ public:
} }
void push() { void push() {
SASSERT(m_r_solver.basis_heading_is_correct()); lp_assert(m_r_solver.basis_heading_is_correct());
SASSERT(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); lp_assert(!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_column_types.size() == m_r_A.column_count());
m_stacked_simplex_strategy = settings().simplex_strategy(); m_stacked_simplex_strategy = settings().simplex_strategy();
m_stacked_simplex_strategy.push(); m_stacked_simplex_strategy.push();
m_column_types.push(); m_column_types.push();
// rational // rational
if (!settings().use_tableau()) if (!settings().use_tableau())
m_r_A.push(); m_r_A.push();
m_r_low_bounds.push(); m_r_lower_bounds.push();
m_r_upper_bounds.push(); m_r_upper_bounds.push();
if (!settings().use_tableau()) { if (!settings().use_tableau()) {
push_vector(m_r_pushed_basis, m_r_basis); push_vector(m_r_pushed_basis, m_r_basis);
@ -207,7 +204,7 @@ public:
template <typename K> template <typename K>
void push_vector(stacked_vector<K> & pushed_vector, const vector<K> & vector) { void push_vector(stacked_vector<K> & pushed_vector, const vector<K> & vector) {
SASSERT(pushed_vector.size() <= vector.size()); lp_assert(pushed_vector.size() <= vector.size());
for (unsigned i = 0; i < vector.size();i++) { for (unsigned i = 0; i < vector.size();i++) {
if (i == pushed_vector.size()) { if (i == pushed_vector.size()) {
pushed_vector.push_back(vector[i]); pushed_vector.push_back(vector[i]);
@ -234,7 +231,7 @@ public:
// rationals // rationals
if (!settings().use_tableau()) if (!settings().use_tableau())
m_r_A.pop(k); m_r_A.pop(k);
m_r_low_bounds.pop(k); m_r_lower_bounds.pop(k);
m_r_upper_bounds.pop(k); m_r_upper_bounds.pop(k);
m_column_types.pop(k); m_column_types.pop(k);
@ -257,8 +254,8 @@ public:
pop_basis(k); pop_basis(k);
m_stacked_simplex_strategy.pop(k); m_stacked_simplex_strategy.pop(k);
settings().simplex_strategy() = m_stacked_simplex_strategy; settings().simplex_strategy() = m_stacked_simplex_strategy;
SASSERT(m_r_solver.basis_heading_is_correct()); lp_assert(m_r_solver.basis_heading_is_correct());
SASSERT(!need_to_presolve_with_double_solver() || m_d_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 { 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<mpq> & delta) { bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair<mpq> & delta) {
auto & x = m_r_x[j]; auto & x = m_r_x[j];
switch (pos_type) { switch (pos_type) {
case at_low_bound: case at_lower_bound:
if (x == m_r_solver.m_low_bounds[j]) if (x == m_r_solver.m_lower_bounds[j])
return false; return false;
delta = m_r_solver.m_low_bounds[j] - x; delta = m_r_solver.m_lower_bounds[j] - x;
m_r_solver.m_x[j] = m_r_solver.m_low_bounds[j]; m_r_solver.m_x[j] = m_r_solver.m_lower_bounds[j];
break; break;
case at_fixed: case at_fixed:
case at_upper_bound: case at_upper_bound:
@ -300,30 +297,30 @@ public:
delta = m_r_solver.m_upper_bounds[j] - x; delta = m_r_solver.m_upper_bounds[j] - x;
x = m_r_solver.m_upper_bounds[j]; x = m_r_solver.m_upper_bounds[j];
break; break;
case column_type::low_bound: case column_type::lower_bound:
delta = m_r_solver.m_low_bounds[j] - x; delta = m_r_solver.m_lower_bounds[j] - x;
x = m_r_solver.m_low_bounds[j]; x = m_r_solver.m_lower_bounds[j];
break; break;
case column_type::boxed: case column_type::boxed:
if (x > m_r_solver.m_upper_bounds[j]) { if (x > m_r_solver.m_upper_bounds[j]) {
delta = m_r_solver.m_upper_bounds[j] - x; delta = m_r_solver.m_upper_bounds[j] - x;
x += m_r_solver.m_upper_bounds[j]; x += m_r_solver.m_upper_bounds[j];
} else { } else {
delta = m_r_solver.m_low_bounds[j] - x; delta = m_r_solver.m_lower_bounds[j] - x;
x = m_r_solver.m_low_bounds[j]; x = m_r_solver.m_lower_bounds[j];
} }
break; break;
case column_type::fixed: case column_type::fixed:
delta = m_r_solver.m_low_bounds[j] - x; delta = m_r_solver.m_lower_bounds[j] - x;
x = m_r_solver.m_low_bounds[j]; x = m_r_solver.m_lower_bounds[j];
break; break;
default: default:
SASSERT(false); lp_assert(false);
} }
break; break;
default: default:
SASSERT(false); lp_unreachable();
} }
m_r_solver.remove_column_from_inf_set(j); m_r_solver.remove_column_from_inf_set(j);
return true; return true;
@ -332,7 +329,7 @@ public:
void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) { 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) { for (auto &t : signature) {
unsigned j = t.first; unsigned j = t.first;
if (m_r_heading[j] >= 0) if (m_r_heading[j] >= 0)
@ -344,12 +341,11 @@ public:
for (const auto & cc : m_r_solver.m_A.m_columns[j]){ for (const auto & cc : m_r_solver.m_A.m_columns[j]){
unsigned i = cc.m_i; unsigned i = cc.m_i;
unsigned jb = m_r_solver.m_basis[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_x_with_delta_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc));
m_r_solver.update_column_in_inf_set(jb);
} }
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<L,K> & s) { void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver<L,K> & s) {
for (auto &t : signature) { for (auto &t : signature) {
unsigned j = t.first; unsigned j = t.first;
SASSERT(m_r_heading[j] < 0); lp_assert(m_r_heading[j] < 0);
auto pos_type = t.second; auto pos_type = t.second;
switch (pos_type) { switch (pos_type) {
case at_low_bound: case at_lower_bound:
s.m_x[j] = s.m_low_bounds[j]; s.m_x[j] = s.m_lower_bounds[j];
break; break;
case at_fixed: case at_fixed:
case at_upper_bound: case at_upper_bound:
@ -374,33 +370,33 @@ public:
case not_at_bound: case not_at_bound:
switch (m_column_types[j]) { switch (m_column_types[j]) {
case column_type::free_column: case column_type::free_column:
SASSERT(false); // unreachable lp_assert(false); // unreachable
case column_type::upper_bound: case column_type::upper_bound:
s.m_x[j] = s.m_upper_bounds[j]; s.m_x[j] = s.m_upper_bounds[j];
break; break;
case column_type::low_bound: case column_type::lower_bound:
s.m_x[j] = s.m_low_bounds[j]; s.m_x[j] = s.m_lower_bounds[j];
break; break;
case column_type::boxed: case column_type::boxed:
if (settings().random_next() % 2) { if (settings().random_next() % 2) {
s.m_x[j] = s.m_low_bounds[j]; s.m_x[j] = s.m_lower_bounds[j];
} else { } else {
s.m_x[j] = s.m_upper_bounds[j]; s.m_x[j] = s.m_upper_bounds[j];
} }
break; break;
case column_type::fixed: case column_type::fixed:
s.m_x[j] = s.m_low_bounds[j]; s.m_x[j] = s.m_lower_bounds[j];
break; break;
default: default:
SASSERT(false); lp_assert(false);
} }
break; break;
default: 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(); s.solve_Ax_eq_b();
} }
@ -433,7 +429,7 @@ public:
// the queues of delayed indices // the queues of delayed indices
std::queue<unsigned> entr_q, leav_q; std::queue<unsigned> entr_q, leav_q;
auto * l = cs.m_factorization; 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) { for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) {
unsigned entering = trace_of_basis_change[i]; unsigned entering = trace_of_basis_change[i];
unsigned leaving = trace_of_basis_change[i+1]; unsigned leaving = trace_of_basis_change[i+1];
@ -461,8 +457,8 @@ public:
continue; continue;
} }
} }
SASSERT(cs.m_basis_heading[entering] < 0); lp_assert(cs.m_basis_heading[entering] < 0);
SASSERT(cs.m_basis_heading[leaving] >= 0); lp_assert(cs.m_basis_heading[leaving] >= 0);
if (l->get_status() == LU_status::OK) { if (l->get_status() == LU_status::OK) {
l->prepare_entering(entering, w); // to init vector w l->prepare_entering(entering, w); // to init vector w
l->replace_column(zero_of_type<L>(), w, cs.m_basis_heading[leaving]); l->replace_column(zero_of_type<L>(), w, cs.m_basis_heading[leaving]);
@ -486,7 +482,7 @@ public:
void solve_on_signature_tableau(const lar_solution_signature & signature, const vector<unsigned> & changes_of_basis) { void solve_on_signature_tableau(const lar_solution_signature & signature, const vector<unsigned> & changes_of_basis) {
r_basis_is_OK(); 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); 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 if (!r) { // it is the case where m_d_solver gives a degenerated basis
@ -505,10 +501,10 @@ public:
return; return;
m_r_solver.stop_tracing_basis_changes(); m_r_solver.stop_tracing_basis_changes();
// and now catch up in the double solver // 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); 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) { bool adjust_x_of_column(unsigned j) {
@ -522,16 +518,16 @@ public:
} }
m_r_solver.snap_column_to_bound_tableau(j); 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); m_r_solver.m_inf_set.erase(j);
*/ */
SASSERT(false); lp_assert(false);
return true; return true;
} }
bool catch_up_in_lu_tableau(const vector<unsigned> & trace_of_basis_change, const vector<int> & basis_heading) { bool catch_up_in_lu_tableau(const vector<unsigned> & trace_of_basis_change, const vector<int> & basis_heading) {
SASSERT(r_basis_is_OK()); lp_assert(r_basis_is_OK());
// the queues of delayed indices // the queues of delayed indices
std::queue<unsigned> entr_q, leav_q; std::queue<unsigned> entr_q, leav_q;
for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) {
@ -561,8 +557,8 @@ public:
continue; continue;
} }
} }
SASSERT(m_r_solver.m_basis_heading[entering] < 0); lp_assert(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[leaving] >= 0);
m_r_solver.change_basis_unconditionally(entering, leaving); m_r_solver.change_basis_unconditionally(entering, leaving);
if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) { if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) {
// unroll the last step // unroll the last step
@ -572,12 +568,12 @@ public:
#endif #endif
m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]); m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]);
#ifdef Z3DEBUG #ifdef Z3DEBUG
SASSERT(t); lp_assert(t);
#endif #endif
return false; return false;
} }
} }
SASSERT(r_basis_is_OK()); lp_assert(r_basis_is_OK());
return true; return true;
} }
@ -587,14 +583,14 @@ public:
if (!m_r_solver.m_settings.use_tableau()) if (!m_r_solver.m_settings.use_tableau())
return true; return true;
for (unsigned j : m_r_solver.m_basis) { for (unsigned j : m_r_solver.m_basis) {
SASSERT(m_r_solver.m_A.m_columns[j].size() == 1); lp_assert(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<mpq>()); lp_assert(m_r_solver.m_A.get_val(m_r_solver.m_A.m_columns[j][0]) == one_of_type<mpq>());
} }
for (unsigned j =0; j < m_r_solver.m_basis_heading.size(); j++) { 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_basis_heading[j] >= 0) continue;
if (m_r_solver.m_column_types[j] == column_type::fixed) continue; if (m_r_solver.m_column_types[j] == column_type::fixed) continue;
SASSERT(static_cast<unsigned>(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size()); lp_assert(static_cast<unsigned>(- 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( m_r_solver.m_basis_heading[j] <= -1);
} }
#endif #endif
return true; 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 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); catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver);
m_r_solver.find_feasible_solution(); m_r_solver.find_feasible_solution();
m_d_basis = m_r_basis; m_d_basis = m_r_basis;
@ -630,7 +625,7 @@ public:
return; return;
m_r_solver.stop_tracing_basis_changes(); m_r_solver.stop_tracing_basis_changes();
// and now catch up in the double solver // 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); 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 <typename L, typename K> template <typename L, typename K>
void extract_signature_from_lp_core_solver(const lp_primal_core_solver<L, K> & solver, lar_solution_signature & signature) { void extract_signature_from_lp_core_solver(const lp_primal_core_solver<L, K> & solver, lar_solution_signature & signature) {
signature.clear(); signature.clear();
SASSERT(signature.size() == 0); lp_assert(signature.size() == 0);
for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) { for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) {
if (solver.m_basis_heading[j] < 0) { if (solver.m_basis_heading[j] < 0) {
signature[j] = solver.get_non_basic_column_value_position(j); signature[j] = solver.get_non_basic_column_value_position(j);
@ -666,27 +661,27 @@ public:
void get_bounds_for_double_solver() { void get_bounds_for_double_solver() {
unsigned n = m_n(); unsigned n = m_n();
m_d_low_bounds.resize(n); m_d_lower_bounds.resize(n);
m_d_upper_bounds.resize(n); m_d_upper_bounds.resize(n);
double delta = find_delta_for_strict_boxed_bounds().get_double(); double delta = find_delta_for_strict_boxed_bounds().get_double();
if (delta > 0.000001) if (delta > 0.000001)
delta = 0.000001; delta = 0.000001;
for (unsigned j = 0; j < n; j++) { for (unsigned j = 0; j < n; j++) {
if (low_bound_is_set(j)) { if (lower_bound_is_set(j)) {
const auto & lb = m_r_solver.m_low_bounds[j]; const auto & lb = m_r_solver.m_lower_bounds[j];
m_d_low_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); m_d_lower_bounds[j] = lb.x.get_double() + delta * lb.y.get_double();
} }
if (upper_bound_is_set(j)) { if (upper_bound_is_set(j)) {
const auto & ub = m_r_solver.m_upper_bounds[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(); 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( void scale_problem_for_doubles(
static_matrix<double, double>& A, static_matrix<double, double>& A,
vector<double> & low_bounds, vector<double> & lower_bounds,
vector<double> & upper_bounds) { vector<double> & upper_bounds) {
vector<double> column_scale_vector; vector<double> column_scale_vector;
vector<double> right_side_vector(A.column_count()); vector<double> right_side_vector(A.column_count());
@ -706,8 +701,8 @@ public:
if (m_r_solver.column_has_upper_bound(j)) { if (m_r_solver.column_has_upper_bound(j)) {
upper_bounds[j] /= column_scale_vector[j]; upper_bounds[j] /= column_scale_vector[j];
} }
if (m_r_solver.column_has_low_bound(j)) { if (m_r_solver.column_has_lower_bound(j)) {
low_bounds[j] /= column_scale_vector[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]) { switch (m_column_types[j]) {
case column_type::free_column: case column_type::free_column:
case column_type::upper_bound: case column_type::upper_bound:
return false; return false;
case column_type::low_bound: case column_type::lower_bound:
case column_type::boxed: case column_type::boxed:
case column_type::fixed: case column_type::fixed:
return true; return true;
default: default:
SASSERT(false); lp_assert(false);
} }
return false; return false;
} }
@ -752,27 +747,27 @@ public:
bool upper_bound_is_set(unsigned j) const { bool upper_bound_is_set(unsigned j) const {
switch (m_column_types[j]) { switch (m_column_types[j]) {
case column_type::free_column: case column_type::free_column:
case column_type::low_bound: case column_type::lower_bound:
return false; return false;
case column_type::upper_bound: case column_type::upper_bound:
case column_type::boxed: case column_type::boxed:
case column_type::fixed: case column_type::fixed:
return true; return true;
default: default:
SASSERT(false); lp_assert(false);
} }
return false; return false;
} }
void update_delta(mpq& delta, numeric_pair<mpq> const& l, numeric_pair<mpq> const& u) const { void update_delta(mpq& delta, numeric_pair<mpq> const& l, numeric_pair<mpq> const& u) const {
SASSERT(l <= u); lp_assert(l <= u);
if (l.x < u.x && l.y > u.y) { if (l.x < u.x && l.y > u.y) {
mpq delta1 = (u.x - l.x) / (l.y - u.y); mpq delta1 = (u.x - l.x) / (l.y - u.y);
if (delta1 < delta) { if (delta1 < delta) {
delta = delta1; 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++ ) { for (unsigned j = 0; j < m_r_A.column_count(); j++ ) {
if (m_column_types()[j] != column_type::boxed) if (m_column_types()[j] != column_type::boxed)
continue; 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; return delta;
} }
@ -791,8 +785,8 @@ public:
mpq find_delta_for_strict_bounds(const mpq & initial_delta) const{ mpq find_delta_for_strict_bounds(const mpq & initial_delta) const{
mpq delta = initial_delta; mpq delta = initial_delta;
for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { for (unsigned j = 0; j < m_r_A.column_count(); j++ ) {
if (low_bound_is_set(j)) if (lower_bound_is_set(j))
update_delta(delta, m_r_low_bounds[j], m_r_x[j]); update_delta(delta, m_r_lower_bounds[j], m_r_x[j]);
if (upper_bound_is_set(j)) if (upper_bound_is_set(j))
update_delta(delta, m_r_x[j], m_r_upper_bounds[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(); m_r_solver.init_column_row_non_zeroes();
} }
linear_combination_iterator<mpq> * get_column_iterator(unsigned j) { bool column_is_fixed(unsigned j) const {
if (settings().use_tableau()) { return m_column_types()[j] == column_type::fixed ||
return new iterator_on_column<mpq, numeric_pair<mpq>>(m_r_solver.m_A.m_columns[j], m_r_solver.m_A); ( m_column_types()[j] == column_type::boxed &&
} else { m_r_solver.m_lower_bounds[j] == m_r_solver.m_upper_bounds[j]);
m_r_solver.solve_Bd(j); }
return new iterator_on_indexed_vector<mpq>(m_r_solver.m_ed);
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<unsigned>& r_basis() const { return m_r_basis; }
const vector<unsigned>& r_nbasis() const { return m_r_nbasis; }
}; };
} }

View file

@ -54,7 +54,7 @@ lar_core_solver::lar_core_solver(
m_r_heading, m_r_heading,
m_costs_dummy, m_costs_dummy,
m_column_types(), m_column_types(),
m_r_low_bounds(), m_r_lower_bounds(),
m_r_upper_bounds(), m_r_upper_bounds(),
settings, settings,
column_names), column_names),
@ -66,15 +66,15 @@ lar_core_solver::lar_core_solver(
m_d_heading, m_d_heading,
m_d_costs_dummy, m_d_costs_dummy,
m_column_types(), m_column_types(),
m_d_low_bounds, m_d_lower_bounds,
m_d_upper_bounds, m_d_upper_bounds,
settings, settings,
column_names){} column_names){}
void lar_core_solver::init_costs(bool first_time) { void lar_core_solver::init_costs(bool first_time) {
SASSERT(false); // should not be called lp_assert(false); // should not be called
// SASSERT(this->m_x.size() >= this->m_n()); // lp_assert(this->m_x.size() >= this->m_n());
// SASSERT(this->m_column_types.size() >= this->m_n()); // lp_assert(this->m_column_types.size() >= this->m_n());
// if (first_time) // if (first_time)
// this->m_costs.resize(this->m_n()); // this->m_costs.resize(this->m_n());
// X inf = this->m_infeasibility; // 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)) { // if (!(first_time || inf >= this->m_infeasibility)) {
// LP_OUT(this->m_settings, "iter = " << this->total_iterations() << std::endl); // 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); // 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) // if (inf == this->m_infeasibility)
// this->m_iters_with_no_cost_growing++; // 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]) { if (x > this->m_upper_bounds[j]) {
this->m_costs[j] = 1; this->m_costs[j] = 1;
this->m_infeasibility += x - this->m_upper_bounds[j]; this->m_infeasibility += x - this->m_upper_bounds[j];
} else if (x < this->m_low_bounds[j]) { } else if (x < this->m_lower_bounds[j]) {
this->m_infeasibility += this->m_low_bounds[j] - x; this->m_infeasibility += this->m_lower_bounds[j] - x;
this->m_costs[j] = -1; this->m_costs[j] = -1;
} else { } else {
this->m_costs[j] = numeric_traits<T>::zero(); this->m_costs[j] = numeric_traits<T>::zero();
} }
break; break;
case low_bound: case lower_bound:
if (x < this->m_low_bounds[j]) { if (x < this->m_lower_bounds[j]) {
this->m_costs[j] = -1; this->m_costs[j] = -1;
this->m_infeasibility += this->m_low_bounds[j] - x; this->m_infeasibility += this->m_lower_bounds[j] - x;
} else { } else {
this->m_costs[j] = numeric_traits<T>::zero(); this->m_costs[j] = numeric_traits<T>::zero();
} }
@ -135,7 +135,7 @@ void lar_core_solver::init_cost_for_column(unsigned j) {
this->m_costs[j] = numeric_traits<T>::zero(); this->m_costs[j] = numeric_traits<T>::zero();
break; break;
default: default:
SASSERT(false); lp_assert(false);
break; break;
}*/ }*/
} }
@ -154,7 +154,7 @@ int lar_core_solver::column_is_out_of_bounds(unsigned j) {
return 1; return 1;
} }
return 0; return 0;
case low_bound: case lower_bound:
if (this->x_below_low_bound(j)) { if (this->x_below_low_bound(j)) {
return -1; return -1;
} }
@ -168,30 +168,14 @@ int lar_core_solver::column_is_out_of_bounds(unsigned j) {
return 0; return 0;
break; break;
}*/ }*/
SASSERT(false); lp_assert(false);
return true; return true;
} }
void lar_core_solver::calculate_pivot_row(unsigned i) { void lar_core_solver::calculate_pivot_row(unsigned i) {
SASSERT(!m_r_solver.use_tableau()); m_r_solver.calculate_pivot_row(i);
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);
} }
@ -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() { 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]; 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_sum_sign = m_r_solver.inf_sign_of_column(bj);
m_infeasible_linear_combination.clear(); 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() { void lar_core_solver::solve() {
SASSERT(m_r_solver.non_basic_columns_are_set_correctly()); lp_assert(m_r_solver.non_basic_columns_are_set_correctly());
SASSERT(m_r_solver.inf_set_is_correct()); lp_assert(m_r_solver.inf_set_is_correct());
if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) { TRACE("find_feas_stats", tout << "infeasibles = " << m_r_solver.m_inf_set.size() << ", int_infs = " << get_number_of_non_ints() << std::endl;);
m_r_solver.set_status(OPTIMAL); if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) {
return; m_r_solver.set_status(lp_status::OPTIMAL);
} return;
}
++settings().st().m_need_to_solve_inf; ++settings().st().m_need_to_solve_inf;
SASSERT(!m_r_solver.A_mult_x_is_off()); CASSERT("A_off", !m_r_solver.A_mult_x_is_off());
SASSERT((!settings().use_tableau()) || r_basis_is_OK()); lp_assert((!settings().use_tableau()) || r_basis_is_OK());
if (need_to_presolve_with_double_solver()) { if (need_to_presolve_with_double_solver()) {
prefix_d(); prefix_d();
lar_solution_signature solution_signature; lar_solution_signature solution_signature;
vector<unsigned> changes_of_basis = find_solution_signature_with_doubles(solution_signature); vector<unsigned> changes_of_basis = find_solution_signature_with_doubles(solution_signature);
if (m_d_solver.get_status() == TIME_EXHAUSTED) { if (m_d_solver.get_status() == lp_status::TIME_EXHAUSTED) {
m_r_solver.set_status(TIME_EXHAUSTED); m_r_solver.set_status(lp_status::TIME_EXHAUSTED);
return; return;
} }
if (settings().use_tableau()) if (settings().use_tableau())
solve_on_signature_tableau(solution_signature, changes_of_basis); solve_on_signature_tableau(solution_signature, changes_of_basis);
else else
solve_on_signature(solution_signature, changes_of_basis); 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 { } else {
if (!settings().use_tableau()) { if (!settings().use_tableau()) {
bool snapped = m_r_solver.snap_non_basic_x_to_bound(); 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) if (snapped)
m_r_solver.solve_Ax_eq_b(); m_r_solver.solve_Ax_eq_b();
} }
@ -306,16 +300,16 @@ void lar_core_solver::solve() {
m_r_solver.find_feasible_solution(); m_r_solver.find_feasible_solution();
else else
m_r_solver.solve(); 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(); fill_not_improvable_zero_sum();
} else if (m_r_solver.get_status() != UNBOUNDED) { } else if (m_r_solver.get_status() != lp_status::UNBOUNDED) {
m_r_solver.set_status(OPTIMAL); m_r_solver.set_status(lp_status::OPTIMAL);
} }
SASSERT(r_basis_is_OK()); lp_assert(r_basis_is_OK());
SASSERT(m_r_solver.non_basic_columns_are_set_correctly()); lp_assert(m_r_solver.non_basic_columns_are_set_correctly());
SASSERT(m_r_solver.inf_set_is_correct()); lp_assert(m_r_solver.inf_set_is_correct());
} }

2264
src/util/lp/lar_solver.cpp Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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<double,double>(class lp::static_matrix<double,double> &);

View file

@ -1,22 +1,22 @@
/*++ /*++
Copyright (c) 2017 Microsoft Corporation Copyright (c) 2017 Microsoft Corporation
Module Name: Module Name:
<name> <name>
Abstract: Abstract:
<abstract> <abstract>
Author: Author:
Lev Nachmanson (levnach) Lev Nachmanson (levnach)
Revision History: Revision History:
--*/ --*/
#pragma once #pragma once
#include "util/lp/indexed_vector.h" #include "util/lp/indexed_vector.h"
namespace lp { namespace lp {
@ -25,7 +25,7 @@ struct lar_term {
std::unordered_map<unsigned, mpq> m_coeffs; std::unordered_map<unsigned, mpq> m_coeffs;
mpq m_v; mpq m_v;
lar_term() {} 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); auto it = m_coeffs.find(j);
if (it == m_coeffs.end()) { if (it == m_coeffs.end()) {
m_coeffs.emplace(j, c); 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<unsigned>(m_coeffs.size()); } unsigned size() const { return static_cast<unsigned>(m_coeffs.size()); }
const std::unordered_map<unsigned, mpq> & coeffs() const { const std::unordered_map<unsigned, mpq> & coeffs() const {
@ -45,7 +49,7 @@ struct lar_term {
lar_term(const vector<std::pair<mpq, unsigned>>& coeffs, lar_term(const vector<std::pair<mpq, unsigned>>& coeffs,
const mpq & v) : m_v(v) { const mpq & v) : m_v(v) {
for (const auto & p : coeffs) { 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 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; if (it == m_coeffs.end()) return;
const mpq & b = it->second; const mpq & b = it->second;
for (unsigned it_j :li.m_index) { 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); m_coeffs.erase(it);
} }
@ -75,5 +79,61 @@ struct lar_term {
bool contains(unsigned j) const { bool contains(unsigned j) const {
return m_coeffs.find(j) != m_coeffs.end(); return m_coeffs.find(j) != m_coeffs.end();
} }
void negate() {
for (auto & t : m_coeffs)
t.second.neg();
}
template <typename T>
T apply(const vector<T>& 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<mpq>();
}
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<unsigned, mpq>::const_iterator m_it;
typedef const_iterator self_type;
typedef ival value_type;
typedef ival reference;
// typedef std::pair<const unsigned, mpq>* 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<unsigned, mpq>::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(); }
}; };
} }

31
src/util/lp/lia_move.h Normal file
View file

@ -0,0 +1,31 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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
};
}

View file

@ -1,65 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
template <typename T>
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 <typename T>
struct linear_combination_iterator_on_vector : linear_combination_iterator<T> {
vector<std::pair<T, unsigned>> & 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<T> * clone() {
return new linear_combination_iterator_on_vector(m_vector);
}
linear_combination_iterator_on_vector(vector<std::pair<T, unsigned>> & vec):
m_vector(vec),
m_offset(0)
{}
unsigned size() const { return m_vector.size(); }
};
}

View file

@ -1,67 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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<lconstraint_kind>(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));
}
}
}
}

View file

@ -1,42 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<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<unsigned, unsigned> m_improved_low_bounds; // these maps map a column index to the corresponding index in ibounds
std::unordered_map<unsigned, unsigned> m_improved_upper_bounds;
lar_solver & m_lar_solver;
public:
vector<implied_bound> 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"; }
};
}

View file

@ -22,7 +22,7 @@ Revision History:
#include <string> #include <string>
#include "util/vector.h" #include "util/vector.h"
#include <functional> #include <functional>
#include "util/lp/lp_core_solver_base.hpp" #include "util/lp/lp_core_solver_base_def.h"
template bool lp::lp_core_solver_base<double, double>::A_mult_x_is_off() const; template bool lp::lp_core_solver_base<double, double>::A_mult_x_is_off() const;
template bool lp::lp_core_solver_base<double, double>::A_mult_x_is_off_on_index(const vector<unsigned> &) const; template bool lp::lp_core_solver_base<double, double>::A_mult_x_is_off_on_index(const vector<unsigned> &) const;
template bool lp::lp_core_solver_base<double, double>::basis_heading_is_correct() const; template bool lp::lp_core_solver_base<double, double>::basis_heading_is_correct() const;
@ -144,3 +144,5 @@ template bool lp::lp_core_solver_base<lp::mpq, lp::mpq>::inf_set_is_correct() co
template bool lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::infeasibility_costs_are_correct() const;
template bool lp::lp_core_solver_base<lp::mpq, lp::mpq >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base<lp::mpq, lp::mpq >::infeasibility_costs_are_correct() const;
template bool lp::lp_core_solver_base<double, double >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base<double, double >::infeasibility_costs_are_correct() const;
template void lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::calculate_pivot_row(unsigned int);
template bool lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::remove_from_basis(unsigned int);

View file

@ -28,6 +28,7 @@ Revision History:
#include "util/lp/lu.h" #include "util/lp/lu.h"
#include "util/lp/permutation_matrix.h" #include "util/lp/permutation_matrix.h"
#include "util/lp/column_namer.h" #include "util/lp/column_namer.h"
namespace lp { namespace lp {
template <typename T, typename X> // X represents the type of the x variable and the bounds template <typename T, typename X> // X represents the type of the x variable and the bounds
@ -38,7 +39,17 @@ class lp_core_solver_base {
private: private:
lp_status m_status; lp_status m_status;
public: 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; } bool current_x_is_infeasible() const { return m_inf_set.size() != 0; }
int_set m_inf_set; int_set m_inf_set;
bool m_using_infeas_costs; bool m_using_infeas_costs;
@ -58,13 +69,13 @@ public:
lp_settings & m_settings; lp_settings & m_settings;
vector<T> m_y; // the buffer for yB = cb vector<T> m_y; // the buffer for yB = cb
// a device that is able to solve Bx=c, xB=d, and change the basis // a device that is able to solve Bx=c, xB=d, and change the basis
lu<T, X> * m_factorization; lu<static_matrix<T, X>> * m_factorization;
const column_namer & m_column_names; const column_namer & m_column_names;
indexed_vector<T> m_w; // the vector featuring in 24.3 of the Chvatal book indexed_vector<T> m_w; // the vector featuring in 24.3 of the Chvatal book
vector<T> m_d; // the vector of reduced costs vector<T> m_d; // the vector of reduced costs
indexed_vector<T> m_ed; // the solution of B*m_ed = a indexed_vector<T> m_ed; // the solution of B*m_ed = a
const vector<column_type> & m_column_types; const vector<column_type> & m_column_types;
const vector<X> & m_low_bounds; const vector<X> & m_lower_bounds;
const vector<X> & m_upper_bounds; const vector<X> & m_upper_bounds;
vector<T> m_column_norms; // the approximate squares of column norms that help choosing a profitable column vector<T> m_column_norms; // the approximate squares of column norms that help choosing a profitable column
vector<X> m_copy_of_xB; vector<X> m_copy_of_xB;
@ -74,6 +85,7 @@ public:
bool m_tracing_basis_changes; bool m_tracing_basis_changes;
int_set* m_pivoted_rows; int_set* m_pivoted_rows;
bool m_look_for_feasible_solution_only; bool m_look_for_feasible_solution_only;
void start_tracing_basis_changes() { void start_tracing_basis_changes() {
m_trace_of_basis_change_vector.resize(0); m_trace_of_basis_change_vector.resize(0);
m_tracing_basis_changes = true; m_tracing_basis_changes = true;
@ -108,7 +120,7 @@ public:
lp_settings & settings, lp_settings & settings,
const column_namer& column_names, const column_namer& column_names,
const vector<column_type> & column_types, const vector<column_type> & column_types,
const vector<X> & low_bound_values, const vector<X> & lower_bound_values,
const vector<X> & upper_bound_values); const vector<X> & upper_bound_values);
void allocate_basis_heading(); void allocate_basis_heading();
@ -197,11 +209,11 @@ public:
bool need_to_pivot_to_basis_tableau() const { 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(); unsigned m = m_A.row_count();
for (unsigned i = 0; i < m; i++) { for (unsigned i = 0; i < m; i++) {
unsigned bj = m_basis[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<mpq>()) return true; if (m_A.m_columns[bj].size() > 1 || m_A.get_val(m_A.m_columns[bj][0]) != one_of_type<mpq>()) return true;
} }
return false; return false;
@ -210,10 +222,9 @@ public:
bool reduced_costs_are_correct_tableau() const { bool reduced_costs_are_correct_tableau() const {
if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
return true; return true;
SASSERT(m_A.is_correct()); lp_assert(m_A.is_correct());
if (m_using_infeas_costs) { if (m_using_infeas_costs) {
if (infeasibility_costs_are_correct() == false) { if (infeasibility_costs_are_correct() == false) {
std::cout << "infeasibility_costs_are_correct() does not hold" << std::endl;
return false; return false;
} }
} }
@ -222,9 +233,6 @@ public:
for (unsigned j = 0; j < n; j++) { for (unsigned j = 0; j < n; j++) {
if (m_basis_heading[j] >= 0) { if (m_basis_heading[j] >= 0) {
if (!is_zero(m_d[j])) { if (!is_zero(m_d[j])) {
std::cout << "case a\n";
print_column_info(j, std::cout);
return false; return false;
} }
} else { } else {
@ -233,8 +241,6 @@ public:
d -= this->m_costs[this->m_basis[cc.m_i]] * this->m_A.get_val(cc); d -= this->m_costs[this->m_basis[cc.m_i]] * this->m_A.get_val(cc);
} }
if (m_d[j] != d) { if (m_d[j] != d) {
std::cout << "case b\n";
print_column_info(j, std::cout);
return false; return false;
} }
} }
@ -253,14 +259,14 @@ public:
} }
bool x_below_low_bound(unsigned p) const { 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_costs_are_correct() const;
bool infeasibility_cost_is_correct_for_column(unsigned j) const; bool infeasibility_cost_is_correct_for_column(unsigned j) const;
bool x_above_low_bound(unsigned p) const { bool x_above_lower_bound(unsigned p) const {
return above_bound(m_x[p], m_low_bounds[p]); return above_bound(m_x[p], m_lower_bounds[p]);
} }
bool x_below_upper_bound(unsigned p) const { bool x_below_upper_bound(unsigned p) const {
@ -271,15 +277,15 @@ public:
bool x_above_upper_bound(unsigned p) const { bool x_above_upper_bound(unsigned p) const {
return above_bound(m_x[p], m_upper_bounds[p]); return above_bound(m_x[p], m_upper_bounds[p]);
} }
bool x_is_at_low_bound(unsigned j) const { bool x_is_at_lower_bound(unsigned j) const {
return at_bound(m_x[j], m_low_bounds[j]); return at_bound(m_x[j], m_lower_bounds[j]);
} }
bool x_is_at_upper_bound(unsigned j) const { bool x_is_at_upper_bound(unsigned j) const {
return at_bound(m_x[j], m_upper_bounds[j]); return at_bound(m_x[j], m_upper_bounds[j]);
} }
bool x_is_at_bound(unsigned j) const { 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; bool column_is_feasible(unsigned j) const;
@ -318,8 +324,8 @@ public:
void fill_reduced_costs_from_m_y_by_rows(); void fill_reduced_costs_from_m_y_by_rows();
void copy_rs_to_xB(vector<X> & rs); void copy_rs_to_xB(vector<X> & rs);
virtual bool low_bounds_are_set() const { return false; } virtual bool lower_bounds_are_set() const { return false; }
X low_bound_value(unsigned j) const { return m_low_bounds[j]; } X lower_bound_value(unsigned j) const { return m_lower_bounds[j]; }
X upper_bound_value(unsigned j) const { return m_upper_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]; } column_type get_column_type(unsigned j) const {return m_column_types[j]; }
@ -329,7 +335,7 @@ public:
} }
X bound_span(unsigned j) const { 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; std::string column_name(unsigned column) const;
@ -357,21 +363,21 @@ public:
case column_type::fixed: case column_type::fixed:
if (x_is_at_bound(j)) if (x_is_at_bound(j))
break; break;
m_x[j] = m_low_bounds[j]; m_x[j] = m_lower_bounds[j];
return true; return true;
case column_type::boxed: case column_type::boxed:
if (x_is_at_bound(j)) if (x_is_at_bound(j))
break; // we should preserve x if possible break; // we should preserve x if possible
// snap randomly // snap randomly
if (m_settings.random_next() % 2 == 1) if (m_settings.random_next() % 2 == 1)
m_x[j] = m_low_bounds[j]; m_x[j] = m_lower_bounds[j];
else else
m_x[j] = m_upper_bounds[j]; m_x[j] = m_upper_bounds[j];
return true; return true;
case column_type::low_bound: case column_type::lower_bound:
if (x_is_at_low_bound(j)) if (x_is_at_lower_bound(j))
break; break;
m_x[j] = m_low_bounds[j]; m_x[j] = m_lower_bounds[j];
return true; return true;
case column_type::upper_bound: case column_type::upper_bound:
if (x_is_at_upper_bound(j)) if (x_is_at_upper_bound(j))
@ -385,50 +391,47 @@ public:
} }
bool make_column_feasible(unsigned j, numeric_pair<mpq> & delta) { bool make_column_feasible(unsigned j, numeric_pair<mpq> & delta) {
SASSERT(m_basis_heading[j] < 0); bool ret = false;
lp_assert(m_basis_heading[j] < 0);
auto & x = m_x[j]; auto & x = m_x[j];
switch (m_column_types[j]) { switch (m_column_types[j]) {
case column_type::fixed: case column_type::fixed:
SASSERT(m_low_bounds[j] == m_upper_bounds[j]); lp_assert(m_lower_bounds[j] == m_upper_bounds[j]);
if (x != m_low_bounds[j]) { if (x != m_lower_bounds[j]) {
delta = m_low_bounds[j] - x; delta = m_lower_bounds[j] - x;
x = m_low_bounds[j]; ret = true;;
return true;
} }
break; break;
case column_type::boxed: case column_type::boxed:
if (x < m_low_bounds[j]) { if (x < m_lower_bounds[j]) {
delta = m_low_bounds[j] - x; delta = m_lower_bounds[j] - x;
x = m_low_bounds[j]; ret = true;;
return true;
} }
if (x > m_upper_bounds[j]) { if (x > m_upper_bounds[j]) {
delta = m_upper_bounds[j] - x; delta = m_upper_bounds[j] - x;
x = m_upper_bounds[j]; ret = true;
return true;
} }
break; break;
case column_type::low_bound: case column_type::lower_bound:
if (x < m_low_bounds[j]) { if (x < m_lower_bounds[j]) {
delta = m_low_bounds[j] - x; delta = m_lower_bounds[j] - x;
x = m_low_bounds[j]; ret = true;
return true;
} }
break; break;
case column_type::upper_bound: case column_type::upper_bound:
if (x > m_upper_bounds[j]) { if (x > m_upper_bounds[j]) {
delta = m_upper_bounds[j] - x; delta = m_upper_bounds[j] - x;
x = m_upper_bounds[j]; ret = true;
return true;
} }
break; break;
case column_type::free_column:
break;
default: default:
SASSERT(false);
break; break;
} }
return false; if (ret)
add_delta_to_x_and_call_tracker(j, delta);
return ret;
} }
@ -444,6 +447,8 @@ public:
void init_lu(); void init_lu();
int pivots_in_column_and_row_are_different(int entering, int leaving) const; int pivots_in_column_and_row_are_different(int entering, int leaving) const;
void pivot_fixed_vars_from_basis(); void pivot_fixed_vars_from_basis();
bool remove_from_basis(unsigned j);
bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector<T> & w);
bool pivot_for_tableau_on_basis(); bool pivot_for_tableau_on_basis();
bool pivot_row_for_tableau_on_basis(unsigned row); bool pivot_row_for_tableau_on_basis(unsigned row);
void init_basic_part_of_basis_heading() { void init_basic_part_of_basis_heading() {
@ -473,7 +478,7 @@ public:
} }
void change_basis_unconditionally(unsigned entering, unsigned leaving) { 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]; int place_in_non_basis = -1 - m_basis_heading[entering];
if (static_cast<unsigned>(place_in_non_basis) >= m_nbasis.size()) { if (static_cast<unsigned>(place_in_non_basis) >= m_nbasis.size()) {
// entering variable in not in m_nbasis, we need to put it back; // 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) { 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_basis = m_basis_heading[leaving];
int place_in_non_basis = - m_basis_heading[entering] - 1; int place_in_non_basis = - m_basis_heading[entering] - 1;
@ -522,8 +528,8 @@ public:
if (!this->x_is_at_bound(j)) if (!this->x_is_at_bound(j))
return false; return false;
break; break;
case column_type::low_bound: case column_type::lower_bound:
if (!this->x_is_at_low_bound(j)) if (!this->x_is_at_lower_bound(j))
return false; return false;
break; break;
case column_type::upper_bound: case column_type::upper_bound:
@ -533,7 +539,7 @@ public:
case column_type::free_column: case column_type::free_column:
break; break;
default: default:
SASSERT(false); lp_assert(false);
break; break;
} }
return true; return true;
@ -541,7 +547,6 @@ public:
bool non_basic_columns_are_set_correctly() const { bool non_basic_columns_are_set_correctly() const {
for (unsigned j : this->m_nbasis) for (unsigned j : this->m_nbasis)
if (!column_is_feasible(j)) { if (!column_is_feasible(j)) {
print_column_info(j, std::cout);
return false; return false;
} }
return true; return true;
@ -552,10 +557,10 @@ public:
switch (m_column_types[j]) { switch (m_column_types[j]) {
case column_type::fixed: case column_type::fixed:
case column_type::boxed: 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; break;
case column_type::low_bound: case column_type::lower_bound:
out << m_low_bounds[j] << std::endl; out << m_lower_bounds[j] << std::endl;
break; break;
case column_type::upper_bound: case column_type::upper_bound:
out << m_upper_bounds[j] << std::endl; out << m_upper_bounds[j] << std::endl;
@ -566,36 +571,38 @@ public:
} }
void print_column_info(unsigned j, std::ostream & out) const { 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]) { switch (m_column_types[j]) {
case column_type::fixed: case column_type::fixed:
case column_type::boxed: 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; break;
case column_type::low_bound: case column_type::lower_bound:
out << m_low_bounds[j] << std::endl; out << " [" << m_lower_bounds[j] << "," << "oo" << "]";
break; break;
case column_type::upper_bound: case column_type::upper_bound:
out << m_upper_bounds[j] << std::endl; out << " [-oo, " << m_upper_bounds[j] << ']';
break; break;
case column_type::free_column: case column_type::free_column:
out << " [-oo, oo]";
break; break;
default: default:
SASSERT(false); lp_assert(false);
} }
std::cout << "basis heading = " << m_basis_heading[j] << std::endl; // out << "basis heading = " << m_basis_heading[j] << std::endl;
std::cout << "x = " << m_x[j] << std::endl; out << " x = " << m_x[j];
/* if (m_basis_heading[j] >= 0)
std::cout << "cost = " << m_costs[j] << std::endl; out << " base\n";
std:: cout << "m_d = " << m_d[j] << std::endl;*/ 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]) { switch(m_column_types[j]) {
case column_type::free_column: case column_type::free_column:
case column_type::low_bound: case column_type::lower_bound:
return false; return false;
default: default:
return true; return true;
@ -605,13 +612,13 @@ public:
bool bounds_for_boxed_are_set_correctly() const { bool bounds_for_boxed_are_set_correctly() const {
for (unsigned j = 0; j < m_column_types.size(); j++) { for (unsigned j = 0; j < m_column_types.size(); j++) {
if (m_column_types[j] != column_type::boxed) continue; 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 false;
} }
return true; return true;
} }
bool column_has_low_bound(unsigned j) { bool column_has_lower_bound(unsigned j) const {
switch(m_column_types[j]) { switch(m_column_types[j]) {
case column_type::free_column: case column_type::free_column:
case column_type::upper_bound: case column_type::upper_bound:
@ -671,26 +678,43 @@ public:
return m_inf_set.contains(j); return m_inf_set.contains(j);
} }
void update_column_in_inf_set(unsigned j) { void update_x_with_feasibility_tracking(unsigned j, const X & v) {
if (column_is_feasible(j)) { m_x[j] = v;
m_inf_set.erase(j); track_column_feasibility(j);
} else { }
m_inf_set.insert(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) { void insert_column_into_inf_set(unsigned j) {
m_inf_set.insert(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) { void remove_column_from_inf_set(unsigned j) {
m_inf_set.erase(j); m_inf_set.erase(j);
SASSERT(column_is_feasible(j)); lp_assert(column_is_feasible(j));
} }
bool costs_on_nbasis_are_zeros() const { 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++) { for (unsigned j = 0; j < this->m_n(); j++) {
if (this->m_basis_heading[j] < 0) if (this->m_basis_heading[j] < 0)
SASSERT(is_zero(this->m_costs[j])); lp_assert(is_zero(this->m_costs[j]));
} }
return true; return true;
} }
@ -701,5 +725,10 @@ public:
const unsigned & iters_with_no_cost_growing() const { const unsigned & iters_with_no_cost_growing() const {
return m_iters_with_no_cost_growing; 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];
}
}; };
} }

View file

@ -35,11 +35,11 @@ lp_core_solver_base(static_matrix<T, X> & A,
lp_settings & settings, lp_settings & settings,
const column_namer& column_names, const column_namer& column_names,
const vector<column_type> & column_types, const vector<column_type> & column_types,
const vector<X> & low_bound_values, const vector<X> & lower_bound_values,
const vector<X> & upper_bound_values): const vector<X> & upper_bound_values):
m_total_iterations(0), m_total_iterations(0),
m_iters_with_no_cost_growing(0), m_iters_with_no_cost_growing(0),
m_status(FEASIBLE), m_status(lp_status::FEASIBLE),
m_inf_set(A.column_count()), m_inf_set(A.column_count()),
m_using_infeas_costs(false), m_using_infeas_costs(false),
m_pivot_row_of_B_1(A.row_count()), m_pivot_row_of_B_1(A.row_count()),
@ -59,7 +59,7 @@ lp_core_solver_base(static_matrix<T, X> & A,
m_d(m_n()), m_d(m_n()),
m_ed(m_m()), m_ed(m_m()),
m_column_types(column_types), m_column_types(column_types),
m_low_bounds(low_bound_values), m_lower_bounds(lower_bound_values),
m_upper_bounds(upper_bound_values), m_upper_bounds(upper_bound_values),
m_column_norms(m_n()), m_column_norms(m_n()),
m_copy_of_xB(m_m()), m_copy_of_xB(m_m()),
@ -68,7 +68,7 @@ lp_core_solver_base(static_matrix<T, X> & A,
m_tracing_basis_changes(false), m_tracing_basis_changes(false),
m_pivoted_rows(nullptr), m_pivoted_rows(nullptr),
m_look_for_feasible_solution_only(false) { m_look_for_feasible_solution_only(false) {
SASSERT(bounds_for_boxed_are_set_correctly()); lp_assert(bounds_for_boxed_are_set_correctly());
init(); init();
init_basis_heading_and_non_basic_columns_vector(); init_basis_heading_and_non_basic_columns_vector();
} }
@ -76,7 +76,7 @@ lp_core_solver_base(static_matrix<T, X> & A,
template <typename T, typename X> void lp_core_solver_base<T, X>:: template <typename T, typename X> void lp_core_solver_base<T, X>::
allocate_basis_heading() { // the rest of initilization will be handled by the factorization class allocate_basis_heading() { // the rest of initilization will be handled by the factorization class
init_basis_heading_and_non_basic_columns_vector(); init_basis_heading_and_non_basic_columns_vector();
SASSERT(basis_heading_is_correct()); lp_assert(basis_heading_is_correct());
} }
template <typename T, typename X> void lp_core_solver_base<T, X>:: template <typename T, typename X> void lp_core_solver_base<T, X>::
init() { init() {
@ -142,7 +142,7 @@ solve_yB(vector<T> & y) {
// } // }
// } // }
template <typename T, typename X> void lp_core_solver_base<T, X>::solve_Bd(unsigned entering, indexed_vector<T> & column) { template <typename T, typename X> void lp_core_solver_base<T, X>::solve_Bd(unsigned entering, indexed_vector<T> & column) {
SASSERT(!m_settings.use_tableau()); lp_assert(!m_settings.use_tableau());
if (m_factorization == nullptr) { if (m_factorization == nullptr) {
init_factorization(m_factorization, m_A, m_basis, m_settings); init_factorization(m_factorization, m_A, m_basis, m_settings);
} }
@ -152,19 +152,19 @@ template <typename T, typename X> void lp_core_solver_base<T, X>::solve_Bd(unsig
template <typename T, typename X> void lp_core_solver_base<T, X>:: template <typename T, typename X> void lp_core_solver_base<T, X>::
solve_Bd(unsigned entering) { solve_Bd(unsigned entering) {
SASSERT(m_ed.is_OK()); lp_assert(m_ed.is_OK());
m_factorization->solve_Bd(entering, m_ed, m_w); m_factorization->solve_Bd(entering, m_ed, m_w);
if (this->precise()) if (this->precise())
m_columns_nz[entering] = m_ed.m_index.size(); m_columns_nz[entering] = m_ed.m_index.size();
SASSERT(m_ed.is_OK()); lp_assert(m_ed.is_OK());
SASSERT(m_w.is_OK()); lp_assert(m_w.is_OK());
#ifdef Z3DEBUG #ifdef Z3DEBUG
// auto B = get_B(*m_factorization, m_basis); // auto B = get_B(*m_factorization, m_basis);
// vector<T> a(m_m()); // vector<T> a(m_m());
// m_A.copy_column_to_vector(entering, a); // m_A.copy_column_to_vector(entering, a);
// vector<T> cd(m_ed.m_data); // vector<T> cd(m_ed.m_data);
// B.apply_from_left(cd, m_settings); // B.apply_from_left(cd, m_settings);
// SASSERT(vectors_are_equal(cd , a)); // lp_assert(vectors_are_equal(cd , a));
#endif #endif
} }
@ -223,16 +223,11 @@ restore_m_ed(T * buffer) {
template <typename T, typename X> bool lp_core_solver_base<T, X>:: template <typename T, typename X> bool lp_core_solver_base<T, X>::
A_mult_x_is_off() const { 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<T>::precise()) { if (numeric_traits<T>::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); X delta = m_b[i] - m_A.dot_product_with_row(i, m_x);
if (delta != numeric_traits<X>::zero()) { if (delta != numeric_traits<X>::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; return true;
} }
} }
@ -259,17 +254,12 @@ A_mult_x_is_off() const {
} }
template <typename T, typename X> bool lp_core_solver_base<T, X>:: template <typename T, typename X> bool lp_core_solver_base<T, X>::
A_mult_x_is_off_on_index(const vector<unsigned> & index) const { A_mult_x_is_off_on_index(const vector<unsigned> & index) const {
SASSERT(m_x.size() == m_A.column_count()); lp_assert(m_x.size() == m_A.column_count());
if (numeric_traits<T>::precise()) return false; if (numeric_traits<T>::precise()) return false;
#if RUN_A_MULT_X_IS_OFF_FOR_PRECESE #if RUN_A_MULT_X_IS_OFF_FOR_PRECESE
for (unsigned i : index) { for (unsigned i : index) {
X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); X delta = m_b[i] - m_A.dot_product_with_row(i, m_x);
if (delta != numeric_traits<X>::zero()) { if (delta != numeric_traits<X>::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; return true;
} }
} }
@ -299,13 +289,13 @@ A_mult_x_is_off_on_index(const vector<unsigned> & index) const {
// from page 182 of Istvan Maros's book // from page 182 of Istvan Maros's book
template <typename T, typename X> void lp_core_solver_base<T, X>:: template <typename T, typename X> void lp_core_solver_base<T, X>::
calculate_pivot_row_of_B_1(unsigned pivot_row) { calculate_pivot_row_of_B_1(unsigned pivot_row) {
SASSERT(! use_tableau()); lp_assert(! use_tableau());
SASSERT(m_pivot_row_of_B_1.is_OK()); lp_assert(m_pivot_row_of_B_1.is_OK());
m_pivot_row_of_B_1.clear(); m_pivot_row_of_B_1.clear();
m_pivot_row_of_B_1.set_value(numeric_traits<T>::one(), pivot_row); m_pivot_row_of_B_1.set_value(numeric_traits<T>::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); 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()) { for (unsigned j : non_basis()) {
switch (m_column_types[j]) { switch (m_column_types[j]) {
case column_type::boxed: 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; break;
case column_type::low_bound: case column_type::lower_bound:
m_x[j] = m_low_bounds[j]; m_x[j] = m_lower_bounds[j];
SASSERT(column_is_dual_feasible(j)); lp_assert(column_is_dual_feasible(j));
break; break;
case column_type::upper_bound: case column_type::upper_bound:
m_x[j] = m_upper_bounds[j]; m_x[j] = m_upper_bounds[j];
SASSERT(column_is_dual_feasible(j)); lp_assert(column_is_dual_feasible(j));
break; break;
default: default:
break; break;
@ -411,21 +401,18 @@ column_is_dual_feasible(unsigned j) const {
switch (m_column_types[j]) { switch (m_column_types[j]) {
case column_type::fixed: case column_type::fixed:
case column_type::boxed: 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)); (x_is_at_upper_bound(j) && d_is_not_positive(j));
case column_type::low_bound: case column_type::lower_bound:
return x_is_at_low_bound(j) && d_is_not_negative(j); return x_is_at_lower_bound(j) && d_is_not_negative(j);
case column_type::upper_bound: case column_type::upper_bound:
LP_OUT(m_settings, "upper_bound type should be switched to low_bound" << std::endl); lp_assert(false); // impossible case
SASSERT(false); // impossible case
case column_type::free_column: case column_type::free_column:
return numeric_traits<X>::is_zero(m_d[j]); return numeric_traits<X>::is_zero(m_d[j]);
default: default:
LP_OUT(m_settings, "column = " << j << std::endl); lp_unreachable();
LP_OUT(m_settings, "unexpected column type = " << column_type_to_string(m_column_types[j]) << std::endl);
SASSERT(false);
} }
SASSERT(false); lp_unreachable();
return false; return false;
} }
template <typename T, typename X> bool lp_core_solver_base<T, X>:: template <typename T, typename X> bool lp_core_solver_base<T, X>::
@ -484,14 +471,14 @@ template <typename T, typename X> bool lp_core_solver_base<T, X>::column_is_feas
case column_type::boxed: case column_type::boxed:
if (this->above_bound(x, this->m_upper_bounds[j])) { if (this->above_bound(x, this->m_upper_bounds[j])) {
return false; 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; return false;
} else { } else {
return true; return true;
} }
break; break;
case column_type::low_bound: case column_type::lower_bound:
if (this->below_bound(x, this->m_low_bounds[j])) { if (this->below_bound(x, this->m_lower_bounds[j])) {
return false; return false;
} else { } else {
return true; return true;
@ -508,7 +495,7 @@ template <typename T, typename X> bool lp_core_solver_base<T, X>::column_is_feas
return true; return true;
break; break;
default: default:
SASSERT(false); lp_unreachable();
} }
return false; // it is unreachable return false; // it is unreachable
} }
@ -530,9 +517,6 @@ template <typename T, typename X> bool lp_core_solver_base<T, X>::inf_set_is_cor
bool is_feas = column_is_feasible(j); bool is_feas = column_is_feasible(j);
if (is_feas == belongs_to_set) { 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; return false;
} }
} }
@ -549,7 +533,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) {
if (!find_x_by_solving()) { if (!find_x_by_solving()) {
restore_x(entering, tt); restore_x(entering, tt);
if(A_mult_x_is_off()) { if(A_mult_x_is_off()) {
m_status = FLOATING_POINT_ERROR; m_status = lp_status::FLOATING_POINT_ERROR;
m_iters_with_no_cost_growing++; m_iters_with_no_cost_growing++;
return false; 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) { if (m_factorization->get_status() != LU_status::OK) {
std::stringstream s; std::stringstream s;
// s << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations(); // 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;
} }
return false; return false;
@ -581,19 +565,19 @@ update_basis_and_x(int entering, int leaving, X const & tt) {
init_lu(); init_lu();
if (m_factorization->get_status() != LU_status::OK) { if (m_factorization->get_status() != LU_status::OK) {
if (m_look_for_feasible_solution_only && !precise()) { if (m_look_for_feasible_solution_only && !precise()) {
m_status = UNSTABLE; m_status = lp_status::UNSTABLE;
delete m_factorization; delete m_factorization;
m_factorization = nullptr; m_factorization = nullptr;
return false; return false;
} }
// LP_OUT(m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations() << std::endl); // LP_OUT(m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations() << std::endl);
restore_x_and_refactor(entering, leaving, tt); restore_x_and_refactor(entering, leaving, tt);
if (m_status == FLOATING_POINT_ERROR) if (m_status == lp_status::FLOATING_POINT_ERROR)
return false; return false;
SASSERT(!A_mult_x_is_off()); CASSERT("A_off", !A_mult_x_is_off());
m_iters_with_no_cost_growing++; m_iters_with_no_cost_growing++;
// LP_OUT(m_settings, "rolled back after failing of init_factorization()" << std::endl); // LP_OUT(m_settings, "rolled back after failing of init_factorization()" << std::endl);
m_status = UNSTABLE; m_status = lp_status::UNSTABLE;
return false; return false;
} }
return true; return true;
@ -602,7 +586,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) {
template <typename T, typename X> bool lp_core_solver_base<T, X>:: template <typename T, typename X> bool lp_core_solver_base<T, X>::
divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) {
SASSERT(numeric_traits<T>::precise()); lp_assert(numeric_traits<T>::precise());
int pivot_index = -1; int pivot_index = -1;
auto & row = m_A.m_rows[pivot_row]; auto & row = m_A.m_rows[pivot_row];
unsigned size = row.size(); unsigned size = row.size();
@ -629,7 +613,7 @@ divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) {
} }
template <typename T, typename X> bool lp_core_solver_base<T, X>:: template <typename T, typename X> bool lp_core_solver_base<T, X>::
pivot_column_tableau(unsigned j, unsigned piv_row_index) { 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; return false;
auto &column = m_A.m_columns[j]; auto &column = m_A.m_columns[j];
int pivot_col_cell_index = -1; int pivot_col_cell_index = -1;
@ -643,7 +627,7 @@ pivot_column_tableau(unsigned j, unsigned piv_row_index) {
return false; return false;
if (pivot_col_cell_index != 0) { if (pivot_col_cell_index != 0) {
SASSERT(column.size() > 1); lp_assert(column.size() > 1);
// swap the pivot column cell with the head cell // swap the pivot column cell with the head cell
auto c = column[0]; auto c = column[0];
column[0] = column[pivot_col_cell_index]; column[0] = column[pivot_col_cell_index];
@ -654,7 +638,7 @@ pivot_column_tableau(unsigned j, unsigned piv_row_index) {
} }
while (column.size() > 1) { while (column.size() > 1) {
auto & c = column.back(); 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)) { if(! m_A.pivot_row_to_row_given_cell(piv_row_index, c, j)) {
return false; return false;
} }
@ -702,7 +686,7 @@ non_basis_is_correctly_represented_in_heading() const {
} }
for (unsigned j = 0; j < m_A.column_count(); j++) { for (unsigned j = 0; j < m_A.column_count(); j++) {
if (m_basis_heading[j] >= 0) { if (m_basis_heading[j] >= 0) {
SASSERT(static_cast<unsigned>(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j); lp_assert(static_cast<unsigned>(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j);
} }
} }
return true; return true;
@ -710,26 +694,22 @@ non_basis_is_correctly_represented_in_heading() const {
template <typename T, typename X> bool lp_core_solver_base<T, X>:: template <typename T, typename X> bool lp_core_solver_base<T, X>::
basis_heading_is_correct() const { basis_heading_is_correct() const {
SASSERT(m_basis_heading.size() == m_A.column_count()); lp_assert(m_basis_heading.size() == m_A.column_count());
SASSERT(m_basis.size() == m_A.row_count()); lp_assert(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_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()) { if (!basis_has_no_doubles()) {
// std::cout << "basis_has_no_doubles" << std::endl;
return false; return false;
} }
if (!non_basis_has_no_doubles()) { if (!non_basis_has_no_doubles()) {
// std::cout << "non_basis_has_no_doubles" << std::endl;
return false; return false;
} }
if (!basis_is_correctly_represented_in_heading()) { if (!basis_is_correctly_represented_in_heading()) {
// std::cout << "basis_is_correctly_represented_in_heading" << std::endl;
return false; return false;
} }
if (!non_basis_is_correctly_represented_in_heading()) { if (!non_basis_is_correctly_represented_in_heading()) {
// std::cout << "non_basis_is_correctly_represented_in_heading" << std::endl;
return false; return false;
} }
@ -856,12 +836,12 @@ solve_Ax_eq_b() {
template <typename T, typename X> void lp_core_solver_base<T, X>:: template <typename T, typename X> void lp_core_solver_base<T, X>::
snap_non_basic_x_to_bound_and_free_to_zeroes() { snap_non_basic_x_to_bound_and_free_to_zeroes() {
for (unsigned j : non_basis()) { for (unsigned j : non_basis()) {
SASSERT(j < m_x.size()); lp_assert(j < m_x.size());
switch (m_column_types[j]) { switch (m_column_types[j]) {
case column_type::fixed: case column_type::fixed:
case column_type::boxed: case column_type::boxed:
case column_type::low_bound: case column_type::lower_bound:
m_x[j] = m_low_bounds[j]; m_x[j] = m_lower_bounds[j];
break; break;
case column_type::upper_bound: case column_type::upper_bound:
m_x[j] = m_upper_bounds[j]; m_x[j] = m_upper_bounds[j];
@ -894,23 +874,23 @@ template <typename T, typename X> non_basic_column_value_position lp_core_solver
get_non_basic_column_value_position(unsigned j) const { get_non_basic_column_value_position(unsigned j) const {
switch (m_column_types[j]) { switch (m_column_types[j]) {
case column_type::fixed: 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: case column_type::free_column:
return free_of_bounds; return free_of_bounds;
case column_type::boxed: 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: x_is_at_upper_bound(j)? at_upper_bound:
not_at_bound not_at_bound
); );
case column_type::low_bound: case column_type::lower_bound:
return x_is_at_low_bound(j)? at_low_bound : not_at_bound; return x_is_at_lower_bound(j)? at_lower_bound : not_at_bound;
case column_type::upper_bound: case column_type::upper_bound:
return x_is_at_upper_bound(j)? at_upper_bound : not_at_bound; return x_is_at_upper_bound(j)? at_upper_bound : not_at_bound;
default: default:
SASSERT(false); lp_unreachable();
} }
SASSERT(false); lp_unreachable();
return at_low_bound; return at_lower_bound;
} }
template <typename T, typename X> void lp_core_solver_base<T, X>::init_lu() { template <typename T, typename X> void lp_core_solver_base<T, X>::init_lu() {
@ -938,59 +918,80 @@ template <typename T, typename X> void lp_core_solver_base<T, X>::transpose_row
transpose_basis(i, j); transpose_basis(i, j);
m_A.transpose_rows(i, j); m_A.transpose_rows(i, j);
} }
// j is the new basic column, j_basic - the leaving column
template <typename T, typename X> bool lp_core_solver_base<T, X>::pivot_column_general(unsigned j, unsigned j_basic, indexed_vector<T> & 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<T>(), 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 <typename T, typename X> void lp_core_solver_base<T, X>::pivot_fixed_vars_from_basis() { template <typename T, typename X> void lp_core_solver_base<T, X>::pivot_fixed_vars_from_basis() {
// run over basis and non-basis at the same time // run over basis and non-basis at the same time
indexed_vector<T> w(m_basis.size()); // the buffer indexed_vector<T> w(m_basis.size()); // the buffer
unsigned i = 0; // points to basis unsigned i = 0; // points to basis
unsigned j = 0; // points to nonbasis for (; i < m_basis.size(); i++) {
for (; i < m_basis.size() && j < m_nbasis.size(); i++) { unsigned basic_j = m_basis[i];
unsigned ii = m_basis[i];
unsigned jj;
if (get_column_type(ii) != column_type::fixed) continue; if (get_column_type(basic_j) != column_type::fixed) continue;
while (j < m_nbasis.size()) { T a;
for (; j < m_nbasis.size(); j++) { unsigned j;
jj = m_nbasis[j]; for (auto &c : m_A.m_rows[i]) {
if (get_column_type(jj) != column_type::fixed) 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; 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<T>(), 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 <typename T, typename X> bool lp_core_solver_base<T, X>::remove_from_basis(unsigned basic_j) {
indexed_vector<T> 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 <typename T, typename X> bool template <typename T, typename X> bool
lp_core_solver_base<T, X>::infeasibility_costs_are_correct() const { lp_core_solver_base<T, X>::infeasibility_costs_are_correct() const {
if (! this->m_using_infeas_costs) if (! this->m_using_infeas_costs)
return true; return true;
SASSERT(costs_on_nbasis_are_zeros()); lp_assert(costs_on_nbasis_are_zeros());
for (unsigned j :this->m_basis) { for (unsigned j :this->m_basis) {
if (!infeasibility_cost_is_correct_for_column(j)) { 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; return false;
} }
if (!is_zero(m_d[j])) { if (!is_zero(m_d[j])) {
std::cout << "m_d is not zero\n";
print_column_info(j, std::cout);
return false; return false;
} }
} }
@ -1012,7 +1013,7 @@ lp_core_solver_base<T, X>::infeasibility_cost_is_correct_for_column(unsigned j)
} }
return is_zero(this->m_costs[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)) { if (this->x_below_low_bound(j)) {
return this->m_costs[j] == -r; return this->m_costs[j] == -r;
} }
@ -1026,9 +1027,31 @@ lp_core_solver_base<T, X>::infeasibility_cost_is_correct_for_column(unsigned j)
case column_type::free_column: case column_type::free_column:
return is_zero(this->m_costs[j]); return is_zero(this->m_costs[j]);
default: default:
SASSERT(false); lp_assert(false);
return true; return true;
} }
} }
template <typename T, typename X>
void lp_core_solver_base<T, X>::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);
}
} }

View file

@ -22,7 +22,7 @@ Revision History:
#include <string> #include <string>
#include "util/vector.h" #include "util/vector.h"
#include <functional> #include <functional>
#include "util/lp/lp_dual_core_solver.hpp" #include "util/lp/lp_dual_core_solver_def.h"
template void lp::lp_dual_core_solver<lp::mpq, lp::mpq>::start_with_initial_basis_and_make_it_dual_feasible(); template void lp::lp_dual_core_solver<lp::mpq, lp::mpq>::start_with_initial_basis_and_make_it_dual_feasible();
template void lp::lp_dual_core_solver<lp::mpq, lp::mpq>::solve(); template void lp::lp_dual_core_solver<lp::mpq, lp::mpq>::solve();
template lp::lp_dual_core_solver<double, double>::lp_dual_core_solver(lp::static_matrix<double, double>&, vector<bool>&, template lp::lp_dual_core_solver<double, double>::lp_dual_core_solver(lp::static_matrix<double, double>&, vector<bool>&,

View file

@ -56,7 +56,7 @@ public:
vector<int> & heading, vector<int> & heading,
vector<T> & costs, vector<T> & costs,
vector<column_type> & column_type_array, vector<column_type> & column_type_array,
vector<X> & low_bound_values, vector<X> & lower_bound_values,
vector<X> & upper_bound_values, vector<X> & upper_bound_values,
lp_settings & settings, lp_settings & settings,
const column_namer & column_names): const column_namer & column_names):
@ -70,7 +70,7 @@ public:
settings, settings,
column_names, column_names,
column_type_array, column_type_array,
low_bound_values, lower_bound_values,
upper_bound_values), upper_bound_values),
m_can_enter_basis(can_enter_basis), m_can_enter_basis(can_enter_basis),
m_a_wave(this->m_m()), m_a_wave(this->m_m()),
@ -110,7 +110,7 @@ public:
bool done(); 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); 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 // 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) { 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(); void add_tight_breakpoints_and_q_to_flipped_set();
@ -207,6 +207,6 @@ public:
void solve(); void solve();
bool low_bounds_are_set() const override { return true; } bool lower_bounds_are_set() const override { return true; }
}; };
} }

View file

@ -38,7 +38,7 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_non_ba
while (j--) { while (j--) {
if (this->m_basis_heading[j] >= 0 ) continue; if (this->m_basis_heading[j] >= 0 ) continue;
if (m_can_enter_basis[j]) { 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); nb.push_back(j);
this->m_basis_heading[j] = - static_cast<int>(nb.size()); this->m_basis_heading[j] = - static_cast<int>(nb.size());
} }
@ -97,25 +97,25 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::start_with_ini
} }
template <typename T, typename X> bool lp_dual_core_solver<T, X>::done() { template <typename T, typename X> bool lp_dual_core_solver<T, X>::done() {
if (this->get_status() == OPTIMAL) { if (this->get_status() == lp_status::OPTIMAL) {
return true; return true;
} }
if (this->total_iterations() > this->m_settings.max_total_number_of_iterations) { // debug !!!! 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 true;
} }
return false; // todo, need to be more cases return false; // todo, need to be more cases
} }
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_low_bound(unsigned p) { template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_lower_bound(unsigned p) {
SASSERT(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m()); lp_assert(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m());
T del = this->m_x[p] - this->m_low_bounds[p]; T del = this->m_x[p] - this->m_lower_bounds[p];
del *= del; del *= del;
return del / this->m_betas[this->m_basis_heading[p]]; return del / this->m_betas[this->m_basis_heading[p]];
} }
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_upper_bound(unsigned p) { template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_upper_bound(unsigned p) {
SASSERT(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m()); lp_assert(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m());
T del = this->m_x[p] - this->m_upper_bounds[p]; T del = this->m_x[p] - this->m_upper_bounds[p];
del *= del; del *= del;
return del / this->m_betas[this->m_basis_heading[p]]; return del / this->m_betas[this->m_basis_heading[p]];
@ -127,7 +127,7 @@ template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(u
case column_type::fixed: case column_type::fixed:
case column_type::boxed: case column_type::boxed:
if (this->x_below_low_bound(p)) { 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 del;
} }
if (this->x_above_upper_bound(p)) { if (this->x_above_upper_bound(p)) {
@ -135,9 +135,9 @@ template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(u
return del; return del;
} }
return numeric_traits<T>::zero(); return numeric_traits<T>::zero();
case column_type::low_bound: case column_type::lower_bound:
if (this->x_below_low_bound(p)) { 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 del;
} }
return numeric_traits<T>::zero(); return numeric_traits<T>::zero();
@ -150,12 +150,12 @@ template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(u
return numeric_traits<T>::zero(); return numeric_traits<T>::zero();
break; break;
case column_type::free_column: case column_type::free_column:
SASSERT(numeric_traits<T>::is_zero(this->m_d[p])); lp_assert(numeric_traits<T>::is_zero(this->m_d[p]));
return numeric_traits<T>::zero(); return numeric_traits<T>::zero();
default: default:
SASSERT(false); lp_unreachable();
} }
SASSERT(false); lp_unreachable();
return numeric_traits<T>::zero(); return numeric_traits<T>::zero();
} }
@ -185,8 +185,8 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::pricing_loop(u
} }
} while (i != initial_offset_in_rows && rows_left); } while (i != initial_offset_in_rows && rows_left);
if (m_r == -1) { if (m_r == -1) {
if (this->get_status() != UNSTABLE) { if (this->get_status() != lp_status::UNSTABLE) {
this->set_status(OPTIMAL); this->set_status(lp_status::OPTIMAL);
} }
} else { } else {
m_p = this->m_basis[m_r]; m_p = this->m_basis[m_r];
@ -196,10 +196,10 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::pricing_loop(u
return; return;
} }
// failure in advance_on_known_p // failure in advance_on_known_p
if (this->get_status() == FLOATING_POINT_ERROR) { if (this->get_status() == lp_status::FLOATING_POINT_ERROR) {
return; return;
} }
this->set_status(UNSTABLE); this->set_status(lp_status::UNSTABLE);
m_forbidden_rows.insert(m_r); m_forbidden_rows.insert(m_r);
} }
} }
@ -224,9 +224,9 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::advance_on_kno
int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p); int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p);
if (!pivot_compare_result){;} if (!pivot_compare_result){;}
else if (pivot_compare_result == 2) { // the sign is changed, cannot continue else if (pivot_compare_result == 2) { // the sign is changed, cannot continue
SASSERT(false); // not implemented yet lp_unreachable(); // not implemented yet
} else { } else {
SASSERT(pivot_compare_result == 1); lp_assert(pivot_compare_result == 1);
this->init_lu(); this->init_lu();
} }
DSE_FTran(); DSE_FTran();
@ -243,38 +243,38 @@ template <typename T, typename X> int lp_dual_core_solver<T, X>::define_sign_of_
if (this->x_above_upper_bound(m_p)) { if (this->x_above_upper_bound(m_p)) {
return 1; return 1;
} }
SASSERT(false); lp_unreachable();
case column_type::low_bound: case column_type::lower_bound:
if (this->x_below_low_bound(m_p)) { if (this->x_below_low_bound(m_p)) {
return -1; return -1;
} }
SASSERT(false); lp_unreachable();
case column_type::upper_bound: case column_type::upper_bound:
if (this->x_above_upper_bound(m_p)) { if (this->x_above_upper_bound(m_p)) {
return 1; return 1;
} }
SASSERT(false); lp_unreachable();
default: default:
SASSERT(false); lp_unreachable();
} }
SASSERT(false); lp_unreachable();
return 0; return 0;
} }
template <typename T, typename X> bool lp_dual_core_solver<T, X>::can_be_breakpoint(unsigned j) { template <typename T, typename X> bool lp_dual_core_solver<T, X>::can_be_breakpoint(unsigned j) {
if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false; if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false;
switch (this->m_column_types[j]) { switch (this->m_column_types[j]) {
case column_type::low_bound: case column_type::lower_bound:
SASSERT(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bounds[j])); 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; return m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
case column_type::upper_bound: 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; return m_sign_of_alpha_r * this->m_pivot_row[j] < 0;
case column_type::boxed: 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; 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 case column_type::fixed: // is always dual feasible so we ingore it
return false; return false;
@ -302,28 +302,28 @@ template <typename T, typename X> T lp_dual_core_solver<T, X>::get_delta() {
switch (this->m_column_types[m_p]) { switch (this->m_column_types[m_p]) {
case column_type::boxed: case column_type::boxed:
if (this->x_below_low_bound(m_p)) { 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)) { if (this->x_above_upper_bound(m_p)) {
return this->m_x[m_p] - this->m_upper_bounds[m_p]; return this->m_x[m_p] - this->m_upper_bounds[m_p];
} }
SASSERT(false); lp_unreachable();
case column_type::low_bound: case column_type::lower_bound:
if (this->x_below_low_bound(m_p)) { 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: case column_type::upper_bound:
if (this->x_above_upper_bound(m_p)) { if (this->x_above_upper_bound(m_p)) {
return get_edge_steepness_for_upper_bound(m_p); return get_edge_steepness_for_upper_bound(m_p);
} }
SASSERT(false); lp_unreachable();
case column_type::fixed: case column_type::fixed:
return this->m_x[m_p] - this->m_upper_bounds[m_p]; return this->m_x[m_p] - this->m_upper_bounds[m_p];
default: default:
SASSERT(false); lp_unreachable();
} }
SASSERT(false); lp_unreachable();
return zero_of_type<T>(); return zero_of_type<T>();
} }
@ -370,11 +370,11 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::update_betas()
template <typename T, typename X> void lp_dual_core_solver<T, X>::apply_flips() { template <typename T, typename X> void lp_dual_core_solver<T, X>::apply_flips() {
for (unsigned j : m_flipped_boxed) { for (unsigned j : m_flipped_boxed) {
SASSERT(this->x_is_at_bound(j)); lp_assert(this->x_is_at_bound(j));
if (this->x_is_at_low_bound(j)) { if (this->x_is_at_lower_bound(j)) {
this->m_x[j] = this->m_upper_bounds[j]; this->m_x[j] = this->m_upper_bounds[j];
} else { } else {
this->m_x[j] = this->m_low_bounds[j]; this->m_x[j] = this->m_lower_bounds[j];
} }
} }
} }
@ -382,17 +382,17 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::apply_flips()
template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_column_to_bounds(unsigned j) { template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_column_to_bounds(unsigned j) {
switch (this->m_column_type[j]) { switch (this->m_column_type[j]) {
case column_type::fixed: case column_type::fixed:
this->m_x[j] = this->m_low_bounds[j]; this->m_x[j] = this->m_lower_bounds[j];
break; break;
case column_type::boxed: case column_type::boxed:
if (this->x_is_at_low_bound(j)) { if (this->x_is_at_lower_bound(j)) {
this->m_x[j] = this->m_low_bounds[j]; this->m_x[j] = this->m_lower_bounds[j];
} else { } else {
this->m_x[j] = this->m_upper_bounds[j]; this->m_x[j] = this->m_upper_bounds[j];
} }
break; break;
case column_type::low_bound: case column_type::lower_bound:
this->m_x[j] = this->m_low_bounds[j]; this->m_x[j] = this->m_lower_bounds[j];
break; break;
case column_type::upper_bound: case column_type::upper_bound:
this->m_x[j] = this->m_upper_bounds[j]; this->m_x[j] = this->m_upper_bounds[j];
@ -400,7 +400,7 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_column
case column_type::free_column: case column_type::free_column:
break; break;
default: default:
SASSERT(false); lp_unreachable();
} }
} }
@ -456,15 +456,15 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::basis_change_a
return false; return false;
} }
SASSERT(d_is_correct()); lp_assert(d_is_correct());
return true; return true;
} }
template <typename T, typename X> void lp_dual_core_solver<T, X>::recover_leaving() { template <typename T, typename X> void lp_dual_core_solver<T, X>::recover_leaving() {
switch (m_entering_boundary_position) { switch (m_entering_boundary_position) {
case at_low_bound: case at_lower_bound:
case at_fixed: 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; break;
case at_upper_bound: case at_upper_bound:
this->m_x[m_q] = this->m_upper_bounds[m_q]; this->m_x[m_q] = this->m_upper_bounds[m_q];
@ -472,7 +472,7 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::recover_leavin
case free_of_bounds: case free_of_bounds:
this->m_x[m_q] = zero_of_type<X>(); this->m_x[m_q] = zero_of_type<X>();
default: default:
SASSERT(false); lp_unreachable();
} }
} }
@ -481,12 +481,12 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::revert_to_prev
this->change_basis_unconditionally(m_p, m_q); this->change_basis_unconditionally(m_p, m_q);
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings);
if (this->m_factorization->get_status() != LU_status::OK) { 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; return;
} }
recover_leaving(); recover_leaving();
if (!this->find_x_by_solving()) { if (!this->find_x_by_solving()) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
return; return;
} }
recalculate_xB_and_d(); recalculate_xB_and_d();
@ -497,23 +497,23 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::revert_to_prev
template <typename T, typename X> bool lp_dual_core_solver<T, X>::snap_runaway_nonbasic_column(unsigned j) { template <typename T, typename X> bool lp_dual_core_solver<T, X>::snap_runaway_nonbasic_column(unsigned j) {
switch (this->m_column_types[j]) { switch (this->m_column_types[j]) {
case column_type::fixed: case column_type::fixed:
case column_type::low_bound: case column_type::lower_bound:
if (!this->x_is_at_low_bound(j)) { if (!this->x_is_at_lower_bound(j)) {
this->m_x[j] = this->m_low_bounds[j]; this->m_x[j] = this->m_lower_bounds[j];
return true; return true;
} }
break; break;
case column_type::boxed: 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]); 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_low_bound) { if (closer_to_lower_bound) {
if (!this->x_is_at_low_bound(j)) { if (!this->x_is_at_lower_bound(j)) {
this->m_x[j] = this->m_low_bounds[j]; this->m_x[j] = this->m_lower_bounds[j];
return true; return true;
} }
} else { } else {
if (!this->x_is_at_upper_bound(j)) { 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; return true;
} }
} }
@ -535,12 +535,6 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::snap_runaway_n
template <typename T, typename X> bool lp_dual_core_solver<T, X>::problem_is_dual_feasible() const { template <typename T, typename X> bool lp_dual_core_solver<T, X>::problem_is_dual_feasible() const {
for (unsigned j : this->non_basis()){ for (unsigned j : this->non_basis()){
if (!this->column_is_dual_feasible(j)) { 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; return false;
} }
} }
@ -566,10 +560,10 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::delta_keeps_th
} }
template <typename T, typename X> void lp_dual_core_solver<T, X>::set_status_to_tentative_dual_unbounded_or_dual_unbounded() { template <typename T, typename X> void lp_dual_core_solver<T, X>::set_status_to_tentative_dual_unbounded_or_dual_unbounded() {
if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) { if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) {
this->set_status(DUAL_UNBOUNDED); this->set_status(lp_status::DUAL_UNBOUNDED);
} else { } else {
this->set_status(TENTATIVE_DUAL_UNBOUNDED); this->set_status(lp_status::TENTATIVE_DUAL_UNBOUNDED);
} }
} }
@ -599,10 +593,10 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::tight_breakpoi
template <typename T, typename X> T lp_dual_core_solver<T, X>::calculate_harris_delta_on_breakpoint_set() { template <typename T, typename X> T lp_dual_core_solver<T, X>::calculate_harris_delta_on_breakpoint_set() {
bool first_time = true; bool first_time = true;
T ret = zero_of_type<T>(); T ret = zero_of_type<T>();
SASSERT(m_breakpoint_set.size() > 0); lp_assert(m_breakpoint_set.size() > 0);
for (auto j : m_breakpoint_set) { for (auto j : m_breakpoint_set) {
T t; 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<T>::zero()) + m_harris_tolerance) / this->m_pivot_row[j]); t = abs((std::max(this->m_d[j], numeric_traits<T>::zero()) + m_harris_tolerance) / this->m_pivot_row[j]);
} else { } else {
t = abs((std::min(this->m_d[j], numeric_traits<T>::zero()) - m_harris_tolerance) / this->m_pivot_row[j]); t = abs((std::min(this->m_d[j], numeric_traits<T>::zero()) - m_harris_tolerance) / this->m_pivot_row[j]);
@ -620,7 +614,7 @@ template <typename T, typename X> T lp_dual_core_solver<T, X>::calculate_harris_
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_tight_set_on_harris_delta(const T & harris_delta ){ template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_tight_set_on_harris_delta(const T & harris_delta ){
m_tight_set.clear(); m_tight_set.clear();
for (auto j : m_breakpoint_set) { 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<T>::zero()) / this->m_pivot_row[j]) <= harris_delta){ if (abs(std::max(this->m_d[j], numeric_traits<T>::zero()) / this->m_pivot_row[j]) <= harris_delta){
m_tight_set.insert(j); m_tight_set.insert(j);
} }
@ -648,7 +642,7 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_on_tigh
} }
} }
m_tight_set.erase(m_q); m_tight_set.erase(m_q);
SASSERT(m_q != -1); lp_assert(m_q != -1);
} }
template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_and_tight_set() { template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_and_tight_set() {
@ -675,7 +669,7 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::ratio_test() {
set_status_to_tentative_dual_unbounded_or_dual_unbounded(); set_status_to_tentative_dual_unbounded_or_dual_unbounded();
return false; return false;
} }
this->set_status(FEASIBLE); this->set_status(lp_status::FEASIBLE);
find_q_and_tight_set(); find_q_and_tight_set();
if (!tight_breakpoinst_are_all_boxed()) break; if (!tight_breakpoinst_are_all_boxed()) break;
T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign; T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign;
@ -731,19 +725,19 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::update_xb_afte
template <typename T, typename X> void lp_dual_core_solver<T, X>::one_iteration() { template <typename T, typename X> void lp_dual_core_solver<T, X>::one_iteration() {
unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving(); 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(); 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(); number_of_rows_to_try = this->m_m();
} else { } else {
this->set_status(FEASIBLE); this->set_status(lp_status::FEASIBLE);
} }
pricing_loop(number_of_rows_to_try, offset_in_rows); pricing_loop(number_of_rows_to_try, offset_in_rows);
SASSERT(problem_is_dual_feasible()); lp_assert(problem_is_dual_feasible());
} }
template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // see the page 35 template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // see the page 35
SASSERT(d_is_correct()); lp_assert(d_is_correct());
SASSERT(problem_is_dual_feasible()); lp_assert(problem_is_dual_feasible());
SASSERT(this->basis_heading_is_correct()); lp_assert(this->basis_heading_is_correct());
this->set_total_iterations(0); this->set_total_iterations(0);
this->iters_with_no_cost_growing() = 0; this->iters_with_no_cost_growing() = 0;
do { do {
@ -751,7 +745,7 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // s
return; return;
} }
one_iteration(); 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->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); && this->total_iterations() <= this->m_settings.max_total_number_of_iterations);
} }

View file

@ -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<lp::mpq, lp::mpq>::get_current_cost() const; template lp::mpq lp::lp_dual_simplex<lp::mpq, lp::mpq>::get_current_cost() const;
template void lp::lp_dual_simplex<lp::mpq, lp::mpq>::find_maximal_solution(); template void lp::lp_dual_simplex<lp::mpq, lp::mpq>::find_maximal_solution();
template double lp::lp_dual_simplex<double, double>::get_current_cost() const; template double lp::lp_dual_simplex<double, double>::get_current_cost() const;

View file

@ -28,7 +28,7 @@ template <typename T, typename X>
class lp_dual_simplex: public lp_solver<T, X> { class lp_dual_simplex: public lp_solver<T, X> {
lp_dual_core_solver<T, X> * m_core_solver; lp_dual_core_solver<T, X> * m_core_solver;
vector<T> m_b_copy; vector<T> m_b_copy;
vector<T> 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<T> 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<column_type> m_column_types_of_core_solver; vector<column_type> m_column_types_of_core_solver;
vector<column_type> m_column_types_of_logicals; vector<column_type> m_column_types_of_logicals;
vector<bool> m_can_enter_basis; vector<bool> m_can_enter_basis;

View file

@ -22,61 +22,61 @@ namespace lp{
template <typename T, typename X> void lp_dual_simplex<T, X>::decide_on_status_after_stage1() { template <typename T, typename X> void lp_dual_simplex<T, X>::decide_on_status_after_stage1() {
switch (m_core_solver->get_status()) { 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())) { 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 { } else {
this->m_status = UNBOUNDED; this->m_status = lp_status::UNBOUNDED;
} }
break; break;
case DUAL_UNBOUNDED: case lp_status::DUAL_UNBOUNDED:
SASSERT(false); lp_unreachable();
case ITERATIONS_EXHAUSTED: case lp_status::ITERATIONS_EXHAUSTED:
this->m_status = ITERATIONS_EXHAUSTED; this->m_status = lp_status::ITERATIONS_EXHAUSTED;
break; break;
case TIME_EXHAUSTED: case lp_status::TIME_EXHAUSTED:
this->m_status = TIME_EXHAUSTED; this->m_status = lp_status::TIME_EXHAUSTED;
break; break;
case FLOATING_POINT_ERROR: case lp_status::FLOATING_POINT_ERROR:
this->m_status = FLOATING_POINT_ERROR; this->m_status = lp_status::FLOATING_POINT_ERROR;
break; break;
default: default:
SASSERT(false); lp_unreachable();
} }
} }
template <typename T, typename X> void lp_dual_simplex<T, X>::fix_logical_for_stage2(unsigned j) { template <typename T, typename X> void lp_dual_simplex<T, X>::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()]) { switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) {
case column_type::low_bound: case column_type::lower_bound:
m_low_bounds[j] = numeric_traits<T>::zero(); m_lower_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::low_bound; m_column_types_of_core_solver[j] = column_type::lower_bound;
m_can_enter_basis[j] = true; m_can_enter_basis[j] = true;
break; break;
case column_type::fixed: case column_type::fixed:
this->m_upper_bounds[j] = m_low_bounds[j] = numeric_traits<T>::zero(); this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::fixed; m_column_types_of_core_solver[j] = column_type::fixed;
m_can_enter_basis[j] = false; m_can_enter_basis[j] = false;
break; break;
default: default:
SASSERT(false); lp_unreachable();
} }
} }
template <typename T, typename X> void lp_dual_simplex<T, X>::fix_structural_for_stage2(unsigned j) { template <typename T, typename X> void lp_dual_simplex<T, X>::fix_structural_for_stage2(unsigned j) {
column_info<T> * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; column_info<T> * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]];
switch (ci->get_column_type()) { switch (ci->get_column_type()) {
case column_type::low_bound: case column_type::lower_bound:
m_low_bounds[j] = numeric_traits<T>::zero(); m_lower_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::low_bound; m_column_types_of_core_solver[j] = column_type::lower_bound;
m_can_enter_basis[j] = true; m_can_enter_basis[j] = true;
break; break;
case column_type::fixed: case column_type::fixed:
case column_type::upper_bound: case column_type::upper_bound:
SASSERT(false); lp_unreachable();
case column_type::boxed: case column_type::boxed:
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
m_low_bounds[j] = numeric_traits<T>::zero(); m_lower_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::boxed; m_column_types_of_core_solver[j] = column_type::boxed;
m_can_enter_basis[j] = true; m_can_enter_basis[j] = true;
break; break;
@ -85,7 +85,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fix_structural_for
m_column_types_of_core_solver[j] = column_type::free_column; m_column_types_of_core_solver[j] = column_type::free_column;
break; break;
default: default:
SASSERT(false); lp_unreachable();
} }
// T cost_was = this->m_costs[j]; // T cost_was = this->m_costs[j];
this->set_scaled_cost(j); this->set_scaled_cost(j);
@ -114,23 +114,23 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::solve_for_stage2()
m_core_solver->solve_yB(m_core_solver->m_y); m_core_solver->solve_yB(m_core_solver->m_y);
m_core_solver->fill_reduced_costs_from_m_y_by_rows(); 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->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(); m_core_solver->solve();
switch (m_core_solver->get_status()) { switch (m_core_solver->get_status()) {
case OPTIMAL: case lp_status::OPTIMAL:
this->m_status = OPTIMAL; this->m_status = lp_status::OPTIMAL;
break; break;
case DUAL_UNBOUNDED: case lp_status::DUAL_UNBOUNDED:
this->m_status = INFEASIBLE; this->m_status = lp_status::INFEASIBLE;
break; break;
case TIME_EXHAUSTED: case lp_status::TIME_EXHAUSTED:
this->m_status = TIME_EXHAUSTED; this->m_status = lp_status::TIME_EXHAUSTED;
break; break;
case FLOATING_POINT_ERROR: case lp_status::FLOATING_POINT_ERROR:
this->m_status = FLOATING_POINT_ERROR; this->m_status = lp_status::FLOATING_POINT_ERROR;
break; break;
default: default:
SASSERT(false); lp_unreachable();
} }
this->m_second_stage_iterations = m_core_solver->total_iterations(); this->m_second_stage_iterations = m_core_solver->total_iterations();
this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations);
@ -144,7 +144,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_x_with_zeros(
} }
template <typename T, typename X> void lp_dual_simplex<T, X>::stage1() { template <typename T, typename X> void lp_dual_simplex<T, X>::stage1() {
SASSERT(m_core_solver == nullptr); lp_assert(m_core_solver == nullptr);
this->m_x.resize(this->m_A->column_count(), numeric_traits<T>::zero()); this->m_x.resize(this->m_A->column_count(), numeric_traits<T>::zero());
if (this->m_settings.get_message_ostream() != nullptr) if (this->m_settings.get_message_ostream() != nullptr)
this->print_statistics_on_A(*this->m_settings.get_message_ostream()); this->print_statistics_on_A(*this->m_settings.get_message_ostream());
@ -158,7 +158,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::stage1() {
this->m_heading, this->m_heading,
this->m_costs, this->m_costs,
this->m_column_types_of_core_solver, this->m_column_types_of_core_solver,
this->m_low_bounds, this->m_lower_bounds,
this->m_upper_bounds, this->m_upper_bounds,
this->m_settings, this->m_settings,
*this); *this);
@ -166,7 +166,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::stage1() {
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); 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())) { if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
// skipping stage 1 // skipping stage 1
m_core_solver->set_status(OPTIMAL); m_core_solver->set_status(lp_status::OPTIMAL);
m_core_solver->set_total_iterations(0); m_core_solver->set_total_iterations(0);
} else { } else {
m_core_solver->solve(); m_core_solver->solve();
@ -192,7 +192,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_s
} }
template <typename T, typename X> column_type lp_dual_simplex<T, X>::get_column_type(unsigned j) { template <typename T, typename X> column_type lp_dual_simplex<T, X>::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()) { if (j >= this->number_of_core_structurals()) {
return m_column_types_of_logicals[j - this->number_of_core_structurals()]; return m_column_types_of_logicals[j - this->number_of_core_structurals()];
} }
@ -201,12 +201,12 @@ template <typename T, typename X> column_type lp_dual_simplex<T, X>::get_column_
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { template <typename T, typename X> void lp_dual_simplex<T, X>::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 // 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()); this->m_core_solver_columns_to_external_columns.end());
T free_bound = T(1e4); // see 4.8 T free_bound = T(1e4); // see 4.8
unsigned jj = this->m_core_solver_columns_to_external_columns[j]; 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<T> * ci = this->m_map_from_var_index_to_column_info[jj]; column_info<T> * ci = this->m_map_from_var_index_to_column_info[jj];
switch (ci->get_column_type()) { switch (ci->get_column_type()) {
case column_type::upper_bound: { case column_type::upper_bound: {
@ -216,10 +216,10 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_
throw_exception(s.str()); throw_exception(s.str());
break; break;
} }
case column_type::low_bound: { case column_type::lower_bound: {
m_can_enter_basis[j] = true; m_can_enter_basis[j] = true;
this->set_scaled_cost(j); this->set_scaled_cost(j);
this->m_low_bounds[j] = numeric_traits<T>::zero(); this->m_lower_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] =numeric_traits<T>::one(); this->m_upper_bounds[j] =numeric_traits<T>::one();
break; break;
} }
@ -227,30 +227,30 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_
m_can_enter_basis[j] = true; m_can_enter_basis[j] = true;
this->set_scaled_cost(j); this->set_scaled_cost(j);
this->m_upper_bounds[j] = free_bound; this->m_upper_bounds[j] = free_bound;
this->m_low_bounds[j] = -free_bound; this->m_lower_bounds[j] = -free_bound;
break; break;
} }
case column_type::boxed: case column_type::boxed:
m_can_enter_basis[j] = false; m_can_enter_basis[j] = false;
this->m_costs[j] = numeric_traits<T>::zero(); this->m_costs[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] = this->m_low_bounds[j] = numeric_traits<T>::zero(); // is it needed? this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits<T>::zero(); // is it needed?
break; break;
default: default:
SASSERT(false); lp_unreachable();
} }
m_column_types_of_core_solver[j] = column_type::boxed; m_column_types_of_core_solver[j] = column_type::boxed;
} }
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) {
this->m_costs[j] = 0; this->m_costs[j] = 0;
SASSERT(get_column_type(j) != column_type::upper_bound); lp_assert(get_column_type(j) != column_type::upper_bound);
if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::low_bound))) { if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) {
m_column_types_of_core_solver[j] = column_type::boxed; m_column_types_of_core_solver[j] = column_type::boxed;
this->m_low_bounds[j] = numeric_traits<T>::zero(); this->m_lower_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] = numeric_traits<T>::one(); this->m_upper_bounds[j] = numeric_traits<T>::one();
} else { } else {
m_column_types_of_core_solver[j] = column_type::fixed; m_column_types_of_core_solver[j] = column_type::fixed;
this->m_low_bounds[j] = numeric_traits<T>::zero(); this->m_lower_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] = numeric_traits<T>::zero(); this->m_upper_bounds[j] = numeric_traits<T>::zero();
} }
} }
@ -269,7 +269,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_and_bou
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row,
unsigned & slack_var, unsigned & slack_var,
unsigned & artificial) { 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]]; 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 // we need to bring the program to the form Ax = b
T rs = this->m_b[row]; T rs = this->m_b[row];
@ -283,7 +283,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_s
break; break;
case Greater_or_equal: 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<T>::one(); (*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
if (rs > 0) { if (rs > 0) {
// adding one artificial // adding one artificial
@ -301,7 +301,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_s
break; break;
case Less_or_equal: case Less_or_equal:
// introduce a non-negative slack variable // 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<T>::one(); (*this->m_A)(row, slack_var) = numeric_traits<T>::one();
if (rs < 0) { if (rs < 0) {
// adding one artificial // adding one artificial
@ -328,7 +328,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::augment_matrix_A_a
m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials);
this->m_costs.resize(n); this->m_costs.resize(n);
this->m_upper_bounds.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); m_can_enter_basis.resize(n);
this->m_basis.resize(this->m_A->row_count()); this->m_basis.resize(this->m_A->row_count());
} }
@ -351,7 +351,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::find_maximal_solut
this->flip_costs(); // do it for now, todo ( remove the flipping) this->flip_costs(); // do it for now, todo ( remove the flipping)
this->cleanup(); this->cleanup();
if (this->m_status == INFEASIBLE) { if (this->m_status == lp_status::INFEASIBLE) {
return; return;
} }
this->fill_matrix_A_and_init_right_side(); this->fill_matrix_A_and_init_right_side();
@ -361,7 +361,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::find_maximal_solut
fill_first_stage_solver_fields(); fill_first_stage_solver_fields();
copy_m_b_aside_and_set_it_to_zeros(); copy_m_b_aside_and_set_it_to_zeros();
stage1(); stage1();
if (this->m_status == FEASIBLE) { if (this->m_status == lp_status::FEASIBLE) {
stage2(); stage2();
} }
} }

View file

@ -23,8 +23,8 @@ Revision History:
#include "util/vector.h" #include "util/vector.h"
#include <functional> #include <functional>
#include "util/lp/lar_solver.h" #include "util/lp/lar_solver.h"
#include "util/lp/lp_primal_core_solver.hpp" #include "util/lp/lp_primal_core_solver_def.h"
#include "util/lp/lp_primal_core_solver_tableau.h" #include "util/lp/lp_primal_core_solver_tableau_def.h"
namespace lp { namespace lp {
template void lp_primal_core_solver<double, double>::find_feasible_solution(); template void lp_primal_core_solver<double, double>::find_feasible_solution();

View file

@ -37,10 +37,9 @@ Revision History:
#include "util/lp/breakpoint.h" #include "util/lp/breakpoint.h"
#include "util/lp/binary_heap_priority_queue.h" #include "util/lp/binary_heap_priority_queue.h"
#include "util/lp/int_set.h" #include "util/lp/int_set.h"
#include "util/lp/iterator_on_row.h"
namespace lp { 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 // The right side b is given implicitly by x and the basis
template <typename T, typename X> template <typename T, typename X>
class lp_primal_core_solver:public lp_core_solver_base<T, X> { class lp_primal_core_solver:public lp_core_solver_base<T, X> {
@ -85,7 +84,7 @@ public:
// unsigned len = 100000000; // unsigned len = 100000000;
// for (unsigned j : this->m_inf_set.m_index) { // for (unsigned j : this->m_inf_set.m_index) {
// int i = this->m_basis_heading[j]; // int i = this->m_basis_heading[j];
// SASSERT(i >= 0); // lp_assert(i >= 0);
// unsigned row_len = this->m_A.m_rows[i].size(); // unsigned row_len = this->m_A.m_rows[i].size();
// if (row_len < len) { // if (row_len < len) {
// choices.clear(); // choices.clear();
@ -113,52 +112,52 @@ public:
bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const { 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 // 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 // we have xbj = -aj * xj
SASSERT(this->m_basis_heading[j] < 0); lp_assert(this->m_basis_heading[j] < 0);
SASSERT(this->column_is_feasible(j)); lp_assert(this->column_is_feasible(j));
switch (this->m_column_types[j]) { switch (this->m_column_types[j]) {
case column_type::free_column: return true; case column_type::free_column: return true;
case column_type::fixed: return false; case column_type::fixed: return false;
case column_type::low_bound: case column_type::lower_bound:
if (sign < 0) if (sign < 0)
return true; return true;
return !this->x_is_at_low_bound(j); return !this->x_is_at_lower_bound(j);
case column_type::upper_bound: case column_type::upper_bound:
if (sign > 0) if (sign > 0)
return true; return true;
return !this->x_is_at_upper_bound(j); return !this->x_is_at_upper_bound(j);
case column_type::boxed: case column_type::boxed:
if (sign < 0) 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); return !this->x_is_at_upper_bound(j);
} }
SASSERT(false); // cannot be here lp_assert(false); // cannot be here
return false; return false;
} }
bool needs_to_grow(unsigned bj) const { 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]) { switch(this->m_column_types[bj]) {
case column_type::free_column: case column_type::free_column:
return false; return false;
case column_type::fixed: case column_type::fixed:
case column_type::low_bound: case column_type::lower_bound:
case column_type::boxed: case column_type::boxed:
return this-> x_below_low_bound(bj); return this-> x_below_low_bound(bj);
default: default:
return false; return false;
} }
SASSERT(false); // unreachable lp_assert(false); // unreachable
return false; return false;
} }
int inf_sign_of_column(unsigned bj) const { 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]) { switch(this->m_column_types[bj]) {
case column_type::free_column: case column_type::free_column:
return 0; return 0;
case column_type::low_bound: case column_type::lower_bound:
return 1; return 1;
case column_type::fixed: case column_type::fixed:
case column_type::boxed: case column_type::boxed:
@ -166,7 +165,7 @@ public:
default: default:
return -1; return -1;
} }
SASSERT(false); // unreachable lp_assert(false); // unreachable
return 0; return 0;
} }
@ -174,15 +173,15 @@ public:
bool monoid_can_decrease(const row_cell<T> & rc) const { bool monoid_can_decrease(const row_cell<T> & rc) const {
unsigned j = rc.m_j; unsigned j = rc.m_j;
SASSERT(this->column_is_feasible(j)); lp_assert(this->column_is_feasible(j));
switch (this->m_column_types[j]) { switch (this->m_column_types[j]) {
case column_type::free_column: case column_type::free_column:
return true; return true;
case column_type::fixed: case column_type::fixed:
return false; return false;
case column_type::low_bound: case column_type::lower_bound:
if (is_pos(rc.get_val())) { if (is_pos(rc.get_val())) {
return this->x_above_low_bound(j); return this->x_above_lower_bound(j);
} }
return true; return true;
@ -194,28 +193,28 @@ public:
return this->x_below_upper_bound(j); return this->x_below_upper_bound(j);
case column_type::boxed: case column_type::boxed:
if (is_pos(rc.get_val())) { 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); return this->x_below_upper_bound(j);
default: default:
return false; return false;
} }
SASSERT(false); // unreachable lp_assert(false); // unreachable
return false; return false;
} }
bool monoid_can_increase(const row_cell<T> & rc) const { bool monoid_can_increase(const row_cell<T> & rc) const {
unsigned j = rc.m_j; unsigned j = rc.m_j;
SASSERT(this->column_is_feasible(j)); lp_assert(this->column_is_feasible(j));
switch (this->m_column_types[j]) { switch (this->m_column_types[j]) {
case column_type::free_column: case column_type::free_column:
return true; return true;
case column_type::fixed: case column_type::fixed:
return false; return false;
case column_type::low_bound: case column_type::lower_bound:
if (is_neg(rc.get_val())) { if (is_neg(rc.get_val())) {
return this->x_above_low_bound(j); return this->x_above_lower_bound(j);
} }
return true; return true;
@ -227,14 +226,14 @@ public:
return this->x_below_upper_bound(j); return this->x_below_upper_bound(j);
case column_type::boxed: case column_type::boxed:
if (is_neg(rc.get_val())) { 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); return this->x_below_upper_bound(j);
default: default:
return false; return false;
} }
SASSERT(false); // unreachable lp_assert(false); // unreachable
return false; 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) { 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); 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) { void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
SASSERT(m < 0 && this->m_column_types[j] == column_type::low_bound); 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_low_bounds[j], theta, unlimited); 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) { void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
SASSERT(m > 0 && this->m_column_types[j] == column_type::low_bound); lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound);
limit_inf_on_low_bound_m_pos(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited); 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) { 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); 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<unsigned> & leavings, T m, X & t, T & abs_of_d_of_leaving); void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector<unsigned> & leavings, T m, X & t, T & abs_of_d_of_leaving);
vector<T> m_low_bounds_dummy; // needed for the base class only vector<T> m_lower_bounds_dummy; // needed for the base class only
X get_max_bound(vector<X> & b); X get_max_bound(vector<X> & b);
@ -403,7 +402,7 @@ public:
bool need_to_switch_costs() const { bool need_to_switch_costs() const {
if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
return false; 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; return this->current_x_is_feasible() == this->m_using_infeas_costs;
} }
@ -420,7 +419,7 @@ public:
// returns the number of iterations // returns the number of iterations
unsigned solve(); unsigned solve();
lu<T, X> * factorization() {return this->m_factorization;} lu<static_matrix<T, X>> * factorization() {return this->m_factorization;}
void delete_factorization(); void delete_factorization();
@ -445,7 +444,7 @@ public:
void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, const X &theta ) { 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_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) if (j == -1)
return -1; return -1;
SASSERT(!this->column_is_feasible(j)); lp_assert(!this->column_is_feasible(j));
switch (this->m_column_types[j]) { switch (this->m_column_types[j]) {
case column_type::fixed: case column_type::fixed:
case column_type::upper_bound: case column_type::upper_bound:
new_val_for_leaving = this->m_upper_bounds[j]; new_val_for_leaving = this->m_upper_bounds[j];
break; break;
case column_type::low_bound: case column_type::lower_bound:
new_val_for_leaving = this->m_low_bounds[j]; new_val_for_leaving = this->m_lower_bounds[j];
break; break;
case column_type::boxed: case column_type::boxed:
if (this->x_above_upper_bound(j)) if (this->x_above_upper_bound(j))
new_val_for_leaving = this->m_upper_bounds[j]; new_val_for_leaving = this->m_upper_bounds[j];
else else
new_val_for_leaving = this->m_low_bounds[j]; new_val_for_leaving = this->m_lower_bounds[j];
break; break;
default: default:
SASSERT(false); lp_assert(false);
new_val_for_leaving = numeric_traits<T>::zero(); // does not matter new_val_for_leaving = numeric_traits<T>::zero(); // does not matter
} }
return j; return j;
@ -484,7 +483,7 @@ public:
X new_val_for_leaving; X new_val_for_leaving;
int leaving = find_leaving_tableau_rows(new_val_for_leaving); int leaving = find_leaving_tableau_rows(new_val_for_leaving);
if (leaving == -1) { if (leaving == -1) {
this->set_status(OPTIMAL); this->set_status(lp_status::OPTIMAL);
return; return;
} }
@ -500,14 +499,14 @@ public:
T a_ent; T a_ent;
int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent); int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent);
if (entering == -1) { if (entering == -1) {
this->set_status(INFEASIBLE); this->set_status(lp_status::INFEASIBLE);
return; return;
} }
X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent;
advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta ); 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()) if (this->current_x_is_feasible())
this->set_status(OPTIMAL); this->set_status(lp_status::OPTIMAL);
} }
void fill_breakpoints_array(unsigned entering); 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 update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta);
void decide_on_status_when_cannot_find_entering() { void decide_on_status_when_cannot_find_entering() {
SASSERT(!need_to_switch_costs()); lp_assert(!need_to_switch_costs());
this->set_status(this->current_x_is_feasible()? OPTIMAL: INFEASIBLE); 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) { // void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) {
// SASSERT(m < 0); // lp_assert(m < 0);
// SASSERT(this->m_column_type[j] == low_bound || this->m_column_type[j] == boxed); // lp_assert(this->m_column_type[j] == lower_bound || this->m_column_type[j] == boxed);
// const X & eps = harris_eps_for_bound(this->m_low_bounds[j]); // const X & eps = harris_eps_for_bound(this->m_lower_bounds[j]);
// if (this->above_bound(this->m_x[j], this->m_low_bounds[j])) { // if (this->above_bound(this->m_x[j], this->m_lower_bounds[j])) {
// theta = std::min((this->m_low_bounds[j] -this->m_x[j] - eps) / m, theta); // theta = std::min((this->m_lower_bounds[j] -this->m_x[j] - eps) / m, theta);
// if (theta < zero_of_type<X>()) theta = zero_of_type<X>(); // if (theta < zero_of_type<X>()) theta = zero_of_type<X>();
// } // }
// } // }
void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { 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); lp_assert(m < 0);
const X& eps = harris_eps_for_bound(this->m_low_bounds[j]); const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]);
limit_theta((this->m_low_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited);
if (theta < zero_of_type<X>()) theta = zero_of_type<X>(); if (theta < zero_of_type<X>()) theta = zero_of_type<X>();
} }
bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
// x gets smaller // x gets smaller
SASSERT(m < 0); lp_assert(m < 0);
if (numeric_traits<T>::precise()) { if (numeric_traits<T>::precise()) {
if (this->below_bound(x, bound)) return false; if (this->below_bound(x, bound)) return false;
if (this->above_bound(x, bound)) { 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) { bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
// x gets larger // x gets larger
SASSERT(m > 0); lp_assert(m > 0);
if (numeric_traits<T>::precise()) { if (numeric_traits<T>::precise()) {
if (this->above_bound(x, bound)) return false; if (this->above_bound(x, bound)) return false;
if (this->below_bound(x, bound)) { if (this->below_bound(x, bound)) {
@ -591,17 +590,17 @@ public:
return true; 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<T>::precise()) { if (numeric_traits<T>::precise()) {
// x gets larger // x gets larger
SASSERT(m > 0); lp_assert(m > 0);
if (this->below_bound(x, bound)) { if (this->below_bound(x, bound)) {
limit_theta((bound - x) / m, theta, unlimited); limit_theta((bound - x) / m, theta, unlimited);
} }
} }
else { else {
// x gets larger // x gets larger
SASSERT(m > 0); lp_assert(m > 0);
const X& eps = harris_eps_for_bound(bound); const X& eps = harris_eps_for_bound(bound);
if (this->below_bound(x, bound)) { if (this->below_bound(x, bound)) {
limit_theta((bound - x + eps) / m, theta, unlimited); 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) { void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
// x gets smaller // x gets smaller
SASSERT(m < 0); lp_assert(m < 0);
const X& eps = harris_eps_for_bound(bound); const X& eps = harris_eps_for_bound(bound);
if (this->above_bound(x, bound)) { if (this->above_bound(x, bound)) {
limit_theta((bound - x - eps) / m, theta, unlimited); 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) { 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 & 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)) { if (this->below_bound(x, lbound)) {
const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); 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) { 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 & x = this->m_x[j];
const X & ubound = this->m_upper_bounds[j]; const X & ubound = this->m_upper_bounds[j];
if (this->above_bound(x, ubound)) { if (this->above_bound(x, ubound)) {
const X& eps = harris_eps_for_bound(ubound); const X& eps = harris_eps_for_bound(ubound);
limit_theta((ubound - x - eps) / m, theta, unlimited); limit_theta((ubound - x - eps) / m, theta, unlimited);
} else { } else {
const X & lbound = this->m_low_bounds[j]; const X & lbound = this->m_lower_bounds[j];
if (this->above_bound(x, lbound)){ if (this->above_bound(x, lbound)){
const X& eps = harris_eps_for_bound(lbound); const X& eps = harris_eps_for_bound(lbound);
limit_theta((lbound - x - eps) / m, theta, unlimited); 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) { 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]); 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])) { 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); 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 ) { 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]); 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); limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited);
if (theta < zero_of_type<X>()) { if (theta < zero_of_type<X>()) {
@ -679,7 +678,7 @@ public:
// j is a basic column or the entering, in any case x[j] has to stay feasible. // 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 // 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 // or
// x[j] + t * m <= this->m_upper_bounds[j] + harris_feasibility_tolerance ( if m > 0) // 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) { 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); limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(j, m, theta, unlimited);
} }
break; break;
case column_type::low_bound: case column_type::lower_bound:
if (this->current_x_is_feasible()) { if (this->current_x_is_feasible()) {
if (m < 0) if (m < 0)
limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited);
} else { } else {
if (m < 0) 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 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; break;
// case fixed: // case fixed:
@ -735,7 +734,7 @@ public:
break; break;
default: default:
SASSERT(false); lp_unreachable();
} }
if (!unlimited && theta < zero_of_type<X>()) { if (!unlimited && theta < zero_of_type<X>()) {
theta = zero_of_type<X>(); theta = zero_of_type<X>();
@ -770,7 +769,7 @@ public:
void init_reduced_costs(); 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); int advance_on_sorted_breakpoints(unsigned entering, X & t);
@ -794,7 +793,7 @@ public:
if (this->m_basis_heading[j] < 0) if (this->m_basis_heading[j] < 0)
continue; continue;
if (!this->column_is_feasible(j)) 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)) if (this->x_above_upper_bound(j))
return 1; return 1;
break; break;
case column_type::low_bound: case column_type::lower_bound:
if (this->x_below_low_bound(j)) if (this->x_below_low_bound(j))
return -1; return -1;
break; break;
@ -818,7 +817,7 @@ public:
case column_type::free_column: case column_type::free_column:
return 0; return 0;
default: default:
SASSERT(false); lp_assert(false);
} }
return 0; return 0;
} }
@ -842,18 +841,18 @@ public:
case column_type::fixed: case column_type::fixed:
return 0; return 0;
case column_type::boxed: case column_type::boxed:
if (this->x_is_at_low_bound(j)) if (this->x_is_at_lower_bound(j))
return 1; return 1;
return -1; return -1;
break; break;
case column_type::low_bound: case column_type::lower_bound:
return 1; return 1;
break; break;
case column_type::upper_bound: case column_type::upper_bound:
return -1; return -1;
break; break;
default: default:
SASSERT(false); lp_assert(false);
} }
return 0; return 0;
@ -879,7 +878,7 @@ public:
// the delta is between the old and the new cost (old - new) // 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) { 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<unsigned>(this->m_basis_heading[j]); unsigned i = static_cast<unsigned>(this->m_basis_heading[j]);
for (const row_cell<T> & rc : this->m_A.m_rows[i]) { for (const row_cell<T> & rc : this->m_A.m_rows[i]) {
unsigned k = rc.m_j; unsigned k = rc.m_j;
@ -906,7 +905,7 @@ public:
vector<int> & heading, vector<int> & heading,
vector<T> & costs, vector<T> & costs,
const vector<column_type> & column_type_array, const vector<column_type> & column_type_array,
const vector<X> & low_bound_values, const vector<X> & lower_bound_values,
const vector<X> & upper_bound_values, const vector<X> & upper_bound_values,
lp_settings & settings, lp_settings & settings,
const column_namer& column_names): const column_namer& column_names):
@ -919,7 +918,7 @@ public:
settings, settings,
column_names, column_names,
column_type_array, column_type_array,
low_bound_values, lower_bound_values,
upper_bound_values), upper_bound_values),
m_beta(A.row_count()), m_beta(A.row_count()),
m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_epsilon_of_reduced_cost(T(1)/T(10000000)),
@ -930,7 +929,7 @@ public:
} else { } else {
m_converted_harris_eps = zero_of_type<T>(); m_converted_harris_eps = zero_of_type<T>();
} }
this->set_status(UNKNOWN); this->set_status(lp_status::UNKNOWN);
} }
// constructor // constructor
@ -954,12 +953,12 @@ public:
settings, settings,
column_names, column_names,
column_type_array, column_type_array,
m_low_bounds_dummy, m_lower_bounds_dummy,
upper_bound_values), upper_bound_values),
m_beta(A.row_count()), m_beta(A.row_count()),
m_converted_harris_eps(convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance)) { m_converted_harris_eps(convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance)) {
SASSERT(initial_x_is_correct()); lp_assert(initial_x_is_correct());
m_low_bounds_dummy.resize(A.column_count(), zero_of_type<T>()); m_lower_bounds_dummy.resize(A.column_count(), zero_of_type<T>());
m_enter_price_eps = numeric_traits<T>::precise() ? numeric_traits<T>::zero() : T(1e-5); m_enter_price_eps = numeric_traits<T>::precise() ? numeric_traits<T>::zero() : T(1e-5);
#ifdef Z3DEBUG #ifdef Z3DEBUG
// check_correctness(); // check_correctness();
@ -972,7 +971,7 @@ public:
basis_set.insert(this->m_basis[i]); basis_set.insert(this->m_basis[i]);
} }
for (unsigned j = 0; j < this->m_n(); j++) { for (unsigned j = 0; j < this->m_n(); j++) {
if (this->column_has_low_bound(j) && this->m_x[j] < numeric_traits<T>::zero()) { if (this->column_has_lower_bound(j) && this->m_x[j] < numeric_traits<T>::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); 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; return false;
} }
@ -983,7 +982,7 @@ public:
} }
if (basis_set.find(j) != basis_set.end()) continue; 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<T>::zero() != this->m_x[j]) { if (numeric_traits<T>::zero() != this->m_x[j]) {
LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits<T>::zero() << " is not equal to " << this->m_x[j] << std::endl); LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits<T>::zero() << " is not equal to " << this->m_x[j] << std::endl);
return false; return false;

View file

@ -25,12 +25,12 @@ Revision History:
#include <string> #include <string>
#include "util/lp/lp_primal_core_solver.h" #include "util/lp/lp_primal_core_solver.h"
namespace lp { 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 // The right side b is given implicitly by x and the basis
template <typename T, typename X> template <typename T, typename X>
void lp_primal_core_solver<T, X>::sort_non_basis_rational() { void lp_primal_core_solver<T, X>::sort_non_basis_rational() {
SASSERT(numeric_traits<T>::precise()); lp_assert(numeric_traits<T>::precise());
if (this->m_settings.use_tableau()) { if (this->m_settings.use_tableau()) {
std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { 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); unsigned ca = this->m_A.number_of_non_zeroes_in_column(a);
@ -84,12 +84,12 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_on_breakpoin
bool ret; bool ret;
const T & d = this->m_d[j]; const T & d = this->m_d[j];
switch (this->m_column_types[j]) { switch (this->m_column_types[j]) {
case column_type::low_bound: case column_type::lower_bound:
SASSERT(this->x_is_at_low_bound(j)); lp_assert(this->x_is_at_lower_bound(j));
ret = d < -m_epsilon_of_reduced_cost; ret = d < -m_epsilon_of_reduced_cost;
break; break;
case column_type::upper_bound: 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; ret = d > m_epsilon_of_reduced_cost;
break; break;
case column_type::fixed: case column_type::fixed:
@ -97,16 +97,16 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_on_breakpoin
break; break;
case column_type::boxed: case column_type::boxed:
{ {
bool low_bound = this->x_is_at_low_bound(j); bool lower_bound = this->x_is_at_lower_bound(j);
SASSERT(low_bound || this->x_is_at_upper_bound(j)); lp_assert(lower_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); ret = (lower_bound && d < -m_epsilon_of_reduced_cost) || ((!lower_bound) && d > m_epsilon_of_reduced_cost);
} }
break; break;
case column_type::free_column: case column_type::free_column:
ret = d > m_epsilon_of_reduced_cost || d < - m_epsilon_of_reduced_cost; ret = d > m_epsilon_of_reduced_cost || d < - m_epsilon_of_reduced_cost;
break; break;
default: default:
SASSERT(false); lp_unreachable();
ret = false; ret = false;
break; break;
} }
@ -125,7 +125,7 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis(unsign
if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost) if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost)
return true; return true;
break; break;
case column_type::low_bound: case column_type::lower_bound:
if (dj > m_epsilon_of_reduced_cost) return true;; if (dj > m_epsilon_of_reduced_cost) return true;;
break; break;
case column_type::upper_bound: case column_type::upper_bound:
@ -137,19 +137,19 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis(unsign
return true; return true;
break; break;
} else if (dj < - m_epsilon_of_reduced_cost) { } 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; return true;
} }
break; break;
default: default:
SASSERT(false); lp_unreachable();
break; break;
} }
return false; return false;
} }
template <typename T, typename X> template <typename T, typename X>
bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis_precise(unsigned j) const { bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis_precise(unsigned j) const {
SASSERT (numeric_traits<T>::precise()); lp_assert (numeric_traits<T>::precise());
if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search) if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search)
return column_is_benefitial_for_entering_on_breakpoints(j); return column_is_benefitial_for_entering_on_breakpoints(j);
const T& dj = this->m_d[j]; const T& dj = this->m_d[j];
@ -159,9 +159,9 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis_precis
if (!is_zero(dj)) if (!is_zero(dj))
return true; return true;
break; break;
case column_type::low_bound: case column_type::lower_bound:
if (dj > zero_of_type<T>()) return true; if (dj > zero_of_type<T>()) 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; return true;
} }
break; break;
@ -177,12 +177,12 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis_precis
return true; return true;
break; break;
} else if (dj < zero_of_type<T>()) { } else if (dj < zero_of_type<T>()) {
if (this->m_x[j] > this->m_low_bounds[j]) if (this->m_x[j] > this->m_lower_bounds[j])
return true; return true;
} }
break; break;
default: default:
SASSERT(false); lp_unreachable();
break; break;
} }
return false; return false;
@ -190,7 +190,7 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis_precis
template <typename T, typename X> template <typename T, typename X>
int lp_primal_core_solver<T, X>::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) int lp_primal_core_solver<T, X>::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1)
SASSERT(numeric_traits<T>::precise()); lp_assert(numeric_traits<T>::precise());
if (number_of_benefitial_columns_to_go_over == 0) if (number_of_benefitial_columns_to_go_over == 0)
return -1; return -1;
if (this->m_basis_sort_counter == 0) { if (this->m_basis_sort_counter == 0) {
@ -274,7 +274,7 @@ int lp_primal_core_solver<T, X>::choose_entering_column(unsigned number_of_benef
template <typename T, typename X> int lp_primal_core_solver<T, X>::advance_on_sorted_breakpoints(unsigned entering, X &t) { template <typename T, typename X> int lp_primal_core_solver<T, X>::advance_on_sorted_breakpoints(unsigned entering, X &t) {
T slope_at_entering = this->m_d[entering]; T slope_at_entering = this->m_d[entering];
breakpoint<X> * last_bp = nullptr; breakpoint<X> * 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) { while (m_breakpoint_indices_queue.is_empty() == false) {
unsigned bi = m_breakpoint_indices_queue.dequeue(); unsigned bi = m_breakpoint_indices_queue.dequeue();
breakpoint<X> *b = &m_breakpoints[bi]; breakpoint<X> *b = &m_breakpoints[bi];
@ -289,7 +289,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::advance_on_so
} }
} }
} }
SASSERT (last_bp != nullptr); lp_assert (last_bp != nullptr);
t = last_bp->m_delta; t = last_bp->m_delta;
return last_bp->m_j; return last_bp->m_j;
} }
@ -297,13 +297,13 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::advance_on_so
template <typename T, typename X> int template <typename T, typename X> int
lp_primal_core_solver<T, X>::find_leaving_and_t_with_breakpoints(unsigned entering, X & t){ lp_primal_core_solver<T, X>::find_leaving_and_t_with_breakpoints(unsigned entering, X & t){
SASSERT(this->precise() == false); lp_assert(this->precise() == false);
fill_breakpoints_array(entering); fill_breakpoints_array(entering);
return advance_on_sorted_breakpoints(entering, t); return advance_on_sorted_breakpoints(entering, t);
} }
template <typename T, typename X> bool lp_primal_core_solver<T, X>::get_harris_theta(X & theta) { template <typename T, typename X> bool lp_primal_core_solver<T, X>::get_harris_theta(X & theta) {
SASSERT(this->m_ed.is_OK()); lp_assert(this->m_ed.is_OK());
bool unlimited = true; bool unlimited = true;
for (unsigned i : this->m_ed.m_index) { for (unsigned i : this->m_ed.m_index) {
if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue;
@ -360,13 +360,13 @@ template <typename T, typename X> bool lp_primal_core_solver<T, X>::try_jump_to_
if (m_sign_of_entering_delta > 0) { if (m_sign_of_entering_delta > 0) {
t = this->m_upper_bounds[entering] - this->m_x[entering]; t = this->m_upper_bounds[entering] - this->m_x[entering];
if (unlimited || t <= theta){ if (unlimited || t <= theta){
SASSERT(t >= zero_of_type<X>()); lp_assert(t >= zero_of_type<X>());
return true; return true;
} }
} else { // m_sign_of_entering_delta == -1 } 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) { if (unlimited || t <= theta) {
SASSERT(t >= zero_of_type<X>()); lp_assert(t >= zero_of_type<X>());
return true; return true;
} }
} }
@ -375,16 +375,16 @@ template <typename T, typename X> bool lp_primal_core_solver<T, X>::try_jump_to_
if (m_sign_of_entering_delta > 0) { if (m_sign_of_entering_delta > 0) {
t = this->m_upper_bounds[entering] - this->m_x[entering]; t = this->m_upper_bounds[entering] - this->m_x[entering];
if (unlimited || t <= theta){ if (unlimited || t <= theta){
SASSERT(t >= zero_of_type<X>()); lp_assert(t >= zero_of_type<X>());
return true; return true;
} }
} }
return false; return false;
case column_type::low_bound: case column_type::lower_bound:
if (m_sign_of_entering_delta < 0) { 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) { if (unlimited || t <= theta) {
SASSERT(t >= zero_of_type<X>()); lp_assert(t >= zero_of_type<X>());
return true; return true;
} }
} }
@ -404,7 +404,7 @@ try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t ) {
return true; return true;
} }
// m_sign_of_entering_delta == -1 // 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; return true;
} }
@ -420,7 +420,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
do { do {
unsigned i = this->m_ed.m_index[k]; unsigned i = this->m_ed.m_index[k];
const T & ed = this->m_ed[i]; const T & ed = this->m_ed[i];
SASSERT(!numeric_traits<T>::is_zero(ed)); lp_assert(!numeric_traits<T>::is_zero(ed));
unsigned j = this->m_basis[i]; unsigned j = this->m_basis[i];
limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited);
if (!unlimited) { if (!unlimited) {
@ -439,7 +439,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
while (k != initial_k) { while (k != initial_k) {
unsigned i = this->m_ed.m_index[k]; unsigned i = this->m_ed.m_index[k];
const T & ed = this->m_ed[i]; const T & ed = this->m_ed[i];
SASSERT(!numeric_traits<T>::is_zero(ed)); lp_assert(!numeric_traits<T>::is_zero(ed));
unsigned j = this->m_basis[i]; unsigned j = this->m_basis[i];
unlimited = true; unlimited = true;
limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited);
@ -479,7 +479,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leavi
return find_leaving_and_t_with_breakpoints(entering, t); return find_leaving_and_t_with_breakpoints(entering, t);
X theta; X theta;
bool unlimited = get_harris_theta(theta); bool unlimited = get_harris_theta(theta);
SASSERT(unlimited || theta >= zero_of_type<X>()); lp_assert(unlimited || theta >= zero_of_type<X>());
if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering;
if (unlimited) if (unlimited)
return -1; return -1;
@ -489,7 +489,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leavi
// m is the multiplier. updating t in a way that holds the following // 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 // or
// x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0)
template <typename T, typename X> void template <typename T, typename X> void
@ -501,7 +501,7 @@ lp_primal_core_solver<T, X>::get_bound_on_variable_and_update_leaving_precisely(
return; return;
default:break; 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<X>::is_neg(tt)) if (numeric_traits<X>::is_neg(tt))
tt = zero_of_type<X>(); tt = zero_of_type<X>();
if (leavings.size() == 0 || tt < t || (tt == t && m > abs_of_d_of_leaving)) { if (leavings.size() == 0 || tt < t || (tt == t && m > abs_of_d_of_leaving)) {
@ -516,7 +516,7 @@ lp_primal_core_solver<T, X>::get_bound_on_variable_and_update_leaving_precisely(
} else if (m < 0){ } else if (m < 0){
switch (this->m_column_types[j]) { // check that j has an upper bound switch (this->m_column_types[j]) { // check that j has an upper bound
case column_type::free_column: case column_type::free_column:
case column_type::low_bound: case column_type::lower_bound:
return; return;
default:break; default:break;
} }
@ -548,7 +548,7 @@ template <typename T, typename X> X lp_primal_core_solver<T, X>::get_max_boun
template <typename T, typename X> void lp_primal_core_solver<T, X>::check_Ax_equal_b() { template <typename T, typename X> void lp_primal_core_solver<T, X>::check_Ax_equal_b() {
dense_matrix<T, X> d(this->m_A); dense_matrix<T, X> d(this->m_A);
T * ls = d.apply_from_left_with_different_dims(this->m_x); T * ls = d.apply_from_left_with_different_dims(this->m_x);
SASSERT(vectors_are_equal<T>(ls, this->m_b, this->m_m())); lp_assert(vectors_are_equal<T>(ls, this->m_b, this->m_m()));
delete [] ls; delete [] ls;
} }
template <typename T, typename X> void lp_primal_core_solver<T, X>::check_the_bounds() { template <typename T, typename X> void lp_primal_core_solver<T, X>::check_the_bounds() {
@ -558,8 +558,8 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::check_the
} }
template <typename T, typename X> void lp_primal_core_solver<T, X>::check_bound(unsigned i) { template <typename T, typename X> void lp_primal_core_solver<T, X>::check_bound(unsigned i) {
SASSERT (!(this->column_has_low_bound(i) && (numeric_traits<T>::zero() > this->m_x[i]))); lp_assert (!(this->column_has_lower_bound(i) && (numeric_traits<T>::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_upper_bound(i) && (this->m_upper_bounds[i] < this->m_x[i])));
} }
template <typename T, typename X> void lp_primal_core_solver<T, X>::check_correctness() { template <typename T, typename X> void lp_primal_core_solver<T, X>::check_correctness() {
@ -575,8 +575,8 @@ void lp_primal_core_solver<T, X>::update_reduced_costs_from_pivot_row(unsigned e
// the basis heading has changed already // the basis heading has changed already
#ifdef Z3DEBUG #ifdef Z3DEBUG
auto & basis_heading = this->m_basis_heading; auto & basis_heading = this->m_basis_heading;
SASSERT(basis_heading[entering] >= 0 && static_cast<unsigned>(basis_heading[entering]) < this->m_m()); lp_assert(basis_heading[entering] >= 0 && static_cast<unsigned>(basis_heading[entering]) < this->m_m());
SASSERT(basis_heading[leaving] < 0); lp_assert(basis_heading[leaving] < 0);
#endif #endif
T pivot = this->m_pivot_row[entering]; T pivot = this->m_pivot_row[entering];
T dq = this->m_d[entering]/pivot; T dq = this->m_d[entering]/pivot;
@ -599,7 +599,7 @@ void lp_primal_core_solver<T, X>::update_reduced_costs_from_pivot_row(unsigned e
template <typename T, typename X> int lp_primal_core_solver<T, X>::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { template <typename T, typename X> int lp_primal_core_solver<T, X>::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) {
if (numeric_traits<T>::precise()) return 0; if (numeric_traits<T>::precise()) return 0;
T reduced_at_entering_was = this->m_d[entering]; // can benefit from going over non-zeros of m_ed 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]; T refreshed_cost = this->m_costs[entering];
unsigned i = this->m_m(); unsigned i = this->m_m();
while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i]; while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i];
@ -634,7 +634,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::backup_an
m_costs_backup = this->m_costs; m_costs_backup = this->m_costs;
} else { } else {
T cost_max = std::max(max_abs_in_vector(this->m_costs), T(1)); 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++) for (unsigned j = 0; j < this->m_costs.size(); j++)
m_costs_backup.push_back(this->m_costs[j] /= cost_max); m_costs_backup.push_back(this->m_costs[j] /= cost_max);
} }
@ -664,16 +664,16 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run(
template <typename T, typename X> void lp_primal_core_solver<T, X>::calc_working_vector_beta_for_column_norms(){ template <typename T, typename X> void lp_primal_core_solver<T, X>::calc_working_vector_beta_for_column_norms(){
SASSERT(numeric_traits<T>::precise() == false); lp_assert(numeric_traits<T>::precise() == false);
SASSERT(this->m_ed.is_OK()); lp_assert(this->m_ed.is_OK());
SASSERT(m_beta.is_OK()); lp_assert(m_beta.is_OK());
m_beta = this->m_ed; 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); this->m_factorization->solve_yB_with_error_check_indexed(m_beta, this->m_basis_heading, this->m_basis, this->m_settings);
} }
template <typename T, typename X> template <typename T, typename X>
void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving(int entering, X & t) { void lp_primal_core_solver<T, X>::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); 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()) { if (this->A_mult_x_is_off_on_index(this->m_ed.m_index) && !this->find_x_by_solving()) {
this->init_lu(); this->init_lu();
@ -685,7 +685,7 @@ void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving(int entering
} }
} }
if (this->m_using_infeas_costs) { 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(); init_infeasibility_costs_for_changed_basis_only();
} }
if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
@ -698,10 +698,10 @@ void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving(int entering
} }
template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_entering_and_leaving(int entering, int leaving, X & t) { template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_entering_and_leaving(int entering, int leaving, X & t) {
SASSERT(entering >= 0 && m_non_basis_list.back() == static_cast<unsigned>(entering)); lp_assert(entering >= 0 && m_non_basis_list.back() == static_cast<unsigned>(entering));
SASSERT(this->m_using_infeas_costs || t >= zero_of_type<X>()); lp_assert(this->m_using_infeas_costs || t >= zero_of_type<X>());
SASSERT(leaving >= 0 && entering >= 0); lp_assert(leaving >= 0 && entering >= 0);
SASSERT(entering != leaving || !is_zero(t)); // otherwise nothing changes lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes
if (entering == leaving) { if (entering == leaving) {
advance_on_entering_equal_leaving(entering, t); advance_on_entering_equal_leaving(entering, t);
return; return;
@ -713,14 +713,14 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
int pivot_compare_result = this->pivots_in_column_and_row_are_different(entering, leaving); int pivot_compare_result = this->pivots_in_column_and_row_are_different(entering, leaving);
if (!pivot_compare_result){;} if (!pivot_compare_result){;}
else if (pivot_compare_result == 2) { // the sign is changed, cannot continue 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()++; this->iters_with_no_cost_growing()++;
return; return;
} else { } else {
SASSERT(pivot_compare_result == 1); lp_assert(pivot_compare_result == 1);
this->init_lu(); this->init_lu();
if (this->m_factorization == nullptr || this->m_factorization->get_status() != LU_status::OK) { 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()++; this->iters_with_no_cost_growing()++;
return; return;
} }
@ -732,10 +732,10 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
t = -t; t = -t;
} }
if (!this->update_basis_and_x(entering, leaving, 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; return;
if (this->m_look_for_feasible_solution_only) { if (this->m_look_for_feasible_solution_only) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
return; return;
} }
init_reduced_costs(); init_reduced_costs();
@ -748,7 +748,7 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
} }
if (this->current_x_is_feasible()) { if (this->current_x_is_feasible()) {
this->set_status(FEASIBLE); this->set_status(lp_status::FEASIBLE);
if (this->m_look_for_feasible_solution_only) if (this->m_look_for_feasible_solution_only)
return; return;
} }
@ -761,7 +761,7 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
} else { } else {
update_reduced_costs_from_pivot_row(entering, leaving); update_reduced_costs_from_pivot_row(entering, leaving);
} }
SASSERT(!need_to_switch_costs()); lp_assert(!need_to_switch_costs());
std::list<unsigned>::iterator it = m_non_basis_list.end(); std::list<unsigned>::iterator it = m_non_basis_list.end();
it--; it--;
* it = static_cast<unsigned>(leaving); * it = static_cast<unsigned>(leaving);
@ -769,13 +769,13 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_entering_precise(int entering) { template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_entering_precise(int entering) {
SASSERT(numeric_traits<T>::precise()); lp_assert(numeric_traits<T>::precise());
SASSERT(entering > -1); lp_assert(entering > -1);
this->solve_Bd(entering); this->solve_Bd(entering);
X t; X t;
int leaving = find_leaving_and_t_precise(entering, t); int leaving = find_leaving_and_t_precise(entering, t);
if (leaving == -1) { if (leaving == -1) {
this->set_status(UNBOUNDED); this->set_status(lp_status::UNBOUNDED);
return; return;
} }
advance_on_entering_and_leaving(entering, leaving, t); advance_on_entering_and_leaving(entering, leaving, t);
@ -786,12 +786,12 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_e
advance_on_entering_precise(entering); advance_on_entering_precise(entering);
return; return;
} }
SASSERT(entering > -1); lp_assert(entering > -1);
this->solve_Bd(entering); this->solve_Bd(entering);
int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering); int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering);
if (refresh_result) { if (refresh_result) {
if (this->m_look_for_feasible_solution_only) { if (this->m_look_for_feasible_solution_only) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
return; return;
} }
@ -806,7 +806,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_e
int leaving = find_leaving_and_t(entering, t); int leaving = find_leaving_and_t(entering, t);
if (leaving == -1){ if (leaving == -1){
if (!this->current_x_is_feasible()) { if (!this->current_x_is_feasible()) {
SASSERT(!numeric_traits<T>::precise()); // we cannot have unbounded with inf costs lp_assert(!numeric_traits<T>::precise()); // we cannot have unbounded with inf costs
// if (m_look_for_feasible_solution_only) { // if (m_look_for_feasible_solution_only) {
// this->m_status = INFEASIBLE; // this->m_status = INFEASIBLE;
@ -814,19 +814,19 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_e
// } // }
if (this->get_status() == UNSTABLE) { if (this->get_status() == lp_status::UNSTABLE) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
return; return;
} }
init_infeasibility_costs(); init_infeasibility_costs();
this->set_status(UNSTABLE); this->set_status(lp_status::UNSTABLE);
return; return;
} }
if (this->get_status() == TENTATIVE_UNBOUNDED) { if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) {
this->set_status(UNBOUNDED); this->set_status(lp_status::UNBOUNDED);
} else { } else {
this->set_status(TENTATIVE_UNBOUNDED); this->set_status(lp_status::TENTATIVE_UNBOUNDED);
} }
return; return;
} }
@ -840,7 +840,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::push_forw
template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::get_number_of_non_basic_column_to_try_for_enter() { template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::get_number_of_non_basic_column_to_try_for_enter() {
unsigned ret = static_cast<unsigned>(this->m_nbasis.size()); unsigned ret = static_cast<unsigned>(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 return ret; // we really need to find entering with a large reduced cost
if (ret > 300) { if (ret > 300) {
ret = (unsigned)(ret * this->m_settings.percent_of_entering_to_check / 100); ret = (unsigned)(ret * this->m_settings.percent_of_entering_to_check / 100);
@ -864,15 +864,15 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_column
template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve() { template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve() {
if (numeric_traits<T>::precise() && this->m_settings.use_tableau()) if (numeric_traits<T>::precise() && this->m_settings.use_tableau())
return solve_with_tableau(); return solve_with_tableau();
init_run(); init_run();
if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { 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; return 0;
} }
if ((!numeric_traits<T>::precise()) && this->A_mult_x_is_off()) { if ((!numeric_traits<T>::precise()) && this->A_mult_x_is_off()) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
return 0; return 0;
} }
do { do {
@ -880,10 +880,11 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
return this->total_iterations(); return this->total_iterations();
} }
one_iteration(); 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()) { switch (this->get_status()) {
case OPTIMAL: // double check that we are at optimum case lp_status::OPTIMAL: // double check that we are at optimum
case INFEASIBLE: case lp_status::INFEASIBLE:
if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
break; break;
if (!numeric_traits<T>::precise()) { if (!numeric_traits<T>::precise()) {
@ -892,7 +893,7 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
this->init_lu(); this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) { if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status (FLOATING_POINT_ERROR); this->set_status (lp_status::FLOATING_POINT_ERROR);
break; break;
} }
init_reduced_costs(); init_reduced_costs();
@ -900,7 +901,7 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
decide_on_status_when_cannot_find_entering(); decide_on_status_when_cannot_find_entering();
break; break;
} }
this->set_status(UNKNOWN); this->set_status(lp_status::UNKNOWN);
} else { // precise case } else { // precise case
if (this->m_look_for_feasible_solution_only) { // todo: keep the reduced costs correct all the time! if (this->m_look_for_feasible_solution_only) { // todo: keep the reduced costs correct all the time!
init_reduced_costs(); init_reduced_costs();
@ -908,31 +909,31 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
decide_on_status_when_cannot_find_entering(); decide_on_status_when_cannot_find_entering();
break; break;
} }
this->set_status(UNKNOWN); this->set_status(lp_status::UNKNOWN);
} }
} }
break; break;
case TENTATIVE_UNBOUNDED: case lp_status::TENTATIVE_UNBOUNDED:
this->init_lu(); this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) { if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
break; break;
} }
init_reduced_costs(); init_reduced_costs();
break; break;
case UNBOUNDED: case lp_status::UNBOUNDED:
if (this->current_x_is_infeasible()) { if (this->current_x_is_infeasible()) {
init_reduced_costs(); init_reduced_costs();
this->set_status(UNKNOWN); this->set_status(lp_status::UNKNOWN);
} }
break; break;
case UNSTABLE: case lp_status::UNSTABLE:
SASSERT(! (numeric_traits<T>::precise())); lp_assert(! (numeric_traits<T>::precise()));
this->init_lu(); this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) { if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
break; break;
} }
init_reduced_costs(); init_reduced_costs();
@ -941,13 +942,13 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
default: default:
break; // do nothing 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 this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements
&& &&
@ -955,7 +956,7 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
&& &&
!(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); !(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 this->current_x_is_feasible() == false
|| ||
@ -972,7 +973,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::delete_fa
// according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming"
template <typename T, typename X> void lp_primal_core_solver<T, X>::init_column_norms() { template <typename T, typename X> void lp_primal_core_solver<T, X>::init_column_norms() {
SASSERT(numeric_traits<T>::precise() == false); lp_assert(numeric_traits<T>::precise() == false);
for (unsigned j = 0; j < this->m_n(); j++) { for (unsigned j = 0; j < this->m_n(); j++) {
this->m_column_norms[j] = T(static_cast<int>(this->m_A.m_columns[j].size() + 1)) this->m_column_norms[j] = T(static_cast<int>(this->m_A.m_columns[j].size() + 1))
@ -982,7 +983,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::init_column_
// debug only // debug only
template <typename T, typename X> T lp_primal_core_solver<T, X>::calculate_column_norm_exactly(unsigned j) { template <typename T, typename X> T lp_primal_core_solver<T, X>::calculate_column_norm_exactly(unsigned j) {
SASSERT(numeric_traits<T>::precise() == false); lp_assert(numeric_traits<T>::precise() == false);
indexed_vector<T> w(this->m_m()); indexed_vector<T> w(this->m_m());
this->m_A.copy_column_to_vector(j, w); this->m_A.copy_column_to_vector(j, w);
vector<T> d(this->m_m()); vector<T> d(this->m_m());
@ -994,8 +995,8 @@ template <typename T, typename X> T lp_primal_core_solver<T, X>::calculate_colum
} }
template <typename T, typename X> void lp_primal_core_solver<T, X>::update_or_init_column_norms(unsigned entering, unsigned leaving) { template <typename T, typename X> void lp_primal_core_solver<T, X>::update_or_init_column_norms(unsigned entering, unsigned leaving) {
SASSERT(numeric_traits<T>::precise() == false); lp_assert(numeric_traits<T>::precise() == false);
SASSERT(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); 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) { if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) {
m_column_norm_update_counter = 0; m_column_norm_update_counter = 0;
init_column_norms(); init_column_norms();
@ -1007,7 +1008,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::update_or
// following Swietanowski - A new steepest ... // following Swietanowski - A new steepest ...
template <typename T, typename X> void lp_primal_core_solver<T, X>::update_column_norms(unsigned entering, unsigned leaving) { template <typename T, typename X> void lp_primal_core_solver<T, X>::update_column_norms(unsigned entering, unsigned leaving) {
SASSERT(numeric_traits<T>::precise() == false); lp_assert(numeric_traits<T>::precise() == false);
T pivot = this->m_pivot_row[entering]; T pivot = this->m_pivot_row[entering];
T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot;
if (!numeric_traits<T>::precise()) { if (!numeric_traits<T>::precise()) {
@ -1042,8 +1043,8 @@ template <typename T, typename X> T lp_primal_core_solver<T, X>::calculate_no
// calling it stage1 is too cryptic // calling it stage1 is too cryptic
template <typename T, typename X> void lp_primal_core_solver<T, X>::find_feasible_solution() { template <typename T, typename X> void lp_primal_core_solver<T, X>::find_feasible_solution() {
this->m_look_for_feasible_solution_only = true; this->m_look_for_feasible_solution_only = true;
SASSERT(this->non_basic_columns_are_set_correctly()); lp_assert(this->non_basic_columns_are_set_correctly());
this->set_status(UNKNOWN); this->set_status(lp_status::UNKNOWN);
solve(); solve();
} }
@ -1087,15 +1088,15 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::fill_breakpo
template <typename T, typename X> bool lp_primal_core_solver<T, X>::done() { template <typename T, typename X> bool lp_primal_core_solver<T, X>::done() {
if (this->get_status() == OPTIMAL || this->get_status() == FLOATING_POINT_ERROR) return true; if (this->get_status() == lp_status::OPTIMAL || this->get_status() == lp_status::FLOATING_POINT_ERROR) return true;
if (this->get_status() == INFEASIBLE) { if (this->get_status() == lp_status::INFEASIBLE) {
return true; return true;
} }
if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) { 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) { 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; return false;
} }
@ -1110,8 +1111,8 @@ void lp_primal_core_solver<T, X>::init_infeasibility_costs_for_changed_basis_onl
template <typename T, typename X> template <typename T, typename X>
void lp_primal_core_solver<T, X>::init_infeasibility_costs() { void lp_primal_core_solver<T, X>::init_infeasibility_costs() {
SASSERT(this->m_x.size() >= this->m_n()); lp_assert(this->m_x.size() >= this->m_n());
SASSERT(this->m_column_types.size() >= this->m_n()); lp_assert(this->m_column_types.size() >= this->m_n());
for (unsigned j = this->m_n(); j--;) for (unsigned j = this->m_n(); j--;)
init_infeasibility_cost_for_column(j); init_infeasibility_cost_for_column(j);
this->m_using_infeas_costs = true; this->m_using_infeas_costs = true;
@ -1135,7 +1136,7 @@ lp_primal_core_solver<T, X>::get_infeasibility_cost_for_column(unsigned j) const
ret = numeric_traits<T>::zero(); ret = numeric_traits<T>::zero();
} }
break; break;
case column_type::low_bound: case column_type::lower_bound:
if (this->x_below_low_bound(j)) { if (this->x_below_low_bound(j)) {
ret = -1; ret = -1;
} else { } else {
@ -1153,7 +1154,7 @@ lp_primal_core_solver<T, X>::get_infeasibility_cost_for_column(unsigned j) const
ret = numeric_traits<T>::zero(); ret = numeric_traits<T>::zero();
break; break;
default: default:
SASSERT(false); lp_assert(false);
ret = numeric_traits<T>::zero(); // does not matter ret = numeric_traits<T>::zero(); // does not matter
break; break;
} }
@ -1189,7 +1190,7 @@ lp_primal_core_solver<T, X>::init_infeasibility_cost_for_column(unsigned j) {
this->m_costs[j] = numeric_traits<T>::zero(); this->m_costs[j] = numeric_traits<T>::zero();
} }
break; break;
case column_type::low_bound: case column_type::lower_bound:
if (this->x_below_low_bound(j)) { if (this->x_below_low_bound(j)) {
this->m_costs[j] = -1; this->m_costs[j] = -1;
} else { } else {
@ -1207,14 +1208,14 @@ lp_primal_core_solver<T, X>::init_infeasibility_cost_for_column(unsigned j) {
this->m_costs[j] = numeric_traits<T>::zero(); this->m_costs[j] = numeric_traits<T>::zero();
break; break;
default: default:
SASSERT(false); lp_assert(false);
break; break;
} }
if (numeric_traits<T>::is_zero(this->m_costs[j])) { if (numeric_traits<T>::is_zero(this->m_costs[j])) {
this->m_inf_set.erase(j); this->remove_column_from_inf_set(j);
} else { } else {
this->m_inf_set.insert(j); this->insert_column_into_inf_set(j);
} }
if (!this->m_settings.use_breakpoints_in_feasibility_search) { if (!this->m_settings.use_breakpoints_in_feasibility_search) {
this->m_costs[j] = - this->m_costs[j]; this->m_costs[j] = - this->m_costs[j];
@ -1227,18 +1228,18 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_column
switch (this->m_column_type[j]) { switch (this->m_column_type[j]) {
case column_type::fixed: case column_type::fixed:
case column_type::boxed: 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; break;
case column_type::upper_bound: case column_type::upper_bound:
out << "( _" << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl; out << "( _" << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl;
break; break;
case column_type::low_bound: case column_type::lower_bound:
out << "( " << this->m_low_bounds[j] << " " << this->m_x[j] << " " << "_ )" << std::endl; out << "( " << this->m_lower_bounds[j] << " " << this->m_x[j] << " " << "_ )" << std::endl;
break; break;
case column_type::free_column: case column_type::free_column:
out << "( _" << this->m_x[j] << "_)" << std::endl; out << "( _" << this->m_x[j] << "_)" << std::endl;
default: default:
SASSERT(false); lp_unreachable();
} }
} }
@ -1277,7 +1278,7 @@ template <typename T, typename X> std::string lp_primal_core_solver<T, X>::break
case upper_break: return "upper_break"; case upper_break: return "upper_break";
case fixed_break: return "fixed_break"; case fixed_break: return "fixed_break";
default: default:
SASSERT(false); lp_assert(false);
break; break;
} }
return "type is not found"; return "type is not found";
@ -1290,7 +1291,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_breakp
template <typename T, typename X> template <typename T, typename X>
void lp_primal_core_solver<T, X>::init_reduced_costs() { void lp_primal_core_solver<T, X>::init_reduced_costs() {
SASSERT(!this->use_tableau()); lp_assert(!this->use_tableau());
if (this->current_x_is_infeasible() && !this->m_using_infeas_costs) { if (this->current_x_is_infeasible() && !this->m_using_infeas_costs) {
init_infeasibility_costs(); init_infeasibility_costs();
} else if (this->current_x_is_feasible() && this->m_using_infeas_costs) { } else if (this->current_x_is_feasible() && this->m_using_infeas_costs) {
@ -1305,12 +1306,12 @@ void lp_primal_core_solver<T, X>::init_reduced_costs() {
template <typename T, typename X> void lp_primal_core_solver<T, X>::change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering) { template <typename T, typename X> void lp_primal_core_solver<T, X>::change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering) {
if (b->m_j == 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; slope_at_entering += m_sign_of_entering_delta;
return; 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]; unsigned i_row = this->m_basis_heading[b->m_j];
const T & d = - this->m_ed[i_row]; const T & d = - this->m_ed[i_row];
if (numeric_traits<T>::is_zero(d)) return; if (numeric_traits<T>::is_zero(d)) return;
@ -1329,27 +1330,27 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::change_sl
slope_at_entering += delta; slope_at_entering += delta;
break; break;
default: default:
SASSERT(false); lp_assert(false);
} }
} }
template <typename T, typename X> void lp_primal_core_solver<T, X>::try_add_breakpoint_in_row(unsigned i) { template <typename T, typename X> void lp_primal_core_solver<T, X>::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 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 if (d == 0) return; // the change of x[m_entering] will not change the corresponding basis x
unsigned j = this->m_basis[i]; unsigned j = this->m_basis[i];
const X & x = this->m_x[j]; const X & x = this->m_x[j];
switch (this->m_column_types[j]) { switch (this->m_column_types[j]) {
case column_type::fixed: 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; break;
case column_type::boxed: 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]); try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]);
break; break;
case column_type::low_bound: case column_type::lower_bound:
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]);
break; break;
case column_type::upper_bound: case column_type::upper_bound:
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]);
@ -1357,7 +1358,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::try_add_b
case column_type::free_column: case column_type::free_column:
break; break;
default: default:
SASSERT(false); lp_assert(false);
break; break;
} }
} }
@ -1369,10 +1370,10 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_bound_
switch (this->m_column_types[j]) { switch (this->m_column_types[j]) {
case column_type::fixed: case column_type::fixed:
case column_type::boxed: 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; break;
case column_type::low_bound: case column_type::lower_bound:
out << "[" << this->m_low_bounds[j] << ", inf" << std::endl; out << "[" << this->m_lower_bounds[j] << ", inf" << std::endl;
break; break;
case column_type::upper_bound: case column_type::upper_bound:
out << "inf ," << this->m_upper_bounds[j] << "]" << std::endl; out << "inf ," << this->m_upper_bounds[j] << "]" << std::endl;
@ -1381,7 +1382,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_bound_
out << "inf, inf" << std::endl; out << "inf, inf" << std::endl;
break; break;
default: default:
SASSERT(false); lp_assert(false);
break; break;
} }
} }

View file

@ -28,14 +28,14 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::one_iteratio
else { else {
advance_on_entering_tableau(entering); advance_on_entering_tableau(entering);
} }
SASSERT(this->inf_set_is_correct()); lp_assert(this->inf_set_is_correct());
} }
template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_entering_tableau(int entering) { template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_entering_tableau(int entering) {
X t; X t;
int leaving = find_leaving_and_t_tableau(entering, t); int leaving = find_leaving_and_t_tableau(entering, t);
if (leaving == -1) { if (leaving == -1) {
this->set_status(UNBOUNDED); this->set_status(lp_status::UNBOUNDED);
return; return;
} }
advance_on_entering_and_leaving_tableau(entering, leaving, t); advance_on_entering_and_leaving_tableau(entering, leaving, t);
@ -52,7 +52,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::choose_enteri
//this moment m_y = cB * B(-1) //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(); unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter();
SASSERT(numeric_traits<T>::precise()); lp_assert(numeric_traits<T>::precise());
if (number_of_benefitial_columns_to_go_over == 0) if (number_of_benefitial_columns_to_go_over == 0)
return -1; return -1;
if (this->m_basis_sort_counter == 0) { if (this->m_basis_sort_counter == 0) {
@ -100,25 +100,26 @@ template <typename T, typename X>
unsigned lp_primal_core_solver<T, X>::solve_with_tableau() { unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
init_run_tableau(); init_run_tableau();
if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { 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; return 0;
} }
if ((!numeric_traits<T>::precise()) && this->A_mult_x_is_off()) { if ((!numeric_traits<T>::precise()) && this->A_mult_x_is_off()) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
return 0; return 0;
} }
do { 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())) { 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(); return this->total_iterations();
} }
if (this->m_settings.use_tableau_rows()) if (this->m_settings.use_tableau_rows()) {
one_iteration_tableau_rows(); one_iteration_tableau_rows();
}
else else
one_iteration_tableau(); one_iteration_tableau();
switch (this->get_status()) { switch (this->get_status()) {
case OPTIMAL: // double check that we are at optimum case lp_status::OPTIMAL: // double check that we are at optimum
case INFEASIBLE: case lp_status::INFEASIBLE:
if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
break; break;
if (!numeric_traits<T>::precise()) { if (!numeric_traits<T>::precise()) {
@ -127,7 +128,7 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
this->init_lu(); this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) { if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
break; break;
} }
init_reduced_costs(); init_reduced_costs();
@ -135,7 +136,7 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
decide_on_status_when_cannot_find_entering(); decide_on_status_when_cannot_find_entering();
break; break;
} }
this->set_status(UNKNOWN); this->set_status(lp_status::UNKNOWN);
} else { // precise case } else { // precise case
if ((!this->infeasibility_costs_are_correct())) { if ((!this->infeasibility_costs_are_correct())) {
init_reduced_costs_tableau(); // forcing recalc init_reduced_costs_tableau(); // forcing recalc
@ -143,31 +144,31 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
decide_on_status_when_cannot_find_entering(); decide_on_status_when_cannot_find_entering();
break; break;
} }
this->set_status(UNKNOWN); this->set_status(lp_status::UNKNOWN);
} }
} }
break; break;
case TENTATIVE_UNBOUNDED: case lp_status::TENTATIVE_UNBOUNDED:
this->init_lu(); this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) { if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
break; break;
} }
init_reduced_costs(); init_reduced_costs();
break; break;
case UNBOUNDED: case lp_status::UNBOUNDED:
if (this->current_x_is_infeasible()) { if (this->current_x_is_infeasible()) {
init_reduced_costs(); init_reduced_costs();
this->set_status(UNKNOWN); this->set_status(lp_status::UNKNOWN);
} }
break; break;
case UNSTABLE: case lp_status::UNSTABLE:
SASSERT(! (numeric_traits<T>::precise())); lp_assert(! (numeric_traits<T>::precise()));
this->init_lu(); this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) { if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR); this->set_status(lp_status::FLOATING_POINT_ERROR);
break; break;
} }
init_reduced_costs(); init_reduced_costs();
@ -176,13 +177,13 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
default: default:
break; // do nothing 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 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<T, X>::solve_with_tableau() {
this->m_settings.get_cancel_flag() == false); this->m_settings.get_cancel_flag() == false);
if (this->m_settings.get_cancel_flag()) { if (this->m_settings.get_cancel_flag()) {
this->set_status(CANCELLED); this->set_status(lp_status::CANCELLED);
} }
SASSERT( lp_assert(
this->get_status() == FLOATING_POINT_ERROR this->get_status() == lp_status::FLOATING_POINT_ERROR
|| ||
this->get_status() == CANCELLED this->get_status() == lp_status::CANCELLED
|| ||
this->current_x_is_feasible() == false this->current_x_is_feasible() == false
|| ||
@ -208,13 +209,13 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
} }
template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) { template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) {
SASSERT(this->A_mult_x_is_off() == false); CASSERT("A_off", this->A_mult_x_is_off() == false);
SASSERT(leaving >= 0 && entering >= 0); lp_assert(leaving >= 0 && entering >= 0);
SASSERT((this->m_settings.simplex_strategy() == lp_assert((this->m_settings.simplex_strategy() ==
simplex_strategy_enum::tableau_rows) || simplex_strategy_enum::tableau_rows) ||
m_non_basis_list.back() == static_cast<unsigned>(entering)); m_non_basis_list.back() == static_cast<unsigned>(entering));
SASSERT(this->m_using_infeas_costs || !is_neg(t)); lp_assert(this->m_using_infeas_costs || !is_neg(t));
SASSERT(entering != leaving || !is_zero(t)); // otherwise nothing changes lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes
if (entering == leaving) { if (entering == leaving) {
advance_on_entering_equal_leaving_tableau(entering, t); advance_on_entering_equal_leaving_tableau(entering, t);
return; return;
@ -225,7 +226,7 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
t = -t; t = -t;
} }
this->update_basis_and_x_tableau(entering, leaving, 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; this->iters_with_no_cost_growing() = 0;
} else { } else {
this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); this->pivot_column_tableau(entering, this->m_basis_heading[leaving]);
@ -240,7 +241,7 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
this->init_reduced_costs_tableau(); this->init_reduced_costs_tableau();
} }
SASSERT(!need_to_switch_costs()); lp_assert(!need_to_switch_costs());
std::list<unsigned>::iterator it = m_non_basis_list.end(); std::list<unsigned>::iterator it = m_non_basis_list.end();
it--; it--;
* it = static_cast<unsigned>(leaving); * it = static_cast<unsigned>(leaving);
@ -249,7 +250,7 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
template <typename T, typename X> template <typename T, typename X>
void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving_tableau(int entering, X & t) { void lp_primal_core_solver<T, X>::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); this->update_x_tableau(entering, t * m_sign_of_entering_delta);
if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
return; return;
@ -270,7 +271,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
const column_cell & c = col[k]; const column_cell & c = col[k];
unsigned i = c.m_i; unsigned i = c.m_i;
const T & ed = this->m_A.get_val(c); const T & ed = this->m_A.get_val(c);
SASSERT(!numeric_traits<T>::is_zero(ed)); lp_assert(!numeric_traits<T>::is_zero(ed));
unsigned j = this->m_basis[i]; unsigned j = this->m_basis[i];
limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited);
if (!unlimited) { if (!unlimited) {
@ -289,7 +290,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
const column_cell & c = col[k]; const column_cell & c = col[k];
unsigned i = c.m_i; unsigned i = c.m_i;
const T & ed = this->m_A.get_val(c); const T & ed = this->m_A.get_val(c);
SASSERT(!numeric_traits<T>::is_zero(ed)); lp_assert(!numeric_traits<T>::is_zero(ed));
unsigned j = this->m_basis[i]; unsigned j = this->m_basis[i];
unlimited = true; unlimited = true;
limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited);
@ -321,13 +322,12 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
return m_leaving_candidates[k]; return m_leaving_candidates[k];
} }
template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run_tableau() { template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run_tableau() {
// print_matrix(&(this->m_A), std::cout); CASSERT("A_off", this->A_mult_x_is_off() == false);
SASSERT(this->A_mult_x_is_off() == false); lp_assert(basis_columns_are_set_correctly());
SASSERT(basis_columns_are_set_correctly());
this->m_basis_sort_counter = 0; // to initiate the sort of the basis this->m_basis_sort_counter = 0; // to initiate the sort of the basis
this->set_total_iterations(0); this->set_total_iterations(0);
this->iters_with_no_cost_growing() = 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) if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)
return; return;
if (this->m_settings.backup_costs) if (this->m_settings.backup_costs)
@ -341,13 +341,13 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run_tab
} }
if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
init_tableau_rows(); init_tableau_rows();
SASSERT(this->reduced_costs_are_correct_tableau()); lp_assert(this->reduced_costs_are_correct_tableau());
SASSERT(!this->need_to_pivot_to_basis_tableau()); lp_assert(!this->need_to_pivot_to_basis_tableau());
} }
template <typename T, typename X> bool lp_primal_core_solver<T, X>:: template <typename T, typename X> bool lp_primal_core_solver<T, X>::
update_basis_and_x_tableau(int entering, int leaving, X const & tt) { 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); update_x_tableau(entering, tt);
this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); this->pivot_column_tableau(entering, this->m_basis_heading[leaving]);
this->change_basis(entering, leaving); this->change_basis(entering, leaving);
@ -355,36 +355,34 @@ update_basis_and_x_tableau(int entering, int leaving, X const & tt) {
} }
template <typename T, typename X> void lp_primal_core_solver<T, X>:: template <typename T, typename X> void lp_primal_core_solver<T, X>::
update_x_tableau(unsigned entering, const X& delta) { update_x_tableau(unsigned entering, const X& delta) {
this->add_delta_to_x_and_call_tracker(entering, delta);
if (!this->m_using_infeas_costs) { if (!this->m_using_infeas_costs) {
this->m_x[entering] += delta;
for (const auto & c : this->m_A.m_columns[entering]) { for (const auto & c : this->m_A.m_columns[entering]) {
unsigned i = c.m_i; unsigned i = c.m_i;
this->m_x[this->m_basis[i]] -= delta * this->m_A.get_val(c); this->update_x_with_delta_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c));
this->update_column_in_inf_set(this->m_basis[i]);
} }
} else { // m_using_infeas_costs == true } else { // m_using_infeas_costs == true
this->m_x[entering] += delta; lp_assert(this->column_is_feasible(entering));
SASSERT(this->column_is_feasible(entering)); lp_assert(this->m_costs[entering] == zero_of_type<T>());
SASSERT(this->m_costs[entering] == zero_of_type<T>());
// m_d[entering] can change because of the cost change for basic columns. // m_d[entering] can change because of the cost change for basic columns.
for (const auto & c : this->m_A.m_columns[entering]) { for (const auto & c : this->m_A.m_columns[entering]) {
unsigned i = c.m_i; unsigned i = c.m_i;
unsigned j = this->m_basis[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); update_inf_cost_for_column_tableau(j);
if (is_zero(this->m_costs[j])) if (is_zero(this->m_costs[j]))
this->m_inf_set.erase(j); this->remove_column_from_inf_set(j);
else 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 <typename T, typename X> void lp_primal_core_solver<T, X>:: template <typename T, typename X> void lp_primal_core_solver<T, X>::
update_inf_cost_for_column_tableau(unsigned j) { update_inf_cost_for_column_tableau(unsigned j) {
SASSERT(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows); lp_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows);
SASSERT(this->m_using_infeas_costs); lp_assert(this->m_using_infeas_costs);
T new_cost = get_infeasibility_cost_for_column(j); T new_cost = get_infeasibility_cost_for_column(j);
T delta = this->m_costs[j] - new_cost; T delta = this->m_costs[j] - new_cost;
if (is_zero(delta)) if (is_zero(delta))

View file

@ -22,7 +22,7 @@ Revision History:
#include <string> #include <string>
#include "util/vector.h" #include "util/vector.h"
#include <functional> #include <functional>
#include "util/lp/lp_primal_simplex.hpp" #include "util/lp/lp_primal_simplex_def.h"
template bool lp::lp_primal_simplex<double, double>::bounds_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&); template bool lp::lp_primal_simplex<double, double>::bounds_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&);
template bool lp::lp_primal_simplex<double, double>::row_constraints_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&); template bool lp::lp_primal_simplex<double, double>::row_constraints_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&);
template double lp::lp_primal_simplex<double, double>::get_current_cost() const; template double lp::lp_primal_simplex<double, double>::get_current_cost() const;

View file

@ -26,12 +26,11 @@ Revision History:
#include "util/lp/column_info.h" #include "util/lp/column_info.h"
#include "util/lp/lp_primal_core_solver.h" #include "util/lp/lp_primal_core_solver.h"
#include "util/lp/lp_solver.h" #include "util/lp/lp_solver.h"
#include "util/lp/iterator_on_row.h"
namespace lp { namespace lp {
template <typename T, typename X> template <typename T, typename X>
class lp_primal_simplex: public lp_solver<T, X> { class lp_primal_simplex: public lp_solver<T, X> {
lp_primal_core_solver<T, X> * m_core_solver; lp_primal_core_solver<T, X> * m_core_solver;
vector<X> m_low_bounds; vector<X> m_lower_bounds;
private: private:
unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); }

View file

@ -76,14 +76,14 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::fill_costs_and_x
int row, int row,
unsigned & slack_var, unsigned & slack_var,
unsigned & artificial) { 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]]; 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 // we need to bring the program to the form Ax = b
T rs = this->m_b[row]; T rs = this->m_b[row];
T artificial_cost = - numeric_traits<T>::one(); T artificial_cost = - numeric_traits<T>::one();
switch (constraint.m_relation) { switch (constraint.m_relation) {
case Equal: // no slack variable here 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_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero
this->m_basis[row] = artificial; this->m_basis[row] = artificial;
if (rs >= 0) { if (rs >= 0) {
@ -97,13 +97,13 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::fill_costs_and_x
break; break;
case Greater_or_equal: 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<T>::one(); (*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
if (rs > 0) { if (rs > 0) {
SASSERT(numeric_traits<T>::is_zero(this->m_x[slack_var])); lp_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
// adding one artificial // 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<T>::one(); (*this->m_A)(row, artificial) = numeric_traits<T>::one();
this->m_costs[artificial] = artificial_cost; this->m_costs[artificial] = artificial_cost;
this->m_basis[row] = artificial; this->m_basis[row] = artificial;
@ -118,13 +118,13 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::fill_costs_and_x
break; break;
case Less_or_equal: case Less_or_equal:
// introduce a non-negative slack variable // 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<T>::one(); (*this->m_A)(row, slack_var) = numeric_traits<T>::one();
if (rs < 0) { if (rs < 0) {
// adding one artificial // adding one artificial
SASSERT(numeric_traits<T>::is_zero(this->m_x[slack_var])); lp_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
this->m_column_types[artificial] = column_type::low_bound; this->m_column_types[artificial] = column_type::lower_bound;
(*this->m_A)(row, artificial) = - numeric_traits<T>::one(); (*this->m_A)(row, artificial) = - numeric_traits<T>::one();
this->m_costs[artificial] = artificial_cost; this->m_costs[artificial] = artificial_cost;
this->m_x[artificial] = - rs; this->m_x[artificial] = - rs;
@ -192,12 +192,12 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::fill_A_x_and_bas
} }
template <typename T, typename X> void lp_primal_simplex<T, X>::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { template <typename T, typename X> void lp_primal_simplex<T, X>::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); 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; unsigned ext_row = ext_row_it->second;
auto constr_it = this->m_constraints.find(ext_row); 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; auto & constraint = constr_it->second;
unsigned j = this->m_A->column_count(); // j is a slack variable unsigned j = this->m_A->column_count(); // j is a slack variable
this->m_A->add_column(); this->m_A->add_column();
@ -208,34 +208,34 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::fill_A_x_and_bas
this->m_x[j] = this->m_b[row]; this->m_x[j] = this->m_b[row];
(*this->m_A)(row, j) = numeric_traits<T>::one(); (*this->m_A)(row, j) = numeric_traits<T>::one();
this->m_column_types[j] = column_type::fixed; this->m_column_types[j] = column_type::fixed;
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>(); this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type<X>();
break; break;
case Greater_or_equal: case Greater_or_equal:
this->m_x[j] = - this->m_b[row]; this->m_x[j] = - this->m_b[row];
(*this->m_A)(row, j) = - numeric_traits<T>::one(); (*this->m_A)(row, j) = - numeric_traits<T>::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<X>(); this->m_upper_bounds[j] = zero_of_type<X>();
break; break;
case Less_or_equal: case Less_or_equal:
this->m_x[j] = this->m_b[row]; this->m_x[j] = this->m_b[row];
(*this->m_A)(row, j) = numeric_traits<T>::one(); (*this->m_A)(row, j) = numeric_traits<T>::one();
this->m_column_types[j] = column_type::low_bound; this->m_column_types[j] = column_type::lower_bound;
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>(); this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type<X>();
break; break;
default: default:
SASSERT(false); lp_unreachable();
} }
} }
template <typename T, typename X> void lp_primal_simplex<T, X>::solve_with_total_inf() { template <typename T, typename X> void lp_primal_simplex<T, X>::solve_with_total_inf() {
int total_vars = this->m_A->column_count() + this->row_count(); int total_vars = this->m_A->column_count() + this->row_count();
if (total_vars == 0) { if (total_vars == 0) {
this->m_status = OPTIMAL; this->m_status = lp_status::OPTIMAL;
return; return;
} }
m_low_bounds.clear(); m_lower_bounds.clear();
m_low_bounds.resize(total_vars, zero_of_type<X>()); // low bounds are shifted ot zero m_lower_bounds.resize(total_vars, zero_of_type<X>()); // low bounds are shifted ot zero
this->m_x.resize(total_vars, numeric_traits<T>::zero()); this->m_x.resize(total_vars, numeric_traits<T>::zero());
this->m_basis.resize(this->row_count()); this->m_basis.resize(this->row_count());
this->m_costs.clear(); this->m_costs.clear();
@ -253,7 +253,7 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::solve_with_total
this->m_heading, this->m_heading,
this->m_costs, this->m_costs,
this->m_column_types, this->m_column_types,
m_low_bounds, m_lower_bounds,
this->m_upper_bounds, this->m_upper_bounds,
this->m_settings, *this); this->m_settings, *this);
m_core_solver->solve(); m_core_solver->solve();
@ -278,7 +278,6 @@ template <typename T, typename X> bool lp_primal_simplex<T, X>::bounds_hold(std:
} }
if (!it.second->bounds_hold(sol_it->second)) { 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); it.second->bounds_hold(sol_it->second);
return false; return false;
} }
@ -296,10 +295,10 @@ template <typename T, typename X> T lp_primal_simplex<T, X>::get_row_value(unsig
T ret = numeric_traits<T>::zero(); T ret = numeric_traits<T>::zero();
for (auto & pair : it->second) { for (auto & pair : it->second) {
auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); 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<T> * ci = cit->second; column_info<T> * ci = cit->second;
auto sol_it = solution.find(ci->get_name()); 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; T column_val = sol_it->second;
if (out != nullptr) { if (out != nullptr) {
(*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") ";
@ -344,7 +343,7 @@ template <typename T, typename X> bool lp_primal_simplex<T, X>::row_constraint_h
} }
return true;; return true;;
} }
SASSERT(false); lp_unreachable();
return false; // it is unreachable return false; // it is unreachable
} }

View file

@ -19,7 +19,7 @@ Revision History:
--*/ --*/
#include <memory> #include <memory>
#include "util/vector.h" #include "util/vector.h"
#include "util/lp/lp_settings.hpp" #include "util/lp/lp_settings_def.h"
template bool lp::vectors_are_equal<double>(vector<double> const&, vector<double> const&); template bool lp::vectors_are_equal<double>(vector<double> const&, vector<double> const&);
template bool lp::vectors_are_equal<lp::mpq>(vector<lp::mpq > const&, vector<lp::mpq> const&); template bool lp::vectors_are_equal<lp::mpq>(vector<lp::mpq > const&, vector<lp::mpq> const&);

View file

@ -31,13 +31,16 @@ namespace lp {
typedef unsigned var_index; typedef unsigned var_index;
typedef unsigned constraint_index; typedef unsigned constraint_index;
typedef unsigned row_index; typedef unsigned row_index;
typedef vector<std::pair<mpq, constraint_index>> explanation_t;
enum class column_type { enum class column_type {
free_column = 0, free_column = 0,
low_bound = 1, lower_bound = 1,
upper_bound = 2, upper_bound = 2,
boxed = 3, boxed = 3,
fixed = 4 fixed = 4
}; };
enum class simplex_strategy_enum { enum class simplex_strategy_enum {
undecided = 3, undecided = 3,
@ -48,7 +51,7 @@ enum class simplex_strategy_enum {
std::string column_type_to_string(column_type t); std::string column_type_to_string(column_type t);
enum lp_status { enum class lp_status {
UNKNOWN, UNKNOWN,
INFEASIBLE, INFEASIBLE,
TENTATIVE_UNBOUNDED, 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); 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 <typename X> bool is_epsilon_small(const X & v, const double& eps); // forward definition template <typename X> bool is_epsilon_small(const X & v, const double& eps); // forward definition
@ -91,11 +94,22 @@ public:
}; };
struct stats { struct stats {
unsigned m_make_feasible;
unsigned m_total_iterations; unsigned m_total_iterations;
unsigned m_iters_with_no_cost_growing; unsigned m_iters_with_no_cost_growing;
unsigned m_num_factorizations; unsigned m_num_factorizations;
unsigned m_num_of_implied_bounds; unsigned m_num_of_implied_bounds;
unsigned m_need_to_solve_inf; 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(); } stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); } void reset() { memset(this, 0, sizeof(*this)); }
}; };
@ -115,53 +129,76 @@ private:
}; };
default_lp_resource_limit m_default_resource_limit; default_lp_resource_limit m_default_resource_limit;
lp_resource_limit* m_resource_limit; lp_resource_limit* m_resource_limit;
// used for debug output // used for debug output
std::ostream* m_debug_out; std::ostream* m_debug_out;
// used for messages, for example, the computation progress messages // used for messages, for example, the computation progress messages
std::ostream* m_message_out; std::ostream* m_message_out;
stats m_stats; stats m_stats;
random_gen m_rand; random_gen m_rand;
public: public:
unsigned reps_in_scaler; unsigned reps_in_scaler;
// when the absolute value of an element is less than pivot_epsilon // when the absolute value of an element is less than pivot_epsilon
// in pivoting, we treat it as a zero // in pivoting, we treat it as a zero
double pivot_epsilon; double pivot_epsilon;
// see Chatal, page 115 // 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 // 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 ... // 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; double entering_diag_epsilon;
int c_partial_pivoting; // this is the constant c from page 410 int c_partial_pivoting; // this is the constant c from page 410
unsigned depth_of_rook_search; unsigned depth_of_rook_search;
bool using_partial_pivoting; bool using_partial_pivoting;
// dissertation of Achim Koberstein // dissertation of Achim Koberstein
// if Bx - b is different at any component more that refactor_epsilon then we refactor // if Bx - b is different at any component more that refactor_epsilon then we refactor
double refactor_tolerance; double refactor_tolerance;
double pivot_tolerance; double pivot_tolerance;
double zero_tolerance; double zero_tolerance;
double drop_tolerance; double drop_tolerance;
double tolerance_for_artificials; double tolerance_for_artificials;
double can_be_taken_to_basis_tolerance; 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 unsigned percent_of_entering_to_check; // we try to find a profitable column in a percentage of the columns
bool use_scaling; bool use_scaling;
double scaling_maximum; double scaling_maximum;
double scaling_minimum; double scaling_minimum;
double harris_feasibility_tolerance; // page 179 of Istvan Maros double harris_feasibility_tolerance; // page 179 of Istvan Maros
double ignore_epsilon_of_harris; double ignore_epsilon_of_harris;
unsigned max_number_of_iterations_with_no_improvements; unsigned max_number_of_iterations_with_no_improvements;
unsigned max_total_number_of_iterations; unsigned max_total_number_of_iterations;
double time_limit; // the maximum time limit of the total run time in seconds double time_limit; // the maximum time limit of the total run time in seconds
// dual section // dual section
double dual_feasibility_tolerance; // // page 71 of the PhD thesis of Achim Koberstein 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 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 double relative_primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein
// end of dual section
bool m_bound_propagation; 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 { bool bound_progation() const {
return m_bound_propagation; return m_bound_propagation;
} }
@ -172,15 +209,15 @@ public:
lp_settings() : m_default_resource_limit(*this), lp_settings() : m_default_resource_limit(*this),
m_resource_limit(&m_default_resource_limit), m_resource_limit(&m_default_resource_limit),
m_debug_out( &std::cout), m_debug_out(&std::cout),
m_message_out(&std::cout), m_message_out(&std::cout),
reps_in_scaler(20), reps_in_scaler(20),
pivot_epsilon(0.00000001), pivot_epsilon(0.00000001),
positive_price_epsilon(1e-7), positive_price_epsilon(1e-7),
entering_diag_epsilon ( 1e-8), entering_diag_epsilon (1e-8),
c_partial_pivoting ( 10), // this is the constant c from page 410 c_partial_pivoting (10), // this is the constant c from page 410
depth_of_rook_search ( 4), depth_of_rook_search (4),
using_partial_pivoting ( true), using_partial_pivoting (true),
// dissertation of Achim Koberstein // dissertation of Achim Koberstein
// if Bx - b is different at any component more that refactor_epsilon then we refactor // if Bx - b is different at any component more that refactor_epsilon then we refactor
refactor_tolerance ( 1e-4), refactor_tolerance ( 1e-4),
@ -189,7 +226,6 @@ public:
drop_tolerance ( 1e-14), drop_tolerance ( 1e-14),
tolerance_for_artificials ( 1e-4), tolerance_for_artificials ( 1e-4),
can_be_taken_to_basis_tolerance ( 0.00001), 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 percent_of_entering_to_check ( 5),// we try to find a profitable column in a percentage of the columns
use_scaling ( true), use_scaling ( true),
scaling_maximum ( 1), scaling_maximum ( 1),
@ -214,7 +250,15 @@ public:
use_breakpoints_in_feasibility_search(false), use_breakpoints_in_feasibility_search(false),
max_row_length_for_bound_propagation(300), max_row_length_for_bound_propagation(300),
backup_costs(true), 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; } void set_resource_limit(lp_resource_limit& lim) { m_resource_limit = &lim; }
@ -284,8 +328,6 @@ public:
return is_eps_small_general<T>(t, tolerance_for_artificials); return is_eps_small_general<T>(t, tolerance_for_artificials);
} }
// the method of lar solver to use // 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 { simplex_strategy_enum simplex_strategy() const {
return m_simplex_strategy; return m_simplex_strategy;
} }
@ -307,20 +349,9 @@ public:
return m_simplex_strategy == simplex_strategy_enum::tableau_rows; 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 #ifdef Z3DEBUG
static unsigned ddd; // used for debugging static unsigned ddd; // used for debugging
#endif #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 }; // end of lp_settings class
@ -343,7 +374,7 @@ inline std::string T_to_string(const numeric_pair<mpq> & t) {
inline std::string T_to_string(const mpq & t) { inline std::string T_to_string(const mpq & t) {
std::ostringstream strs; std::ostringstream strs;
strs << t.get_double(); strs << t;
return strs.str(); 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 // 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 // we also suppose that before the last push the vector was increasing
inline void ensure_increasing(vector<unsigned> & v) { inline void ensure_increasing(vector<unsigned> & v) {
SASSERT(v.size() > 0); lp_assert(v.size() > 0);
unsigned j = v.size() - 1; unsigned j = v.size() - 1;
for (; j > 0; j-- ) for (; j > 0; j-- )
if (v[j] <= v[j - 1]) { if (v[j] <= v[j - 1]) {

View file

@ -26,30 +26,30 @@ std::string column_type_to_string(column_type t) {
switch (t) { switch (t) {
case column_type::fixed: return "fixed"; case column_type::fixed: return "fixed";
case column_type::boxed: return "boxed"; 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::upper_bound: return "upper_bound";
case column_type::free_column: return "free_column"; case column_type::free_column: return "free_column";
default: SASSERT(false); default: lp_unreachable();
} }
return "unknown"; // it is unreachable return "unknown"; // it is unreachable
} }
const char* lp_status_to_string(lp_status status) { const char* lp_status_to_string(lp_status status) {
switch (status) { switch (status) {
case UNKNOWN: return "UNKNOWN"; case lp_status::UNKNOWN: return "UNKNOWN";
case INFEASIBLE: return "INFEASIBLE"; case lp_status::INFEASIBLE: return "INFEASIBLE";
case UNBOUNDED: return "UNBOUNDED"; case lp_status::UNBOUNDED: return "UNBOUNDED";
case TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED"; case lp_status::TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED";
case DUAL_UNBOUNDED: return "DUAL_UNBOUNDED"; case lp_status::DUAL_UNBOUNDED: return "DUAL_UNBOUNDED";
case OPTIMAL: return "OPTIMAL"; case lp_status::OPTIMAL: return "OPTIMAL";
case FEASIBLE: return "FEASIBLE"; case lp_status::FEASIBLE: return "FEASIBLE";
case FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR"; case lp_status::FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR";
case TIME_EXHAUSTED: return "TIME_EXHAUSTED"; case lp_status::TIME_EXHAUSTED: return "TIME_EXHAUSTED";
case ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED"; case lp_status::ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED";
case EMPTY: return "EMPTY"; case lp_status::EMPTY: return "EMPTY";
case UNSTABLE: return "UNSTABLE"; case lp_status::UNSTABLE: return "UNSTABLE";
default: default:
SASSERT(false); lp_unreachable();
} }
return "UNKNOWN"; // it is 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 == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED;
if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED; if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED;
if (status == "EMPTY") return lp_status::EMPTY; if (status == "EMPTY") return lp_status::EMPTY;
SASSERT(false); lp_unreachable();
return lp_status::UNKNOWN; // it is unreachable return lp_status::UNKNOWN; // it is unreachable
} }
@ -74,14 +74,12 @@ bool vectors_are_equal(T * a, vector<T> &b, unsigned n) {
if (numeric_traits<T>::precise()) { if (numeric_traits<T>::precise()) {
for (unsigned i = 0; i < n; i ++){ for (unsigned i = 0; i < n; i ++){
if (!numeric_traits<T>::is_zero(a[i] - b[i])) { if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
// std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false; return false;
} }
} }
} else { } else {
for (unsigned i = 0; i < n; i ++){ for (unsigned i = 0; i < n; i ++){
if (std::abs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) { if (std::abs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) {
// std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false; return false;
} }
} }
@ -97,7 +95,6 @@ bool vectors_are_equal(const vector<T> & a, const vector<T> &b) {
if (numeric_traits<T>::precise()) { if (numeric_traits<T>::precise()) {
for (unsigned i = 0; i < n; i ++){ for (unsigned i = 0; i < n; i ++){
if (!numeric_traits<T>::is_zero(a[i] - b[i])) { if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
// std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false; return false;
} }
} }
@ -112,7 +109,6 @@ bool vectors_are_equal(const vector<T> & a, const vector<T> &b) {
} }
if (fabs(da - db) > 0.000001) { if (fabs(da - db) > 0.000001) {
// std::cout << "a[" << i <<"] = " << a[i] << ", but " << "b[" << i <<"] = " << b[i] << std::endl;
return false; return false;
} }
} }

View file

@ -18,7 +18,7 @@ Revision History:
--*/ --*/
#include <string> #include <string>
#include "util/lp/lp_solver.hpp" #include "util/lp/lp_solver_def.h"
template void lp::lp_solver<double, double>::add_constraint(lp::lp_relation, double, unsigned int); template void lp::lp_solver<double, double>::add_constraint(lp::lp_relation, double, unsigned int);
template void lp::lp_solver<double, double>::cleanup(); template void lp::lp_solver<double, double>::cleanup();
template void lp::lp_solver<double, double>::count_slacks_and_artificials(); template void lp::lp_solver<double, double>::count_slacks_and_artificials();
@ -34,7 +34,6 @@ template void lp::lp_solver<double, double>::print_statistics_on_A(std::ostream
template bool lp::lp_solver<double, double>::problem_is_empty(); template bool lp::lp_solver<double, double>::problem_is_empty();
template void lp::lp_solver<double, double>::scale(); template void lp::lp_solver<double, double>::scale();
template void lp::lp_solver<double, double>::set_scaled_cost(unsigned int); template void lp::lp_solver<double, double>::set_scaled_cost(unsigned int);
template std::string lp::lp_solver<double, double>::get_column_name(unsigned int) const;
template lp::lp_solver<double, double>::~lp_solver(); template lp::lp_solver<double, double>::~lp_solver();
template void lp::lp_solver<lp::mpq, lp::mpq>::add_constraint(lp::lp_relation, lp::mpq, unsigned int); template void lp::lp_solver<lp::mpq, lp::mpq>::add_constraint(lp::lp_relation, lp::mpq, unsigned int);
template void lp::lp_solver<lp::mpq, lp::mpq>::cleanup(); template void lp::lp_solver<lp::mpq, lp::mpq>::cleanup();
@ -54,4 +53,3 @@ template void lp::lp_solver<lp::mpq, lp::mpq>::scale();
template void lp::lp_solver<lp::mpq, lp::mpq>::set_scaled_cost(unsigned int); template void lp::lp_solver<lp::mpq, lp::mpq>::set_scaled_cost(unsigned int);
template lp::lp_solver<lp::mpq, lp::mpq>::~lp_solver(); template lp::lp_solver<lp::mpq, lp::mpq>::~lp_solver();
template double lp::lp_solver<double, double>::get_column_value_by_name(std::string) const; template double lp::lp_solver<double, double>::get_column_value_by_name(std::string) const;
template std::string lp::lp_solver<lp::mpq, lp::mpq>::get_column_name(unsigned int) const;

Some files were not shown because too many files have changed in this diff Show more