3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-10 19:27:06 +00:00

add changes in lp with validate_bound and maximize_term

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
This commit is contained in:
Lev Nachmanson 2023-11-01 17:15:51 -07:00 committed by Lev Nachmanson
parent ebd4d1a300
commit ca6cb0af95
12 changed files with 368 additions and 216 deletions

View file

@ -122,7 +122,7 @@ public:
m_r_solver.m_d.resize(m_r_A.column_count());
m_stacked_simplex_strategy.pop(k);
m_r_solver.m_settings.set_simplex_strategy(m_stacked_simplex_strategy);
m_r_solver.m_settings.simplex_strategy() = m_stacked_simplex_strategy;
m_infeasible_linear_combination.reset();
lp_assert(m_r_solver.basis_heading_is_correct());
}

View file

@ -192,23 +192,22 @@ namespace lp {
stats().m_max_rows = A_r().row_count();
if (strategy_is_undecided())
decide_on_strategy_and_adjust_initial_state();
auto strategy_was = settings().simplex_strategy();
settings().set_simplex_strategy(simplex_strategy_enum::tableau_rows);
flet f(settings().simplex_strategy(), simplex_strategy_enum::tableau_rows);
m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true;
auto ret = solve();
settings().set_simplex_strategy(strategy_was);
return ret;
}
lp_status lar_solver::solve() {
if (m_status == lp_status::INFEASIBLE)
if (m_status == lp_status::INFEASIBLE || m_status == lp_status::CANCELLED)
return m_status;
solve_with_core_solver();
if (m_status != lp_status::INFEASIBLE) {
if (m_settings.bound_propagation())
detect_rows_with_changed_bounds();
}
if (m_status == lp_status::INFEASIBLE || m_status == lp_status::CANCELLED)
return m_status;
if (m_settings.bound_propagation())
detect_rows_with_changed_bounds();
clear_columns_with_changed_bounds();
return m_status;
@ -284,17 +283,20 @@ namespace lp {
m_constraints.pop(k);
m_simplex_strategy.pop(k);
m_settings.set_simplex_strategy(m_simplex_strategy);
m_settings.simplex_strategy() = m_simplex_strategy;
lp_assert(sizes_are_correct());
lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau());
m_usage_in_terms.pop(k);
m_dependencies.pop_scope(k);
// init the nbasis sorting
require_nbasis_sort();
set_status(lp_status::UNKNOWN);
}
bool lar_solver::maximize_term_on_tableau(const lar_term& term,
impq& term_max) {
flet f(m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only, false);
if (settings().simplex_strategy() == simplex_strategy_enum::undecided)
decide_on_strategy_and_adjust_initial_state();
@ -303,7 +305,7 @@ namespace lp {
lp_status st = m_mpq_lar_core_solver.m_r_solver.get_status();
TRACE("lar_solver", tout << st << "\n";);
SASSERT(m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis());
if (st == lp_status::UNBOUNDED) {
if (st == lp_status::UNBOUNDED || st == lp_status::CANCELLED) {
return false;
}
else {
@ -312,38 +314,85 @@ namespace lp {
}
}
bool lar_solver::improve_bound(lpvar j, bool improve_lower_bound) {
lar_term term = get_term_to_maximize(j);
if (improve_lower_bound)
term.negate();
impq bound;
if (!maximize_term_on_corrected_r_solver(term, bound))
return false;
return false;
// TODO
if (improve_lower_bound) {
bound.neg();
if (column_has_lower_bound(j) && bound.x == column_lower_bound(j).x)
return false;
SASSERT(!column_has_lower_bound(j) || column_lower_bound(j).x < bound.x);
// TODO - explain new lower bound.
// Seems the relevant information is in the "costs" that are used when
// setting the optimization objecive. The costs are cleared after a call so
// maybe have some way of extracting bound dependencies from the costs.
u_dependency* dep = nullptr;
update_column_type_and_bound(j, bound.y > 0 ? lconstraint_kind::GT : lconstraint_kind::GE, bound.x, dep);
}
else {
if (column_has_upper_bound(j) && bound.x == column_upper_bound(j).x)
return false;
SASSERT(!column_has_upper_bound(j) || column_upper_bound(j).x > bound.x);
// similar for upper bounds
u_dependency* dep = nullptr;
update_column_type_and_bound(j, bound.y < 0 ? lconstraint_kind::LT : lconstraint_kind::LE, bound.x, dep);
// get dependencies of the corresponding bounds from max_coeffs
u_dependency* lar_solver::get_dependencies_of_maximum(const vector<std::pair<mpq,lpvar>>& max_coeffs) {
const auto& s = this->m_mpq_lar_core_solver.m_r_solver;
// The linear combinations of d_j*x[j] = the term that got maximized, where (d_j, j) is in max_coeffs
// Every j with positive coeff is at its upper bound,
// and every j with negative coeff is at its lower bound: so the sum cannot be increased.
// All variables j in the sum are non-basic.
u_dependency* dep = nullptr;
for (const auto & [d_j, j]: max_coeffs) {
SASSERT (!d_j.is_zero());
TRACE("lar_solver_improve_bounds", tout << "d[" << j << "] = " << d_j << "\n";
s.print_column_info(j, tout););
const ul_pair& ul = m_columns_to_ul_pairs[j];
u_dependency * bound_dep;
if (d_j.is_pos())
bound_dep = ul.upper_bound_witness();
else
bound_dep = ul.lower_bound_witness();
TRACE("lar_solver_improve_bounds", {
svector<constraint_index> cs;
m_dependencies.linearize(bound_dep, cs);
for (auto c : cs)
m_constraints.display(tout, c) << "\n";
});
SASSERT(bound_dep != nullptr);
dep = m_dependencies.mk_join(dep, bound_dep);
}
return true;
return dep;
}
// returns nullptr if the bound is not improved, otherwise returns the witness of the bound
u_dependency* lar_solver::find_improved_bound(lpvar j, bool lower_bound, mpq& bound) {
SASSERT(is_feasible());
if (lower_bound && column_has_lower_bound(j) && get_column_value(j) == column_lower_bound(j))
return nullptr; // cannot do better
if (!lower_bound && column_has_upper_bound(j) && get_column_value(j) == column_upper_bound(j))
return nullptr; // cannot do better
lar_term term = get_term_to_maximize(j);
if (lower_bound)
term.negate();
vector<std::pair<mpq, unsigned>> max_coeffs;
TRACE("lar_solver_improve_bounds", tout << "j = " << j << ", "; print_term(term, tout << "term to maximize\n"););
impq term_max;
if (!maximize_term_on_feasible_r_solver(term, term_max, &max_coeffs))
return nullptr;
// term_max is equal to the sum of m_d[j]*x[j] over all non basic j.
// For the sum to be at the maximum all non basic variables should be at their bounds: if (m_d[j] > 0) x[j] = u[j], otherwise x[j] = l[j]. At upper bounds we have u[j].y <= 0, and at lower bounds we have l[j].y >= 0, therefore for the sum term_max.y <= 0.
SASSERT(!term_max.y.is_pos());
// To keep it simpler we ignore possible improvements from non-strict to strict bounds.
bound = term_max.x;
if (lower_bound) {
bound.neg();
if (column_is_int(j))
bound = ceil(bound);
if (column_has_lower_bound(j) && column_is_int(j) && bound <= column_lower_bound(j).x)
return nullptr;
TRACE("lar_solver_improve_bounds",
tout << "setting lower bound for " << j << " to " << bound << "\n";
if (column_has_lower_bound(j)) tout << "bound was = " << column_lower_bound(j) << "\n";);
}
else {
if (column_is_int(j))
bound = floor(bound);
if (column_has_upper_bound(j)) {
if (bound >= column_upper_bound(j).x)
return nullptr;
}
TRACE("lar_solver_improve_bounds",
tout << "setting upper bound for " << j << " to " << bound << "\n";
if (column_has_upper_bound(j)) tout << "bound was = " << column_upper_bound(j) << "\n";;);
}
return get_dependencies_of_maximum(max_coeffs);
}
bool lar_solver::costs_are_zeros_for_r_solver() const {
@ -361,36 +410,29 @@ namespace lp {
void lar_solver::set_costs_to_zero(const lar_term& term) {
auto& rslv = m_mpq_lar_core_solver.m_r_solver;
auto& jset = m_mpq_lar_core_solver.m_r_solver.inf_heap(); // hijack this set that should be empty right now
lp_assert(jset.empty());
auto& d = rslv.m_d;
auto& costs = rslv.m_costs;
for (lar_term::ival p : term) {
unsigned j = p.column();
rslv.m_costs[j] = zero_of_type<mpq>();
costs[j] = zero_of_type<mpq>();
int i = rslv.m_basis_heading[j];
if (i < 0)
jset.insert(j);
else {
if (i < 0)
d[j] = zero_of_type<mpq>();
else
for (const auto& rc : A_r().m_rows[i])
jset.insert(rc.var());
}
d[rc.var()] = zero_of_type<mpq>();
}
for (unsigned j : jset)
rslv.m_d[j] = zero_of_type<mpq>();
jset.clear();
lp_assert(reduced_costs_are_zeroes_for_r_solver());
lp_assert(costs_are_zeros_for_r_solver());
}
void lar_solver::prepare_costs_for_r_solver(const lar_term& term) {
TRACE("lar_solver", print_term(term, tout << "prepare: ") << "\n";);
move_non_basic_columns_to_bounds();
auto& rslv = m_mpq_lar_core_solver.m_r_solver;
lp_assert(costs_are_zeros_for_r_solver());
lp_assert(reduced_costs_are_zeroes_for_r_solver());
move_non_basic_columns_to_bounds();
rslv.m_costs.resize(A_r().column_count(), zero_of_type<mpq>());
for (lar_term::ival p : term) {
unsigned j = p.column();
@ -400,7 +442,8 @@ namespace lp {
else
rslv.update_reduced_cost_for_basic_column_cost_change(-p.coeff(), j);
}
rslv.m_costs_backup = rslv.m_costs;
if (settings().backup_costs)
rslv.m_costs_backup = rslv.m_costs;
lp_assert(rslv.reduced_costs_are_correct_tableau());
}
@ -469,38 +512,33 @@ namespace lp {
change_basic_columns_dependend_on_a_given_nb_column(j, delta);
}
bool lar_solver::maximize_term_on_corrected_r_solver(lar_term& term,
impq& term_max) {
bool lar_solver::maximize_term_on_feasible_r_solver(lar_term& term,
impq& term_max, vector<std::pair<mpq, lpvar>>* max_coeffs = nullptr) {
settings().backup_costs = false;
bool ret = false;
TRACE("lar_solver", print_term(term, tout << "maximize: ") << "\n" << constraints() << ", strategy = " << (int)settings().simplex_strategy() << "\n";);
switch (settings().simplex_strategy()) {
case simplex_strategy_enum::tableau_rows:
settings().set_simplex_strategy(simplex_strategy_enum::tableau_costs);
prepare_costs_for_r_solver(term);
ret = maximize_term_on_tableau(term, term_max);
settings().set_simplex_strategy(simplex_strategy_enum::tableau_rows);
set_costs_to_zero(term);
m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL);
return ret;
case simplex_strategy_enum::tableau_costs:
prepare_costs_for_r_solver(term);
ret = maximize_term_on_tableau(term, term_max);
set_costs_to_zero(term);
m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL);
return ret;
default:
UNREACHABLE(); // wrong mode
TRACE("lar_solver", print_term(term, tout << "maximize: ") << "\n"
<< constraints() << ", strategy = " << (int)settings().simplex_strategy() << "\n";);
if (settings().simplex_strategy() != simplex_strategy_enum::tableau_costs)
require_nbasis_sort();
flet f(settings().simplex_strategy(), simplex_strategy_enum::tableau_costs);
prepare_costs_for_r_solver(term);
ret = maximize_term_on_tableau(term, term_max);
if (ret && max_coeffs != nullptr) {
for (unsigned j = 0; j < column_count(); j++) {
const mpq& d_j = m_mpq_lar_core_solver.m_r_solver.m_d[j];
if (d_j.is_zero())
continue;
max_coeffs->push_back(std::make_pair(d_j, j));
TRACE("lar_solver", tout<<"m_d["<<j<<"] = " << d_j<< ",\n";print_column_info(j, tout) << "\n";);
}
}
return false;
set_costs_to_zero(term);
m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL);
return ret;
}
// returns true iff the row of j has a non-fixed column different from j
bool lar_solver::remove_from_basis(unsigned j) {
// returns true iff the row of j has a non-fixed column different from j
bool lar_solver::remove_from_basis(unsigned j) {
lp_assert(is_base(j));
unsigned i = row_of_basic_column(j);
for (const auto & c : A_r().m_rows[i])
@ -524,24 +562,12 @@ namespace lp {
lp_status lar_solver::maximize_term(unsigned j_or_term,
impq& term_max) {
TRACE("lar_solver", print_values(tout););
SASSERT(m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis());
lar_term term = get_term_to_maximize(j_or_term);
if (term.is_empty()) {
return lp_status::UNBOUNDED;
}
impq prev_value;
if (term.is_empty()) return lp_status::UNBOUNDED;
impq prev_value = term.apply(m_mpq_lar_core_solver.m_r_x);
auto backup = m_mpq_lar_core_solver.m_r_x;
if (m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis()) {
prev_value = term.apply(m_mpq_lar_core_solver.m_r_x);
}
else {
m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = false;
if (solve() != lp_status::OPTIMAL)
return lp_status::UNBOUNDED;
}
m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = false;
if (!maximize_term_on_corrected_r_solver(term, term_max)) {
if (!maximize_term_on_feasible_r_solver(term, term_max, nullptr)) {
m_mpq_lar_core_solver.m_r_x = backup;
return lp_status::UNBOUNDED;
}
@ -794,7 +820,8 @@ namespace lp {
switch (this->get_status()) {
case lp_status::OPTIMAL:
case lp_status::FEASIBLE:
case lp_status::UNBOUNDED:
case lp_status::UNBOUNDED:
SASSERT(m_mpq_lar_core_solver.m_r_solver.inf_heap().size() == 0);
return true;
default:
return false;
@ -1093,7 +1120,7 @@ namespace lp {
mpq lar_solver::get_value(column_index const& j) const {
SASSERT(get_status() == lp_status::OPTIMAL || get_status() == lp_status::FEASIBLE);
SASSERT(m_columns_with_changed_bounds.empty());
VERIFY(m_columns_with_changed_bounds.empty());
numeric_pair<mpq> const& rp = get_column_value(j);
return from_model_in_impq_to_mpq(rp);
}
@ -1129,9 +1156,12 @@ namespace lp {
return;
mpq delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(mpq(1));
for (unsigned j = 0; j < number_of_vars(); j++) {
auto& r = m_mpq_lar_core_solver.m_r_x[j];
if (!r.y.is_zero())
r = impq(r.x + delta * r.y);
auto& v = m_mpq_lar_core_solver.m_r_x[j];
if (!v.y.is_zero()) {
v = impq(v.x + delta * v.y);
TRACE("lar_solver_feas", tout << "x[" << j << "] = " << v << "\n";);
SASSERT(!column_is_int(j) || v.is_int());
}
}
}
@ -1546,6 +1576,7 @@ namespace lp {
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);
require_nbasis_sort();
}
}
@ -1818,7 +1849,98 @@ namespace lp {
update_column_type_and_bound(j, kind, right_side, dep);
}
bool lar_solver::validate_bound(lpvar j, lconstraint_kind kind, const mpq& rs, u_dependency* dep) {
if (m_validate_blocker) return true;
lar_solver solver;
solver.m_validate_blocker = true;
TRACE("lar_solver_validate", tout << "j = " << j << " " << lconstraint_kind_string(kind) << " " << rs << std::endl;);
add_dep_constraints_to_solver(solver, dep);
if (solver.external_to_local(j) == null_lpvar) {
return false; // we have to mention j in the dep
}
if (kind != EQ) {
add_bound_negation_to_solver(solver, j, kind, rs);
solver.find_feasible_solution();
return solver.get_status() == lp_status::INFEASIBLE;
}
else {
solver.push();
add_bound_negation_to_solver(solver, j, LE, rs);
solver.find_feasible_solution();
if (solver.get_status() != lp_status::INFEASIBLE)
return false;
solver.pop();
add_bound_negation_to_solver(solver, j, GE, rs);
solver.find_feasible_solution();
return solver.get_status() == lp_status::INFEASIBLE;
}
}
void lar_solver::add_dep_constraints_to_solver(lar_solver& ls, u_dependency* dep) {
auto constraints = flatten(dep);
for (auto c : constraints)
add_constraint_to_validate(ls, c);
}
void lar_solver::add_bound_negation_to_solver(lar_solver& ls, lpvar j, lconstraint_kind kind, const mpq& right_side) {
j = ls.external_to_local(j);
switch (kind) {
case LE:
ls.add_var_bound(j, GT, right_side);
break;
case LT:
ls.add_var_bound(j, GE, right_side);
break;
case GE:
ls.add_var_bound(j, LT, right_side);
break;
case GT:
ls.add_var_bound(j, LE, right_side);
break;
default:
UNREACHABLE();
break;
}
}
void lar_solver::add_constraint_to_validate(lar_solver& ls, constraint_index ci) {
auto const& c = m_constraints[ci];
TRACE("lar_solver_validate", tout << "adding constr with column = "<< c.column() << "\n"; m_constraints.display(tout, c); tout << std::endl;);
vector<std::pair<mpq, var_index>> coeffs;
for (auto p : c.coeffs()) {
lpvar jext = p.second;
lpvar j = ls.external_to_local(jext);
if (j == null_lpvar) {
ls.add_var(jext, column_is_int(jext));
j = ls.external_to_local(jext);
}
coeffs.push_back(std::make_pair(p.first, j));
}
lpvar column_ext = c.column();
unsigned j = ls.external_to_local(column_ext);
var_index tv;
if (j == UINT_MAX) {
tv = ls.add_term(coeffs, column_ext);
}
else {
tv = ls.add_term(coeffs, null_lpvar);
}
ls.add_var_bound(tv, c.kind(), c.rhs());
}
void lar_solver::update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) {
SASSERT(validate_bound(j, kind, right_side, dep));
TRACE(
"lar_solver_feas",
tout << "j" << j << " " << lconstraint_kind_string(kind) << " " << right_side << std::endl;
if (dep) {
tout << "dep:\n";
auto cs = flatten(dep);
for (auto c : cs) {
constraints().display(tout, c);
tout << std::endl;
}
});
if (column_has_upper_bound(j))
update_column_type_and_bound_with_ub(j, kind, right_side, dep);
else
@ -1826,12 +1948,12 @@ namespace lp {
if (is_base(j) && column_is_fixed(j))
m_fixed_base_var_set.insert(j);
TRACE("lar_solver_feas", tout << "j = " << j << " became " << (this->column_is_feasible(j) ? "feas" : "non-feas") << ", and " << (this->column_is_bounded(j) ? "bounded" : "non-bounded") << std::endl;);
TRACE("lar_solver_feas", tout << "j = " << j << " became " << (this->column_is_feasible(j) ? "feas" : "non-feas") << ", and " << (this->column_is_bounded(j) ? "bounded" : "non-bounded") << std::endl;);
}
void lar_solver::insert_to_columns_with_changed_bounds(unsigned j) {
m_columns_with_changed_bounds.insert(j);
TRACE("lar_solver", tout << "column " << j << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";);
m_columns_with_changed_bounds.insert(j);
TRACE("lar_solver", tout << "column " << j << (column_is_feasible(j) ? " feas" : " non-feas") << "\n";);
}
void lar_solver::update_column_type_and_bound_check_on_equal(unsigned j,
@ -1873,11 +1995,22 @@ namespace lp {
void lar_solver::decide_on_strategy_and_adjust_initial_state() {
lp_assert(strategy_is_undecided());
m_settings.set_simplex_strategy(simplex_strategy_enum::tableau_rows); // todo: when to switch to tableau_costs?
m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows;
adjust_initial_state();
}
struct scoped_backup {
lar_solver& m_s;
scoped_backup(lar_solver& s) : m_s(s) {
m_s.backup_x();
}
~scoped_backup() {
m_s.restore_x();
}
};
void lar_solver::adjust_initial_state() {
switch (m_settings.simplex_strategy()) {
case simplex_strategy_enum::tableau_rows:
@ -2038,7 +2171,7 @@ namespace lp {
UNREACHABLE();
}
}
// clang-format off
void lar_solver::update_bound_with_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) {
lp_assert(!column_has_lower_bound(j) && column_has_upper_bound(j));
lp_assert(m_mpq_lar_core_solver.m_column_types[j] == column_type::upper_bound);
@ -2091,7 +2224,7 @@ namespace lp {
UNREACHABLE();
}
}
// clang-format on
void lar_solver::update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep) {
lp_assert(!column_has_lower_bound(j) && !column_has_upper_bound(j));
@ -2128,7 +2261,7 @@ namespace lp {
}
insert_to_columns_with_changed_bounds(j);
}
// clang-format off
bool lar_solver::column_corresponds_to_term(unsigned j) const {
return tv::is_term(m_var_register.local_to_external(j));
}
@ -2378,7 +2511,19 @@ namespace lp {
for (auto j : m_columns_with_changed_bounds)
detect_rows_with_changed_bounds_for_column(j);
}
std::ostream& lar_solver::print_explanation(
std::ostream& out, const explanation& exp,
std::function<std::string(lpvar)> var_str) const {
out << "expl: ";
unsigned i = 0;
for (auto p : exp) {
out << "(" << p.ci() << ")";
constraints().display(out, var_str, p.ci());
if (++i < exp.size())
out << " ";
}
return out;
}
} // namespace lp

View file

@ -155,12 +155,20 @@ class lar_solver : public column_namer {
lpvar& crossed_bounds_column() { return m_crossed_bounds_column; }
private:
private:
bool validate_bound(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep);
void add_dep_constraints_to_solver(lar_solver& ls, u_dependency* dep);
void add_bound_negation_to_solver(lar_solver& ls, lpvar j, lconstraint_kind kind, const mpq& right_side);
void add_constraint_to_validate(lar_solver& ls, constraint_index ci);
bool m_validate_blocker = false;
void update_column_type_and_bound_check_on_equal(unsigned j, const mpq& right_side, constraint_index ci, unsigned&);
void update_column_type_and_bound(unsigned j, const mpq& right_side, constraint_index ci);
public:
void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep);
private:
bool validate_blocker() const { return m_validate_blocker; }
bool & validate_blocker() { return m_validate_blocker; }
void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep);
private:
void require_nbasis_sort() { m_mpq_lar_core_solver.m_r_solver.m_nbasis_sort_counter = 0; }
void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep);
void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep);
void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep);
@ -203,7 +211,9 @@ class lar_solver : public column_namer {
bool reduced_costs_are_zeroes_for_r_solver() const;
void set_costs_to_zero(const lar_term& term);
void prepare_costs_for_r_solver(const lar_term& term);
bool maximize_term_on_corrected_r_solver(lar_term& term, impq& term_max);
bool maximize_term_on_feasible_r_solver(lar_term& term, impq& term_max, vector<std::pair<mpq,lpvar>>* max_coeffs);
u_dependency* get_dependencies_of_maximum(const vector<std::pair<mpq,lpvar>>& max_coeffs);
void pop_core_solver_params();
void pop_core_solver_params(unsigned k);
void set_upper_bound_witness(var_index j, u_dependency* ci);
@ -263,7 +273,12 @@ class lar_solver : public column_namer {
mutable std::unordered_set<mpq> m_set_of_different_singles;
mutable mpq m_delta;
public:
public:
u_dependency* find_improved_bound(lpvar j, bool is_lower, mpq& bound);
std::ostream& print_explanation(
std::ostream& out, const explanation& exp,
std::function<std::string(lpvar)> var_str = [](lpvar j) { return std::string("j") + T_to_string(j); }) const;
// this function just looks at the status
bool is_feasible() const;
@ -302,8 +317,6 @@ class lar_solver : public column_namer {
lp_status maximize_term(unsigned j_or_term, impq& term_max);
bool improve_bound(lpvar j, bool is_lower);
inline core_solver_pretty_printer<lp::mpq, lp::impq> pp(std::ostream& out) const {
return core_solver_pretty_printer<lp::mpq, lp::impq>(m_mpq_lar_core_solver.m_r_solver, out);
}

View file

@ -23,10 +23,8 @@ Revision History:
#include "util/vector.h"
#include <functional>
#include "math/lp/lp_core_solver_base_def.h"
template bool lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &);
template bool lp::lp_core_solver_base<lp::mpq, lp::mpq>::basis_heading_is_correct() const ;
template bool lp::lp_core_solver_base<lp::mpq, lp::mpq>::column_is_dual_feasible(unsigned int) const;
template bool lp::lp_core_solver_base<lp::mpq, lp::mpq>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &);
template void lp::lp_core_solver_base<lp::mpq, lp::mpq>::add_delta_to_entering(unsigned int, const lp::mpq&);
template void lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::init();
template void lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::init_basis_heading_and_non_basic_columns_vector();
@ -35,7 +33,6 @@ template lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::lp_core_s
vector<unsigned int >&, vector<unsigned> &, vector<int> &, vector<lp::numeric_pair<lp::mpq> >&, vector<lp::mpq>&, lp::lp_settings&, const column_namer&, const vector<lp::column_type >&,
const vector<lp::numeric_pair<lp::mpq> >&,
const vector<lp::numeric_pair<lp::mpq> >&);
template bool lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair<lp::mpq>, std::ostream&);
template void lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::add_delta_to_entering(unsigned int, const lp::numeric_pair<lp::mpq>&);
template lp::lp_core_solver_base<lp::mpq, lp::mpq>::lp_core_solver_base(
@ -50,7 +47,6 @@ template lp::lp_core_solver_base<lp::mpq, lp::mpq>::lp_core_solver_base(
const vector<lp::column_type >&,
const vector<lp::mpq>&,
const vector<lp::mpq>&);
template bool lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &);
template std::string lp::lp_core_solver_base<lp::mpq, lp::mpq>::column_name(unsigned int) const;
template void lp::lp_core_solver_base<lp::mpq, lp::mpq>::pretty_print(std::ostream & out);
template std::string lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::column_name(unsigned int) const;

View file

@ -19,6 +19,7 @@ Revision History:
--*/
#pragma once
#include <set>
#include <list>
#include "util/vector.h"
#include <string>
#include "math/lp/lp_utils.h"
@ -88,7 +89,7 @@ public:
const vector<column_type> & m_column_types;
const vector<X> & m_lower_bounds;
const vector<X> & m_upper_bounds;
unsigned m_basis_sort_counter;
unsigned m_nbasis_sort_counter;
vector<unsigned> m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving
bool m_tracing_basis_changes;
// these rows are changed by adding to them a multiple of the pivot row
@ -165,10 +166,6 @@ public:
void print_statistics(char const* str, X cost, std::ostream & message_stream);
bool print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream & message_stream);
bool print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const* str, std::ostream & message_stream);
bool print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream & message_stream);
unsigned total_iterations() const { return m_total_iterations; }
@ -277,7 +274,7 @@ public:
bool non_basis_has_no_doubles() const;
bool basis_is_correctly_represented_in_heading() const ;
bool non_basis_is_correctly_represented_in_heading() const ;
bool non_basis_is_correctly_represented_in_heading(std::list<unsigned>*) const ;
bool basis_heading_is_correct() const;
@ -416,6 +413,7 @@ public:
TRACE("lp_core", tout << "inf col "; print_column_info(j, tout) << "\n";);
return false;
}
return true;
}

View file

@ -56,7 +56,7 @@ lp_core_solver_base(static_matrix<T, X> & A,
m_column_types(column_types),
m_lower_bounds(lower_bound_values),
m_upper_bounds(upper_bound_values),
m_basis_sort_counter(0),
m_nbasis_sort_counter(0),
m_tracing_basis_changes(false),
m_touched_rows(nullptr),
m_look_for_feasible_solution_only(false) {
@ -133,37 +133,6 @@ print_statistics(char const* str, X cost, std::ostream & out) {
<< ", nonzeros = " << m_A.number_of_non_zeroes() << std::endl;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream & str) {
unsigned total_iterations = inc_total_iterations();
if (m_settings.report_frequency != 0) {
if (m_settings.print_statistics && (total_iterations % m_settings.report_frequency == 0)) {
print_statistics("", X(), str);
}
}
return time_is_over();
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const* str, std::ostream & out) {
unsigned total_iterations = inc_total_iterations();
if (m_settings.report_frequency != 0)
if (m_settings.print_statistics && (total_iterations % m_settings.report_frequency == 0)) {
print_statistics(str, get_cost(), out);
}
return time_is_over();
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream & out) {
unsigned total_iterations = inc_total_iterations();
if (m_settings.report_frequency != 0)
if (m_settings.print_statistics && (total_iterations % m_settings.report_frequency == 0)) {
print_statistics("", cost, out);
}
return time_is_over();
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
column_is_dual_feasible(unsigned j) const {
switch (m_column_types[j]) {
@ -194,18 +163,6 @@ d_is_not_positive(unsigned j) const {
return m_d[j] <= numeric_traits<T>::zero();
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
time_is_over() {
if (m_settings.get_cancel_flag()) {
m_status = lp_status::TIME_EXHAUSTED;
return true;
}
else {
return false;
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
rs_minus_Anx(vector<X> & rs) {
unsigned row = m_m();
@ -360,7 +317,7 @@ basis_is_correctly_represented_in_heading() const {
return true;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
non_basis_is_correctly_represented_in_heading() const {
non_basis_is_correctly_represented_in_heading(std::list<unsigned>* non_basis_list) const {
for (unsigned i = 0; i < m_nbasis.size(); i++)
if (m_basis_heading[m_nbasis[i]] != - static_cast<int>(i) - 1)
return false;
@ -368,7 +325,34 @@ non_basis_is_correctly_represented_in_heading() const {
for (unsigned j = 0; j < m_A.column_count(); j++)
if (m_basis_heading[j] >= 0)
lp_assert(static_cast<unsigned>(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j);
if (non_basis_list == nullptr) return true;
std::unordered_set<unsigned> nbasis_set(this->m_nbasis.size());
for (unsigned j : this->m_nbasis)
nbasis_set.insert(j);
if (non_basis_list->size() != nbasis_set.size()) {
TRACE("lp_core", tout << "non_basis_list.size() = " << non_basis_list->size() << ", nbasis_set.size() = " << nbasis_set.size() << "\n";);
return false;
}
for (auto it = non_basis_list->begin(); it != non_basis_list->end(); it++) {
if (nbasis_set.find(*it) == nbasis_set.end()) {
TRACE("lp_core", tout << "column " << *it << " is in m_non_basis_list but not in m_nbasis\n";);
return false;
}
}
// check for duplicates in m_non_basis_list
nbasis_set.clear();
for (auto it = non_basis_list->begin(); it != non_basis_list->end(); it++) {
if (nbasis_set.find(*it) != nbasis_set.end()) {
TRACE("lp_core", tout << "column " << *it << " is in m_non_basis_list twice\n";);
return false;
}
nbasis_set.insert(*it);
}
return true;
}
@ -390,7 +374,7 @@ template <typename T, typename X> bool lp_core_solver_base<T, X>::
if (!basis_is_correctly_represented_in_heading())
return false;
if (!non_basis_is_correctly_represented_in_heading())
if (!non_basis_is_correctly_represented_in_heading(nullptr))
return false;
return true;

View file

@ -592,7 +592,7 @@ namespace lp {
theta = zero_of_type<X>();
}
}
bool correctly_moved_to_bounds(lpvar) const;
bool column_is_benefitial_for_entering_basis(unsigned j) const;
void init_infeasibility_costs();
void print_column(unsigned j, std::ostream &out);

View file

@ -40,49 +40,57 @@ void lp_primal_core_solver<T, X>::sort_non_basis() {
if (ca != 0 && cb == 0) return true;
return ca < cb;
});
m_non_basis_list.clear();
// reinit m_basis_heading
for (unsigned j = 0; j < this->m_nbasis.size(); j++) {
unsigned col = this->m_nbasis[j];
this->m_basis_heading[col] = - static_cast<int>(j) - 1;
m_non_basis_list.push_back(col);
m_non_basis_list.resize(this->m_nbasis.size());
// initialize m_non_basis_list from m_nbasis by using an iterator on m_non_basis_list
auto it = m_non_basis_list.begin();
unsigned j = 0;
for (; j < this->m_nbasis.size(); j++, ++it) {
unsigned col = *it = this->m_nbasis[j];
this->m_basis_heading[col] = -static_cast<int>(j) - 1;
}
}
template <typename T, typename X>
bool lp_primal_core_solver<T, X>::correctly_moved_to_bounds(unsigned j) const {
switch (this->m_column_types[j]) {
case column_type::fixed:
return this->m_x[j] == this->m_lower_bounds[j];
case column_type::boxed:
return this->m_x[j] == this->m_lower_bounds[j] || this->m_x[j] == this->m_upper_bounds[j];
case column_type::lower_bound:
return this->m_x[j] == this->m_lower_bounds[j];
case column_type::upper_bound:
return this->m_x[j] == this->m_upper_bounds[j];
case column_type::free_column:
return true;
default:
UNREACHABLE();
return false;
}
}
template <typename T, typename X>
bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis(unsigned j) const {
const T& dj = this->m_d[j];
TRACE("lar_solver", tout << "dj=" << dj << "\n";);
if (dj.is_zero()) return false;
TRACE("lar_solver", tout << "d[" << j <<"] = " << dj << "\n";);
SASSERT(correctly_moved_to_bounds(j));
switch (this->m_column_types[j]) {
case column_type::fixed: break;
case column_type::free_column:
if (!is_zero(dj))
return true;
break;
return true;
case column_type::lower_bound:
if (dj > zero_of_type<T>()) return true;
if (dj < 0 && this->m_x[j] > this->m_lower_bounds[j]){
return true;
}
break;
case column_type::upper_bound:
if (dj < zero_of_type<T>()) return true;
if (dj > 0 && this->m_x[j] < this->m_upper_bounds[j]) {
return true;
}
break;
case column_type::boxed:
if (dj > zero_of_type<T>()) {
if (this->m_x[j] < this->m_upper_bounds[j])
return true;
break;
} else if (dj < zero_of_type<T>()) {
if (this->m_x[j] > this->m_lower_bounds[j])
return true;
}
if (dj > zero_of_type<T>() && this->m_x[j] == this->m_lower_bounds[j])
return true;
if (dj < zero_of_type<T>() && this->m_x[j] == this->m_upper_bounds[j])
return true;
break;
default:
UNREACHABLE();

View file

@ -44,19 +44,22 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_e
advance_on_entering_and_leaving_tableau(entering, leaving, t);
}
template <typename T, typename X> int lp_primal_core_solver<T, X>::choose_entering_column_tableau() {
//this moment m_y = cB * B(-1)
if (this->m_nbasis_sort_counter == 0) {
sort_non_basis();
this->m_nbasis_sort_counter = 20;
}
else {
this->m_nbasis_sort_counter--;
SASSERT(non_basis_is_correctly_represented_in_heading(&m_non_basis_list));
}
unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter();
if (number_of_benefitial_columns_to_go_over == 0)
return -1;
if (this->m_basis_sort_counter == 0) {
sort_non_basis();
this->m_basis_sort_counter = 20;
}
else {
this->m_basis_sort_counter--;
}
unsigned j_nz = this->m_m() + 1; // this number is greater than the max column size
std::list<unsigned>::iterator entering_iter = m_non_basis_list.end();
unsigned n = 0;
@ -98,7 +101,8 @@ unsigned lp_primal_core_solver<T, X>::solve() {
}
do {
if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over( "feas t", * this->m_settings.get_message_ostream())) {
if (this->m_settings.get_cancel_flag()) {
this->set_status(lp_status::CANCELLED);
return this->total_iterations();
}
if (this->m_settings.use_tableau_rows()) {
@ -256,8 +260,6 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run_tableau() {
lp_assert(basis_columns_are_set_correctly());
this->m_basis_sort_counter = 0; // to initiate the sort of the basis
// this->set_total_iterations(0);
this->iters_with_no_cost_growing() = 0;
lp_assert(this->inf_heap_is_correct());
if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)

View file

@ -262,7 +262,7 @@ public:
// the method of lar solver to use
simplex_strategy_enum simplex_strategy() const { return m_simplex_strategy; }
void set_simplex_strategy(simplex_strategy_enum s) { m_simplex_strategy = s; }
simplex_strategy_enum & simplex_strategy() { return m_simplex_strategy; }
bool use_tableau_rows() const { return m_simplex_strategy == simplex_strategy_enum::tableau_rows; }
#ifdef Z3DEBUG

View file

@ -1786,6 +1786,9 @@ void core::set_use_nra_model(bool m) {
}
void core::propagate() {
#if Z3DEBUG
flet f(lra.validate_blocker(), true);
#endif
clear();
m_monomial_bounds.unit_propagate();
m_monics_with_changed_bounds.reset();

View file

@ -2030,6 +2030,9 @@ public:
}
final_check_status check_nla_continue() {
#if Z3DEBUG
flet f(lp().validate_blocker(), true);
#endif
lbool r = m_nla->check();
switch (r) {
case l_false: