3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 17:15:31 +00:00

updates to sorting networks

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2017-09-23 22:36:19 -05:00
parent 3c4ac9aee5
commit edb3569599
18 changed files with 2070 additions and 170 deletions

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_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:
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(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"; }
};
}

201
src/util/lp/cut_solver.h Normal file
View file

@ -0,0 +1,201 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Nikolaj Bjorner, Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/trace.h"
#include "util/lp/lp_settings.h"
namespace lp {
template <typename T>
class cut_solver {
struct ineq { // we only have less or equal, which is enough for integral variables
mpq m_bound;
vector<std::pair<T, var_index>> m_term;
ineq(vector<std::pair<T, var_index>>& term, mpq bound):m_bound(bound),m_term(term) {
}
};
vector<ineq> m_ineqs;
enum class lbool {
l_false, // false
l_true, // true
l_undef // undef
};
enum class literal_type {
BOOL,
INEQ,
BOUND
};
struct literal {
literal_type m_tag;
bool m_sign; // true means the pointed inequality is negated, or bound is negated, or boolean value is negated
unsigned m_id;
unsigned m_index_of_ineq; // index into m_ineqs
bool m_bool_val; // used if m_tag is equal to BOOL
mpq m_bound; // used if m_tag is BOUND
literal(bool sign, bool val): m_tag(literal_type::BOOL),
m_bool_val(val){
}
literal(bool sign, unsigned index_of_ineq) : m_tag(literal_type::INEQ), m_index_of_ineq(index_of_ineq) {}
};
bool lhs_is_int(const vector<std::pair<mpq, var_index>> & lhs) const {
for (auto & p : lhs)
if (p.first.is_int() == false) return false;
return true;
}
public:
void add_ineq(vector<std::pair<mpq, var_index>> & lhs, mpq rhs) {
lp_assert(lhs_is_int(lhs));
lp_assert(rhs.is_int());
m_ineqs.push_back(ineq(lhs, rhs));
}
bool m_inconsistent; // tracks if state is consistent
unsigned m_scope_lvl; // tracks the number of case splits
svector<literal> m_trail;
// backtracking state from the SAT solver:
struct scope {
unsigned m_trail_lim; // pointer into assignment stack
unsigned m_clauses_to_reinit_lim; // ignore for now
bool m_inconsistent; // really needed?
};
svector<scope> m_scopes;
bool at_base_lvl() const { return m_scope_lvl == 0; }
lbool check() {
init_search();
propagate();
while (true) {
lbool r = bounded_search();
if (r != lbool::l_undef)
return r;
restart();
simplify_problem();
if (check_inconsistent()) return lbool::l_false;
gc();
}
}
cut_solver() {
}
void init_search() {
// TBD
// initialize data-structures
}
void simplify_problem() {
// no-op
}
void gc() {
// no-op
}
void restart() {
// no-op for now
}
bool check_inconsistent() {
// TBD
return false;
}
lbool bounded_search() {
while (true) {
checkpoint();
bool done = false;
while (!done) {
lbool is_sat = propagate_and_backjump_step(done);
if (is_sat != lbool::l_true) return is_sat;
}
gc();
if (!decide()) {
lbool is_sat = final_check();
if (is_sat != lbool::l_undef) {
return is_sat;
}
}
}
}
void checkpoint() {
// check for cancelation
}
void cleanup() {
}
lbool propagate_and_backjump_step(bool& done) {
done = true;
propagate();
if (!inconsistent())
return lbool::l_true;
if (!resolve_conflict())
return lbool::l_false;
if (at_base_lvl()) {
cleanup(); // cleaner may propagate frozen clauses
if (inconsistent()) {
TRACE("sat", tout << "conflict at level 0\n";);
return lbool::l_false;
}
gc();
}
done = false;
return lbool::l_true;
}
lbool final_check() {
// there are no more case splits, and all clauses are satisfied.
// prepare the model for external consumption.
return lbool::l_true;
}
bool resolve_conflict() {
while (true) {
bool r = resolve_conflict_core();
// after pop, clauses are reinitialized,
// this may trigger another conflict.
if (!r)
return false;
if (!inconsistent())
return true;
}
}
bool resolve_conflict_core() {
// this is where the main action is.
return true;
}
void propagate() {
// this is where the main action is.
}
bool decide() {
// this is where the main action is.
// pick the next variable and bound or value on the variable.
// return false if all variables have been assigned.
return false;
}
bool inconsistent() const { return m_inconsistent; }
};
}

View file

@ -0,0 +1,334 @@
/*
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

@ -0,0 +1,591 @@
/*++
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

@ -0,0 +1,408 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
// this is a part of lp_primal_core_solver that deals with the tableau
#include "util/lp/lp_primal_core_solver.h"
namespace lp {
template <typename T, typename X> void lp_primal_core_solver<T, X>::one_iteration_tableau() {
int entering = choose_entering_column_tableau();
if (entering == -1) {
decide_on_status_when_cannot_find_entering();
}
else {
advance_on_entering_tableau(entering);
}
SASSERT(this->inf_set_is_correct());
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_entering_tableau(int entering) {
X t;
int leaving = find_leaving_and_t_tableau(entering, t);
if (leaving == -1) {
this->set_status(UNBOUNDED);
return;
}
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_rows() {
int i = find_inf_row();
if (i == -1)
return -1;
return find_shortest_beneficial_column_in_row(i);
}
*/
template <typename T, typename X> int lp_primal_core_solver<T, X>::choose_entering_column_tableau() {
//this moment m_y = cB * B(-1)
unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter();
SASSERT(numeric_traits<T>::precise());
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();
for (auto non_basis_iter = m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) {
unsigned j = *non_basis_iter;
if (!column_is_benefitial_for_entering_basis(j))
continue;
// if we are here then j is a candidate to enter the basis
unsigned t = this->m_A.number_of_non_zeroes_in_column(j);
if (t < j_nz) {
j_nz = t;
entering_iter = non_basis_iter;
if (number_of_benefitial_columns_to_go_over)
number_of_benefitial_columns_to_go_over--;
}
else if (t == j_nz && this->m_settings.random_next() % 2 == 0) {
entering_iter = non_basis_iter;
}
}// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb);
if (entering_iter == m_non_basis_list.end())
return -1;
unsigned entering = *entering_iter;
m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1;
if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search)
m_sign_of_entering_delta = -m_sign_of_entering_delta;
m_non_basis_list.erase(entering_iter);
m_non_basis_list.push_back(entering);
return entering;
}
template <typename T, typename X>
unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
init_run_tableau();
if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) {
this->set_status(FEASIBLE);
return 0;
}
if ((!numeric_traits<T>::precise()) && this->A_mult_x_is_off()) {
this->set_status(FLOATING_POINT_ERROR);
return 0;
}
do {
if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->m_using_infeas_costs? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) {
return this->total_iterations();
}
if (this->m_settings.use_tableau_rows())
one_iteration_tableau_rows();
else
one_iteration_tableau();
switch (this->get_status()) {
case OPTIMAL: // double check that we are at optimum
case INFEASIBLE:
if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
break;
if (!numeric_traits<T>::precise()) {
if(this->m_look_for_feasible_solution_only)
break;
this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
if (choose_entering_column(1) == -1) {
decide_on_status_when_cannot_find_entering();
break;
}
this->set_status(UNKNOWN);
} else { // precise case
if ((!this->infeasibility_costs_are_correct())) {
init_reduced_costs_tableau(); // forcing recalc
if (choose_entering_column_tableau() == -1) {
decide_on_status_when_cannot_find_entering();
break;
}
this->set_status(UNKNOWN);
}
}
break;
case TENTATIVE_UNBOUNDED:
this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
break;
case UNBOUNDED:
if (this->current_x_is_infeasible()) {
init_reduced_costs();
this->set_status(UNKNOWN);
}
break;
case UNSTABLE:
SASSERT(! (numeric_traits<T>::precise()));
this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
break;
default:
break; // do nothing
}
} while (this->get_status() != FLOATING_POINT_ERROR
&&
this->get_status() != UNBOUNDED
&&
this->get_status() != OPTIMAL
&&
this->get_status() != INFEASIBLE
&&
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->current_x_is_feasible() && this->m_look_for_feasible_solution_only));
SASSERT(this->get_status() == FLOATING_POINT_ERROR
||
this->current_x_is_feasible() == false
||
this->calc_current_x_is_feasible_include_non_basis());
return this->total_iterations();
}
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);
SASSERT(leaving >= 0 && entering >= 0);
SASSERT((this->m_settings.simplex_strategy() ==
simplex_strategy_enum::tableau_rows) ||
m_non_basis_list.back() == static_cast<unsigned>(entering));
SASSERT(this->m_using_infeas_costs || !is_neg(t));
SASSERT(entering != leaving || !is_zero(t)); // otherwise nothing changes
if (entering == leaving) {
advance_on_entering_equal_leaving_tableau(entering, t);
return;
}
if (!is_zero(t)) {
if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search ) {
if (m_sign_of_entering_delta == -1)
t = -t;
}
this->update_basis_and_x_tableau(entering, leaving, t);
SASSERT(this->A_mult_x_is_off() == false);
this->iters_with_no_cost_growing() = 0;
} else {
this->pivot_column_tableau(entering, this->m_basis_heading[leaving]);
this->change_basis(entering, leaving);
}
if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
return;
if (this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) {
if (need_to_switch_costs()) {
this->init_reduced_costs_tableau();
}
SASSERT(!need_to_switch_costs());
std::list<unsigned>::iterator it = m_non_basis_list.end();
it--;
* it = static_cast<unsigned>(leaving);
}
}
template <typename T, typename X>
void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving_tableau(int entering, X & t) {
SASSERT(!this->A_mult_x_is_off() );
this->update_x_tableau(entering, t * m_sign_of_entering_delta);
if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
return;
if (need_to_switch_costs()) {
init_reduced_costs_tableau();
}
this->iters_with_no_cost_growing() = 0;
}
template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_and_t_tableau(unsigned entering, X & t) {
unsigned k = 0;
bool unlimited = true;
unsigned row_min_nz = this->m_n() + 1;
m_leaving_candidates.clear();
auto & col = this->m_A.m_columns[entering];
unsigned col_size = col.size();
for (;k < col_size && unlimited; k++) {
const column_cell & c = col[k];
unsigned i = c.m_i;
const T & ed = this->m_A.get_val(c);
SASSERT(!numeric_traits<T>::is_zero(ed));
unsigned j = this->m_basis[i];
limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited);
if (!unlimited) {
m_leaving_candidates.push_back(j);
row_min_nz = this->m_A.m_rows[i].size();
}
}
if (unlimited) {
if (try_jump_to_another_bound_on_entering_unlimited(entering, t))
return entering;
return -1;
}
X ratio;
for (;k < col_size; k++) {
const column_cell & c = col[k];
unsigned i = c.m_i;
const T & ed = this->m_A.get_val(c);
SASSERT(!numeric_traits<T>::is_zero(ed));
unsigned j = this->m_basis[i];
unlimited = true;
limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited);
if (unlimited) continue;
unsigned i_nz = this->m_A.m_rows[i].size();
if (ratio < t) {
t = ratio;
m_leaving_candidates.clear();
m_leaving_candidates.push_back(j);
row_min_nz = i_nz;
} else if (ratio == t && i_nz < row_min_nz) {
m_leaving_candidates.clear();
m_leaving_candidates.push_back(j);
row_min_nz = this->m_A.m_rows[i].size();
} else if (ratio == t && i_nz == row_min_nz) {
m_leaving_candidates.push_back(j);
}
}
ratio = t;
unlimited = false;
if (try_jump_to_another_bound_on_entering(entering, t, ratio, unlimited)) {
t = ratio;
return entering;
}
if (m_leaving_candidates.size() == 1)
return m_leaving_candidates[0];
k = this->m_settings.random_next() % m_leaving_candidates.size();
return m_leaving_candidates[k];
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run_tableau() {
// print_matrix(&(this->m_A), std::cout);
SASSERT(this->A_mult_x_is_off() == false);
SASSERT(basis_columns_are_set_correctly());
this->m_basis_sort_counter = 0; // to initiate the sort of the basis
this->set_total_iterations(0);
this->iters_with_no_cost_growing() = 0;
SASSERT(this->inf_set_is_correct());
if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)
return;
if (this->m_settings.backup_costs)
backup_and_normalize_costs();
m_epsilon_of_reduced_cost = numeric_traits<X>::precise() ? zero_of_type<T>() : T(1) / T(10000000);
if (this->m_settings.use_breakpoints_in_feasibility_search)
m_breakpoint_indices_queue.resize(this->m_n());
if (!numeric_traits<X>::precise()) {
this->m_column_norm_update_counter = 0;
init_column_norms();
}
if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
init_tableau_rows();
SASSERT(this->reduced_costs_are_correct_tableau());
SASSERT(!this->need_to_pivot_to_basis_tableau());
}
template <typename T, typename X> bool lp_primal_core_solver<T, X>::
update_basis_and_x_tableau(int entering, int leaving, X const & tt) {
SASSERT(this->use_tableau());
update_x_tableau(entering, tt);
this->pivot_column_tableau(entering, this->m_basis_heading[leaving]);
this->change_basis(entering, leaving);
return true;
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::
update_x_tableau(unsigned entering, const X& delta) {
if (!this->m_using_infeas_costs) {
this->m_x[entering] += delta;
for (const auto & c : this->m_A.m_columns[entering]) {
unsigned i = c.m_i;
this->m_x[this->m_basis[i]] -= delta * this->m_A.get_val(c);
this->update_column_in_inf_set(this->m_basis[i]);
}
} else { // m_using_infeas_costs == true
this->m_x[entering] += delta;
SASSERT(this->column_is_feasible(entering));
SASSERT(this->m_costs[entering] == zero_of_type<T>());
// m_d[entering] can change because of the cost change for basic columns.
for (const auto & c : this->m_A.m_columns[entering]) {
unsigned i = c.m_i;
unsigned j = this->m_basis[i];
this->m_x[j] -= delta * this->m_A.get_val(c);
update_inf_cost_for_column_tableau(j);
if (is_zero(this->m_costs[j]))
this->m_inf_set.erase(j);
else
this->m_inf_set.insert(j);
}
}
SASSERT(this->A_mult_x_is_off() == false);
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::
update_inf_cost_for_column_tableau(unsigned j) {
SASSERT(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows);
SASSERT(this->m_using_infeas_costs);
T new_cost = get_infeasibility_cost_for_column(j);
T delta = this->m_costs[j] - new_cost;
if (is_zero(delta))
return;
this->m_costs[j] = new_cost;
update_reduced_cost_for_basic_column_cost_change(delta, j);
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::init_reduced_costs_tableau() {
if (this->current_x_is_infeasible() && !this->m_using_infeas_costs) {
init_infeasibility_costs();
} else if (this->current_x_is_feasible() && this->m_using_infeas_costs) {
if (this->m_look_for_feasible_solution_only)
return;
this->m_costs = m_costs_backup;
this->m_using_infeas_costs = false;
}
unsigned size = this->m_basis_heading.size();
for (unsigned j = 0; j < size; j++) {
if (this->m_basis_heading[j] >= 0)
this->m_d[j] = zero_of_type<T>();
else {
T& d = this->m_d[j] = this->m_costs[j];
for (auto & cc : this->m_A.m_columns[j]) {
d -= this->m_costs[this->m_basis[cc.m_i]] * this->m_A.get_val(cc);
}
}
}
}
}

View file

@ -24,6 +24,28 @@ Notes:
#ifndef SORTING_NETWORK_H_
#define SORTING_NETWORK_H_
enum sorting_network_encoding {
grouped_at_most_1,
bimander_at_most_1,
ordered_at_most_1
};
inline std::ostream& operator<<(std::ostream& out, sorting_network_encoding enc) {
switch (enc) {
case grouped_at_most_1: return out << "grouped";
case bimander_at_most_1: return out << "bimander";
case ordered_at_most_1: return out << "ordered";
}
return out << "???";
}
struct sorting_network_config {
sorting_network_encoding m_encoding;
sorting_network_config() {
m_encoding = grouped_at_most_1;
}
};
template <typename Ext>
class sorting_network {
typedef typename Ext::vector vect;
@ -88,7 +110,7 @@ Notes:
}
public:
sorting_network(Ext& ext):
sorting_network(Ext& ext):
m_ext(ext),
m_current(&m_currentv),
m_next(&m_nextv)
@ -121,6 +143,7 @@ Notes:
class psort_nw {
typedef typename psort_expr::literal literal;
typedef typename psort_expr::literal_vector literal_vector;
sorting_network_config m_cfg;
class vc {
unsigned v; // number of vertices
@ -185,7 +208,7 @@ Notes:
}
};
psort_nw(psort_expr& c): ctx(c) {}
psort_nw(psort_expr& c, sorting_network_config const& cfg): ctx(c), m_cfg(cfg) {}
literal ge(bool full, unsigned k, unsigned n, literal const* xs) {
if (k > n) {
@ -220,7 +243,17 @@ Notes:
else if (k == 1) {
literal_vector ors;
// scoped_stats _ss(m_stats, k, n);
return mk_at_most_1(full, n, xs, ors, false);
switch (m_cfg.m_encoding) {
case grouped_at_most_1:
return mk_at_most_1(full, n, xs, ors, false);
case bimander_at_most_1:
return mk_at_most_1_bimander(full, n, xs, ors);
case ordered_at_most_1:
return mk_ordered_atmost_1(full, n, xs);
default:
UNREACHABLE();
return xs[0];
}
}
else {
SASSERT(2*k <= n);
@ -278,7 +311,7 @@ Notes:
if (n == 1) {
return ors[0];
}
literal result = fresh();
literal result = fresh("or");
add_implies_or(result, n, ors);
add_or_implies(result, n, ors);
return result;
@ -317,15 +350,26 @@ Notes:
if (ands.size() == 1) {
return ands[0];
}
literal result = fresh();
literal result = fresh("and");
add_implies_and(result, ands);
add_and_implies(result, ands);
return result;
}
literal mk_exactly_1(bool full, unsigned n, literal const* xs) {
TRACE("pb", tout << "exactly 1 with " << n << " arguments " << (full?"full":"not full") << "\n";);
literal_vector ors;
literal r1 = mk_at_most_1(full, n, xs, ors, true);
literal r1;
switch (m_cfg.m_encoding) {
case grouped_at_most_1:
r1 = mk_at_most_1(full, n, xs, ors, true);
break;
case bimander_at_most_1:
r1 = mk_at_most_1_bimander(full, n, xs, ors);
break;
case ordered_at_most_1:
return mk_ordered_exactly_1(full, n, xs);
}
if (full) {
r1 = mk_and(r1, mk_or(ors));
@ -340,12 +384,8 @@ Notes:
TRACE("pb_verbose", tout << (full?"full":"partial") << " ";
for (unsigned i = 0; i < n; ++i) tout << xs[i] << " ";
tout << "\n";);
if (n >= 4 && false) {
return mk_at_most_1_bimander(full, n, xs, ors);
}
literal_vector in(n, xs);
literal result = fresh();
literal result = fresh("at-most-1");
unsigned inc_size = 4;
literal_vector ands;
ands.push_back(result);
@ -387,7 +427,7 @@ Notes:
// xs[0] + ... + xs[n-1] <= 1 => and_x
if (full) {
literal and_i = fresh();
literal and_i = fresh("and");
for (unsigned i = 0; i < n; ++i) {
literal_vector lits;
lits.push_back(and_i);
@ -407,29 +447,6 @@ Notes:
}
#if 0
literal result = fresh();
// result => xs[0] + ... + xs[n-1] <= 1
for (unsigned i = 0; i < n; ++i) {
for (unsigned j = i + 1; j < n; ++j) {
add_clause(ctx.mk_not(result), ctx.mk_not(xs[i]), ctx.mk_not(xs[j]));
}
}
// xs[0] + ... + xs[n-1] <= 1 => result
for (unsigned i = 0; i < n; ++i) {
literal_vector lits;
lits.push_back(result);
for (unsigned j = 0; j < n; ++j) {
if (j != i) lits.push_back(xs[j]);
}
add_clause(lits);
}
return result;
#endif
#if 1
// r <=> and( or(!xi,!xj))
//
literal_vector ands;
@ -439,30 +456,100 @@ Notes:
}
}
return mk_and(ands);
#else
// r <=> or (and !x_{j != i})
literal_vector ors;
for (unsigned i = 0; i < n; ++i) {
literal_vector ands;
for (unsigned j = 0; j < n; ++j) {
if (j != i) {
ands.push_back(ctx.mk_not(xs[j]));
}
}
ors.push_back(mk_and(ands));
}
return mk_or(ors);
#endif
}
literal mk_ordered_exactly_1(bool full, unsigned n, literal const* xs) {
return mk_ordered_1(full, true, n, xs);
}
literal mk_ordered_atmost_1(bool full, unsigned n, literal const* xs) {
return mk_ordered_1(full, false, n, xs);
}
literal mk_ordered_1(bool full, bool is_eq, unsigned n, literal const* xs) {
if (n <= 1 && !is_eq) return ctx.mk_true();
if (n == 0) {
return ctx.mk_false();
}
if (n == 1) {
return xs[0];
}
// y0 -> y1
// x0 -> y0
// x1 -> y1
// r, y0 -> ~x1
// r, y1 -> ~x2
// r -> x3 | y1
// r -> ~x3 | ~y1
// x0,x1,x2, .., x_{n-1}, x_n
// y0,y1,y2, .., y_{n-1}
// y_i -> y_{i+1} i = 0, ..., n - 2
// x_i -> y_i i = 0, ..., n - 1
// r, y_i -> ~x_{i+1} i = 0, ..., n - 1
// exactly 1:
// r -> x_n | y_{n-1}
// full (exactly 1):
// two_i -> y_i & x_{i+1}
// zero -> ~x_n
// zero -> ~y_{n-1}
// r | zero | two_0 | ... | two_{n-1}
// full atmost 1:
// r | two | two_0 | ... | two_{n-1}
literal r = fresh("ordered");
literal_vector ys;
for (unsigned i = 0; i + 1 < n; ++i) {
ys.push_back(fresh("y"));
}
for (unsigned i = 0; i + 2 < n; ++i) {
add_clause(ctx.mk_not(ys[i]), ys[i + 1]);
}
for (unsigned i = 0; i + 1 < n; ++i) {
add_clause(ctx.mk_not(xs[i]), ys[i]);
add_clause(ctx.mk_not(r), ctx.mk_not(ys[i]), ctx.mk_not(xs[i + 1]));
}
add_clause(ctx.mk_not(r), ys[n-2], xs[n-1]);
add_clause(ctx.mk_not(r), ctx.mk_not(ys[n-2]), ctx.mk_not(xs[n-1]));
for (unsigned i = 1; i < n - 1; ++i) {
add_clause(ctx.mk_not(ys[i]), xs[i], ys[i-1]);
}
add_clause(ctx.mk_not(ys[0]), xs[0]);
if (full) {
literal_vector twos;
for (unsigned i = 0; i < n - 1; ++i) {
twos.push_back(fresh("two"));
}
add_clause(ctx.mk_not(twos[0]), ys[0]);
add_clause(ctx.mk_not(twos[0]), xs[1]);
for (unsigned i = 1; i < n - 1; ++i) {
add_clause(ctx.mk_not(twos[i]), ys[i], twos[i-1]);
add_clause(ctx.mk_not(twos[i]), xs[i + 1], twos[i-1]);
}
if (is_eq) {
literal zero = fresh("zero");
add_clause(ctx.mk_not(zero), ctx.mk_not(xs[n-1]));
add_clause(ctx.mk_not(zero), ctx.mk_not(ys[n-2]));
add_clause(r, zero, twos.back());
}
else {
add_clause(r, twos.back());
}
}
return r;
}
//
literal mk_at_most_1_bimander(bool full, unsigned n, literal const* xs, literal_vector& ors) {
if (full) {
return mk_at_most_1(full, n, xs, ors, true);
}
literal_vector in(n, xs);
literal result = fresh();
literal result = fresh("bimander");
unsigned inc_size = 2;
literal_vector ands;
for (unsigned i = 0; i < n; i += inc_size) {
@ -477,7 +564,7 @@ Notes:
}
literal_vector bits;
for (unsigned k = 0; k < nbits; ++k) {
bits.push_back(fresh());
bits.push_back(fresh("bit"));
}
for (unsigned i = 0; i < ors.size(); ++i) {
for (unsigned k = 0; k < nbits; ++k) {
@ -539,9 +626,9 @@ Notes:
return ctx.mk_min(a, b);
}
literal fresh() {
literal fresh(char const* n) {
m_stats.m_num_compiled_vars++;
return ctx.fresh();
return ctx.fresh(n);
}
void add_clause(literal l1, literal l2, literal l3) {
literal lits[3] = { l1, l2, l3 };
@ -558,7 +645,6 @@ Notes:
m_stats.m_num_compiled_clauses++;
m_stats.m_num_clause_vars += n;
literal_vector tmp(n, ls);
TRACE("pb_verbose", for (unsigned i = 0; i < n; ++i) tout << ls[i] << " "; tout << "\n";);
ctx.mk_clause(n, tmp.c_ptr());
}
@ -925,7 +1011,7 @@ Notes:
SASSERT(b <= c);
SASSERT(a + b >= c);
for (unsigned i = 0; i < c; ++i) {
out.push_back(fresh());
out.push_back(fresh("dsmerge"));
}
if (m_t != GE) {
for (unsigned i = 0; i < a; ++i) {
@ -983,7 +1069,7 @@ Notes:
SASSERT(m <= n);
literal_vector lits;
for (unsigned i = 0; i < m; ++i) {
out.push_back(fresh());
out.push_back(fresh("dsort"));
}
if (m_t != GE) {
for (unsigned k = 1; k <= m; ++k) {