/*++ Copyright (c) 2014 Microsoft Corporation Module Name: simplex_def.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: Sign of base variables can vary. Sign could possibly be normalized to positive. Otherwise, sign could be accounted in pivoting. --*/ #ifndef SIMPLEX_DEF_H_ #define SIMPLEX_DEF_H_ namespace simplex { template const typename simplex::var_t simplex::null_var = UINT_MAX; template typename simplex::row simplex::add_row(var_t base_var, unsigned num_vars, var_t const* vars, numeral const* coeffs) { m_base_vars.reset(); row r = M.mk_row(); for (unsigned i = 0; i < num_vars; ++i) { if (!m.is_zero(coeffs[i])) { var_t v = vars[i]; if (is_base(v)) { m_base_vars.push_back(i); } M.add_var(r, coeffs[i], v); } } scoped_numeral mul(m), a(m), b(m), c(m); m.set(mul, 1); for (unsigned i = 0; i < m_base_vars.size(); ++i) { var_t v = vars[m_base_vars[i]]; m.mul(coeffs[m_base_vars[i]], mul, a); m.set(b, m_vars[v].m_base_coeff); m.lcm(a, b, c); TRACE("simplex", m.display(tout << " a: ", a); m.display(tout << " b v" << v << " : ", b); m.display(tout << " c: ", c); tout << "\n"; M.display_row(tout, r); M.display_row(tout, row(m_vars[v].m_base2row)); if (m.is_zero(b)) { display(tout); }); SASSERT(is_base(v)); m.abs(c); m.div(c, a, b); m.div(c, m_vars[v].m_base_coeff, a); m.mul(mul, b, mul); M.mul(r, b); m.neg(a); M.add(r, a, row(m_vars[v].m_base2row)); TRACE("simplex", M.display_row(tout, r);); } scoped_numeral base_coeff(m); scoped_eps_numeral value(em), tmp(em); row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { var_t v = it->m_var; if (v == base_var) { m.set(base_coeff, it->m_coeff); } else { SASSERT(!is_base(v)); em.mul(m_vars[v].m_value, it->m_coeff, tmp); em.add(value, tmp, value); } } SASSERT(!m.is_zero(base_coeff)); TRACE("simplex", for (unsigned i = 0; i < num_vars; ++i) { m.display(tout << "v" << vars[i] << " * ", coeffs[i]); tout << " "; if (i + 1 < num_vars) tout << " + "; } tout << "\n"; row_iterator it2 = M.row_begin(r); bool first = true; for (; it2 != end; ++it2) { if (!first) tout << " + "; tout << "v" << it2->m_var << " * "; m.display(tout, it2->m_coeff); tout << " "; first = false; } tout << "\n"; ); SASSERT(!is_base(base_var)); em.neg(value); em.div(value, base_coeff, value); while (m_row2base.size() <= r.id()) { m_row2base.push_back(null_var); } m_row2base[r.id()] = base_var; m_vars[base_var].m_base2row = r.id(); m_vars[base_var].m_is_base = true; m.set(m_vars[base_var].m_base_coeff, base_coeff); em.set(m_vars[base_var].m_value, value); add_patch(base_var); SASSERT(well_formed_row(r)); SASSERT(well_formed()); return r; } template typename simplex::row simplex::get_infeasible_row() { SASSERT(is_base(m_infeasible_var)); unsigned row_id = m_vars[m_infeasible_var].m_base2row; return row(row_id); } template void simplex::add_patch(var_t v) { SASSERT(is_base(v)); if (outside_bounds(v)) { TRACE("simplex", tout << "Add patch: v" << v << "\n";); m_to_patch.insert(v); } } template void simplex::del_row(row const& r) { var_t var = m_row2base[r.id()]; m_vars[var].m_is_base = false; m_vars[var].m_lower_valid = false; m_vars[var].m_upper_valid = false; m_row2base[r.id()] = null_var; M.del(r); SASSERT(M.col_begin(var) == M.col_end(var)); SASSERT(well_formed()); } template void simplex::del_row(var_t var) { TRACE("simplex", tout << var << "\n";); row r; if (is_base(var)) { r = row(m_vars[var].m_base2row); } else { col_iterator it = M.col_begin(var), end = M.col_end(var); if (it == end) { return; } typename matrix::row_entry const& re = it.get_row_entry(); r = it.get_row(); var_t old_base = m_row2base[r.id()]; scoped_eps_numeral new_value(em); var_info& vi = m_vars[old_base]; if (below_lower(old_base)) { new_value = vi.m_lower; } else if (above_upper(old_base)) { new_value = vi.m_upper; } else { new_value = vi.m_value; } // need to move var such that old_base comes in bound. update_and_pivot(old_base, var, re.m_coeff, new_value); SASSERT(is_base(var)); SASSERT(m_vars[var].m_base2row == r.id()); SASSERT(!below_lower(old_base) && !above_upper(old_base)); } del_row(r); TRACE("simplex", display(tout);); SASSERT(well_formed()); } template bool simplex::above_lower(var_t var, eps_numeral const& b) const { var_info const& vi = m_vars[var]; return !vi.m_lower_valid || em.gt(b, vi.m_lower); } template bool simplex::below_upper(var_t var, eps_numeral const& b) const { var_info const& vi = m_vars[var]; return !vi.m_upper_valid || em.lt(b, vi.m_upper); } template void simplex::set_lower(var_t var, eps_numeral const& b) { var_info& vi = m_vars[var]; em.set(vi.m_lower, b); vi.m_lower_valid = true; TRACE("simplex", em.display(tout << "v" << var << " lower: ", b); em.display(tout << " value: ", vi.m_value);); SASSERT(!vi.m_upper_valid || em.le(b, vi.m_upper)); if (!vi.m_is_base && em.lt(vi.m_value, b)) { scoped_eps_numeral delta(em); em.sub(b, vi.m_value, delta); update_value(var, delta); } else if (vi.m_is_base && em.lt(vi.m_value, b)) { SASSERT(outside_bounds(var)); add_patch(var); } SASSERT(well_formed()); } template void simplex::set_upper(var_t var, eps_numeral const& b) { var_info& vi = m_vars[var]; em.set(vi.m_upper, b); vi.m_upper_valid = true; SASSERT(!vi.m_lower_valid || em.le(vi.m_lower, b)); if (!vi.m_is_base && em.gt(vi.m_value, b)) { scoped_eps_numeral delta(em); em.sub(b, vi.m_value, delta); update_value(var, delta); } else if (vi.m_is_base && em.lt(b, vi.m_value)) { SASSERT(outside_bounds(var)); add_patch(var); } SASSERT(well_formed()); } template void simplex::unset_lower(var_t var) { m_vars[var].m_lower_valid = false; } template void simplex::unset_upper(var_t var) { m_vars[var].m_upper_valid = false; } template void simplex::set_value(var_t var, eps_numeral const& b) { scoped_eps_numeral delta(em); em.sub(b, m_vars[var].m_value, delta); update_value(var, delta); SASSERT(well_formed()); } template typename simplex::eps_numeral const& simplex::get_value(var_t v) { return m_vars[v].m_value; } template void simplex::display(std::ostream& out) const { M.display(out); for (unsigned i = 0; i < m_vars.size(); ++i) { var_info const& vi = m_vars[i]; out << "v" << i << " "; out << em.to_string(vi.m_value); out << " ["; if (vi.m_lower_valid) out << em.to_string(vi.m_lower); else out << "-oo"; out << ":"; if (vi.m_upper_valid) out << em.to_string(vi.m_upper); else out << "oo"; out << "] "; if (vi.m_is_base) out << "b:" << vi.m_base2row << " "; //col_iterator it = M.col_begin(i), end = M.col_end(i); //for (; it != end; ++it) { // out << "r" << it.get_row().id() << " "; //} out << "\n"; } } template void simplex::display_row(std::ostream& out, row const& r, bool values) { row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { m.display(out, it->m_coeff); out << "*v" << it->m_var << " "; if (values) { var_info const& vi = m_vars[it->m_var]; out << em.to_string(vi.m_value); out << " ["; if (vi.m_lower_valid) out << em.to_string(vi.m_lower); else out << "-oo"; out << ":"; if (vi.m_upper_valid) out << em.to_string(vi.m_upper); else out << "oo"; out << "] "; } } out << "\n"; } template void simplex::ensure_var(var_t v) { while (v >= m_vars.size()) { M.ensure_var(m_vars.size()); m_vars.push_back(var_info()); } if (m_to_patch.get_bounds() <= v) { m_to_patch.set_bounds(2*v+1); } } template void simplex::reset() { M.reset(); m_to_patch.reset(); m_vars.reset(); m_row2base.reset(); m_left_basis.reset(); m_base_vars.reset(); } template lbool simplex::make_feasible() { ++m_stats.m_num_checks; m_left_basis.reset(); m_infeasible_var = null_var; unsigned num_iterations = 0; unsigned num_repeated = 0; var_t v = null_var; m_bland = false; SASSERT(well_formed()); while ((v = select_var_to_fix()) != null_var) { TRACE("simplex", display(tout << "v" << v << "\n");); if (!m_limit.inc() || num_iterations > m_max_iterations) { return l_undef; } check_blands_rule(v, num_repeated); if (!make_var_feasible(v)) { m_to_patch.insert(v); m_infeasible_var = v; ++m_stats.m_num_infeasible; return l_false; } ++num_iterations; } SASSERT(well_formed()); return l_true; } /** \brief Make x_j the new base variable for row of x_i. x_i is assumed base variable of row r_i. x_j is assumed to have coefficient a_ij in r_i. a_ii*x_i + a_ij*x_j + r_i = 0 current value of x_i is v_i new value of x_i is new_value a_ii*(x_i + new_value - x_i) + a_ij*((x_i - new_value)*a_ii/a_ij + x_j) + r_i = 0 Let r_k be a row where x_j has coefficient x_kj != 0. r_k <- r_k * a_ij - r_i * a_kj */ template void simplex::update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value) { SASSERT(is_base(x_i)); SASSERT(!is_base(x_j)); var_info& x_iI = m_vars[x_i]; scoped_eps_numeral theta(em); theta = x_iI.m_value; theta -= new_value; numeral const& a_ii = x_iI.m_base_coeff; em.mul(theta, a_ii, theta); em.div(theta, a_ij, theta); update_value(x_j, theta); SASSERT(em.eq(x_iI.m_value, new_value)); pivot(x_i, x_j, a_ij); } template void simplex::pivot(var_t x_i, var_t x_j, numeral const& a_ij) { ++m_stats.m_num_pivots; var_info& x_iI = m_vars[x_i]; var_info& x_jI = m_vars[x_j]; unsigned r_i = x_iI.m_base2row; m_row2base[r_i] = x_j; x_jI.m_base2row = r_i; m.set(x_jI.m_base_coeff, a_ij); x_jI.m_is_base = true; x_iI.m_is_base = false; add_patch(x_j); SASSERT(well_formed_row(row(r_i))); col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); scoped_numeral a_kj(m), g(m); for (; it != end; ++it) { row r_k = it.get_row(); if (r_k.id() != r_i) { a_kj = it.get_row_entry().m_coeff; a_kj.neg(); M.mul(r_k, a_ij); M.add(r_k, a_kj, row(r_i)); var_t s = m_row2base[r_k.id()]; numeral& coeff = m_vars[s].m_base_coeff; m.mul(coeff, a_ij, coeff); M.gcd_normalize(r_k, g); if (!m.is_one(g)) { m.div(coeff, g, coeff); } SASSERT(well_formed_row(row(r_k))); } } SASSERT(well_formed()); } template void simplex::update_value(var_t v, eps_numeral const& delta) { if (em.is_zero(delta)) { return; } update_value_core(v, delta); col_iterator it = M.col_begin(v), end = M.col_end(v); // v <- v + delta // s*s_coeff + v*v_coeff + R = 0 // -> // (v + delta)*v_coeff + (s - delta*v_coeff/s_coeff)*v + R = 0 for (; it != end; ++it) { row r = it.get_row(); var_t s = m_row2base[r.id()]; var_info& si = m_vars[s]; scoped_eps_numeral delta2(em); numeral const& coeff = it.get_row_entry().m_coeff; em.mul(delta, coeff, delta2); em.div(delta2, si.m_base_coeff, delta2); delta2.neg(); update_value_core(s, delta2); } } template void simplex::update_value_core(var_t v, eps_numeral const& delta) { eps_numeral& val = m_vars[v].m_value; em.add(val, delta, val); if (is_base(v)) { add_patch(v); } } template bool simplex::below_lower(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_lower_valid && em.lt(vi.m_value, vi.m_lower); } template bool simplex::above_upper(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_upper_valid && em.gt(vi.m_value, vi.m_upper); } template bool simplex::above_lower(var_t v) const { var_info const& vi = m_vars[v]; return !vi.m_lower_valid || em.gt(vi.m_value, vi.m_lower); } template bool simplex::below_upper(var_t v) const { var_info const& vi = m_vars[v]; return !vi.m_upper_valid || em.lt(vi.m_value, vi.m_upper); } template bool simplex::at_lower(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_lower_valid && em.eq(vi.m_value, vi.m_lower); } template bool simplex::at_upper(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_upper_valid && em.eq(vi.m_value, vi.m_upper); } template bool simplex::make_var_feasible(var_t x_i) { scoped_numeral a_ij(m); scoped_eps_numeral value(em); bool is_below; if (below_lower(x_i)) { SASSERT(is_base(x_i)); is_below = m.is_pos(m_vars[x_i].m_base_coeff); value = m_vars[x_i].m_lower; } else if (above_upper(x_i)) { SASSERT(is_base(x_i)); is_below = m.is_neg(m_vars[x_i].m_base_coeff); value = m_vars[x_i].m_upper; } else { // x_i is already feasible return true; } var_t x_j = select_pivot(x_i, is_below, a_ij); if (x_j != null_var) { update_and_pivot(x_i, x_j, a_ij, value); } return x_j != null_var; } /** \brief Wrapper for select_pivot_blands and select_pivot_core */ template typename simplex::var_t simplex::select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij) { if (m_bland) { return select_pivot_blands(x_i, is_below, out_a_ij); } return select_pivot_core(x_i, is_below, out_a_ij); } /** \brief Select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. The argument is_below is true (false) if x_i is below its lower bound (above its upper bound). */ template typename simplex::var_t simplex::select_pivot_core(var_t x_i, bool is_below, scoped_numeral & out_a_ij) { SASSERT(is_base(x_i)); var_t max = get_num_vars(); var_t result = max; row r = row(m_vars[x_i].m_base2row); int n; unsigned best_col_sz = UINT_MAX; int best_so_far = INT_MAX; row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { var_t x_j = it->m_var; if (x_i == x_j) continue; numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? m.is_neg(a_ij) : m.is_pos(a_ij); bool is_pos = !is_neg; bool can_pivot = ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j))); if (can_pivot) { int num = get_num_non_free_dep_vars(x_j, best_so_far); unsigned col_sz = M.column_size(x_j); if (num < best_so_far || (num == best_so_far && col_sz < best_col_sz)) { result = x_j; out_a_ij = a_ij; best_so_far = num; best_col_sz = col_sz; n = 1; } else if (num == best_so_far && col_sz == best_col_sz) { n++; if (m_random()%n == 0) { result = x_j; out_a_ij = a_ij; } } } } return result < max ? result : null_var; } /** \brief Return the number of base variables that are non free and are v dependent. The function adds 1 to the result if v is non free. The function returns with a partial result r if r > best_so_far. This function is used to select the pivot variable. */ template int simplex::get_num_non_free_dep_vars(var_t x_j, int best_so_far) { int result = is_non_free(x_j); col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); for (; it != end; ++it) { var_t s = m_row2base[it.get_row().id()]; result += is_non_free(s); if (result > best_so_far) return result; } return result; } /** \brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. */ template typename simplex::var_t simplex::select_pivot_blands(var_t x_i, bool is_below, scoped_numeral & out_a_ij) { SASSERT(is_base(x_i)); unsigned max = get_num_vars(); var_t result = max; row r(m_vars[x_i].m_base2row); row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { var_t x_j = it->m_var; numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? m.is_neg(a_ij) : m.is_pos(a_ij); if (x_i != x_j && ((!is_neg && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { SASSERT(!is_base(x_j)); if (x_j < result) { result = x_j; out_a_ij = a_ij; } } } return result < max ? result : null_var; } template lbool simplex::minimize(var_t v) { // minimize v, such that tableau is feasible. // Assume there are no bounds on v. // Let k*v + c*x = 0 e.g, maximize c*x over // tableau constraints: // // max { c*x | A*x = 0 and l <= x <= u } // // start with feasible assigment // A*x0 = 0 and l <= x0 <= u // // Identify pivot: i, j: such that x_i is base, // there is a row k1*x_i + k2*x_j + R = 0 // and a delta such that: // // x_i' <- x_i + delta // x_j' <- x_j - delta*k1/k2 // l_i <= x_i' <= u_i // l_j <= x_j' <= u_j // and c*x' > c*x // e.g., c*x := c_i*x_i + c_j*x_j + ... // and c_i*delta > c_j*delta*k1/k2 // and x_i < u_i (if delta > 0), l_i < x_i (if delta < 0) // and l_j < x_j (if delta > 0), x_j < u_j (if delta < 0) // // update all rows, including c*x, using the pivot. // // If there is c_i*x_i in c*x such that c_i > 0 // and upper_i = oo and complementary lower_j = -oo // then the objective is unbounded. // // There is a singularity if there is a pivot such that // c_i*delta == c_j*delta*k1/k2, e.g., nothing is improved, // pivot, but use bland's rule to ensure // convergence in the limit. // SASSERT(is_feasible()); scoped_eps_numeral delta(em); scoped_numeral a_ij(m); var_t x_i, x_j; bool inc_x_i, inc_x_j; while (true) { if (!m_limit.inc()) { return l_undef; } select_pivot_primal(v, x_i, x_j, a_ij, inc_x_i, inc_x_j); if (x_j == null_var) { // optimal return l_true; } TRACE("simplex", tout << "x_i: v" << x_i << " x_j: v" << x_j << "\n";); var_info& vj = m_vars[x_j]; if (x_i == null_var) { if (inc_x_j && vj.m_upper_valid) { delta = vj.m_upper; delta -= vj.m_value; update_value(x_j, delta); } else if (!inc_x_j && vj.m_lower_valid) { delta = vj.m_lower; delta -= vj.m_value; update_value(x_j, delta); } else { // unbounded return l_false; } continue; } // TBD: Change the value of x_j directly without pivoting: // // if (!vj.is_fixed() && vj.bounded() && gain >= upper - lower) { // // } // pivot(x_i, x_j, a_ij); TRACE("simplex", display(tout << "after pivot\n");); move_to_bound(x_i, !inc_x_i); SASSERT(well_formed_row(row(m_vars[x_j].m_base2row))); TRACE("simplex", display(tout);); SASSERT(is_feasible()); } return l_true; } template void simplex::move_to_bound(var_t x, bool to_lower) { scoped_eps_numeral delta(em), delta2(em); var_info& vi = m_vars[x]; if (to_lower) { em.sub(vi.m_value, vi.m_lower, delta); } else { em.sub(vi.m_upper, vi.m_value, delta); } TRACE("simplex", tout << "move " << (to_lower?"to_lower":"to_upper") << " v" << x << " delta: " << em.to_string(delta) << "\n";); col_iterator it = M.col_begin(x), end = M.col_end(x); for (; it != end && is_pos(delta); ++it) { // // base_coeff*s + coeff*x + R = 0 // // to_lower coeff > 0 base_coeff > 0 bound(s) // ------------------------------------------------------ // T T T !to_lower // T T F to_lower // T F T to_lower // T F F !to_lower // var_t s = m_row2base[it.get_row().id()]; var_info& vs = m_vars[s]; numeral const& coeff = it.get_row_entry().m_coeff; numeral const& base_coeff = vs.m_base_coeff; SASSERT(!m.is_zero(coeff)); bool base_to_lower = (m.is_pos(coeff) != m.is_pos(base_coeff)) == to_lower; eps_numeral const* bound = 0; if (!base_to_lower && vs.m_upper_valid) { bound = &vs.m_upper; } else if (base_to_lower && vs.m_lower_valid) { bound = &vs.m_lower; } if (bound) { // |delta2*coeff| = |(bound-value)*base_coeff| em.sub(*bound, vs.m_value, delta2); em.mul(delta2, base_coeff, delta2); em.div(delta2, coeff, delta2); em.abs(delta2); TRACE("simplex", tout << "Delta for v" << s << " " << delta2 << "\n";); if (delta2 < delta) { delta = delta2; } } } if (to_lower) { delta.neg(); } update_value(x, delta); } /** \brief Arguments: v - base variable of row(v) to optimize x_i - base variable of row(x_i) to become non-base x_j - variable in row(v) to make a base variable a_ij - coefficient to x_j in row(x_i) inc - whether to increment x_i */ template void simplex::select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc_x_i, bool& inc_x_j) { row r(m_vars[v].m_base2row); row_iterator it = M.row_begin(r), end = M.row_end(r); scoped_eps_numeral gain(em), new_gain(em); scoped_numeral new_a_ij(m); x_i = null_var; x_j = null_var; inc_x_i = false; bool inc_y = false; for (; it != end; ++it) { var_t x = it->m_var; if (x == v) continue; bool inc_x = m.is_pos(it->m_coeff) == m.is_pos(m_vars[v].m_base_coeff); if ((inc_x && at_upper(x)) || (!inc_x && at_lower(x))) { TRACE("simplex", tout << "v" << x << " pos: " << inc_x << " at upper: " << at_upper(x) << " at lower: " << at_lower(x) << "\n";); continue; // variable cannot be used for improving bounds. // TBD check? } var_t y = pick_var_to_leave(x, inc_x, new_gain, new_a_ij, inc_y); if (y == null_var) { // unbounded. x_i = y; x_j = x; inc_x_i = inc_y; inc_x_j = inc_x; a_ij = new_a_ij; break; } bool better = (new_gain > gain) || ((is_zero(new_gain) && is_zero(gain) && (x_i == null_var || y < x_i))); if (better) { TRACE("simplex", em.display(tout << "gain:", gain); em.display(tout << " new gain:", new_gain); tout << " base x_i: " << y << ", new base x_j: " << x << ", inc x_j: " << inc_x << "\n";); x_i = y; x_j = x; inc_x_i = inc_y; inc_x_j = inc_x; gain = new_gain; a_ij = new_a_ij; } } } // // y is a base variable. // v is a base variable. // v*a_v + x*a_x + E = 0 // y*b_y + x*b_x + F = 0 // inc(x) := sign(a_v) == sign(a_x) // sign_eq := sign(b_y) == sign(b_x) // sign_eq => (inc(x) != inc(y)) // !sign_eq => (inc(x) = inc(y)) // -> // inc(y) := sign_eq != inc(x) // template typename simplex::var_t simplex::pick_var_to_leave( var_t x_j, bool inc_x_j, scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc_x_i) { var_t x_i = null_var; gain.reset(); scoped_eps_numeral curr_gain(em); col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); for (; it != end; ++it) { row r = it.get_row(); var_t s = m_row2base[r.id()]; var_info& vi = m_vars[s]; numeral const& a_ij = it.get_row_entry().m_coeff; numeral const& a_ii = vi.m_base_coeff; bool sign_eq = (m.is_pos(a_ii) == m.is_pos(a_ij)); bool inc_s = sign_eq != inc_x_j; TRACE("simplex", tout << "x_j: v" << x_j << ", base x_i: v" << s << ", inc_x_i: " << inc_s << ", inc_x_j: " << inc_x_j << ", upper valid:" << vi.m_upper_valid << ", lower valid:" << vi.m_lower_valid << "\n"; display_row(tout, r);); if ((inc_s && !vi.m_upper_valid) || (!inc_s && !vi.m_lower_valid)) { continue; } // // current gain: (value(x_i)-bound)*a_ii/a_ij // curr_gain = vi.m_value; curr_gain -= inc_s?vi.m_upper:vi.m_lower; em.mul(curr_gain, a_ii, curr_gain); em.div(curr_gain, a_ij, curr_gain); if (is_neg(curr_gain)) { curr_gain.neg(); } if (x_i == null_var || (curr_gain < gain) || (is_zero(gain) && is_zero(curr_gain) && s < x_i)) { x_i = s; gain = curr_gain; new_a_ij = a_ij; inc_x_i = inc_s; TRACE("simplex", tout << "x_j v" << x_j << " x_i v" << x_i << " gain: "; tout << curr_gain << "\n";); } } return x_i; } template void simplex::check_blands_rule(var_t v, unsigned& num_repeated) { if (m_bland) return; if (m_left_basis.contains(v)) { num_repeated++; if (num_repeated > m_blands_rule_threshold) { TRACE("simplex", tout << "using blands rule, " << num_repeated << "\n";); // std::cerr << "BLANDS RULE...\n"; m_bland = true; } } else { m_left_basis.insert(v); } } template typename simplex::pivot_strategy_t simplex::pivot_strategy() { if (m_bland) { return S_BLAND; } return S_DEFAULT; } template typename simplex::var_t simplex::select_var_to_fix() { switch (pivot_strategy()) { case S_BLAND: return select_smallest_var(); case S_GREATEST_ERROR: return select_error_var(false); case S_LEAST_ERROR: return select_error_var(true); default: return select_smallest_var(); } } template typename simplex::var_t simplex::select_error_var(bool least) { var_t best = null_var; scoped_eps_numeral best_error(em); scoped_eps_numeral curr_error(em); typename var_heap::iterator it = m_to_patch.begin(); typename var_heap::iterator end = m_to_patch.end(); for (; it != end; ++it) { var_t v = *it; var_info const& vi = m_vars[v]; if (below_lower(v)) em.sub(vi.m_lower, vi.m_value, curr_error); else if (above_upper(v)) em.sub(vi.m_value, vi.m_lower, curr_error); else continue; SASSERT(is_pos(curr_error)); if ((best == null_var) || (!least && curr_error > best_error) || (least && curr_error < best_error)) { best = v; best_error = curr_error; } } if (best == null_var) m_to_patch.clear(); // all variables are satisfied else m_to_patch.erase(best); return best; } template bool simplex::well_formed() const { SASSERT(M.well_formed()); for (unsigned i = 0; i < m_row2base.size(); ++i) { var_t s = m_row2base[i]; if (s == null_var) continue; SASSERT(i == m_vars[s].m_base2row); VERIFY(well_formed_row(row(i))); } for (unsigned i = 0; i < m_vars.size(); ++i) { if (!is_base(i)) { SASSERT(!above_upper(i)); SASSERT(!below_lower(i)); } } return true; } template bool simplex::is_feasible() const { for (unsigned i = 0; i < m_vars.size(); ++i) { if (below_lower(i) || above_upper(i)) return false; } return true; } template bool simplex::well_formed_row(row const& r) const { var_t s = m_row2base[r.id()]; SASSERT(m_vars[s].m_base2row == r.id()); SASSERT(m_vars[s].m_is_base); // SASSERT(m.is_neg(m_vars[s].m_base_coeff)); row_iterator it = M.row_begin(r), end = M.row_end(r); scoped_eps_numeral sum(em), tmp(em); for (; it != end; ++it) { em.mul(m_vars[it->m_var].m_value, it->m_coeff, tmp); sum += tmp; SASSERT(s != it->m_var || m.eq(m_vars[s].m_base_coeff, it->m_coeff)); } if (!em.is_zero(sum)) { IF_VERBOSE(0, M.display_row(verbose_stream(), r);); TRACE("pb", display(tout << "non-well formed row\n"); M.display_row(tout << "row: ", r);); throw default_exception("non-well formed row"); } return true; } template void simplex::collect_statistics(::statistics & st) const { M.collect_statistics(st); st.update("simplex num pivots", m_stats.m_num_pivots); st.update("simplex num infeasible", m_stats.m_num_infeasible); st.update("simplex num checks", m_stats.m_num_checks); } }; #endif