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:
parent
3c4ac9aee5
commit
edb3569599
18 changed files with 2070 additions and 170 deletions
27
src/util/lp/bound_propagator.h
Normal file
27
src/util/lp/bound_propagator.h
Normal 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
201
src/util/lp/cut_solver.h
Normal 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; }
|
||||
|
||||
};
|
||||
}
|
334
src/util/lp/disjoint_intervals.h
Normal file
334
src/util/lp/disjoint_intervals.h
Normal 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";
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
}
|
591
src/util/lp/init_lar_solver.h
Normal file
591
src/util/lp/init_lar_solver.h
Normal 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);
|
||||
|
||||
}
|
||||
}
|
||||
|
408
src/util/lp/lp_primal_core_solver_tableau.h
Normal file
408
src/util/lp/lp_primal_core_solver_tableau.h
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue