/*++ Copyright (c) 2017 Microsoft Corporation Author: Lev Nachmanson (levnach) --*/ #pragma once #include "util/vector.h" #include #include #include "math/lp/lp_core_solver_base.h" #include #include "math/lp/indexed_vector.h" #include "math/lp/binary_heap_priority_queue.h" #include "math/lp/breakpoint.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/stacked_vector.h" #include "math/lp/lar_solution_signature.h" #include "util/stacked_value.h" namespace lp { class lar_core_solver { // m_sign_of_entering is set to 1 if the entering variable needs // to grow and is set to -1 otherwise int m_sign_of_entering_delta; vector> m_infeasible_linear_combination; int m_infeasible_sum_sign; // todo: get rid of this field vector> m_right_sides_dummy; vector m_costs_dummy; vector m_d_right_sides_dummy; vector m_d_costs_dummy; public: stacked_value m_stacked_simplex_strategy; stacked_vector m_column_types; // r - solver fields, for rational numbers vector> m_r_x; // the solution stacked_vector> m_r_lower_bounds; stacked_vector> m_r_upper_bounds; static_matrix> m_r_A; stacked_vector m_r_pushed_basis; vector m_r_basis; vector m_r_nbasis; vector m_r_heading; stacked_vector m_r_columns_nz; stacked_vector m_r_rows_nz; // d - solver fields, for doubles vector m_d_x; // the solution in doubles vector m_d_lower_bounds; vector m_d_upper_bounds; static_matrix m_d_A; stacked_vector m_d_pushed_basis; vector m_d_basis; vector m_d_nbasis; vector m_d_heading; lp_primal_core_solver> m_r_solver; // solver in rational numbers lp_primal_core_solver m_d_solver; // solver in doubles lar_core_solver( lp_settings & settings, const column_namer & column_names ); lp_settings & settings() { return m_r_solver.m_settings;} const lp_settings & settings() const { return m_r_solver.m_settings;} int get_infeasible_sum_sign() const { return m_infeasible_sum_sign; } const vector> & get_infeasibility_info(int & inf_sign) const { inf_sign = m_infeasible_sum_sign; return m_infeasible_linear_combination; } void fill_not_improvable_zero_sum_from_inf_row(); column_type get_column_type(unsigned j) { return m_column_types[j];} void calculate_pivot_row(unsigned i); void print_pivot_row(std::ostream & out, unsigned row_index) const { for (unsigned j : m_r_solver.m_pivot_row.m_index) { if (numeric_traits::is_pos(m_r_solver.m_pivot_row.m_data[j])) out << "+"; out << m_r_solver.m_pivot_row.m_data[j] << m_r_solver.column_name(j) << " "; } out << " +" << m_r_solver.column_name(m_r_solver.m_basis[row_index]) << std::endl; for (unsigned j : m_r_solver.m_pivot_row.m_index) { m_r_solver.print_column_bound_info(j, out); } m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out); } void advance_on_sorted_breakpoints(unsigned entering); void change_slope_on_breakpoint(unsigned entering, breakpoint> * b, mpq & slope_at_entering); bool row_is_infeasible(unsigned row); bool row_is_evidence(unsigned row); bool find_evidence_row(); void prefix_r(); void prefix_d(); unsigned m_m() const { return m_r_A.row_count(); } unsigned m_n() const { return m_r_A.column_count(); } bool is_tiny() const { return this->m_m() < 10 && this->m_n() < 20; } bool is_empty() const { return this->m_m() == 0 && this->m_n() == 0; } template int get_sign(const L & v) { return v > zero_of_type() ? 1 : (v < zero_of_type() ? -1 : 0); } void fill_evidence(unsigned row); unsigned get_number_of_non_ints() const; void solve(); bool lower_bounds_are_set() const { return true; } const indexed_vector & get_pivot_row() const { return m_r_solver.m_pivot_row; } void fill_not_improvable_zero_sum(); void pop_basis(unsigned k) { if (!settings().use_tableau()) { m_r_pushed_basis.pop(k); m_r_basis = m_r_pushed_basis(); m_r_solver.init_basis_heading_and_non_basic_columns_vector(); m_d_pushed_basis.pop(k); m_d_basis = m_d_pushed_basis(); m_d_solver.init_basis_heading_and_non_basic_columns_vector(); } else { m_d_basis = m_r_basis; m_d_nbasis = m_r_nbasis; m_d_heading = m_r_heading; } } void push() { lp_assert(m_r_solver.basis_heading_is_correct()); lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); lp_assert(m_column_types.size() == m_r_A.column_count()); m_stacked_simplex_strategy = settings().simplex_strategy(); m_stacked_simplex_strategy.push(); m_column_types.push(); // rational if (!settings().use_tableau()) m_r_A.push(); m_r_lower_bounds.push(); m_r_upper_bounds.push(); if (!settings().use_tableau()) { push_vector(m_r_pushed_basis, m_r_basis); push_vector(m_r_columns_nz, m_r_solver.m_columns_nz); push_vector(m_r_rows_nz, m_r_solver.m_rows_nz); } m_d_A.push(); if (!settings().use_tableau()) push_vector(m_d_pushed_basis, m_d_basis); } template void push_vector(stacked_vector & pushed_vector, const vector & vector) { lp_assert(pushed_vector.size() <= vector.size()); for (unsigned i = 0; i < vector.size();i++) { if (i == pushed_vector.size()) { pushed_vector.push_back(vector[i]); } else { pushed_vector[i] = vector[i]; } } pushed_vector.push(); } void pop_markowitz_counts(unsigned k) { m_r_columns_nz.pop(k); m_r_rows_nz.pop(k); m_r_solver.m_columns_nz.resize(m_r_columns_nz.size()); m_r_solver.m_rows_nz.resize(m_r_rows_nz.size()); for (unsigned i = 0; i < m_r_columns_nz.size(); i++) m_r_solver.m_columns_nz[i] = m_r_columns_nz[i]; for (unsigned i = 0; i < m_r_rows_nz.size(); i++) m_r_solver.m_rows_nz[i] = m_r_rows_nz[i]; } void pop(unsigned k) { // rationals if (!settings().use_tableau()) m_r_A.pop(k); m_r_lower_bounds.pop(k); m_r_upper_bounds.pop(k); m_column_types.pop(k); delete m_r_solver.m_factorization; m_r_solver.m_factorization = nullptr; m_r_x.resize(m_r_A.column_count()); m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); if(!settings().use_tableau()) pop_markowitz_counts(k); m_d_A.pop(k); // doubles delete m_d_solver.m_factorization; m_d_solver.m_factorization = nullptr; m_d_x.resize(m_d_A.column_count()); pop_basis(k); m_stacked_simplex_strategy.pop(k); settings().simplex_strategy() = m_stacked_simplex_strategy; lp_assert(m_r_solver.basis_heading_is_correct()); lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); } bool need_to_presolve_with_double_solver() const { return settings().simplex_strategy() == simplex_strategy_enum::lu; } template bool is_zero_vector(const vector & b) { for (const L & m: b) if (!is_zero(m)) return false; return true; } bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair & delta) { auto & x = m_r_x[j]; switch (pos_type) { case at_lower_bound: if (x == m_r_solver.m_lower_bounds[j]) return false; delta = m_r_solver.m_lower_bounds[j] - x; m_r_solver.m_x[j] = m_r_solver.m_lower_bounds[j]; break; case at_fixed: case at_upper_bound: if (x == m_r_solver.m_upper_bounds[j]) return false; delta = m_r_solver.m_upper_bounds[j] - x; x = m_r_solver.m_upper_bounds[j]; break; case free_of_bounds: { return false; } case not_at_bound: switch (m_column_types[j]) { case column_type::free_column: return false; case column_type::upper_bound: delta = m_r_solver.m_upper_bounds[j] - x; x = m_r_solver.m_upper_bounds[j]; break; case column_type::lower_bound: delta = m_r_solver.m_lower_bounds[j] - x; x = m_r_solver.m_lower_bounds[j]; break; case column_type::boxed: if (x > m_r_solver.m_upper_bounds[j]) { delta = m_r_solver.m_upper_bounds[j] - x; x += m_r_solver.m_upper_bounds[j]; } else { delta = m_r_solver.m_lower_bounds[j] - x; x = m_r_solver.m_lower_bounds[j]; } break; case column_type::fixed: delta = m_r_solver.m_lower_bounds[j] - x; x = m_r_solver.m_lower_bounds[j]; break; default: lp_assert(false); } break; default: lp_unreachable(); } m_r_solver.remove_column_from_inf_set(j); return true; } void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) { lp_assert(m_r_solver.inf_set_is_correct()); for (auto &t : signature) { unsigned j = t.first; if (m_r_heading[j] >= 0) continue; auto pos_type = t.second; numeric_pair delta; if (!update_xj_and_get_delta(j, pos_type, delta)) continue; for (const auto & cc : m_r_solver.m_A.m_columns[j]){ unsigned i = cc.var(); unsigned jb = m_r_solver.m_basis[i]; m_r_solver.add_delta_to_x_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc)); } CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); } lp_assert(m_r_solver.inf_set_is_correct()); } template void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver & s) { for (auto &t : signature) { unsigned j = t.first; lp_assert(m_r_heading[j] < 0); auto pos_type = t.second; switch (pos_type) { case at_lower_bound: s.m_x[j] = s.m_lower_bounds[j]; break; case at_fixed: case at_upper_bound: s.m_x[j] = s.m_upper_bounds[j]; break; case free_of_bounds: { s.m_x[j] = zero_of_type(); continue; } case not_at_bound: switch (m_column_types[j]) { case column_type::free_column: lp_assert(false); // unreachable case column_type::upper_bound: s.m_x[j] = s.m_upper_bounds[j]; break; case column_type::lower_bound: s.m_x[j] = s.m_lower_bounds[j]; break; case column_type::boxed: if (settings().random_next() % 2) { s.m_x[j] = s.m_lower_bounds[j]; } else { s.m_x[j] = s.m_upper_bounds[j]; } break; case column_type::fixed: s.m_x[j] = s.m_lower_bounds[j]; break; default: lp_assert(false); } break; default: lp_unreachable(); } } lp_assert(is_zero_vector(s.m_b)); s.solve_Ax_eq_b(); } template void catch_up_in_lu_in_reverse(const vector & trace_of_basis_change, lp_primal_core_solver & cs) { // recover the previous working basis for (unsigned i = trace_of_basis_change.size(); i > 0; i-= 2) { unsigned entering = trace_of_basis_change[i-1]; unsigned leaving = trace_of_basis_change[i-2]; cs.change_basis_unconditionally(entering, leaving); } cs.init_lu(); } //basis_heading is the basis heading of the solver owning trace_of_basis_change // here we compact the trace as we go to avoid unnecessary column changes template void catch_up_in_lu(const vector & trace_of_basis_change, const vector & basis_heading, lp_primal_core_solver & cs) { if (cs.m_factorization == nullptr || cs.m_factorization->m_refactor_counter + trace_of_basis_change.size()/2 >= 200) { for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { unsigned entering = trace_of_basis_change[i]; unsigned leaving = trace_of_basis_change[i+1]; cs.change_basis_unconditionally(entering, leaving); } if (cs.m_factorization != nullptr) { delete cs.m_factorization; cs.m_factorization = nullptr; } } else { indexed_vector w(cs.m_A.row_count()); // the queues of delayed indices std::queue entr_q, leav_q; auto * l = cs.m_factorization; lp_assert(l->get_status() == LU_status::OK); for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { unsigned entering = trace_of_basis_change[i]; unsigned leaving = trace_of_basis_change[i+1]; bool good_e = basis_heading[entering] >= 0 && cs.m_basis_heading[entering] < 0; bool good_l = basis_heading[leaving] < 0 && cs.m_basis_heading[leaving] >= 0; if (!good_e && !good_l) continue; if (good_e && !good_l) { while (!leav_q.empty() && cs.m_basis_heading[leav_q.front()] < 0) leav_q.pop(); if (!leav_q.empty()) { leaving = leav_q.front(); leav_q.pop(); } else { entr_q.push(entering); continue; } } else if (!good_e && good_l) { while (!entr_q.empty() && cs.m_basis_heading[entr_q.front()] >= 0) entr_q.pop(); if (!entr_q.empty()) { entering = entr_q.front(); entr_q.pop(); } else { leav_q.push(leaving); continue; } } lp_assert(cs.m_basis_heading[entering] < 0); lp_assert(cs.m_basis_heading[leaving] >= 0); if (l->get_status() == LU_status::OK) { l->prepare_entering(entering, w); // to init vector w l->replace_column(zero_of_type(), w, cs.m_basis_heading[leaving]); } cs.change_basis_unconditionally(entering, leaving); } if (l->get_status() != LU_status::OK) { delete l; cs.m_factorization = nullptr; } } if (cs.m_factorization == nullptr) { if (numeric_traits::precise()) init_factorization(cs.m_factorization, cs.m_A, cs.m_basis, settings()); } } bool no_r_lu() const { return m_r_solver.m_factorization == nullptr || m_r_solver.m_factorization->get_status() == LU_status::Degenerated; } void solve_on_signature_tableau(const lar_solution_signature & signature, const vector & changes_of_basis) { r_basis_is_OK(); lp_assert(settings().use_tableau()); bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading); if (!r) { // it is the case where m_d_solver gives a degenerated basis prepare_solver_x_with_signature_tableau(signature); // still are going to use the signature partially m_r_solver.find_feasible_solution(); m_d_basis = m_r_basis; m_d_heading = m_r_heading; m_d_nbasis = m_r_nbasis; delete m_d_solver.m_factorization; m_d_solver.m_factorization = nullptr; } else { prepare_solver_x_with_signature_tableau(signature); m_r_solver.start_tracing_basis_changes(); m_r_solver.find_feasible_solution(); if (settings().get_cancel_flag()) return; m_r_solver.stop_tracing_basis_changes(); // and now catch up in the double solver 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); } lp_assert(r_basis_is_OK()); } bool adjust_x_of_column(unsigned j) { /* if (m_r_solver.m_basis_heading[j] >= 0) { return false; } if (m_r_solver.column_is_feasible(j)) { return false; } m_r_solver.snap_column_to_bound_tableau(j); lp_assert(m_r_solver.column_is_feasible(j)); m_r_solver.m_inf_set.erase(j); */ lp_assert(false); return true; } bool catch_up_in_lu_tableau(const vector & trace_of_basis_change, const vector & basis_heading) { lp_assert(r_basis_is_OK()); // the queues of delayed indices std::queue entr_q, leav_q; for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { unsigned entering = trace_of_basis_change[i]; unsigned leaving = trace_of_basis_change[i+1]; bool good_e = basis_heading[entering] >= 0 && m_r_solver.m_basis_heading[entering] < 0; bool good_l = basis_heading[leaving] < 0 && m_r_solver.m_basis_heading[leaving] >= 0; if (!good_e && !good_l) continue; if (good_e && !good_l) { while (!leav_q.empty() && m_r_solver.m_basis_heading[leav_q.front()] < 0) leav_q.pop(); if (!leav_q.empty()) { leaving = leav_q.front(); leav_q.pop(); } else { entr_q.push(entering); continue; } } else if (!good_e && good_l) { while (!entr_q.empty() && m_r_solver.m_basis_heading[entr_q.front()] >= 0) entr_q.pop(); if (!entr_q.empty()) { entering = entr_q.front(); entr_q.pop(); } else { leav_q.push(leaving); continue; } } lp_assert(m_r_solver.m_basis_heading[entering] < 0); lp_assert(m_r_solver.m_basis_heading[leaving] >= 0); m_r_solver.change_basis_unconditionally(entering, leaving); if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) { // unroll the last step m_r_solver.change_basis_unconditionally(leaving, entering); #ifdef Z3DEBUG bool t = #endif m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]); #ifdef Z3DEBUG lp_assert(t); #endif return false; } } lp_assert(r_basis_is_OK()); return true; } bool r_basis_is_OK() const { #ifdef Z3DEBUG if (!m_r_solver.m_settings.use_tableau()) return true; for (unsigned j : m_r_solver.m_basis) { lp_assert(m_r_solver.m_A.m_columns[j].size() == 1); } for (unsigned j =0; j < m_r_solver.m_basis_heading.size(); j++) { if (m_r_solver.m_basis_heading[j] >= 0) continue; if (m_r_solver.m_column_types[j] == column_type::fixed) continue; lp_assert(static_cast(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size()); lp_assert( m_r_solver.m_basis_heading[j] <= -1); } #endif return true; } void solve_on_signature(const lar_solution_signature & signature, const vector & changes_of_basis) { SASSERT(!settings().use_tableau()); if (m_r_solver.m_factorization == nullptr) { for (unsigned j = 0; j < changes_of_basis.size(); j+=2) { unsigned entering = changes_of_basis[j]; unsigned leaving = changes_of_basis[j + 1]; m_r_solver.change_basis_unconditionally(entering, leaving); } init_factorization(m_r_solver.m_factorization, m_r_A, m_r_basis, settings()); } else { catch_up_in_lu(changes_of_basis, m_d_solver.m_basis_heading, m_r_solver); } if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver); m_r_solver.find_feasible_solution(); m_d_basis = m_r_basis; m_d_heading = m_r_heading; m_d_nbasis = m_r_nbasis; delete m_d_solver.m_factorization; m_d_solver.m_factorization = nullptr; } else { prepare_solver_x_with_signature(signature, m_r_solver); m_r_solver.start_tracing_basis_changes(); m_r_solver.find_feasible_solution(); if (settings().get_cancel_flag()) return; m_r_solver.stop_tracing_basis_changes(); // and now catch up in the double solver 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); } } void create_double_matrix(static_matrix & A) { for (unsigned i = 0; i < m_r_A.row_count(); i++) { auto & row = m_r_A.m_rows[i]; for (row_cell & c : row) { A.add_new_element(i, c.var(), c.coeff().get_double()); } } } void fill_basis_d( vector& basis_d, vector& heading_d, vector& nbasis_d){ basis_d = m_r_basis; heading_d = m_r_heading; nbasis_d = m_r_nbasis; } template void extract_signature_from_lp_core_solver(const lp_primal_core_solver & solver, lar_solution_signature & signature) { signature.clear(); lp_assert(signature.size() == 0); for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) { if (solver.m_basis_heading[j] < 0) { signature[j] = solver.get_non_basic_column_value_position(j); } } } void get_bounds_for_double_solver() { unsigned n = m_n(); m_d_lower_bounds.resize(n); m_d_upper_bounds.resize(n); double delta = find_delta_for_strict_boxed_bounds().get_double(); if (delta > 0.000001) delta = 0.000001; for (unsigned j = 0; j < n; j++) { if (lower_bound_is_set(j)) { const auto & lb = m_r_solver.m_lower_bounds[j]; m_d_lower_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); } if (upper_bound_is_set(j)) { const auto & ub = m_r_solver.m_upper_bounds[j]; m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double(); lp_assert(!lower_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_lower_bounds[j])); } } } void scale_problem_for_doubles( static_matrix& A, vector & lower_bounds, vector & upper_bounds) { vector column_scale_vector; vector right_side_vector(A.column_count()); settings().reps_in_scaler = 5; scaler scaler(right_side_vector, A, settings().scaling_minimum, settings().scaling_maximum, column_scale_vector, settings()); if (! scaler.scale()) { // the scale did not succeed, unscaling A.clear(); create_double_matrix(A); } else { for (unsigned j = 0; j < A.column_count(); j++) { if (m_r_solver.column_has_upper_bound(j)) { upper_bounds[j] /= column_scale_vector[j]; } if (m_r_solver.column_has_lower_bound(j)) { lower_bounds[j] /= column_scale_vector[j]; } } } } // returns the trace of basis changes vector find_solution_signature_with_doubles(lar_solution_signature & signature) { if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { vector ret; return ret; } get_bounds_for_double_solver(); extract_signature_from_lp_core_solver(m_r_solver, signature); prepare_solver_x_with_signature(signature, m_d_solver); m_d_solver.start_tracing_basis_changes(); m_d_solver.find_feasible_solution(); if (settings().get_cancel_flag()) return vector(); m_d_solver.stop_tracing_basis_changes(); extract_signature_from_lp_core_solver(m_d_solver, signature); return m_d_solver.m_trace_of_basis_change_vector; } bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: case column_type::upper_bound: return false; case column_type::lower_bound: case column_type::boxed: case column_type::fixed: return true; default: lp_assert(false); } return false; } bool upper_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: case column_type::lower_bound: return false; case column_type::upper_bound: case column_type::boxed: case column_type::fixed: return true; default: lp_assert(false); } return false; } void update_delta(mpq& delta, numeric_pair const& l, numeric_pair const& u) const { lp_assert(l <= u); if (l.x < u.x && l.y > u.y) { mpq delta1 = (u.x - l.x) / (l.y - u.y); if (delta1 < delta) { delta = delta1; } } lp_assert(l.x + delta * l.y <= u.x + delta * u.y); } mpq find_delta_for_strict_boxed_bounds() const{ mpq delta = numeric_traits::one(); for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { if (m_column_types()[j] != column_type::boxed) continue; update_delta(delta, m_r_lower_bounds[j], m_r_upper_bounds[j]); } return delta; } mpq find_delta_for_strict_bounds(const mpq & initial_delta) const{ mpq delta = initial_delta; for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { if (lower_bound_is_set(j)) update_delta(delta, m_r_lower_bounds[j], m_r_x[j]); if (upper_bound_is_set(j)) update_delta(delta, m_r_x[j], m_r_upper_bounds[j]); } return delta; } void init_column_row_nz_for_r_solver() { m_r_solver.init_column_row_non_zeroes(); } bool column_is_fixed(unsigned j) const { return m_column_types()[j] == column_type::fixed || ( m_column_types()[j] == column_type::boxed && m_r_solver.m_lower_bounds[j] == m_r_solver.m_upper_bounds[j]); } bool column_is_free(unsigned j) const { return m_column_types()[j] == column_type::free_column; } 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]; } bool column_is_bounded(unsigned j) const { switch(m_column_types()[j]) { case column_type::fixed: case column_type::boxed: return true; default: return false; } } const vector& r_basis() const { return m_r_basis; } const vector& r_nbasis() const { return m_r_nbasis; } }; }