3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 09:35:32 +00:00

merge LRA

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2017-05-09 10:46:11 -07:00
parent 085d31dca2
commit 911b24784a
120 changed files with 23069 additions and 15 deletions

View file

@ -305,6 +305,7 @@ public:
MATCH_UNARY(is_uminus);
MATCH_UNARY(is_to_real);
MATCH_UNARY(is_to_int);
MATCH_UNARY(is_is_int);
MATCH_BINARY(is_sub);
MATCH_BINARY(is_add);
MATCH_BINARY(is_mul);
@ -377,6 +378,9 @@ public:
app * mk_real(int i) {
return mk_numeral(rational(i), false);
}
app * mk_real(rational const& r) {
return mk_numeral(r, false);
}
app * mk_le(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LE, arg1, arg2); }
app * mk_ge(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_GE, arg1, arg2); }
app * mk_lt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LT, arg1, arg2); }

View file

@ -26,6 +26,7 @@ Notes:
#include "theory_diff_logic.h"
#include "theory_dense_diff_logic.h"
#include "theory_pb.h"
#include "theory_lra.h"
#include "ast_pp.h"
#include "ast_smt_pp.h"
#include "pp_params.hpp"
@ -143,6 +144,9 @@ namespace opt {
else if (typeid(smt::theory_dense_si&) == typeid(*arith_theory)) {
return dynamic_cast<smt::theory_dense_si&>(*arith_theory);
}
else if (typeid(smt::theory_lra&) == typeid(*arith_theory)) {
return dynamic_cast<smt::theory_lra&>(*arith_theory);
}
else {
UNREACHABLE();
return dynamic_cast<smt::theory_mi_arith&>(*arith_theory);
@ -401,6 +405,14 @@ namespace opt {
return th.mk_ge(m_fm, v, val);
}
if (typeid(smt::theory_lra) == typeid(opt)) {
smt::theory_lra& th = dynamic_cast<smt::theory_lra&>(opt);
SASSERT(val.is_finite());
return th.mk_ge(m_fm, v, val.get_numeral());
}
// difference logic?
if (typeid(smt::theory_dense_si) == typeid(opt) &&
val.get_infinitesimal().is_zero()) {
smt::theory_dense_si& th = dynamic_cast<smt::theory_dense_si&>(opt);

View file

@ -35,8 +35,9 @@ Revision History:
#include"error_codes.h"
#include"gparams.h"
#include"env_params.h"
#include "lp_frontend.h"
typedef enum { IN_UNSPECIFIED, IN_SMTLIB, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_Z3_LOG } input_kind;
typedef enum { IN_UNSPECIFIED, IN_SMTLIB, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_Z3_LOG, IN_MPS } input_kind;
std::string g_aux_input_file;
char const * g_input_file = 0;
@ -342,6 +343,10 @@ int STD_CALL main(int argc, char ** argv) {
else if (strcmp(ext, "smt") == 0) {
g_input_kind = IN_SMTLIB;
}
else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 ||
strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) {
g_input_kind = IN_MPS;
}
}
}
switch (g_input_kind) {
@ -367,6 +372,9 @@ int STD_CALL main(int argc, char ** argv) {
case IN_Z3_LOG:
replay_z3_log(g_input_file);
break;
case IN_MPS:
return_value = read_mps_file(g_input_file);
break;
default:
UNREACHABLE();
}

View file

@ -20,6 +20,7 @@ Revision History:
#include"smt_setup.h"
#include"static_features.h"
#include"theory_arith.h"
#include"theory_lra.h"
#include"theory_dense_diff_logic.h"
#include"theory_diff_logic.h"
#include"theory_utvpi.h"
@ -442,7 +443,7 @@ namespace smt {
m_params.m_arith_propagate_eqs = false;
m_params.m_eliminate_term_ite = true;
m_params.m_nnf_cnf = false;
setup_mi_arith();
setup_r_arith();
}
void setup::setup_QF_LRA(static_features const & st) {
@ -467,7 +468,7 @@ namespace smt {
m_params.m_restart_adaptive = false;
}
m_params.m_arith_small_lemma_size = 32;
setup_mi_arith();
setup_r_arith();
}
void setup::setup_QF_LIA() {
@ -539,7 +540,7 @@ namespace smt {
m_params.m_relevancy_lvl = 0;
m_params.m_arith_reflect = false;
m_params.m_nnf_cnf = false;
setup_mi_arith();
setup_r_arith();
}
void setup::setup_QF_BV() {
@ -718,6 +719,10 @@ namespace smt {
m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params));
}
void setup::setup_r_arith() {
m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params));
}
void setup::setup_mi_arith() {
if (m_params.m_arith_mode == AS_OPTINF) {
m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params));

View file

@ -99,6 +99,7 @@ namespace smt {
void setup_card();
void setup_i_arith();
void setup_mi_arith();
void setup_r_arith();
void setup_fpa();
void setup_str();

View file

@ -54,7 +54,7 @@ namespace smt {
}
}
void theory::display_app(std::ostream & out, app * n) const {
std::ostream& theory::display_app(std::ostream & out, app * n) const {
func_decl * d = n->get_decl();
if (n->get_num_args() == 0) {
out << d->get_name();
@ -73,9 +73,10 @@ namespace smt {
else {
out << "#" << n->get_id();
}
return out;
}
void theory::display_flat_app(std::ostream & out, app * n) const {
std::ostream& theory::display_flat_app(std::ostream & out, app * n) const {
func_decl * d = n->get_decl();
if (n->get_num_args() == 0) {
out << d->get_name();
@ -106,6 +107,7 @@ namespace smt {
else {
out << "#" << n->get_id();
}
return out;
}
bool theory::is_relevant_and_shared(enode * n) const {

View file

@ -337,14 +337,14 @@ namespace smt {
virtual void collect_statistics(::statistics & st) const {
}
void display_app(std::ostream & out, app * n) const;
void display_flat_app(std::ostream & out, app * n) const;
void display_var_def(std::ostream & out, theory_var v) const { return display_app(out, get_enode(v)->get_owner()); }
std::ostream& display_app(std::ostream & out, app * n) const;
void display_var_flat_def(std::ostream & out, theory_var v) const { return display_flat_app(out, get_enode(v)->get_owner()); }
std::ostream& display_flat_app(std::ostream & out, app * n) const;
std::ostream& display_var_def(std::ostream & out, theory_var v) const { return display_app(out, get_enode(v)->get_owner()); }
std::ostream& display_var_flat_def(std::ostream & out, theory_var v) const { return display_flat_app(out, get_enode(v)->get_owner()); }
/**
\brief Assume eqs between variable that are equal with respect to the given table.

View file

@ -239,6 +239,7 @@ int main(int argc, char ** argv) {
TST(pdr);
TST_ARGV(ddnf);
TST(model_evaluator);
TST_ARGV(lp);
TST(get_consequences);
TST(pb2bv);
TST_ARGV(cnf_backbones);

View file

@ -0,0 +1,71 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/debug.h"
#include "util/lp/lp_utils.h"
namespace lean {
// the elements with the smallest priority are dequeued first
template <typename T>
class binary_heap_priority_queue {
vector<T> m_priorities;
// indexing for A starts from 1
vector<unsigned> m_heap; // keeps the elements of the queue
vector<int> m_heap_inverse; // o = m_heap[m_heap_inverse[o]]
unsigned m_heap_size = 0;
// is is the child place in heap
void swap_with_parent(unsigned i);
void put_at(unsigned i, unsigned h);
void decrease_priority(unsigned o, T newPriority);
public:
#ifdef LEAN_DEBUG
bool is_consistent() const;
#endif
public:
void remove(unsigned o);
unsigned size() const { return m_heap_size; }
binary_heap_priority_queue(): m_heap(1) {} // the empty constructror
// n is the initial queue capacity.
// The capacity will be enlarged two times automatically if needed
binary_heap_priority_queue(unsigned n);
void clear() {
for (unsigned i = 0; i < m_heap_size; i++) {
unsigned o = m_heap[i+1];
m_heap_inverse[o] = -1;
}
m_heap_size = 0;
}
void resize(unsigned n);
void put_to_heap(unsigned i, unsigned o);
void enqueue_new(unsigned o, const T& priority);
// This method can work with an element that is already in the queue.
// In this case the priority will be changed and the queue adjusted.
void enqueue(unsigned o, const T & priority);
void change_priority_for_existing(unsigned o, const T & priority);
T get_priority(unsigned o) const { return m_priorities[o]; }
bool is_empty() const { return m_heap_size == 0; }
/// return the first element of the queue and removes it from the queue
unsigned dequeue_and_get_priority(T & priority);
void fix_heap_under(unsigned i);
void put_the_last_at_the_top_and_fix_the_heap();
/// return the first element of the queue and removes it from the queue
unsigned dequeue();
unsigned peek() const {
lean_assert(m_heap_size > 0);
return m_heap[1];
}
#ifdef LEAN_DEBUG
void print(std::ostream & out);
#endif
};
}

View file

@ -0,0 +1,193 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/vector.h"
#include "util/lp/binary_heap_priority_queue.h"
namespace lean {
// is is the child place in heap
template <typename T> void binary_heap_priority_queue<T>::swap_with_parent(unsigned i) {
unsigned parent = m_heap[i >> 1];
put_at(i >> 1, m_heap[i]);
put_at(i, parent);
}
template <typename T> void binary_heap_priority_queue<T>::put_at(unsigned i, unsigned h) {
m_heap[i] = h;
m_heap_inverse[h] = i;
}
template <typename T> void binary_heap_priority_queue<T>::decrease_priority(unsigned o, T newPriority) {
m_priorities[o] = newPriority;
int i = m_heap_inverse[o];
while (i > 1) {
if (m_priorities[m_heap[i]] < m_priorities[m_heap[i >> 1]])
swap_with_parent(i);
else
break;
i >>= 1;
}
}
#ifdef LEAN_DEBUG
template <typename T> bool binary_heap_priority_queue<T>::is_consistent() const {
for (int i = 0; i < m_heap_inverse.size(); i++) {
int i_index = m_heap_inverse[i];
lean_assert(i_index <= static_cast<int>(m_heap_size));
lean_assert(i_index == -1 || m_heap[i_index] == i);
}
for (unsigned i = 1; i < m_heap_size; i++) {
unsigned ch = i << 1;
for (int k = 0; k < 2; k++) {
if (ch > m_heap_size) break;
if (!(m_priorities[m_heap[i]] <= m_priorities[m_heap[ch]])){
return false;
}
ch++;
}
}
return true;
}
#endif
template <typename T> void binary_heap_priority_queue<T>::remove(unsigned o) {
T priority_of_o = m_priorities[o];
int o_in_heap = m_heap_inverse[o];
if (o_in_heap == -1) {
return; // nothing to do
}
lean_assert(static_cast<unsigned>(o_in_heap) <= m_heap_size);
if (static_cast<unsigned>(o_in_heap) < m_heap_size) {
put_at(o_in_heap, m_heap[m_heap_size--]);
if (m_priorities[m_heap[o_in_heap]] > priority_of_o) {
fix_heap_under(o_in_heap);
} else { // we need to propogate the m_heap[o_in_heap] up
unsigned i = o_in_heap;
while (i > 1) {
unsigned ip = i >> 1;
if (m_priorities[m_heap[i]] < m_priorities[m_heap[ip]])
swap_with_parent(i);
else
break;
i = ip;
}
}
} else {
lean_assert(static_cast<unsigned>(o_in_heap) == m_heap_size);
m_heap_size--;
}
m_heap_inverse[o] = -1;
// lean_assert(is_consistent());
}
// n is the initial queue capacity.
// The capacity will be enlarged two times automatically if needed
template <typename T> binary_heap_priority_queue<T>::binary_heap_priority_queue(unsigned n) :
m_priorities(n),
m_heap(n + 1), // because the indexing for A starts from 1
m_heap_inverse(n, -1)
{ }
template <typename T> void binary_heap_priority_queue<T>::resize(unsigned n) {
m_priorities.resize(n);
m_heap.resize(n + 1);
m_heap_inverse.resize(n, -1);
}
template <typename T> void binary_heap_priority_queue<T>::put_to_heap(unsigned i, unsigned o) {
m_heap[i] = o;
m_heap_inverse[o] = i;
}
template <typename T> void binary_heap_priority_queue<T>::enqueue_new(unsigned o, const T& priority) {
m_heap_size++;
int i = m_heap_size;
lean_assert(o < m_priorities.size());
m_priorities[o] = priority;
put_at(i, o);
while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) {
swap_with_parent(i);
i >>= 1;
}
}
// This method can work with an element that is already in the queue.
// In this case the priority will be changed and the queue adjusted.
template <typename T> void binary_heap_priority_queue<T>::enqueue(unsigned o, const T & priority) {
if (o >= m_priorities.size()) {
resize(o << 1); // make the size twice larger
}
if (m_heap_inverse[o] == -1)
enqueue_new(o, priority);
else
change_priority_for_existing(o, priority);
}
template <typename T> void binary_heap_priority_queue<T>::change_priority_for_existing(unsigned o, const T & priority) {
if (m_priorities[o] > priority) {
decrease_priority(o, priority);
} else {
m_priorities[o] = priority;
fix_heap_under(m_heap_inverse[o]);
}
}
/// return the first element of the queue and removes it from the queue
template <typename T> unsigned binary_heap_priority_queue<T>::dequeue_and_get_priority(T & priority) {
lean_assert(m_heap_size != 0);
int ret = m_heap[1];
priority = m_priorities[ret];
put_the_last_at_the_top_and_fix_the_heap();
return ret;
}
template <typename T> void binary_heap_priority_queue<T>::fix_heap_under(unsigned i) {
while (true) {
unsigned smallest = i;
unsigned l = i << 1;
if (l <= m_heap_size && m_priorities[m_heap[l]] < m_priorities[m_heap[i]])
smallest = l;
unsigned r = l + 1;
if (r <= m_heap_size && m_priorities[m_heap[r]] < m_priorities[m_heap[smallest]])
smallest = r;
if (smallest != i)
swap_with_parent(smallest);
else
break;
i = smallest;
}
}
template <typename T> void binary_heap_priority_queue<T>::put_the_last_at_the_top_and_fix_the_heap() {
if (m_heap_size > 1) {
put_at(1, m_heap[m_heap_size--]);
fix_heap_under(1);
} else {
m_heap_size--;
}
}
/// return the first element of the queue and removes it from the queue
template <typename T> unsigned binary_heap_priority_queue<T>::dequeue() {
lean_assert(m_heap_size > 0);
int ret = m_heap[1];
put_the_last_at_the_top_and_fix_the_heap();
m_heap_inverse[ret] = -1;
return ret;
}
#ifdef LEAN_DEBUG
template <typename T> void binary_heap_priority_queue<T>::print(std::ostream & out) {
vector<int> index;
vector<T> prs;
while (size()) {
T prior;
int j = dequeue_and_get_priority(prior);
index.push_back(j);
prs.push_back(prior);
out << "(" << j << ", " << prior << ")";
}
out << std::endl;
// restore the queue
for (int i = 0; i < index.size(); i++)
enqueue(index[i], prs[i]);
}
#endif
}

View file

@ -0,0 +1,26 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/numeric_pair.h"
#include "util/lp/binary_heap_priority_queue.hpp"
namespace lean {
template binary_heap_priority_queue<int>::binary_heap_priority_queue(unsigned int);
template unsigned binary_heap_priority_queue<int>::dequeue();
template void binary_heap_priority_queue<int>::enqueue(unsigned int, int const&);
template void binary_heap_priority_queue<double>::enqueue(unsigned int, double const&);
template void binary_heap_priority_queue<mpq>::enqueue(unsigned int, mpq const&);
template void binary_heap_priority_queue<int>::remove(unsigned int);
template unsigned binary_heap_priority_queue<numeric_pair<mpq> >::dequeue();
template unsigned binary_heap_priority_queue<double>::dequeue();
template unsigned binary_heap_priority_queue<mpq>::dequeue();
template void binary_heap_priority_queue<numeric_pair<mpq> >::enqueue(unsigned int, numeric_pair<mpq> const&);
template void binary_heap_priority_queue<numeric_pair<mpq> >::resize(unsigned int);
template void lean::binary_heap_priority_queue<double>::resize(unsigned int);
template binary_heap_priority_queue<unsigned int>::binary_heap_priority_queue(unsigned int);
template void binary_heap_priority_queue<unsigned>::resize(unsigned int);
template unsigned binary_heap_priority_queue<unsigned int>::dequeue();
template void binary_heap_priority_queue<unsigned int>::enqueue(unsigned int, unsigned int const&);
template void binary_heap_priority_queue<unsigned int>::remove(unsigned int);
template void lean::binary_heap_priority_queue<mpq>::resize(unsigned int);
}

View file

@ -0,0 +1,50 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include "util/vector.h"
#include <set>
#include <utility>
#include "util/lp/binary_heap_priority_queue.h"
typedef std::pair<unsigned, unsigned> upair;
namespace lean {
template <typename T>
class binary_heap_upair_queue {
binary_heap_priority_queue<T> m_q;
std::unordered_map<upair, unsigned> m_pairs_to_index;
vector<upair> m_pairs; // inverse to index
vector<unsigned> m_available_spots;
public:
binary_heap_upair_queue(unsigned size);
unsigned dequeue_available_spot();
bool is_empty() const { return m_q.is_empty(); }
unsigned size() const {return m_q.size(); }
bool contains(unsigned i, unsigned j) const { return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end();
}
void remove(unsigned i, unsigned j);
bool ij_index_is_new(unsigned ij_index) const;
void enqueue(unsigned i, unsigned j, const T & priority);
void dequeue(unsigned & i, unsigned &j);
T get_priority(unsigned i, unsigned j) const;
#ifdef LEAN_DEBUG
bool pair_to_index_is_a_bijection() const;
bool available_spots_are_correct() const;
bool is_correct() const {
return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct();
}
#endif
void resize(unsigned size) { m_q.resize(size); }
};
}

View file

@ -0,0 +1,110 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <set>
#include "util/lp/lp_utils.h"
#include "util/lp/binary_heap_upair_queue.h"
namespace lean {
template <typename T> binary_heap_upair_queue<T>::binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) {
for (unsigned i = 0; i < size; i++)
m_available_spots.push_back(i);
}
template <typename T> unsigned
binary_heap_upair_queue<T>::dequeue_available_spot() {
lean_assert(m_available_spots.empty() == false);
unsigned ret = m_available_spots.back();
m_available_spots.pop_back();
return ret;
}
template <typename T> void binary_heap_upair_queue<T>::remove(unsigned i, unsigned j) {
upair p(i, j);
auto it = m_pairs_to_index.find(p);
if (it == m_pairs_to_index.end())
return; // nothing to do
m_q.remove(it->second);
m_available_spots.push_back(it->second);
m_pairs_to_index.erase(it);
}
template <typename T> bool binary_heap_upair_queue<T>::ij_index_is_new(unsigned ij_index) const {
for (auto it : m_pairs_to_index) {
if (it.second == ij_index)
return false;
}
return true;
}
template <typename T> void binary_heap_upair_queue<T>::enqueue(unsigned i, unsigned j, const T & priority) {
upair p(i, j);
auto it = m_pairs_to_index.find(p);
unsigned ij_index;
if (it == m_pairs_to_index.end()) {
// it is a new pair, let us find a spot for it
if (m_available_spots.empty()) {
// we ran out of empty spots
unsigned size_was = static_cast<unsigned>(m_pairs.size());
unsigned new_size = size_was << 1;
for (unsigned i = size_was; i < new_size; i++)
m_available_spots.push_back(i);
m_pairs.resize(new_size);
}
ij_index = dequeue_available_spot();
// lean_assert(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
m_pairs[ij_index] = p;
m_pairs_to_index[p] = ij_index;
} else {
ij_index = it->second;
}
m_q.enqueue(ij_index, priority);
}
template <typename T> void binary_heap_upair_queue<T>::dequeue(unsigned & i, unsigned &j) {
lean_assert(!m_q.is_empty());
unsigned ij_index = m_q.dequeue();
upair & p = m_pairs[ij_index];
i = p.first;
j = p.second;
m_available_spots.push_back(ij_index);
m_pairs_to_index.erase(p);
}
template <typename T> T binary_heap_upair_queue<T>::get_priority(unsigned i, unsigned j) const {
auto it = m_pairs_to_index.find(std::make_pair(i, j));
if (it == m_pairs_to_index.end())
return T(0xFFFFFF); // big number
return m_q.get_priority(it->second);
}
#ifdef LEAN_DEBUG
template <typename T> bool binary_heap_upair_queue<T>::pair_to_index_is_a_bijection() const {
std::set<int> tmp;
for (auto p : m_pairs_to_index) {
unsigned j = p.second;
unsigned size = tmp.size();
tmp.insert(j);
if (tmp.size() == size)
return false;
}
return true;
}
template <typename T> bool binary_heap_upair_queue<T>::available_spots_are_correct() const {
std::set<int> tmp;
for (auto p : m_available_spots){
tmp.insert(p);
}
if (tmp.size() != m_available_spots.size())
return false;
for (auto it : m_pairs_to_index)
if (tmp.find(it.second) != tmp.end())
return false;
return true;
}
#endif
}

View file

@ -0,0 +1,17 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/binary_heap_upair_queue.hpp"
namespace lean {
template binary_heap_upair_queue<int>::binary_heap_upair_queue(unsigned int);
template binary_heap_upair_queue<unsigned int>::binary_heap_upair_queue(unsigned int);
template unsigned binary_heap_upair_queue<int>::dequeue_available_spot();
template unsigned binary_heap_upair_queue<unsigned int>::dequeue_available_spot();
template void binary_heap_upair_queue<int>::enqueue(unsigned int, unsigned int, int const&);
template void binary_heap_upair_queue<int>::remove(unsigned int, unsigned int);
template void binary_heap_upair_queue<unsigned int>::remove(unsigned int, unsigned int);
template void binary_heap_upair_queue<int>::dequeue(unsigned int&, unsigned int&);
template void binary_heap_upair_queue<unsigned int>::enqueue(unsigned int, unsigned int, unsigned int const&);
template void binary_heap_upair_queue<unsigned int>::dequeue(unsigned int&, unsigned int&);
}

View file

@ -0,0 +1,333 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/lp/linear_combination_iterator.h"
#include "implied_bound.h"
#include "test_bound_analyzer.h"
#include <functional>
#include "util/lp/bound_propagator.h"
// We have an equality : sum by j of row[j]*x[j] = rs
// We try to pin a var by pushing the total by using the variable bounds
// In a loop we drive the partial sum down, denoting the variables of this process by _u.
// In the same loop trying to pin variables by pushing the partial sum up, denoting the variable related to it by _l
namespace lean {
class bound_analyzer_on_row {
linear_combination_iterator<mpq> & m_it;
unsigned m_row_or_term_index;
int m_column_of_u = -1; // index of an unlimited from above monoid
// -1 means that such a value is not found, -2 means that at least two of such monoids were found
int m_column_of_l = -1; // index of an unlimited from below monoid
impq m_rs;
bound_propagator & m_bp;
public :
// constructor
bound_analyzer_on_row(
linear_combination_iterator<mpq> &it,
const numeric_pair<mpq>& rs,
unsigned row_or_term_index,
bound_propagator & bp
)
:
m_it(it),
m_row_or_term_index(row_or_term_index),
m_rs(rs),
m_bp(bp)
{}
unsigned j;
void analyze() {
mpq a; unsigned j;
while (((m_column_of_l != -2) || (m_column_of_u != -2)) && m_it.next(a, j))
analyze_bound_on_var_on_coeff(j, a);
if (m_column_of_u >= 0)
limit_monoid_u_from_below();
else if (m_column_of_u == -1)
limit_all_monoids_from_below();
if (m_column_of_l >= 0)
limit_monoid_l_from_above();
else if (m_column_of_l == -1)
limit_all_monoids_from_above();
}
bool bound_is_available(unsigned j, bool low_bound) {
return (low_bound && low_bound_is_available(j)) ||
(!low_bound && upper_bound_is_available(j));
}
bool upper_bound_is_available(unsigned j) const {
switch (m_bp.get_column_type(j))
{
case column_type::fixed:
case column_type::boxed:
case column_type::upper_bound:
return true;
default:
return false;
}
}
bool low_bound_is_available(unsigned j) const {
switch (m_bp.get_column_type(j))
{
case column_type::fixed:
case column_type::boxed:
case column_type::low_bound:
return true;
default:
return false;
}
}
const impq & ub(unsigned j) const {
lean_assert(upper_bound_is_available(j));
return m_bp.get_upper_bound(j);
}
const impq & lb(unsigned j) const {
lean_assert(low_bound_is_available(j));
return m_bp.get_low_bound(j);
}
const mpq & monoid_max_no_mult(bool a_is_pos, unsigned j, bool & strict) const {
if (a_is_pos) {
strict = !is_zero(ub(j).y);
return ub(j).x;
}
strict = !is_zero(lb(j).y);
return lb(j).x;
}
mpq monoid_max(const mpq & a, unsigned j) const {
if (is_pos(a)) {
return a * ub(j).x;
}
return a * lb(j).x;
}
mpq monoid_max(const mpq & a, unsigned j, bool & strict) const {
if (is_pos(a)) {
strict = !is_zero(ub(j).y);
return a * ub(j).x;
}
strict = !is_zero(lb(j).y);
return a * lb(j).x;
}
const mpq & monoid_min_no_mult(bool a_is_pos, unsigned j, bool & strict) const {
if (!a_is_pos) {
strict = !is_zero(ub(j).y);
return ub(j).x;
}
strict = !is_zero(lb(j).y);
return lb(j).x;
}
mpq monoid_min(const mpq & a, unsigned j, bool& strict) const {
if (is_neg(a)) {
strict = !is_zero(ub(j).y);
return a * ub(j).x;
}
strict = !is_zero(lb(j).y);
return a * lb(j).x;
}
mpq monoid_min(const mpq & a, unsigned j) const {
if (is_neg(a)) {
return a * ub(j).x;
}
return a * lb(j).x;
}
void limit_all_monoids_from_above() {
int strict = 0;
mpq total;
lean_assert(is_zero(total));
m_it.reset();
mpq a; unsigned j;
while (m_it.next(a, j)) {
bool str;
total -= monoid_min(a, j, str);
if (str)
strict++;
}
m_it.reset();
while (m_it.next(a, j)) {
bool str;
bool a_is_pos = is_pos(a);
mpq bound = total / a + monoid_min_no_mult(a_is_pos, j, str);
if (a_is_pos) {
limit_j(j, bound, true, false, strict - static_cast<int>(str) > 0);
}
else {
limit_j(j, bound, false, true, strict - static_cast<int>(str) > 0);
}
}
}
void limit_all_monoids_from_below() {
int strict = 0;
mpq total;
lean_assert(is_zero(total));
m_it.reset();
mpq a; unsigned j;
while (m_it.next(a, j)) {
bool str;
total -= monoid_max(a, j, str);
if (str)
strict++;
}
m_it.reset();
while (m_it.next(a, j)) {
bool str;
bool a_is_pos = is_pos(a);
mpq bound = total / a + monoid_max_no_mult(a_is_pos, j, str);
bool astrict = strict - static_cast<int>(str) > 0;
if (a_is_pos) {
limit_j(j, bound, true, true, astrict);
}
else {
limit_j(j, bound, false, false, astrict);
}
}
}
void limit_monoid_u_from_below() {
// we are going to limit from below the monoid m_column_of_u,
// every other monoid is impossible to limit from below
mpq u_coeff, a;
unsigned j;
mpq bound = -m_rs.x;
m_it.reset();
bool strict = false;
while (m_it.next(a, j)) {
if (j == static_cast<unsigned>(m_column_of_u)) {
u_coeff = a;
continue;
}
bool str;
bound -= monoid_max(a, j, str);
if (str)
strict = true;
}
bound /= u_coeff;
if (numeric_traits<impq>::is_pos(u_coeff)) {
limit_j(m_column_of_u, bound, true, true, strict);
} else {
limit_j(m_column_of_u, bound, false, false, strict);
}
}
void limit_monoid_l_from_above() {
// we are going to limit from above the monoid m_column_of_l,
// every other monoid is impossible to limit from above
mpq l_coeff, a;
unsigned j;
mpq bound = -m_rs.x;
bool strict = false;
m_it.reset();
while (m_it.next(a, j)) {
if (j == static_cast<unsigned>(m_column_of_l)) {
l_coeff = a;
continue;
}
bool str;
bound -= monoid_min(a, j, str);
if (str)
strict = true;
}
bound /= l_coeff;
if (is_pos(l_coeff)) {
limit_j(m_column_of_l, bound, true, false, strict);
} else {
limit_j(m_column_of_l, bound, false, true, strict);
}
}
// // it is the coefficent before the bounded column
// void provide_evidence(bool coeff_is_pos) {
// /*
// auto & be = m_ibounds.back();
// bool low_bound = be.m_low_bound;
// if (!coeff_is_pos)
// low_bound = !low_bound;
// auto it = m_it.clone();
// mpq a; unsigned j;
// while (it->next(a, j)) {
// if (be.m_j == j) continue;
// lean_assert(bound_is_available(j, is_neg(a) ? low_bound : !low_bound));
// be.m_vector_of_bound_signatures.emplace_back(a, j, numeric_traits<impq>::
// is_neg(a)? low_bound: !low_bound);
// }
// delete it;
// */
// }
void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_low_bound, bool strict){
m_bp.try_add_bound(u, j, is_low_bound, coeff_before_j_is_pos, m_row_or_term_index, strict);
}
void advance_u(unsigned j) {
if (m_column_of_u == -1)
m_column_of_u = j;
else
m_column_of_u = -2;
}
void advance_l(unsigned j) {
if (m_column_of_l == -1)
m_column_of_l = j;
else
m_column_of_l = -2;
}
void analyze_bound_on_var_on_coeff(int j, const mpq &a) {
switch (m_bp.get_column_type(j)) {
case column_type::low_bound:
if (numeric_traits<mpq>::is_pos(a))
advance_u(j);
else
advance_l(j);
break;
case column_type::upper_bound:
if(numeric_traits<mpq>::is_neg(a))
advance_u(j);
else
advance_l(j);
break;
case column_type::free_column:
advance_u(j);
advance_l(j);
break;
default:
break;
}
}
static void analyze_row(linear_combination_iterator<mpq> &it,
const numeric_pair<mpq>& rs,
unsigned row_or_term_index,
bound_propagator & bp
) {
bound_analyzer_on_row a(it, rs, row_or_term_index, bp);
a.analyze();
}
};
}

View file

@ -0,0 +1,47 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/lar_solver.h"
namespace lean {
bound_propagator::bound_propagator(lar_solver & ls):
m_lar_solver(ls) {}
column_type bound_propagator::get_column_type(unsigned j) const {
return m_lar_solver.m_mpq_lar_core_solver.m_column_types()[j];
}
const impq & bound_propagator::get_low_bound(unsigned j) const {
return m_lar_solver.m_mpq_lar_core_solver.m_r_low_bounds()[j];
}
const impq & bound_propagator::get_upper_bound(unsigned j) const {
return m_lar_solver.m_mpq_lar_core_solver.m_r_upper_bounds()[j];
}
void bound_propagator::try_add_bound(const mpq & v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) {
j = m_lar_solver.adjust_column_index_to_term_index(j);
lconstraint_kind kind = is_low? GE : LE;
if (strict)
kind = static_cast<lconstraint_kind>(kind / 2);
if (!bound_is_interesting(j, kind, v))
return;
unsigned k; // index to ibounds
if (is_low) {
if (try_get_val(m_improved_low_bounds, j, k)) {
auto & found_bound = m_ibounds[k];
if (v > found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict))
found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
} else {
m_improved_low_bounds[j] = m_ibounds.size();
m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
}
} else { // the upper bound case
if (try_get_val(m_improved_upper_bounds, j, k)) {
auto & found_bound = m_ibounds[k];
if (v < found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict))
found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
} else {
m_improved_upper_bounds[j] = m_ibounds.size();
m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
}
}
}
}

View file

@ -0,0 +1,27 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/lp_settings.h"
namespace lean {
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(const 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,
lean::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"; }
};
}

20
src/util/lp/breakpoint.h Normal file
View file

@ -0,0 +1,20 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
namespace lean {
enum breakpoint_type {
low_break, upper_break, fixed_break
};
template <typename X>
struct breakpoint {
unsigned m_j; // the basic column
breakpoint_type m_type;
X m_delta;
breakpoint(){}
breakpoint(unsigned j, X delta, breakpoint_type type):m_j(j), m_type(type), m_delta(delta) {}
};
}

235
src/util/lp/column_info.h Normal file
View file

@ -0,0 +1,235 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include <unordered_map>
#include <string>
#include <algorithm>
#include "util/lp/lp_settings.h"
namespace lean {
inline bool is_valid(unsigned j) { return static_cast<int>(j) >= 0;}
template <typename T>
class column_info {
std::string m_name;
bool m_low_bound_is_set = false;
bool m_low_bound_is_strict = false;
bool m_upper_bound_is_set = false;
bool m_upper_bound_is_strict = false;
T m_low_bound;
T m_upper_bound;
T m_cost = numeric_traits<T>::zero();
T m_fixed_value;
bool m_is_fixed = false;
unsigned m_column_index = static_cast<unsigned>(-1);
public:
bool operator==(const column_info & c) const {
return m_name == c.m_name &&
m_low_bound_is_set == c.m_low_bound_is_set &&
m_low_bound_is_strict == c.m_low_bound_is_strict &&
m_upper_bound_is_set == c.m_upper_bound_is_set&&
m_upper_bound_is_strict == c.m_upper_bound_is_strict&&
(!m_low_bound_is_set || m_low_bound == c.m_low_bound) &&
(!m_upper_bound_is_set || m_upper_bound == c.m_upper_bound) &&
m_cost == c.m_cost&&
m_is_fixed == c.m_is_fixed &&
(!m_is_fixed || m_fixed_value == c.m_fixed_value) &&
m_column_index == c.m_column_index;
}
bool operator!=(const column_info & c) const { return !((*this) == c); }
void set_column_index(unsigned j) {
m_column_index = j;
}
// the default constructor
column_info() {}
column_info(unsigned column_index) : m_column_index(column_index) {
}
column_info(const column_info & ci) {
m_name = ci.m_name;
m_low_bound_is_set = ci.m_low_bound_is_set;
m_low_bound_is_strict = ci.m_low_bound_is_strict;
m_upper_bound_is_set = ci.m_upper_bound_is_set;
m_upper_bound_is_strict = ci.m_upper_bound_is_strict;
m_low_bound = ci.m_low_bound;
m_upper_bound = ci.m_upper_bound;
m_cost = ci.m_cost;
m_fixed_value = ci.m_fixed_value;
m_is_fixed = ci.m_is_fixed;
m_column_index = ci.m_column_index;
}
unsigned get_column_index() const {
return m_column_index;
}
column_type get_column_type() const {
return m_is_fixed? column_type::fixed : (m_low_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::low_bound) : (m_upper_bound_is_set? column_type::upper_bound: column_type::free_column));
}
column_type get_column_type_no_flipping() const {
if (m_is_fixed) {
return column_type::fixed;
}
if (m_low_bound_is_set) {
return m_upper_bound_is_set? column_type::boxed: column_type::low_bound;
}
// we are flipping the bounds!
return m_upper_bound_is_set? column_type::upper_bound
: column_type::free_column;
}
T get_low_bound() const {
lean_assert(m_low_bound_is_set);
return m_low_bound;
}
T get_upper_bound() const {
lean_assert(m_upper_bound_is_set);
return m_upper_bound;
}
bool low_bound_is_set() const {
return m_low_bound_is_set;
}
bool upper_bound_is_set() const {
return m_upper_bound_is_set;
}
T get_shift() {
if (is_fixed()) {
return m_fixed_value;
}
if (is_flipped()){
return m_upper_bound;
}
return m_low_bound_is_set? m_low_bound : numeric_traits<T>::zero();
}
bool is_flipped() {
return m_upper_bound_is_set && !m_low_bound_is_set;
}
bool adjusted_low_bound_is_set() {
return !is_flipped()? low_bound_is_set(): upper_bound_is_set();
}
bool adjusted_upper_bound_is_set() {
return !is_flipped()? upper_bound_is_set(): low_bound_is_set();
}
T get_adjusted_upper_bound() {
return get_upper_bound() - get_low_bound();
}
bool is_fixed() const {
return m_is_fixed;
}
bool is_free() {
return !m_low_bound_is_set && !m_upper_bound_is_set;
}
void set_fixed_value(T v) {
m_is_fixed = true;
m_fixed_value = v;
}
T get_fixed_value() const {
lean_assert(m_is_fixed);
return m_fixed_value;
}
T get_cost() const {
return m_cost;
}
void set_cost(T const & cost) {
m_cost = cost;
}
void set_name(std::string const & s) {
m_name = s;
}
std::string get_name() const {
return m_name;
}
void set_low_bound(T const & l) {
m_low_bound = l;
m_low_bound_is_set = true;
}
void set_upper_bound(T const & l) {
m_upper_bound = l;
m_upper_bound_is_set = true;
}
void unset_low_bound() {
m_low_bound_is_set = false;
}
void unset_upper_bound() {
m_upper_bound_is_set = false;
}
void unset_fixed() {
m_is_fixed = false;
}
bool low_bound_holds(T v) {
return !low_bound_is_set() || v >= m_low_bound -T(0.0000001);
}
bool upper_bound_holds(T v) {
return !upper_bound_is_set() || v <= m_upper_bound + T(0.000001);
}
bool bounds_hold(T v) {
return low_bound_holds(v) && upper_bound_holds(v);
}
bool adjusted_bounds_hold(T v) {
return adjusted_low_bound_holds(v) && adjusted_upper_bound_holds(v);
}
bool adjusted_low_bound_holds(T v) {
return !adjusted_low_bound_is_set() || v >= -T(0.0000001);
}
bool adjusted_upper_bound_holds(T v) {
return !adjusted_upper_bound_is_set() || v <= get_adjusted_upper_bound() + T(0.000001);
}
bool is_infeasible() {
if ((!upper_bound_is_set()) || (!low_bound_is_set()))
return false;
// ok, both bounds are set
bool at_least_one_is_strict = upper_bound_is_strict() || low_bound_is_strict();
if (!at_least_one_is_strict)
return get_upper_bound() < get_low_bound();
// at least on bound is strict
return get_upper_bound() <= get_low_bound(); // the equality is impossible
}
bool low_bound_is_strict() const {
return m_low_bound_is_strict;
}
void set_low_bound_strict(bool val) {
m_low_bound_is_strict = val;
}
bool upper_bound_is_strict() const {
return m_upper_bound_is_strict;
}
void set_upper_bound_strict(bool val) {
m_upper_bound_is_strict = val;
}
};
}

View file

@ -0,0 +1,82 @@
#pragma once
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <string>
#include "util/lp/linear_combination_iterator.h"
namespace lean {
class column_namer {
public:
virtual std::string get_column_name(unsigned j) const = 0;
template <typename T>
void print_linear_iterator(linear_combination_iterator<T>* it, std::ostream & out) const {
vector<std::pair<T, unsigned>> coeff;
T a;
unsigned i;
while (it->next(a, i)) {
coeff.emplace_back(a, i);
}
print_linear_combination_of_column_indices(coeff, out);
}
template <typename T>
void print_linear_iterator_indices_only(linear_combination_iterator<T>* it, std::ostream & out) const {
vector<std::pair<T, unsigned>> coeff;
T a;
unsigned i;
while (it->next(a, i)) {
coeff.emplace_back(a, i);
}
print_linear_combination_of_column_indices_only(coeff, out);
}
template <typename T>
void print_linear_combination_of_column_indices_only(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const {
bool first = true;
for (const auto & it : coeffs) {
auto val = it.first;
if (first) {
first = false;
} else {
if (numeric_traits<T>::is_pos(val)) {
out << " + ";
} else {
out << " - ";
val = -val;
}
}
if (val == -numeric_traits<T>::one())
out << " - ";
else if (val != numeric_traits<T>::one())
out << T_to_string(val);
out << "_" << it.second;
}
}
template <typename T>
void print_linear_combination_of_column_indices(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const {
bool first = true;
for (const auto & it : coeffs) {
auto val = it.first;
if (first) {
first = false;
} else {
if (numeric_traits<T>::is_pos(val)) {
out << " + ";
} else {
out << " - ";
val = -val;
}
}
if (val == -numeric_traits<T>::one())
out << " - ";
else if (val != numeric_traits<T>::one())
out << val;
out << get_column_name(it.second);
}
}
};
}

View file

@ -0,0 +1,118 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include <limits>
#include <string>
#include <algorithm>
#include "util/vector.h"
#include <ostream>
#include "util/lp/lp_settings.h"
#include "util/lp/indexed_vector.h"
namespace lean {
template <typename T, typename X> class lp_core_solver_base; // forward definition
template <typename T, typename X>
class core_solver_pretty_printer {
std::ostream & m_out;
template<typename A> using vector = vector<A>;
typedef std::string string;
lp_core_solver_base<T, X> & m_core_solver;
vector<unsigned> m_column_widths;
vector<vector<string>> m_A;
vector<vector<string>> m_signs;
vector<string> m_costs;
vector<string> m_cost_signs;
vector<string> m_lows; // low bounds
vector<string> m_upps; // upper bounds
vector<string> m_lows_signs;
vector<string> m_upps_signs;
unsigned m_rs_width;
vector<X> m_rs;
unsigned m_title_width;
std::string m_cost_title;
std::string m_basis_heading_title;
std::string m_x_title;
std::string m_low_bounds_title = "low";
std::string m_upp_bounds_title = "upp";
std::string m_exact_norm_title = "exact cn";
std::string m_approx_norm_title = "approx cn";
unsigned ncols() { return m_core_solver.m_A.column_count(); }
unsigned nrows() { return m_core_solver.m_A.row_count(); }
unsigned m_artificial_start = std::numeric_limits<unsigned>::max();
indexed_vector<T> m_w_buff;
indexed_vector<T> m_ed_buff;
vector<T> m_exact_column_norms;
public:
core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver, std::ostream & out);
void init_costs();
~core_solver_pretty_printer();
void init_rs_width();
T current_column_norm();
void init_m_A_and_signs();
void init_column_widths();
void adjust_width_with_low_bound(unsigned column, unsigned & w);
void adjust_width_with_upper_bound(unsigned column, unsigned & w);
void adjust_width_with_bounds(unsigned column, unsigned & w);
void adjust_width_with_basis_heading(unsigned column, unsigned & w) {
w = std::max(w, (unsigned)T_to_string(m_core_solver.m_basis_heading[column]).size());
}
unsigned get_column_width(unsigned column);
unsigned regular_cell_width(unsigned row, unsigned column, std::string name) {
return regular_cell_string(row, column, name).size();
}
std::string regular_cell_string(unsigned row, unsigned column, std::string name);
void set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name);
void print_x();
std::string get_low_bound_string(unsigned j);
std::string get_upp_bound_string(unsigned j);
void print_lows();
void print_upps();
string get_exact_column_norm_string(unsigned col) {
return T_to_string(m_exact_column_norms[col]);
}
void print_exact_norms();
void print_approx_norms();
void print();
void print_basis_heading();
void print_bottom_line() {
m_out << "----------------------" << std::endl;
}
void print_cost();
void print_given_rows(vector<string> & row, vector<string> & signs, X rst);
void print_row(unsigned i);
};
}

View file

@ -0,0 +1,377 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <limits>
#include <string>
#include <algorithm>
#include "util/lp/lp_utils.h"
#include "util/lp/lp_core_solver_base.h"
#include "util/lp/core_solver_pretty_printer.h"
#include "util/lp/numeric_pair.h"
namespace lean {
template <typename T, typename X>
core_solver_pretty_printer<T, X>::core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver, std::ostream & out):
m_out(out),
m_core_solver(core_solver),
m_A(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), "")),
m_signs(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), " ")),
m_costs(ncols(), ""),
m_cost_signs(ncols(), " "),
m_rs(ncols(), zero_of_type<X>()),
m_w_buff(core_solver.m_w),
m_ed_buff(core_solver.m_ed) {
m_column_widths.resize(core_solver.m_A.column_count(), 0),
init_m_A_and_signs();
init_costs();
init_column_widths();
init_rs_width();
m_cost_title = "costs";
m_basis_heading_title = "heading";
m_x_title = "x*";
m_title_width = static_cast<unsigned>(std::max(std::max(m_cost_title.size(), std::max(m_basis_heading_title.size(), m_x_title.size())), m_approx_norm_title.size()));
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_costs() {
if (!m_core_solver.use_tableau()) {
vector<T> local_y(m_core_solver.m_m());
m_core_solver.solve_yB(local_y);
for (unsigned i = 0; i < ncols(); i++) {
if (m_core_solver.m_basis_heading[i] < 0) {
T t = m_core_solver.m_costs[i] - m_core_solver.m_A.dot_product_with_column(local_y, i);
set_coeff(m_costs, m_cost_signs, i, t, m_core_solver.column_name(i));
}
}
} else {
for (unsigned i = 0; i < ncols(); i++) {
if (m_core_solver.m_basis_heading[i] < 0) {
set_coeff(m_costs, m_cost_signs, i, m_core_solver.m_d[i], m_core_solver.column_name(i));
}
}
}
}
template <typename T, typename X> core_solver_pretty_printer<T, X>::~core_solver_pretty_printer() {
m_core_solver.m_w = m_w_buff;
m_core_solver.m_ed = m_ed_buff;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_rs_width() {
m_rs_width = static_cast<unsigned>(T_to_string(m_core_solver.get_cost()).size());
for (unsigned i = 0; i < nrows(); i++) {
unsigned wt = static_cast<unsigned>(T_to_string(m_rs[i]).size());
if (wt > m_rs_width) {
m_rs_width = wt;
}
}
}
template <typename T, typename X> T core_solver_pretty_printer<T, X>::current_column_norm() {
T ret = zero_of_type<T>();
for (auto i : m_core_solver.m_ed.m_index)
ret += m_core_solver.m_ed[i] * m_core_solver.m_ed[i];
return ret;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_m_A_and_signs() {
if (numeric_traits<T>::precise() && m_core_solver.m_settings.use_tableau()) {
for (unsigned column = 0; column < ncols(); column++) {
vector<T> t(nrows(), zero_of_type<T>());
for (const auto & c : m_core_solver.m_A.m_columns[column]){
t[c.m_i] = m_core_solver.m_A.get_val(c);
}
string name = m_core_solver.column_name(column);
for (unsigned row = 0; row < nrows(); row ++) {
set_coeff(
m_A[row],
m_signs[row],
column,
t[row],
name);
m_rs[row] += t[row] * m_core_solver.m_x[column];
}
}
} else {
for (unsigned column = 0; column < ncols(); column++) {
m_core_solver.solve_Bd(column); // puts the result into m_core_solver.m_ed
string name = m_core_solver.column_name(column);
for (unsigned row = 0; row < nrows(); row ++) {
set_coeff(
m_A[row],
m_signs[row],
column,
m_core_solver.m_ed[row],
name);
m_rs[row] += m_core_solver.m_ed[row] * m_core_solver.m_x[column];
}
if (!m_core_solver.use_tableau())
m_exact_column_norms.push_back(current_column_norm() + T(1)); // a conversion missing 1 -> T
}
}
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_column_widths() {
for (unsigned i = 0; i < ncols(); i++) {
m_column_widths[i] = get_column_width(i);
}
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_low_bound(unsigned column, unsigned & w) {
if (!m_core_solver.low_bounds_are_set()) return;
w = std::max(w, (unsigned)T_to_string(m_core_solver.low_bound_value(column)).size());
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_upper_bound(unsigned column, unsigned & w) {
w = std::max(w, (unsigned)T_to_string(m_core_solver.upper_bound_value(column)).size());
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_bounds(unsigned column, unsigned & w) {
switch (m_core_solver.get_column_type(column)) {
case column_type::fixed:
case column_type::boxed:
adjust_width_with_low_bound(column, w);
adjust_width_with_upper_bound(column, w);
break;
case column_type::low_bound:
adjust_width_with_low_bound(column, w);
break;
case column_type::upper_bound:
adjust_width_with_upper_bound(column, w);
break;
case column_type::free_column:
break;
default:
lean_assert(false);
break;
}
}
template <typename T, typename X> unsigned core_solver_pretty_printer<T, X>:: get_column_width(unsigned column) {
unsigned w = static_cast<unsigned>(std::max((size_t)m_costs[column].size(), T_to_string(m_core_solver.m_x[column]).size()));
adjust_width_with_bounds(column, w);
adjust_width_with_basis_heading(column, w);
for (unsigned i = 0; i < nrows(); i++) {
unsigned cellw = static_cast<unsigned>(m_A[i][column].size());
if (cellw > w) {
w = cellw;
}
}
if (!m_core_solver.use_tableau()) {
w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size());
if (m_core_solver.m_column_norms.size() > 0)
w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size());
}
return w;
}
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::regular_cell_string(unsigned row, unsigned /* column */, std::string name) {
T t = fabs(m_core_solver.m_ed[row]);
if ( t == 1) return name;
return T_to_string(t) + name;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name) {
if (numeric_traits<T>::is_zero(t)) {
return;
}
if (col > 0) {
if (t > 0) {
row_signs[col] = "+";
row[col] = t != 1? T_to_string(t) + name : name;
} else {
row_signs[col] = "-";
row[col] = t != -1? T_to_string(-t) + name: name;
}
} else { // col == 0
if (t == -1) {
row[col] = "-" + name;
} else if (t == 1) {
row[col] = name;
} else {
row[col] = T_to_string(t) + name;
}
}
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_x() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - static_cast<int>(m_x_title.size());
m_out << m_x_title;
print_blanks(blanks, m_out);
auto bh = m_core_solver.m_x;
for (unsigned i = 0; i < ncols(); i++) {
string s = T_to_string(bh[i]);
int blanks = m_column_widths[i] - static_cast<int>(s.size());
print_blanks(blanks, m_out);
m_out << s << " "; // the column interval
}
m_out << std::endl;
}
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_low_bound_string(unsigned j) {
switch (m_core_solver.get_column_type(j)){
case column_type::boxed:
case column_type::low_bound:
case column_type::fixed:
if (m_core_solver.low_bounds_are_set())
return T_to_string(m_core_solver.low_bound_value(j));
else
return std::string("0");
break;
default:
return std::string();
}
}
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_upp_bound_string(unsigned j) {
switch (m_core_solver.get_column_type(j)){
case column_type::boxed:
case column_type::upper_bound:
case column_type::fixed:
return T_to_string(m_core_solver.upper_bound_value(j));
break;
default:
return std::string();
}
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_lows() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - static_cast<unsigned>(m_low_bounds_title.size());
m_out << m_low_bounds_title;
print_blanks(blanks, m_out);
for (unsigned i = 0; i < ncols(); i++) {
string s = get_low_bound_string(i);
int blanks = m_column_widths[i] - static_cast<unsigned>(s.size());
print_blanks(blanks, m_out);
m_out << s << " "; // the column interval
}
m_out << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_upps() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - static_cast<unsigned>(m_upp_bounds_title.size());
m_out << m_upp_bounds_title;
print_blanks(blanks, m_out);
for (unsigned i = 0; i < ncols(); i++) {
string s = get_upp_bound_string(i);
int blanks = m_column_widths[i] - static_cast<unsigned>(s.size());
print_blanks(blanks, m_out);
m_out << s << " "; // the column interval
}
m_out << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_exact_norms() {
if (m_core_solver.use_tableau()) return;
int blanks = m_title_width + 1 - static_cast<int>(m_exact_norm_title.size());
m_out << m_exact_norm_title;
print_blanks(blanks, m_out);
for (unsigned i = 0; i < ncols(); i++) {
string s = get_exact_column_norm_string(i);
int blanks = m_column_widths[i] - static_cast<int>(s.size());
print_blanks(blanks, m_out);
m_out << s << " ";
}
m_out << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_approx_norms() {
if (m_core_solver.use_tableau()) return;
int blanks = m_title_width + 1 - static_cast<int>(m_approx_norm_title.size());
m_out << m_approx_norm_title;
print_blanks(blanks, m_out);
for (unsigned i = 0; i < ncols(); i++) {
string s = T_to_string(m_core_solver.m_column_norms[i]);
int blanks = m_column_widths[i] - static_cast<int>(s.size());
print_blanks(blanks, m_out);
m_out << s << " ";
}
m_out << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print() {
for (unsigned i = 0; i < nrows(); i++) {
print_row(i);
}
print_bottom_line();
print_cost();
print_x();
print_basis_heading();
print_lows();
print_upps();
print_exact_norms();
if (m_core_solver.m_column_norms.size() > 0)
print_approx_norms();
m_out << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_basis_heading() {
int blanks = m_title_width + 1 - static_cast<int>(m_basis_heading_title.size());
m_out << m_basis_heading_title;
print_blanks(blanks, m_out);
if (ncols() == 0) {
return;
}
auto bh = m_core_solver.m_basis_heading;
for (unsigned i = 0; i < ncols(); i++) {
string s = T_to_string(bh[i]);
int blanks = m_column_widths[i] - static_cast<unsigned>(s.size());
print_blanks(blanks, m_out);
m_out << s << " "; // the column interval
}
m_out << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_cost() {
int blanks = m_title_width + 1 - static_cast<int>(m_cost_title.size());
m_out << m_cost_title;
print_blanks(blanks, m_out);
print_given_rows(m_costs, m_cost_signs, m_core_solver.get_cost());
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_given_rows(vector<string> & row, vector<string> & signs, X rst) {
for (unsigned col = 0; col < row.size(); col++) {
unsigned width = m_column_widths[col];
string s = row[col];
int number_of_blanks = width - static_cast<unsigned>(s.size());
lean_assert(number_of_blanks >= 0);
print_blanks(number_of_blanks, m_out);
m_out << s << ' ';
if (col < row.size() - 1) {
m_out << signs[col + 1] << ' ';
}
}
m_out << '=';
string rs = T_to_string(rst);
int nb = m_rs_width - static_cast<int>(rs.size());
lean_assert(nb >= 0);
print_blanks(nb + 1, m_out);
m_out << rs << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_row(unsigned i){
print_blanks(m_title_width + 1, m_out);
auto row = m_A[i];
auto sign_row = m_signs[i];
auto rs = m_rs[i];
print_given_rows(row, sign_row, rs);
}
}

View file

@ -0,0 +1,15 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/numeric_pair.h"
#include "util/lp/core_solver_pretty_printer.hpp"
template lean::core_solver_pretty_printer<double, double>::core_solver_pretty_printer(lean::lp_core_solver_base<double, double> &, std::ostream & out);
template void lean::core_solver_pretty_printer<double, double>::print();
template lean::core_solver_pretty_printer<double, double>::~core_solver_pretty_printer();
template lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::core_solver_pretty_printer(lean::lp_core_solver_base<lean::mpq, lean::mpq> &, std::ostream & out);
template void lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::print();
template lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::~core_solver_pretty_printer();
template lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::core_solver_pretty_printer(lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> > &, std::ostream & out);
template lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::~core_solver_pretty_printer();
template void lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::print();

View file

@ -0,0 +1,92 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#ifdef LEAN_DEBUG
#include "util/vector.h"
#include "util/lp/matrix.h"
namespace lean {
// used for debugging purposes only
template <typename T, typename X>
class dense_matrix: public matrix<T, X> {
public:
struct ref {
unsigned m_i;
dense_matrix & m_s;
ref(unsigned i, dense_matrix & s) :m_i(i * s.m_n), m_s(s){}
T & operator[] (unsigned j) {
return m_s.m_values[m_i + j];
}
const T & operator[] (unsigned j) const {
return m_s.m_v[m_i + j];
}
};
ref operator[] (unsigned i) {
return ref(i, *this);
}
unsigned m_m; // number of rows
unsigned m_n; // number of const
vector<T> m_values;
dense_matrix(unsigned m, unsigned n);
dense_matrix operator*=(matrix<T, X> const & a) {
lean_assert(column_count() == a.row_count());
dense_matrix c(row_count(), a.column_count());
for (unsigned i = 0; i < row_count(); i++) {
for (unsigned j = 0; j < a.column_count(); j++) {
T v = numeric_traits<T>::zero();
for (unsigned k = 0; k < a.column_count(); k++) {
v += get_elem(i, k) * a(k, j);
}
c.set_elem(i, j, v);
}
}
*this = c;
return *this;
}
dense_matrix & operator=(matrix<T, X> const & other);
dense_matrix & operator=(dense_matrix const & other);
dense_matrix(matrix<T, X> const * other);
void apply_from_right(T * w);
void apply_from_right(vector <T> & w);
T * apply_from_left_with_different_dims(vector<T> & w);
void apply_from_left(vector<T> & w , lp_settings & ) { apply_from_left(w); }
void apply_from_left(vector<T> & w);
void apply_from_left(X * w, lp_settings & );
void apply_from_left_to_X(vector<X> & w, lp_settings & );
virtual void set_number_of_rows(unsigned /*m*/) {}
virtual void set_number_of_columns(unsigned /*n*/) { }
T get_elem(unsigned i, unsigned j) const { return m_values[i * m_n + j]; }
unsigned row_count() const { return m_m; }
unsigned column_count() const { return m_n; }
void set_elem(unsigned i, unsigned j, const T& val) { m_values[i * m_n + j] = val; }
// This method pivots row i to row i0 by muliplying row i by
// alpha and adding it to row i0.
void pivot_row_to_row(unsigned i, const T& alpha, unsigned i0,
const double & pivot_epsilon);
void swap_columns(unsigned a, unsigned b);
void swap_rows(unsigned a, unsigned b);
void multiply_row_by_constant(unsigned row, T & t);
};
template <typename T, typename X>
dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b);
}
#endif

View file

@ -0,0 +1,186 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/lp_settings.h"
#ifdef LEAN_DEBUG
#include "util/vector.h"
#include "util/lp/numeric_pair.h"
#include "util/lp/dense_matrix.h"
namespace lean {
template <typename T> void print_vector(const vector<T> & t, std::ostream & out);
template <typename T, typename X> dense_matrix<T, X>::dense_matrix(unsigned m, unsigned n) : m_m(m), m_n(n), m_values(m * n, numeric_traits<T>::zero()) {
}
template <typename T, typename X> dense_matrix<T, X>&
dense_matrix<T, X>::operator=(matrix<T, X> const & other){
if ( this == & other)
return *this;
m_values = new T[m_m * m_n];
for (unsigned i = 0; i < m_m; i ++)
for (unsigned j = 0; j < m_n; j++)
m_values[i * m_n + j] = other.get_elem(i, j);
return *this;
}
template <typename T, typename X> dense_matrix<T, X>&
dense_matrix<T, X>::operator=(dense_matrix const & other){
if ( this == & other)
return *this;
m_m = other.m_m;
m_n = other.m_n;
m_values.resize(m_m * m_n);
for (unsigned i = 0; i < m_m; i ++)
for (unsigned j = 0; j < m_n; j++)
m_values[i * m_n + j] = other.get_elem(i, j);
return *this;
}
template <typename T, typename X> dense_matrix<T, X>::dense_matrix(matrix<T, X> const * other) :
m_m(other->row_count()),
m_n(other->column_count()) {
m_values.resize(m_m*m_n);
for (unsigned i = 0; i < m_m; i++)
for (unsigned j = 0; j < m_n; j++)
m_values[i * m_n + j] = other->get_elem(i, j);
}
template <typename T, typename X> void dense_matrix<T, X>::apply_from_right(T * w) {
T * t = new T[m_m];
for (int i = 0; i < m_m; i ++) {
T v = numeric_traits<T>::zero();
for (int j = 0; j < m_m; j++) {
v += w[j]* get_elem(j, i);
}
t[i] = v;
}
for (int i = 0; i < m_m; i++) {
w[i] = t[i];
}
delete [] t;
}
template <typename T, typename X> void dense_matrix<T, X>::apply_from_right(vector <T> & w) {
vector<T> t(m_m, numeric_traits<T>::zero());
for (unsigned i = 0; i < m_m; i ++) {
auto & v = t[i];
for (unsigned j = 0; j < m_m; j++)
v += w[j]* get_elem(j, i);
}
for (unsigned i = 0; i < m_m; i++)
w[i] = t[i];
}
template <typename T, typename X> T* dense_matrix<T, X>::
apply_from_left_with_different_dims(vector<T> & w) {
T * t = new T[m_m];
for (int i = 0; i < m_m; i ++) {
T v = numeric_traits<T>::zero();
for (int j = 0; j < m_n; j++) {
v += w[j]* get_elem(i, j);
}
t[i] = v;
}
return t;
}
template <typename T, typename X> void dense_matrix<T, X>::apply_from_left(vector<T> & w) {
T * t = new T[m_m];
for (unsigned i = 0; i < m_m; i ++) {
T v = numeric_traits<T>::zero();
for (unsigned j = 0; j < m_m; j++) {
v += w[j]* get_elem(i, j);
}
t[i] = v;
}
for (unsigned i = 0; i < m_m; i ++) {
w[i] = t[i];
}
delete [] t;
}
template <typename T, typename X> void dense_matrix<T, X>::apply_from_left(X * w, lp_settings & ) {
T * t = new T[m_m];
for (int i = 0; i < m_m; i ++) {
T v = numeric_traits<T>::zero();
for (int j = 0; j < m_m; j++) {
v += w[j]* get_elem(i, j);
}
t[i] = v;
}
for (int i = 0; i < m_m; i ++) {
w[i] = t[i];
}
delete [] t;
}
template <typename T, typename X> void dense_matrix<T, X>::apply_from_left_to_X(vector<X> & w, lp_settings & ) {
vector<X> t(m_m);
for (int i = 0; i < m_m; i ++) {
X v = zero_of_type<X>();
for (int j = 0; j < m_m; j++) {
v += w[j]* get_elem(i, j);
}
t[i] = v;
}
for (int i = 0; i < m_m; i ++) {
w[i] = t[i];
}
}
// This method pivots row i to row i0 by muliplying row i by
// alpha and adding it to row i0.
template <typename T, typename X> void dense_matrix<T, X>::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0,
const double & pivot_epsilon) {
for (unsigned j = 0; j < m_n; j++) {
m_values[i0 * m_n + j] += m_values[i * m_n + j] * alpha;
if (fabs(m_values[i0 + m_n + j]) < pivot_epsilon) {
m_values[i0 + m_n + j] = numeric_traits<T>::zero();;
}
}
}
template <typename T, typename X> void dense_matrix<T, X>::swap_columns(unsigned a, unsigned b) {
for (unsigned i = 0; i < m_m; i++) {
T t = get_elem(i, a);
set_elem(i, a, get_elem(i, b));
set_elem(i, b, t);
}
}
template <typename T, typename X> void dense_matrix<T, X>::swap_rows(unsigned a, unsigned b) {
for (unsigned i = 0; i < m_n; i++) {
T t = get_elem(a, i);
set_elem(a, i, get_elem(b, i));
set_elem(b, i, t);
}
}
template <typename T, typename X> void dense_matrix<T, X>::multiply_row_by_constant(unsigned row, T & t) {
for (unsigned i = 0; i < m_n; i++) {
set_elem(row, i, t * get_elem(row, i));
}
}
template <typename T, typename X>
dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b){
lean_assert(a.column_count() == b.row_count());
dense_matrix<T, X> ret(a.row_count(), b.column_count());
for (unsigned i = 0; i < ret.m_m; i++)
for (unsigned j = 0; j< ret.m_n; j++) {
T v = numeric_traits<T>::zero();
for (unsigned k = 0; k < a.column_count(); k ++){
v += (a.get_elem(i, k) * b.get_elem(k, j));
}
ret.set_elem(i, j, v);
}
return ret;
}
}
#endif

View file

@ -0,0 +1,24 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/lp_settings.h"
#include "util/lp/dense_matrix.hpp"
#ifdef LEAN_DEBUG
#include "util/vector.h"
template lean::dense_matrix<double, double> lean::operator*<double, double>(lean::matrix<double, double>&, lean::matrix<double, double>&);
template void lean::dense_matrix<double, double>::apply_from_left(vector<double> &);
template lean::dense_matrix<double, double>::dense_matrix(lean::matrix<double, double> const*);
template lean::dense_matrix<double, double>::dense_matrix(unsigned int, unsigned int);
template lean::dense_matrix<double, double>& lean::dense_matrix<double, double>::operator=(lean::dense_matrix<double, double> const&);
template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::dense_matrix(lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> > const*);
template void lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left(vector<lean::mpq>&);
template lean::dense_matrix<lean::mpq, lean::mpq> lean::operator*<lean::mpq, lean::mpq>(lean::matrix<lean::mpq, lean::mpq>&, lean::matrix<lean::mpq, lean::mpq>&);
template lean::dense_matrix<lean::mpq, lean::mpq> & lean::dense_matrix<lean::mpq, lean::mpq>::operator=(lean::dense_matrix<lean::mpq, lean::mpq> const&);
template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::dense_matrix(unsigned int, unsigned int);
template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >& lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::operator=(lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> > const&);
template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> > lean::operator*<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template void lean::dense_matrix<lean::mpq, lean::numeric_pair< lean::mpq> >::apply_from_right( vector< lean::mpq> &);
template void lean::dense_matrix<double,double>::apply_from_right(class vector<double> &);
template void lean::dense_matrix<lean::mpq, lean::mpq>::apply_from_left(vector<lean::mpq>&);
#endif

83
src/util/lp/eta_matrix.h Normal file
View file

@ -0,0 +1,83 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/lp/tail_matrix.h"
#include "util/lp/permutation_matrix.h"
namespace lean {
// This is the sum of a unit matrix and a one-column matrix
template <typename T, typename X>
class eta_matrix
: public tail_matrix<T, X> {
#ifdef LEAN_DEBUG
unsigned m_length;
#endif
unsigned m_column_index;
public:
sparse_vector<T> m_column_vector;
T m_diagonal_element;
#ifdef LEAN_DEBUG
eta_matrix(unsigned column_index, unsigned length):
#else
eta_matrix(unsigned column_index):
#endif
#ifdef LEAN_DEBUG
m_length(length),
#endif
m_column_index(column_index) {}
bool is_dense() const { return false; }
void print(std::ostream & out) {
print_matrix(*this, out);
}
bool is_unit() {
return m_column_vector.size() == 0 && m_diagonal_element == 1;
}
bool set_diagonal_element(T const & diagonal_element) {
m_diagonal_element = diagonal_element;
return !lp_settings::is_eps_small_general(diagonal_element, 1e-12);
}
const T & get_diagonal_element() const {
return m_diagonal_element;
}
void apply_from_left(vector<X> & w, lp_settings & );
template <typename L>
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings);
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
apply_from_left_local(w, settings);
}
void push_back(unsigned row_index, T val ) {
lean_assert(row_index != m_column_index);
m_column_vector.push_back(row_index, val);
}
void apply_from_right(vector<T> & w);
void apply_from_right(indexed_vector<T> & w);
T get_elem(unsigned i, unsigned j) const;
#ifdef LEAN_DEBUG
unsigned row_count() const { return m_length; }
unsigned column_count() const { return m_length; }
void set_number_of_rows(unsigned m) { m_length = m; }
void set_number_of_columns(unsigned n) { m_length = n; }
#endif
void divide_by_diagonal_element() {
m_column_vector.divide(m_diagonal_element);
}
void conjugate_by_permutation(permutation_matrix<T, X> & p);
};
}

136
src/util/lp/eta_matrix.hpp Normal file
View file

@ -0,0 +1,136 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/lp/eta_matrix.h"
namespace lean {
// This is the sum of a unit matrix and a one-column matrix
template <typename T, typename X>
void eta_matrix<T, X>::apply_from_left(vector<X> & w, lp_settings & ) {
auto & w_at_column_index = w[m_column_index];
for (auto & it : m_column_vector.m_data) {
w[it.first] += w_at_column_index * it.second;
}
w_at_column_index /= m_diagonal_element;
}
template <typename T, typename X>
template <typename L>
void eta_matrix<T, X>::
apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
const L w_at_column_index = w[m_column_index];
if (is_zero(w_at_column_index)) return;
if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_column_index] /= m_diagonal_element)) {
w[m_column_index] = zero_of_type<L>();
w.erase_from_index(m_column_index);
}
for (auto & it : m_column_vector.m_data) {
unsigned i = it.first;
if (is_zero(w[i])) {
L v = w[i] = w_at_column_index * it.second;
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
w[i] = zero_of_type<L>();
continue;
}
w.m_index.push_back(i);
} else {
L v = w[i] += w_at_column_index * it.second;
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
w[i] = zero_of_type<L>();
w.erase_from_index(i);
}
}
}
}
template <typename T, typename X>
void eta_matrix<T, X>::apply_from_right(vector<T> & w) {
#ifdef LEAN_DEBUG
// dense_matrix<T, X> deb(*this);
// auto clone_w = clone_vector<T>(w, get_number_of_rows());
// deb.apply_from_right(clone_w);
#endif
T t = w[m_column_index] / m_diagonal_element;
for (auto & it : m_column_vector.m_data) {
t += w[it.first] * it.second;
}
w[m_column_index] = t;
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
// delete clone_w;
#endif
}
template <typename T, typename X>
void eta_matrix<T, X>::apply_from_right(indexed_vector<T> & w) {
if (w.m_index.size() == 0)
return;
#ifdef LEAN_DEBUG
// vector<T> wcopy(w.m_data);
// apply_from_right(wcopy);
#endif
T & t = w[m_column_index];
t /= m_diagonal_element;
bool was_in_index = (!numeric_traits<T>::is_zero(t));
for (auto & it : m_column_vector.m_data) {
t += w[it.first] * it.second;
}
if (numeric_traits<T>::precise() ) {
if (!numeric_traits<T>::is_zero(t)) {
if (!was_in_index)
w.m_index.push_back(m_column_index);
} else {
if (was_in_index)
w.erase_from_index(m_column_index);
}
} else {
if (!lp_settings::is_eps_small_general(t, 1e-14)) {
if (!was_in_index)
w.m_index.push_back(m_column_index);
} else {
if (was_in_index)
w.erase_from_index(m_column_index);
t = zero_of_type<T>();
}
}
#ifdef LEAN_DEBUG
// lean_assert(w.is_OK());
// lean_assert(vectors_are_equal<T>(wcopy, w.m_data));
#endif
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
T eta_matrix<T, X>::get_elem(unsigned i, unsigned j) const {
if (j == m_column_index){
if (i == j) {
return 1 / m_diagonal_element;
}
return m_column_vector[i];
}
return i == j ? numeric_traits<T>::one() : numeric_traits<T>::zero();
}
#endif
template <typename T, typename X>
void eta_matrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & p) {
// this = p * this * p(-1)
#ifdef LEAN_DEBUG
// auto rev = p.get_reverse();
// auto deb = ((*this) * rev);
// deb = p * deb;
#endif
m_column_index = p.get_rev(m_column_index);
for (auto & pair : m_column_vector.m_data) {
pair.first = p.get_rev(pair.first);
}
#ifdef LEAN_DEBUG
// lean_assert(deb == *this);
#endif
}
}

View file

@ -0,0 +1,28 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <memory>
#include "util/vector.h"
#include "util/lp/numeric_pair.h"
#include "util/lp/eta_matrix.hpp"
#ifdef LEAN_DEBUG
template double lean::eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
template lean::mpq lean::eta_matrix<lean::mpq, lean::mpq>::get_elem(unsigned int, unsigned int) const;
template lean::mpq lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::get_elem(unsigned int, unsigned int) const;
#endif
template void lean::eta_matrix<double, double>::apply_from_left(vector<double>&, lean::lp_settings&);
template void lean::eta_matrix<double, double>::apply_from_right(vector<double>&);
template void lean::eta_matrix<double, double>::conjugate_by_permutation(lean::permutation_matrix<double, double>&);
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_left(vector<lean::mpq>&, lean::lp_settings&);
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_right(vector<lean::mpq>&);
template void lean::eta_matrix<lean::mpq, lean::mpq>::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left(vector<lean::numeric_pair<lean::mpq> >&, lean::lp_settings&);
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(vector<lean::mpq>&);
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template void lean::eta_matrix<double, double>::apply_from_left_local<double>(lean::indexed_vector<double>&, lean::lp_settings&);
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_left_local<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left_local<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(lean::indexed_vector<lean::mpq>&);
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_right(lean::indexed_vector<lean::mpq>&);
template void lean::eta_matrix<double, double>::apply_from_right(lean::indexed_vector<double>&);

39
src/util/lp/hash_helper.h Normal file
View file

@ -0,0 +1,39 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include <utility>
#include <functional>
#include "util/numerics/mpq.h"
#ifdef __CLANG__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif
namespace std {
template<>
struct hash<lean::mpq> {
inline size_t operator()(const lean::mpq & v) const {
return v.hash();
}
};
}
template <class T>
inline void hash_combine(std::size_t & seed, const T & v) {
seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
namespace std {
template<typename S, typename T> struct hash<pair<S, T>> {
inline size_t operator()(const pair<S, T> & v) const {
size_t seed = 0;
hash_combine(seed, v.first);
hash_combine(seed, v.second);
return seed;
}
};
}
#ifdef __CLANG__
#pragma clang diagnostic pop
#endif

View file

@ -0,0 +1,42 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/lp_settings.h"
#include "util/lp/lar_constraints.h"
namespace lean {
struct implied_bound {
mpq m_bound;
unsigned m_j; // the column for which the bound has been found
bool m_is_low_bound;
bool m_coeff_before_j_is_pos;
unsigned m_row_or_term_index;
bool m_strict;
lconstraint_kind kind() const {
lconstraint_kind k = m_is_low_bound? GE : LE;
if (m_strict)
k = static_cast<lconstraint_kind>(k / 2);
return k;
}
bool operator==(const implied_bound & o) const {
return m_j == o.m_j && m_is_low_bound == o.m_is_low_bound && m_bound == o.m_bound &&
m_coeff_before_j_is_pos == o.m_coeff_before_j_is_pos &&
m_row_or_term_index == o.m_row_or_term_index && m_strict == o.m_strict;
}
implied_bound(){}
implied_bound(const mpq & a,
unsigned j,
bool low_bound,
bool coeff_before_j_is_pos,
unsigned row_or_term_index,
bool strict):
m_bound(a),
m_j(j),
m_is_low_bound(low_bound),
m_coeff_before_j_is_pos(coeff_before_j_is_pos),
m_row_or_term_index(row_or_term_index),
m_strict(strict) {}
};
}

View file

@ -0,0 +1,55 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
namespace lean {
template <typename T>
class indexed_value {
public:
T m_value;
// the idea is that m_index for a row element gives its column, and for a column element its row
unsigned m_index;
// m_other point is the offset of the corresponding element in its vector : for a row element it point to the column element offset,
// for a column element it points to the row element offset
unsigned m_other;
indexed_value() {}
indexed_value(T v, unsigned i) : m_value(v), m_index(i) {}
indexed_value(T v, unsigned i, unsigned other) :
m_value(v), m_index(i), m_other(other) {
}
indexed_value(const indexed_value & iv) {
m_value = iv.m_value;
m_index = iv.m_index;
m_other = iv.m_other;
}
indexed_value & operator=(const indexed_value & right_side) {
m_value = right_side.m_value;
m_index = right_side.m_index;
m_other = right_side.m_other;
return *this;
}
const T & value() const {
return m_value;
}
void set_value(T val) {
m_value = val;
}
};
#ifdef LEAN_DEBUG
template <typename X>
bool check_vector_for_small_values(indexed_vector<X> & w, lp_settings & settings) {
for (unsigned i : w.m_index) {
const X & v = w[i];
if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v))
return false;
}
return true;
}
#endif
}

View file

@ -0,0 +1,169 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/debug.h"
#include <string>
#include <iomanip>
#include "util/lp/lp_utils.h"
#include "util/lp/lp_settings.h"
#include <unordered_set>
namespace lean {
template <typename T> void print_vector(const vector<T> & t, std::ostream & out);
template <typename T> void print_vector(const buffer<T> & t, std::ostream & out);
template <typename T> void print_sparse_vector(const vector<T> & t, std::ostream & out);
void print_vector(const vector<mpq> & t, std::ostream & out);
template <typename T>
class indexed_vector {
public:
// m_index points to non-zero elements of m_data
vector<T> m_data;
vector<unsigned> m_index;
indexed_vector(unsigned data_size) {
m_data.resize(data_size, numeric_traits<T>::zero());
}
indexed_vector& operator=(const indexed_vector<T>& y) {
for (unsigned i: m_index)
m_data[i] = zero_of_type<T>();
m_index = y.m_index;
m_data.resize(y.data_size());
for (unsigned i : m_index)
m_data[i] = y[i];
return *this;
}
bool operator==(const indexed_vector<T>& y) const {
std::unordered_set<unsigned> y_index;
for (unsigned i : y.m_index)
y_index.insert(i);
std::unordered_set<unsigned> this_index;
for (unsigned i : m_index)
this_index.insert(i);
for (unsigned i : y.m_index) {
if (this_index.find(i) == this_index.end())
return false;
}
for (unsigned i : m_index) {
if (y_index.find(i) == y_index.end())
return false;
}
return vectors_are_equal(m_data, m_data);
}
indexed_vector() {}
void resize(unsigned data_size);
unsigned data_size() const {
return m_data.size();
}
unsigned size() {
return m_index.size();
}
void set_value(const T& value, unsigned index);
void set_value_as_in_dictionary(unsigned index) {
lean_assert(index < m_data.size());
T & loc = m_data[index];
if (is_zero(loc)) {
m_index.push_back(index);
loc = one_of_type<T>(); // use as a characteristic function
}
}
void clear();
void clear_all();
const T& operator[] (unsigned i) const {
return m_data[i];
}
T& operator[] (unsigned i) {
return m_data[i];
}
void clean_up() {
#if 0==1
for (unsigned k = 0; k < m_index.size(); k++) {
unsigned i = m_index[k];
T & v = m_data[i];
if (lp_settings::is_eps_small_general(v, 1e-14)) {
v = zero_of_type<T>();
m_index.erase(m_index.begin() + k--);
}
}
#endif
vector<unsigned> index_copy;
for (unsigned i : m_index) {
T & v = m_data[i];
if (!lp_settings::is_eps_small_general(v, 1e-14)) {
index_copy.push_back(i);
} else if (!numeric_traits<T>::is_zero(v)) {
v = zero_of_type<T>();
}
}
m_index = index_copy;
}
void erase_from_index(unsigned j);
void add_value_at_index_with_drop_tolerance(unsigned j, const T& val_to_add) {
T & v = m_data[j];
bool was_zero = is_zero(v);
v += val_to_add;
if (lp_settings::is_eps_small_general(v, 1e-14)) {
v = zero_of_type<T>();
if (!was_zero) {
erase_from_index(j);
}
} else {
if (was_zero)
m_index.push_back(j);
}
}
void add_value_at_index(unsigned j, const T& val_to_add) {
T & v = m_data[j];
bool was_zero = is_zero(v);
v += val_to_add;
if (is_zero(v)) {
if (!was_zero)
erase_from_index(j);
} else {
if (was_zero)
m_index.push_back(j);
}
}
void restore_index_and_clean_from_data() {
m_index.resize(0);
for (unsigned i = 0; i < m_data.size(); i++) {
T & v = m_data[i];
if (lp_settings::is_eps_small_general(v, 1e-14)) {
v = zero_of_type<T>();
} else {
m_index.push_back(i);
}
}
}
#ifdef LEAN_DEBUG
bool is_OK() const;
void print(std::ostream & out);
#endif
};
}

View file

@ -0,0 +1,110 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/vector.h"
#include "util/lp/indexed_vector.h"
#include "util/lp/lp_settings.h"
namespace lean {
template <typename T>
void print_vector(const vector<T> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++)
out << t[i] << " ";
out << std::endl;
}
template <typename T>
void print_vector(const buffer<T> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++)
out << t[i] << " ";
out << std::endl;
}
template <typename T>
void print_sparse_vector(const vector<T> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++) {
if (is_zero(t[i]))continue;
out << "[" << i << "] = " << t[i] << ", ";
}
out << std::endl;
}
void print_vector(const vector<mpq> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++)
out << t[i].get_double() << std::setprecision(3) << " ";
out << std::endl;
}
template <typename T>
void indexed_vector<T>::resize(unsigned data_size) {
clear();
m_data.resize(data_size, numeric_traits<T>::zero());
lean_assert(is_OK());
}
template <typename T>
void indexed_vector<T>::set_value(const T& value, unsigned index) {
m_data[index] = value;
lean_assert(std::find(m_index.begin(), m_index.end(), index) == m_index.end());
m_index.push_back(index);
}
template <typename T>
void indexed_vector<T>::clear() {
for (unsigned i : m_index)
m_data[i] = numeric_traits<T>::zero();
m_index.resize(0);
}
template <typename T>
void indexed_vector<T>::clear_all() {
unsigned i = m_data.size();
while (i--) m_data[i] = numeric_traits<T>::zero();
m_index.resize(0);
}
template <typename T>
void indexed_vector<T>::erase_from_index(unsigned j) {
auto it = std::find(m_index.begin(), m_index.end(), j);
if (it != m_index.end())
m_index.erase(it);
}
#ifdef LEAN_DEBUG
template <typename T>
bool indexed_vector<T>::is_OK() const {
return true;
const double drop_eps = 1e-14;
for (unsigned i = 0; i < m_data.size(); i++) {
if (!is_zero(m_data[i]) && lp_settings::is_eps_small_general(m_data[i], drop_eps)) {
return false;
}
if (lp_settings::is_eps_small_general(m_data[i], drop_eps) != (std::find(m_index.begin(), m_index.end(), i) == m_index.end())) {
return false;
}
}
std::unordered_set<unsigned> s;
for (unsigned i : m_index) {
//no duplicates!!!
if (s.find(i) != s.end())
return false;
s.insert(i);
if (i >= m_data.size())
return false;
}
return true;
}
template <typename T>
void indexed_vector<T>::print(std::ostream & out) {
out << "m_index " << std::endl;
for (unsigned i = 0; i < m_index.size(); i++) {
out << m_index[i] << " ";
}
out << std::endl;
print_vector(m_data, out);
}
#endif
}

View file

@ -0,0 +1,36 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/vector.h"
#include "util/lp/indexed_vector.hpp"
namespace lean {
template void indexed_vector<double>::clear();
template void indexed_vector<double>::clear_all();
template void indexed_vector<double>::erase_from_index(unsigned int);
template void indexed_vector<double>::set_value(const double&, unsigned int);
template void indexed_vector<mpq>::clear();
template void indexed_vector<unsigned>::clear();
template void indexed_vector<mpq>::clear_all();
template void indexed_vector<mpq>::erase_from_index(unsigned int);
template void indexed_vector<mpq>::resize(unsigned int);
template void indexed_vector<unsigned>::resize(unsigned int);
template void indexed_vector<mpq>::set_value(const mpq&, unsigned int);
template void indexed_vector<unsigned>::set_value(const unsigned&, unsigned int);
#ifdef LEAN_DEBUG
template bool indexed_vector<double>::is_OK() const;
template bool indexed_vector<mpq>::is_OK() const;
template bool indexed_vector<lean::numeric_pair<mpq> >::is_OK() const;
template void lean::indexed_vector< lean::mpq>::print(std::basic_ostream<char,struct std::char_traits<char> > &);
template void lean::indexed_vector<double>::print(std::basic_ostream<char,struct std::char_traits<char> > &);
template void lean::indexed_vector<lean::numeric_pair<lean::mpq> >::print(std::ostream&);
#endif
}
template void lean::print_vector<double>(vector<double> const&, std::ostream&);
template void lean::print_vector<unsigned int>(vector<unsigned int> const&, std::ostream&);
template void lean::print_vector<std::string>(vector<std::string> const&, std::ostream&);
template void lean::print_vector<lean::numeric_pair<lean::mpq> >(vector<lean::numeric_pair<lean::mpq>> const&, std::ostream&);
template void lean::indexed_vector<double>::resize(unsigned int);
template void lean::print_vector< lean::mpq>(vector< lean::mpq> const &, std::basic_ostream<char, std::char_traits<char> > &);
template void lean::print_vector<std::pair<lean::mpq, unsigned int> >(vector<std::pair<lean::mpq, unsigned int>> const&, std::ostream&);
template void lean::indexed_vector<lean::numeric_pair<lean::mpq> >::erase_from_index(unsigned int);

66
src/util/lp/int_set.h Normal file
View file

@ -0,0 +1,66 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/lp/indexed_vector.h"
#include <ostream>
namespace lean {
// serves at a set of non-negative integers smaller than the set size
class int_set {
vector<int> m_data;
public:
vector<int> m_index;
int_set(unsigned size): m_data(size, -1) {}
int_set() {}
bool contains(unsigned j) const {
if (j >= m_data.size())
return false;
return m_data[j] >= 0;
}
void insert(unsigned j) {
lean_assert(j < m_data.size());
if (contains(j)) return;
m_data[j] = m_index.size();
m_index.push_back(j);
}
void erase(unsigned j) {
if (!contains(j)) return;
unsigned pos_j = m_data[j];
unsigned last_pos = m_index.size() - 1;
int last_j = m_index[last_pos];
if (last_pos != pos_j) {
// move last to j spot
m_data[last_j] = pos_j;
m_index[pos_j] = last_j;
}
m_index.pop_back();
m_data[j] = -1;
}
void resize(unsigned size) {
m_data.resize(size, -1);
}
void increase_size_by_one() {
resize(m_data.size() + 1);
}
unsigned data_size() const { return m_data.size(); }
unsigned size() const { return m_index.size();}
bool is_empty() const { return size() == 0; }
void clear() {
for (unsigned j : m_index)
m_data[j] = -1;
m_index.resize(0);
}
void print(std::ostream & out ) const {
for (unsigned j : m_index) {
out << j << " ";
}
out << std::endl;
}
};
}

View file

@ -0,0 +1,50 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/linear_combination_iterator.h"
#include "util/lp/static_matrix.h"
#include "util/lp/lar_term.h"
namespace lean {
template <typename T, typename X>
struct iterator_on_column:linear_combination_iterator<T> {
const vector<column_cell>& m_column; // the offset in term coeffs
const static_matrix<T, X> & m_A;
int m_i = -1; // the initial offset in the column
unsigned size() const { return m_column.size(); }
iterator_on_column(const vector<column_cell>& column, const static_matrix<T,X> & A) // the offset in term coeffs
:
m_column(column),
m_A(A),
m_i(-1) {}
bool next(mpq & a, unsigned & i) {
if (++m_i >= static_cast<int>(m_column.size()))
return false;
const column_cell& c = m_column[m_i];
a = m_A.get_val(c);
i = c.m_i;
return true;
}
bool next(unsigned & i) {
if (++m_i >= static_cast<int>(m_column.size()))
return false;
const column_cell& c = m_column[m_i];
i = c.m_i;
return true;
}
void reset() {
m_i = -1;
}
linear_combination_iterator<mpq> * clone() {
iterator_on_column * r = new iterator_on_column(m_column, m_A);
return r;
}
};
}

View file

@ -0,0 +1,35 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/linear_combination_iterator.h"
namespace lean {
template <typename T>
struct iterator_on_indexed_vector:linear_combination_iterator<T> {
const indexed_vector<T> & m_v;
unsigned m_offset = 0;
iterator_on_indexed_vector(const indexed_vector<T> & v) : m_v(v){}
unsigned size() const { return m_v.m_index.size(); }
bool next(T & a, unsigned & i) {
if (m_offset >= m_v.m_index.size())
return false;
i = m_v.m_index[m_offset++];
a = m_v.m_data[i];
return true;
}
bool next(unsigned & i) {
if (m_offset >= m_v.m_index.size())
return false;
i = m_v.m_index[m_offset++];
return true;
}
void reset() {
m_offset = 0;
}
linear_combination_iterator<T>* clone() {
return new iterator_on_indexed_vector(m_v);
}
};
}

View file

@ -0,0 +1,42 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/iterator_on_indexed_vector.h"
namespace lean {
template <typename T>
struct iterator_on_pivot_row:linear_combination_iterator<T> {
bool m_basis_returned = false;
const indexed_vector<T> & m_v;
unsigned m_basis_j;
iterator_on_indexed_vector<T> m_it;
unsigned size() const { return m_it.size(); }
iterator_on_pivot_row(const indexed_vector<T> & v, unsigned basis_j) : m_v(v), m_basis_j(basis_j), m_it(v) {}
bool next(T & a, unsigned & i) {
if (m_basis_returned == false) {
m_basis_returned = true;
a = one_of_type<T>();
i = m_basis_j;
return true;
}
return m_it.next(a, i);
}
bool next(unsigned & i) {
if (m_basis_returned == false) {
m_basis_returned = true;
i = m_basis_j;
return true;
}
return m_it.next(i);
}
void reset() {
m_basis_returned = false;
m_it.reset();
}
linear_combination_iterator<T> * clone() {
iterator_on_pivot_row * r = new iterator_on_pivot_row(m_v, m_basis_j);
return r;
}
};
}

View file

@ -0,0 +1,37 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/linear_combination_iterator.h"
namespace lean {
template <typename T>
struct iterator_on_row:linear_combination_iterator<T> {
const vector<row_cell<T>> & m_row;
unsigned m_i= 0; // offset
iterator_on_row(const vector<row_cell<T>> & row) : m_row(row)
{}
unsigned size() const { return m_row.size(); }
bool next(T & a, unsigned & i) {
if (m_i == m_row.size())
return false;
auto &c = m_row[m_i++];
i = c.m_j;
a = c.get_val();
return true;
}
bool next(unsigned & i) {
if (m_i == m_row.size())
return false;
auto &c = m_row[m_i++];
i = c.m_j;
return true;
}
void reset() {
m_i = 0;
}
linear_combination_iterator<T>* clone() {
return new iterator_on_row(m_row);
}
};
}

View file

@ -0,0 +1,56 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/linear_combination_iterator.h"
#include "util/lp/numeric_pair.h"
#include "util/lp/lar_term.h"
namespace lean {
struct iterator_on_term_with_basis_var:linear_combination_iterator<mpq> {
std::unordered_map<unsigned, mpq>::const_iterator m_i; // the offset in term coeffs
bool m_term_j_returned = false;
const lar_term & m_term;
unsigned m_term_j;
unsigned size() const {return static_cast<unsigned>(m_term.m_coeffs.size() + 1);}
iterator_on_term_with_basis_var(const lar_term & t, unsigned term_j) :
m_i(t.m_coeffs.begin()),
m_term(t),
m_term_j(term_j) {}
bool next(mpq & a, unsigned & i) {
if (m_term_j_returned == false) {
m_term_j_returned = true;
a = - one_of_type<mpq>();
i = m_term_j;
return true;
}
if (m_i == m_term.m_coeffs.end())
return false;
i = m_i->first;
a = m_i->second;
m_i++;
return true;
}
bool next(unsigned & i) {
if (m_term_j_returned == false) {
m_term_j_returned = true;
i = m_term_j;
return true;
}
if (m_i == m_term.m_coeffs.end())
return false;
i = m_i->first;
m_i++;
return true;
}
void reset() {
m_term_j_returned = false;
m_i = m_term.m_coeffs.begin();
}
linear_combination_iterator<mpq> * clone() {
iterator_on_term_with_basis_var * r = new iterator_on_term_with_basis_var(m_term, m_term_j);
return r;
}
};
}

View file

@ -0,0 +1,86 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include <utility>
#include <unordered_map>
#include <string>
#include <algorithm>
#include "util/lp/lp_utils.h"
#include "util/lp/ul_pair.h"
#include "util/lp/lar_term.h"
namespace lean {
inline lconstraint_kind flip_kind(lconstraint_kind t) {
return static_cast<lconstraint_kind>( - static_cast<int>(t));
}
inline std::string lconstraint_kind_string(lconstraint_kind t) {
switch (t) {
case LE: return std::string("<=");
case LT: return std::string("<");
case GE: return std::string(">=");
case GT: return std::string(">");
case EQ: return std::string("=");
}
lean_unreachable();
return std::string(); // it is unreachable
}
class lar_base_constraint {
public:
lconstraint_kind m_kind;
mpq m_right_side;
virtual vector<std::pair<mpq, var_index>> get_left_side_coefficients() const = 0;
lar_base_constraint() {}
lar_base_constraint(lconstraint_kind kind, const mpq& right_side) :m_kind(kind), m_right_side(right_side) {}
virtual unsigned size() const = 0;
virtual ~lar_base_constraint(){}
virtual mpq get_free_coeff_of_left_side() const { return zero_of_type<mpq>();}
};
struct lar_var_constraint: public lar_base_constraint {
unsigned m_j;
vector<std::pair<mpq, var_index>> get_left_side_coefficients() const {
vector<std::pair<mpq, var_index>> ret;
ret.push_back(std::make_pair(one_of_type<mpq>(), m_j));
return ret;
}
unsigned size() const { return 1;}
lar_var_constraint(unsigned j, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_j(j) { }
};
struct lar_term_constraint: public lar_base_constraint {
const lar_term * m_term;
vector<std::pair<mpq, var_index>> get_left_side_coefficients() const {
return m_term->coeffs_as_vector();
}
unsigned size() const { return m_term->size();}
lar_term_constraint(const lar_term *t, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_term(t) { }
virtual mpq get_free_coeff_of_left_side() const { return m_term->m_v;}
};
class lar_constraint : public lar_base_constraint {
public:
vector<std::pair<mpq, var_index>> m_coeffs;
lar_constraint() {}
lar_constraint(const vector<std::pair<mpq, var_index>> & left_side, lconstraint_kind kind, const mpq & right_side)
: lar_base_constraint(kind, right_side), m_coeffs(left_side) {}
lar_constraint(const lar_base_constraint & c) {
lean_assert(false); // should not be called : todo!
}
unsigned size() const {
return static_cast<unsigned>(m_coeffs.size());
}
vector<std::pair<mpq, var_index>> get_left_side_coefficients() const { return m_coeffs; }
};
}

View file

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

View file

@ -0,0 +1,292 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <string>
#include "util/vector.h"
#include "util/lp/lar_core_solver.h"
#include "util/lp/lar_solution_signature.h"
namespace lean {
lar_core_solver::lar_core_solver(
lp_settings & settings,
const column_namer & column_names
):
m_r_solver(m_r_A,
m_right_sides_dummy,
m_r_x,
m_r_basis,
m_r_nbasis,
m_r_heading,
m_costs_dummy,
m_column_types(),
m_r_low_bounds(),
m_r_upper_bounds(),
settings,
column_names),
m_d_solver(m_d_A,
m_d_right_sides_dummy,
m_d_x,
m_d_basis,
m_d_nbasis,
m_d_heading,
m_d_costs_dummy,
m_column_types(),
m_d_low_bounds,
m_d_upper_bounds,
settings,
column_names){}
void lar_core_solver::init_costs(bool first_time) {
lean_assert(false); // should not be called
// lean_assert(this->m_x.size() >= this->m_n());
// lean_assert(this->m_column_types.size() >= this->m_n());
// if (first_time)
// this->m_costs.resize(this->m_n());
// X inf = this->m_infeasibility;
// this->m_infeasibility = zero_of_type<X>();
// for (unsigned j = this->m_n(); j--;)
// init_cost_for_column(j);
// if (!(first_time || inf >= this->m_infeasibility)) {
// LP_OUT(this->m_settings, "iter = " << this->total_iterations() << std::endl);
// LP_OUT(this->m_settings, "inf was " << T_to_string(inf) << " and now " << T_to_string(this->m_infeasibility) << std::endl);
// lean_assert(false);
// }
// if (inf == this->m_infeasibility)
// this->m_iters_with_no_cost_growing++;
}
void lar_core_solver::init_cost_for_column(unsigned j) {
/*
// If j is a breakpoint column, then we set the cost zero.
// When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function
const numeric_pair<mpq> & x = this->m_x[j];
// set zero cost for each non-basis column
if (this->m_basis_heading[j] < 0) {
this->m_costs[j] = numeric_traits<T>::zero();
return;
}
// j is a basis column
switch (this->m_column_types[j]) {
case fixed:
case column_type::boxed:
if (x > this->m_upper_bounds[j]) {
this->m_costs[j] = 1;
this->m_infeasibility += x - this->m_upper_bounds[j];
} else if (x < this->m_low_bounds[j]) {
this->m_infeasibility += this->m_low_bounds[j] - x;
this->m_costs[j] = -1;
} else {
this->m_costs[j] = numeric_traits<T>::zero();
}
break;
case low_bound:
if (x < this->m_low_bounds[j]) {
this->m_costs[j] = -1;
this->m_infeasibility += this->m_low_bounds[j] - x;
} else {
this->m_costs[j] = numeric_traits<T>::zero();
}
break;
case upper_bound:
if (x > this->m_upper_bounds[j]) {
this->m_costs[j] = 1;
this->m_infeasibility += x - this->m_upper_bounds[j];
} else {
this->m_costs[j] = numeric_traits<T>::zero();
}
break;
case free_column:
this->m_costs[j] = numeric_traits<T>::zero();
break;
default:
lean_assert(false);
break;
}*/
}
// returns m_sign_of_alpha_r
int lar_core_solver::column_is_out_of_bounds(unsigned j) {
/*
switch (this->m_column_type[j]) {
case fixed:
case column_type::boxed:
if (this->x_below_low_bound(j)) {
return -1;
}
if (this->x_above_upper_bound(j)) {
return 1;
}
return 0;
case low_bound:
if (this->x_below_low_bound(j)) {
return -1;
}
return 0;
case upper_bound:
if (this->x_above_upper_bound(j)) {
return 1;
}
return 0;
default:
return 0;
break;
}*/
lean_assert(false);
return true;
}
void lar_core_solver::calculate_pivot_row(unsigned i) {
lean_assert(!m_r_solver.use_tableau());
lean_assert(m_r_solver.m_pivot_row.is_OK());
m_r_solver.m_pivot_row_of_B_1.clear();
m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m());
m_r_solver.m_pivot_row.clear();
m_r_solver.m_pivot_row.resize(m_r_solver.m_n());
if (m_r_solver.m_settings.use_tableau()) {
unsigned basis_j = m_r_solver.m_basis[i];
for (auto & c : m_r_solver.m_A.m_rows[i]) {
if (c.m_j != basis_j)
m_r_solver.m_pivot_row.set_value(c.get_val(), c.m_j);
}
return;
}
m_r_solver.calculate_pivot_row_of_B_1(i);
m_r_solver.calculate_pivot_row_when_pivot_row_of_B1_is_ready(i);
}
void lar_core_solver::prefix_r() {
if (!m_r_solver.m_settings.use_tableau()) {
m_r_solver.m_copy_of_xB.resize(m_r_solver.m_n());
m_r_solver.m_ed.resize(m_r_solver.m_m());
m_r_solver.m_pivot_row.resize(m_r_solver.m_n());
m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m());
m_r_solver.m_w.resize(m_r_solver.m_m());
m_r_solver.m_y.resize(m_r_solver.m_m());
m_r_solver.m_rows_nz.resize(m_r_solver.m_m(), 0);
m_r_solver.m_columns_nz.resize(m_r_solver.m_n(), 0);
init_column_row_nz_for_r_solver();
}
m_r_solver.m_b.resize(m_r_solver.m_m());
if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) {
if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search)
m_r_solver.m_breakpoint_indices_queue.resize(m_r_solver.m_n());
m_r_solver.m_costs.resize(m_r_solver.m_n());
m_r_solver.m_d.resize(m_r_solver.m_n());
m_r_solver.m_using_infeas_costs = true;
}
}
void lar_core_solver::prefix_d() {
m_d_solver.m_b.resize(m_d_solver.m_m());
m_d_solver.m_breakpoint_indices_queue.resize(m_d_solver.m_n());
m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n());
m_d_solver.m_costs.resize(m_d_solver.m_n());
m_d_solver.m_d.resize(m_d_solver.m_n());
m_d_solver.m_ed.resize(m_d_solver.m_m());
m_d_solver.m_pivot_row.resize(m_d_solver.m_n());
m_d_solver.m_pivot_row_of_B_1.resize(m_d_solver.m_m());
m_d_solver.m_w.resize(m_d_solver.m_m());
m_d_solver.m_y.resize(m_d_solver.m_m());
m_d_solver.m_steepest_edge_coefficients.resize(m_d_solver.m_n());
m_d_solver.m_column_norms.clear();
m_d_solver.m_column_norms.resize(m_d_solver.m_n(), 2);
m_d_solver.m_inf_set.clear();
m_d_solver.m_inf_set.resize(m_d_solver.m_n());
}
void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() {
lean_assert(m_r_solver.A_mult_x_is_off() == false);
unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau];
m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj);
m_infeasible_linear_combination.clear();
for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) {
m_infeasible_linear_combination.push_back(std::make_pair( rc.get_val(), rc.m_j));
}
}
void lar_core_solver::fill_not_improvable_zero_sum() {
if (m_r_solver.m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) {
fill_not_improvable_zero_sum_from_inf_row();
return;
}
// reusing the existing mechanism for row_feasibility_loop
m_infeasible_sum_sign = m_r_solver.m_settings.use_breakpoints_in_feasibility_search? -1 : 1;
m_infeasible_linear_combination.clear();
for (auto j : m_r_solver.m_basis) {
const mpq & cost_j = m_r_solver.m_costs[j];
if (!numeric_traits<mpq>::is_zero(cost_j)) {
m_infeasible_linear_combination.push_back(std::make_pair(cost_j, j));
}
}
// m_costs are expressed by m_d ( additional costs), substructing the latter gives 0
for (unsigned j = 0; j < m_r_solver.m_n(); j++) {
if (m_r_solver.m_basis_heading[j] >= 0) continue;
const mpq & d_j = m_r_solver.m_d[j];
if (!numeric_traits<mpq>::is_zero(d_j)) {
m_infeasible_linear_combination.push_back(std::make_pair(-d_j, j));
}
}
}
void lar_core_solver::solve() {
lean_assert(m_r_solver.non_basic_columns_are_set_correctly());
lean_assert(m_r_solver.inf_set_is_correct());
if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) {
m_r_solver.set_status(OPTIMAL);
return;
}
++settings().st().m_need_to_solve_inf;
lean_assert(!m_r_solver.A_mult_x_is_off());
lean_assert((!settings().use_tableau()) || r_basis_is_OK());
if (need_to_presolve_with_double_solver()) {
prefix_d();
lar_solution_signature solution_signature;
vector<unsigned> changes_of_basis = find_solution_signature_with_doubles(solution_signature);
if (m_d_solver.get_status() == TIME_EXHAUSTED) {
m_r_solver.set_status(TIME_EXHAUSTED);
return;
}
if (settings().use_tableau())
solve_on_signature_tableau(solution_signature, changes_of_basis);
else
solve_on_signature(solution_signature, changes_of_basis);
lean_assert(!settings().use_tableau() || r_basis_is_OK());
} else {
if (!settings().use_tableau()) {
bool snapped = m_r_solver.snap_non_basic_x_to_bound();
lean_assert(m_r_solver.non_basic_columns_are_set_correctly());
if (snapped)
m_r_solver.solve_Ax_eq_b();
}
if (m_r_solver.m_look_for_feasible_solution_only)
m_r_solver.find_feasible_solution();
else
m_r_solver.solve();
lean_assert(!settings().use_tableau() || r_basis_is_OK());
}
if (m_r_solver.get_status() == INFEASIBLE) {
fill_not_improvable_zero_sum();
} else if (m_r_solver.get_status() != UNBOUNDED) {
m_r_solver.set_status(OPTIMAL);
}
lean_assert(r_basis_is_OK());
lean_assert(m_r_solver.non_basic_columns_are_set_correctly());
lean_assert(m_r_solver.inf_set_is_correct());
}
}

View file

@ -0,0 +1,10 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <utility>
#include <memory>
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lar_core_solver.hpp"

View file

@ -0,0 +1,13 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/debug.h"
#include "util/lp/lp_settings.h"
#include <unordered_map>
namespace lean {
typedef std::unordered_map<unsigned, non_basic_column_value_position> lar_solution_signature;
}

2135
src/util/lp/lar_solver.h Normal file

File diff suppressed because it is too large Load diff

64
src/util/lp/lar_term.h Normal file
View file

@ -0,0 +1,64 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/indexed_vector.h"
namespace lean {
struct lar_term {
// the term evaluates to sum of m_coeffs + m_v
std::unordered_map<unsigned, mpq> m_coeffs;
mpq m_v;
lar_term() {}
void add_to_map(unsigned j, const mpq& c) {
auto it = m_coeffs.find(j);
if (it == m_coeffs.end()) {
m_coeffs.emplace(j, c);
} else {
it->second += c;
if (is_zero(it->second))
m_coeffs.erase(it);
}
}
unsigned size() const { return static_cast<unsigned>(m_coeffs.size()); }
const std::unordered_map<unsigned, mpq> & coeffs() const {
return m_coeffs;
}
lar_term(const vector<std::pair<mpq, unsigned>>& coeffs,
const mpq & v) : m_v(v) {
for (const auto & p : coeffs) {
add_to_map(p.second, p.first);
}
}
bool operator==(const lar_term & a) const { return false; } // take care not to create identical terms
bool operator!=(const lar_term & a) const { return ! (*this == a);}
// some terms get used in add constraint
// it is the same as the offset in the m_constraints
vector<std::pair<mpq, unsigned>> coeffs_as_vector() const {
vector<std::pair<mpq, unsigned>> ret;
for (const auto & p : m_coeffs) {
ret.push_back(std::make_pair(p.second, p.first));
}
return ret;
}
// j is the basic variable to substitute
void subst(unsigned j, indexed_vector<mpq> & li) {
auto it = m_coeffs.find(j);
if (it == m_coeffs.end()) return;
const mpq & b = it->second;
for (unsigned it_j :li.m_index) {
add_to_map(it_j, - b * li.m_data[it_j]);
}
m_coeffs.erase(it);
}
bool contains(unsigned j) const {
return m_coeffs.find(j) != m_coeffs.end();
}
};
}

View file

@ -0,0 +1,47 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
namespace lean {
template <typename T>
struct linear_combination_iterator {
virtual bool next(T & a, unsigned & i) = 0;
virtual bool next(unsigned & i) = 0;
virtual void reset() = 0;
virtual linear_combination_iterator * clone() = 0;
virtual ~linear_combination_iterator(){}
virtual unsigned size() const = 0;
};
template <typename T>
struct linear_combination_iterator_on_vector : linear_combination_iterator<T> {
vector<std::pair<T, unsigned>> & m_vector;
int m_offset = 0;
bool next(T & a, unsigned & i) {
if(m_offset >= m_vector.size())
return false;
auto & p = m_vector[m_offset];
a = p.first;
i = p.second;
m_offset++;
return true;
}
bool next(unsigned & i) {
if(m_offset >= m_vector.size())
return false;
auto & p = m_vector[m_offset];
i = p.second;
m_offset++;
return true;
}
void reset() {m_offset = 0;}
linear_combination_iterator<T> * clone() {
return new linear_combination_iterator_on_vector(m_vector);
}
linear_combination_iterator_on_vector(vector<std::pair<T, unsigned>> & vec): m_vector(vec) {}
unsigned size() const { return m_vector.size(); }
};
}

View file

@ -0,0 +1,683 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include <set>
#include "util/vector.h"
#include <string>
#include "util/lp/lp_utils.h"
#include "util/lp/core_solver_pretty_printer.h"
#include "util/lp/numeric_pair.h"
#include "util/lp/static_matrix.h"
#include "util/lp/lu.h"
#include "util/lp/permutation_matrix.h"
#include "util/lp/column_namer.h"
namespace lean {
template <typename T, typename X> // X represents the type of the x variable and the bounds
class lp_core_solver_base {
unsigned m_total_iterations = 0;
unsigned inc_total_iterations() { ++m_settings.st().m_total_iterations; return m_total_iterations++; }
private:
lp_status m_status;
public:
bool current_x_is_feasible() const { return m_inf_set.size() == 0; }
bool current_x_is_infeasible() const { return m_inf_set.size() != 0; }
int_set m_inf_set;
bool m_using_infeas_costs = false;
vector<unsigned> m_columns_nz; // m_columns_nz[i] keeps an approximate value of non zeroes the i-th column
vector<unsigned> m_rows_nz; // m_rows_nz[i] keeps an approximate value of non zeroes in the i-th row
indexed_vector<T> m_pivot_row_of_B_1; // the pivot row of the reverse of B
indexed_vector<T> m_pivot_row; // this is the real pivot row of the simplex tableu
static_matrix<T, X> & m_A; // the matrix A
vector<X> & m_b; // the right side
vector<unsigned> & m_basis;
vector<unsigned>& m_nbasis;
vector<int>& m_basis_heading;
vector<X> & m_x; // a feasible solution, the fist time set in the constructor
vector<T> & m_costs;
lp_settings & m_settings;
vector<T> m_y; // the buffer for yB = cb
// a device that is able to solve Bx=c, xB=d, and change the basis
lu<T, X> * m_factorization = nullptr;
const column_namer & m_column_names;
indexed_vector<T> m_w; // the vector featuring in 24.3 of the Chvatal book
vector<T> m_d; // the vector of reduced costs
indexed_vector<T> m_ed; // the solution of B*m_ed = a
unsigned m_iters_with_no_cost_growing = 0;
const vector<column_type> & m_column_types;
const vector<X> & m_low_bounds;
const vector<X> & m_upper_bounds;
vector<T> m_column_norms; // the approximate squares of column norms that help choosing a profitable column
vector<X> m_copy_of_xB;
unsigned m_basis_sort_counter = 0;
vector<T> m_steepest_edge_coefficients;
vector<unsigned> m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving
bool m_tracing_basis_changes = false;
int_set* m_pivoted_rows = nullptr;
bool m_look_for_feasible_solution_only = false;
void start_tracing_basis_changes() {
m_trace_of_basis_change_vector.resize(0);
m_tracing_basis_changes = true;
}
void stop_tracing_basis_changes() {
m_tracing_basis_changes = false;
}
void trace_basis_change(unsigned entering, unsigned leaving) {
unsigned size = m_trace_of_basis_change_vector.size();
if (size >= 2 && m_trace_of_basis_change_vector[size-2] == leaving
&& m_trace_of_basis_change_vector[size -1] == entering) {
m_trace_of_basis_change_vector.pop_back();
m_trace_of_basis_change_vector.pop_back();
} else {
m_trace_of_basis_change_vector.push_back(entering);
m_trace_of_basis_change_vector.push_back(leaving);
}
}
unsigned m_m() const { return m_A.row_count(); } // it is the length of basis. The matrix m_A has m_m rows and the dimension of the matrix A is m_m
unsigned m_n() const { return m_A.column_count(); } // the number of columns in the matrix m_A
lp_core_solver_base(static_matrix<T, X> & A,
vector<X> & b, // the right side vector
vector<unsigned> & basis,
vector<unsigned> & nbasis,
vector<int> & heading,
vector<X> & x,
vector<T> & costs,
lp_settings & settings,
const column_namer& column_names,
const vector<column_type> & column_types,
const vector<X> & low_bound_values,
const vector<X> & upper_bound_values);
void allocate_basis_heading();
void init();
virtual ~lp_core_solver_base() {
if (m_factorization != nullptr)
delete m_factorization;
}
vector<unsigned> & non_basis() {
return m_nbasis;
}
const vector<unsigned> & non_basis() const { return m_nbasis; }
void set_status(lp_status status) {
m_status = status;
}
lp_status get_status() const{
return m_status;
}
void fill_cb(T * y);
void fill_cb(vector<T> & y);
void solve_yB(vector<T> & y);
void solve_Bd(unsigned entering);
void solve_Bd(unsigned entering, indexed_vector<T> & column);
void pretty_print(std::ostream & out);
void save_state(T * w_buffer, T * d_buffer);
void restore_state(T * w_buffer, T * d_buffer);
X get_cost() {
return dot_product(m_costs, m_x);
}
void copy_m_w(T * buffer);
void restore_m_w(T * buffer);
// needed for debugging
void copy_m_ed(T * buffer);
void restore_m_ed(T * buffer);
bool A_mult_x_is_off() const;
bool A_mult_x_is_off_on_index(const vector<unsigned> & index) const;
// from page 182 of Istvan Maros's book
void calculate_pivot_row_of_B_1(unsigned pivot_row);
void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row);
void update_x(unsigned entering, const X & delta);
const T & get_var_value(unsigned j) const {
return m_x[j];
}
void print_statistics(char const* str, X cost, std::ostream & message_stream);
bool print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream & message_stream);
bool print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const* str, std::ostream & message_stream);
bool print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream & message_stream);
unsigned total_iterations() const { return m_total_iterations; }
void set_total_iterations(unsigned s) { m_total_iterations = s; }
void set_non_basic_x_to_correct_bounds();
bool at_bound(const X &x, const X & bound) const {
return !below_bound(x, bound) && !above_bound(x, bound);
}
bool need_to_pivot_to_basis_tableau() const {
lean_assert(m_A.is_correct());
unsigned m = m_A.row_count();
for (unsigned i = 0; i < m; i++) {
unsigned bj = m_basis[i];
lean_assert(m_A.m_columns[bj].size() > 0);
if (m_A.m_columns[bj].size() > 1 || m_A.get_val(m_A.m_columns[bj][0]) != one_of_type<mpq>()) return true;
}
return false;
}
bool reduced_costs_are_correct_tableau() const {
if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
return true;
lean_assert(m_A.is_correct());
if (m_using_infeas_costs) {
if (infeasibility_costs_are_correct() == false) {
std::cout << "infeasibility_costs_are_correct() does not hold" << std::endl;
return false;
}
}
unsigned n = m_A.column_count();
for (unsigned j = 0; j < n; j++) {
if (m_basis_heading[j] >= 0) {
if (!is_zero(m_d[j])) {
std::cout << "case a\n";
print_column_info(j, std::cout);
return false;
}
} else {
auto d = 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);
}
if (m_d[j] != d) {
std::cout << "case b\n";
print_column_info(j, std::cout);
return false;
}
}
}
return true;
}
bool below_bound(const X & x, const X & bound) const {
if (precise()) return x < bound;
return below_bound_numeric<X>(x, bound, m_settings.primal_feasibility_tolerance);
}
bool above_bound(const X & x, const X & bound) const {
if (precise()) return x > bound;
return above_bound_numeric<X>(x, bound, m_settings.primal_feasibility_tolerance);
}
bool x_below_low_bound(unsigned p) const {
return below_bound(m_x[p], m_low_bounds[p]);
}
bool infeasibility_costs_are_correct() const;
bool infeasibility_cost_is_correct_for_column(unsigned j) const;
bool x_above_low_bound(unsigned p) const {
return above_bound(m_x[p], m_low_bounds[p]);
}
bool x_below_upper_bound(unsigned p) const {
return below_bound(m_x[p], m_upper_bounds[p]);
}
bool x_above_upper_bound(unsigned p) const {
return above_bound(m_x[p], m_upper_bounds[p]);
}
bool x_is_at_low_bound(unsigned j) const {
return at_bound(m_x[j], m_low_bounds[j]);
}
bool x_is_at_upper_bound(unsigned j) const {
return at_bound(m_x[j], m_upper_bounds[j]);
}
bool x_is_at_bound(unsigned j) const {
return x_is_at_low_bound(j) || x_is_at_upper_bound(j);
}
bool column_is_feasible(unsigned j) const;
bool calc_current_x_is_feasible_include_non_basis() const;
bool inf_set_is_correct() const;
bool column_is_dual_feasible(unsigned j) const;
bool d_is_not_negative(unsigned j) const;
bool d_is_not_positive(unsigned j) const;
bool time_is_over();
void rs_minus_Anx(vector<X> & rs);
bool find_x_by_solving();
bool update_basis_and_x(int entering, int leaving, X const & tt);
bool basis_has_no_doubles() const;
bool non_basis_has_no_doubles() const;
bool basis_is_correctly_represented_in_heading() const ;
bool non_basis_is_correctly_represented_in_heading() const ;
bool basis_heading_is_correct() const;
void restore_x_and_refactor(int entering, int leaving, X const & t);
void restore_x(unsigned entering, X const & t);
void fill_reduced_costs_from_m_y_by_rows();
void copy_rs_to_xB(vector<X> & rs);
virtual bool low_bounds_are_set() const { return false; }
X low_bound_value(unsigned j) const { return m_low_bounds[j]; }
X upper_bound_value(unsigned j) const { return m_upper_bounds[j]; }
column_type get_column_type(unsigned j) const {return m_column_types[j]; }
bool pivot_row_element_is_too_small_for_ratio_test(unsigned j) {
return m_settings.abs_val_is_smaller_than_pivot_tolerance(m_pivot_row[j]);
}
X bound_span(unsigned j) const {
return m_upper_bounds[j] - m_low_bounds[j];
}
std::string column_name(unsigned column) const;
void copy_right_side(vector<X> & rs);
void add_delta_to_xB(vector<X> & del);
void find_error_in_BxB(vector<X>& rs);
// recalculates the projection of x to B, such that Ax = b, whereab is the right side
void solve_Ax_eq_b();
bool snap_non_basic_x_to_bound() {
bool ret = false;
for (unsigned j : non_basis())
ret = snap_column_to_bound(j) || ret;
return ret;
}
bool snap_column_to_bound(unsigned j) {
switch (m_column_types[j]) {
case column_type::fixed:
if (x_is_at_bound(j))
break;
m_x[j] = m_low_bounds[j];
return true;
case column_type::boxed:
if (x_is_at_bound(j))
break; // we should preserve x if possible
// snap randomly
if (my_random() % 2 == 1)
m_x[j] = m_low_bounds[j];
else
m_x[j] = m_upper_bounds[j];
return true;
case column_type::low_bound:
if (x_is_at_low_bound(j))
break;
m_x[j] = m_low_bounds[j];
return true;
case column_type::upper_bound:
if (x_is_at_upper_bound(j))
break;
m_x[j] = m_upper_bounds[j];
return true;
default:
break;
}
return false;
}
bool make_column_feasible(unsigned j, numeric_pair<mpq> & delta) {
lean_assert(m_basis_heading[j] < 0);
auto & x = m_x[j];
switch (m_column_types[j]) {
case column_type::fixed:
lean_assert(m_low_bounds[j] == m_upper_bounds[j]);
if (x != m_low_bounds[j]) {
delta = m_low_bounds[j] - x;
x = m_low_bounds[j];
return true;
}
break;
case column_type::boxed:
if (x < m_low_bounds[j]) {
delta = m_low_bounds[j] - x;
x = m_low_bounds[j];
return true;
}
if (x > m_upper_bounds[j]) {
delta = m_upper_bounds[j] - x;
x = m_upper_bounds[j];
return true;
}
break;
case column_type::low_bound:
if (x < m_low_bounds[j]) {
delta = m_low_bounds[j] - x;
x = m_low_bounds[j];
return true;
}
break;
case column_type::upper_bound:
if (x > m_upper_bounds[j]) {
delta = m_upper_bounds[j] - x;
x = m_upper_bounds[j];
return true;
}
break;
case column_type::free_column:
break;
default:
lean_assert(false);
break;
}
return false;
}
void snap_non_basic_x_to_bound_and_free_to_zeroes();
void snap_xN_to_bounds_and_fill_xB();
void snap_xN_to_bounds_and_free_columns_to_zeroes();
void init_reduced_costs_for_one_iteration();
non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const;
void init_lu();
int pivots_in_column_and_row_are_different(int entering, int leaving) const;
void pivot_fixed_vars_from_basis();
bool pivot_for_tableau_on_basis();
bool pivot_row_for_tableau_on_basis(unsigned row);
void init_basic_part_of_basis_heading() {
unsigned m = m_basis.size();
for (unsigned i = 0; i < m; i++) {
unsigned column = m_basis[i];
m_basis_heading[column] = i;
}
}
void init_non_basic_part_of_basis_heading() {
this->m_nbasis.clear();
for (int j = m_basis_heading.size(); j--;){
if (m_basis_heading[j] < 0) {
m_nbasis.push_back(j);
// the index of column j in m_nbasis is (- basis_heading[j] - 1)
m_basis_heading[j] = - static_cast<int>(m_nbasis.size());
}
}
}
void init_basis_heading_and_non_basic_columns_vector() {
m_basis_heading.resize(0);
m_basis_heading.resize(m_n(), -1);
init_basic_part_of_basis_heading();
init_non_basic_part_of_basis_heading();
}
void change_basis_unconditionally(unsigned entering, unsigned leaving) {
lean_assert(m_basis_heading[entering] < 0);
int place_in_non_basis = -1 - m_basis_heading[entering];
if (static_cast<unsigned>(place_in_non_basis) >= m_nbasis.size()) {
// entering variable in not in m_nbasis, we need to put it back;
m_basis_heading[entering] = place_in_non_basis = m_nbasis.size();
m_nbasis.push_back(entering);
}
int place_in_basis = m_basis_heading[leaving];
m_basis_heading[entering] = place_in_basis;
m_basis[place_in_basis] = entering;
m_basis_heading[leaving] = -place_in_non_basis - 1;
m_nbasis[place_in_non_basis] = leaving;
if (m_tracing_basis_changes)
trace_basis_change(entering, leaving);
}
void change_basis(unsigned entering, unsigned leaving) {
lean_assert(m_basis_heading[entering] < 0);
int place_in_basis = m_basis_heading[leaving];
int place_in_non_basis = - m_basis_heading[entering] - 1;
m_basis_heading[entering] = place_in_basis;
m_basis[place_in_basis] = entering;
m_basis_heading[leaving] = -place_in_non_basis - 1;
m_nbasis[place_in_non_basis] = leaving;
if (m_tracing_basis_changes)
trace_basis_change(entering, leaving);
}
void restore_basis_change(unsigned entering, unsigned leaving) {
if (m_basis_heading[entering] < 0) {
return; // the basis has not been changed
}
change_basis_unconditionally(leaving, entering);
}
bool non_basic_column_is_set_correctly(unsigned j) const {
if (j >= this->m_n())
return false;
switch (this->m_column_types[j]) {
case column_type::fixed:
case column_type::boxed:
if (!this->x_is_at_bound(j))
return false;
break;
case column_type::low_bound:
if (!this->x_is_at_low_bound(j))
return false;
break;
case column_type::upper_bound:
if (!this->x_is_at_upper_bound(j))
return false;
break;
case column_type::free_column:
break;
default:
lean_assert(false);
break;
}
return true;
}
bool non_basic_columns_are_set_correctly() const {
for (unsigned j : this->m_nbasis)
if (!column_is_feasible(j)) {
print_column_info(j, std::cout);
return false;
}
return true;
}
void print_column_bound_info(unsigned j, std::ostream & out) const {
out << column_name(j) << " type = " << column_type_to_string(m_column_types[j]) << std::endl;
switch (m_column_types[j]) {
case column_type::fixed:
case column_type::boxed:
out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl;
break;
case column_type::low_bound:
out << m_low_bounds[j] << std::endl;
break;
case column_type::upper_bound:
out << m_upper_bounds[j] << std::endl;
break;
default:
break;
}
}
void print_column_info(unsigned j, std::ostream & out) const {
out << "column_index = " << j << ", name = "<< column_name(j) << " type = " << column_type_to_string(m_column_types[j]) << std::endl;
switch (m_column_types[j]) {
case column_type::fixed:
case column_type::boxed:
out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl;
break;
case column_type::low_bound:
out << m_low_bounds[j] << std::endl;
break;
case column_type::upper_bound:
out << m_upper_bounds[j] << std::endl;
break;
case column_type::free_column:
break;
default:
lean_assert(false);
}
std::cout << "basis heading = " << m_basis_heading[j] << std::endl;
std::cout << "x = " << m_x[j] << std::endl;
/*
std::cout << "cost = " << m_costs[j] << std::endl;
std:: cout << "m_d = " << m_d[j] << std::endl;*/
}
bool column_is_free(unsigned j) { return this->m_column_type[j] == free; }
bool column_has_upper_bound(unsigned j) {
switch(m_column_types[j]) {
case column_type::free_column:
case column_type::low_bound:
return false;
default:
return true;
}
}
bool bounds_for_boxed_are_set_correctly() const {
for (unsigned j = 0; j < m_column_types.size(); j++) {
if (m_column_types[j] != column_type::boxed) continue;
if (m_low_bounds[j] > m_upper_bounds[j])
return false;
}
return true;
}
bool column_has_low_bound(unsigned j) {
switch(m_column_types[j]) {
case column_type::free_column:
case column_type::upper_bound:
return false;
default:
return true;
}
}
// only check for basic columns
bool calc_current_x_is_feasible() const {
unsigned i = this->m_m();
while (i--) {
if (!column_is_feasible(m_basis[i]))
return false;
}
return true;
}
int find_pivot_index_in_row(unsigned i, const vector<column_cell> & col) const {
for (const auto & c: col) {
if (c.m_i == i)
return c.m_offset;
}
return -1;
}
void transpose_rows_tableau(unsigned i, unsigned ii);
void pivot_to_reduced_costs_tableau(unsigned i, unsigned j);
bool pivot_column_tableau(unsigned j, unsigned row_index);
bool divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col);
bool precise() const { return numeric_traits<T>::precise(); }
simplex_strategy_enum simplex_strategy() const { return
m_settings.simplex_strategy();
}
bool use_tableau() const { return m_settings.use_tableau(); }
template <typename K>
static void swap(vector<K> &v, unsigned i, unsigned j) {
auto t = v[i];
v[i] = v[j];
v[j] = t;
}
// called when transposing row i and ii
void transpose_basis(unsigned i, unsigned ii) {
swap(m_basis, i, ii);
swap(m_basis_heading, m_basis[i], m_basis[ii]);
}
bool column_is_in_inf_set(unsigned j) const {
return m_inf_set.contains(j);
}
void update_column_in_inf_set(unsigned j) {
if (column_is_feasible(j)) {
m_inf_set.erase(j);
} else {
m_inf_set.insert(j);
}
}
void insert_column_into_inf_set(unsigned j) {
m_inf_set.insert(j);
lean_assert(!column_is_feasible(j));
}
void remove_column_from_inf_set(unsigned j) {
m_inf_set.erase(j);
lean_assert(column_is_feasible(j));
}
bool costs_on_nbasis_are_zeros() const {
lean_assert(this->basis_heading_is_correct());
for (unsigned j = 0; j < this->m_n(); j++) {
if (this->m_basis_heading[j] < 0)
lean_assert(is_zero(this->m_costs[j]));
}
return true;
}
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,131 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <utility>
#include <memory>
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lp_core_solver_base.hpp"
template bool lean::lp_core_solver_base<double, double>::A_mult_x_is_off() const;
template bool lean::lp_core_solver_base<double, double>::A_mult_x_is_off_on_index(const vector<unsigned> &) const;
template bool lean::lp_core_solver_base<double, double>::basis_heading_is_correct() const;
template void lean::lp_core_solver_base<double, double>::calculate_pivot_row_of_B_1(unsigned int);
template void lean::lp_core_solver_base<double, double>::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned);
template bool lean::lp_core_solver_base<double, double>::column_is_dual_feasible(unsigned int) const;
template void lean::lp_core_solver_base<double, double>::fill_reduced_costs_from_m_y_by_rows();
template bool lean::lp_core_solver_base<double, double>::find_x_by_solving();
template lean::non_basic_column_value_position lean::lp_core_solver_base<double, double>::get_non_basic_column_value_position(unsigned int) const;
template lean::non_basic_column_value_position lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::get_non_basic_column_value_position(unsigned int) const;
template lean::non_basic_column_value_position lean::lp_core_solver_base<lean::mpq, lean::mpq>::get_non_basic_column_value_position(unsigned int) const;
template void lean::lp_core_solver_base<double, double>::init_reduced_costs_for_one_iteration();
template lean::lp_core_solver_base<double, double>::lp_core_solver_base(
lean::static_matrix<double, double>&, vector<double>&,
vector<unsigned int >&,
vector<unsigned> &, vector<int> &,
vector<double >&,
vector<double >&,
lean::lp_settings&, const column_namer&, const vector<lean::column_type >&,
const vector<double >&,
const vector<double >&);
template bool lean::lp_core_solver_base<double, double>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &);
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &);
template void lean::lp_core_solver_base<double, double>::restore_x(unsigned int, double const&);
template void lean::lp_core_solver_base<double, double>::set_non_basic_x_to_correct_bounds();
template void lean::lp_core_solver_base<double, double>::snap_xN_to_bounds_and_free_columns_to_zeroes();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_xN_to_bounds_and_free_columns_to_zeroes();
template void lean::lp_core_solver_base<double, double>::solve_Ax_eq_b();
template void lean::lp_core_solver_base<double, double>::solve_Bd(unsigned int);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq>>::solve_Bd(unsigned int, indexed_vector<lean::mpq>&);
template void lean::lp_core_solver_base<double, double>::solve_yB(vector<double >&);
template bool lean::lp_core_solver_base<double, double>::update_basis_and_x(int, int, double const&);
template void lean::lp_core_solver_base<double, double>::update_x(unsigned int, const double&);
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::A_mult_x_is_off() const;
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::A_mult_x_is_off_on_index(const vector<unsigned> &) const;
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::basis_heading_is_correct() const ;
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::calculate_pivot_row_of_B_1(unsigned int);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned);
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_is_dual_feasible(unsigned int) const;
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::fill_reduced_costs_from_m_y_by_rows();
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::find_x_by_solving();
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::init_reduced_costs_for_one_iteration();
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::restore_x(unsigned int, lean::mpq const&);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::set_non_basic_x_to_correct_bounds();
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_Ax_eq_b();
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_Bd(unsigned int);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_yB(vector<lean::mpq>&);
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::update_basis_and_x(int, int, lean::mpq const&);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::update_x(unsigned int, const lean::mpq&);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calculate_pivot_row_of_B_1(unsigned int);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_basis_heading_and_non_basic_columns_vector();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_reduced_costs_for_one_iteration();
template lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::lp_core_solver_base(lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, vector<lean::numeric_pair<lean::mpq> >&, vector<unsigned int >&, vector<unsigned> &, vector<int> &, vector<lean::numeric_pair<lean::mpq> >&, vector<lean::mpq>&, lean::lp_settings&, const column_namer&, const vector<lean::column_type >&,
const vector<lean::numeric_pair<lean::mpq> >&,
const vector<lean::numeric_pair<lean::mpq> >&);
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_cost_and_check_that_the_time_is_over(lean::numeric_pair<lean::mpq>, std::ostream&);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_xN_to_bounds_and_fill_xB();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd(unsigned int);
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::update_basis_and_x(int, int, lean::numeric_pair<lean::mpq> const&);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::update_x(unsigned int, const lean::numeric_pair<lean::mpq>&);
template lean::lp_core_solver_base<lean::mpq, lean::mpq>::lp_core_solver_base(
lean::static_matrix<lean::mpq, lean::mpq>&,
vector<lean::mpq>&,
vector<unsigned int >&,
vector<unsigned> &, vector<int> &,
vector<lean::mpq>&,
vector<lean::mpq>&,
lean::lp_settings&,
const column_namer&,
const vector<lean::column_type >&,
const vector<lean::mpq>&,
const vector<lean::mpq>&);
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &);
template std::string lean::lp_core_solver_base<double, double>::column_name(unsigned int) const;
template void lean::lp_core_solver_base<double, double>::pretty_print(std::ostream & out);
template void lean::lp_core_solver_base<double, double>::restore_state(double*, double*);
template void lean::lp_core_solver_base<double, double>::save_state(double*, double*);
template std::string lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_name(unsigned int) const;
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::pretty_print(std::ostream & out);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::restore_state(lean::mpq*, lean::mpq*);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::save_state(lean::mpq*, lean::mpq*);
template std::string lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::column_name(unsigned int) const;
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pretty_print(std::ostream & out);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::restore_state(lean::mpq*, lean::mpq*);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::save_state(lean::mpq*, lean::mpq*);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB(vector<lean::mpq>&);
template void lean::lp_core_solver_base<double, double>::init_lu();
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::init_lu();
template int lean::lp_core_solver_base<double, double>::pivots_in_column_and_row_are_different(int, int) const;
template int lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pivots_in_column_and_row_are_different(int, int) const;
template int lean::lp_core_solver_base<lean::mpq, lean::mpq>::pivots_in_column_and_row_are_different(int, int) const;
template bool lean::lp_core_solver_base<double, double>::calc_current_x_is_feasible_include_non_basis(void)const;
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::calc_current_x_is_feasible_include_non_basis(void)const;
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calc_current_x_is_feasible_include_non_basis() const;
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pivot_fixed_vars_from_basis();
template bool lean::lp_core_solver_base<double, double>::column_is_feasible(unsigned int) const;
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_is_feasible(unsigned int) const;
// template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_linear_combination_of_column_indices(vector<std::pair<lean::mpq, unsigned int>, std::allocator<std::pair<lean::mpq, unsigned int> > > const&, std::ostream&) const;
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::column_is_feasible(unsigned int) const;
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_non_basic_x_to_bound();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_lu();
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::A_mult_x_is_off_on_index(vector<unsigned int> const&) const;
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::find_x_by_solving();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::restore_x(unsigned int, lean::numeric_pair<lean::mpq> const&);
template bool lean::lp_core_solver_base<double, double>::pivot_for_tableau_on_basis();
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::pivot_for_tableau_on_basis();
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq>>::pivot_for_tableau_on_basis();
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq>>::pivot_column_tableau(unsigned int, unsigned int);
template bool lean::lp_core_solver_base<double, double>::pivot_column_tableau(unsigned int, unsigned int);
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::pivot_column_tableau(unsigned int, unsigned int);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_rows_tableau(unsigned int, unsigned int);
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::inf_set_is_correct() const;
template bool lean::lp_core_solver_base<double, double>::inf_set_is_correct() const;
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::inf_set_is_correct() const;
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::infeasibility_costs_are_correct() const;
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq >::infeasibility_costs_are_correct() const;
template bool lean::lp_core_solver_base<double, double >::infeasibility_costs_are_correct() const;

View file

@ -0,0 +1,197 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/static_matrix.h"
#include "util/lp/lp_core_solver_base.h"
#include <string>
#include <limits>
#include <set>
#include <algorithm>
#include "util/vector.h"
namespace lean {
template <typename T, typename X>
class lp_dual_core_solver:public lp_core_solver_base<T, X> {
public:
vector<bool> & m_can_enter_basis;
int m_r; // the row of the leaving column
int m_p; // leaving column; that is m_p = m_basis[m_r]
T m_delta; // the offset of the leaving basis variable
int m_sign_of_alpha_r; // see page 27
T m_theta_D;
T m_theta_P;
int m_q;
// todo : replace by a vector later
std::set<unsigned> m_breakpoint_set; // it is F in "Progress in the dual simplex method ..."
std::set<unsigned> m_flipped_boxed;
std::set<unsigned> m_tight_set; // it is the set of all breakpoints that become tight when m_q becomes tight
vector<T> m_a_wave;
vector<T> m_betas; // m_betas[i] is approximately a square of the norm of the i-th row of the reverse of B
T m_harris_tolerance;
std::set<unsigned> m_forbidden_rows;
lp_dual_core_solver(static_matrix<T, X> & A,
vector<bool> & can_enter_basis,
vector<X> & b, // the right side vector
vector<X> & x, // the number of elements in x needs to be at least as large as the number of columns in A
vector<unsigned> & basis,
vector<unsigned> & nbasis,
vector<int> & heading,
vector<T> & costs,
vector<column_type> & column_type_array,
vector<X> & low_bound_values,
vector<X> & upper_bound_values,
lp_settings & settings,
const column_namer & column_names):
lp_core_solver_base<T, X>(A,
b,
basis,
nbasis,
heading,
x,
costs,
settings,
column_names,
column_type_array,
low_bound_values,
upper_bound_values),
m_can_enter_basis(can_enter_basis),
m_a_wave(this->m_m()),
m_betas(this->m_m()) {
m_harris_tolerance = numeric_traits<T>::precise()? numeric_traits<T>::zero() : T(this->m_settings.harris_feasibility_tolerance);
this->solve_yB(this->m_y);
this->init_basic_part_of_basis_heading();
fill_non_basis_with_only_able_to_enter_columns();
}
void init_a_wave_by_zeros();
void fill_non_basis_with_only_able_to_enter_columns() {
auto & nb = this->m_nbasis;
nb.reset();
unsigned j = this->m_n();
while (j--) {
if (this->m_basis_heading[j] >= 0 || !m_can_enter_basis[j]) continue;
nb.push_back(j);
this->m_basis_heading[j] = - static_cast<int>(nb.size());
}
}
void restore_non_basis();
bool update_basis(int entering, int leaving);
void recalculate_xB_and_d();
void recalculate_d();
void init_betas();
void adjust_xb_for_changed_xn_and_init_betas();
void start_with_initial_basis_and_make_it_dual_feasible();
bool done();
T get_edge_steepness_for_low_bound(unsigned p);
T get_edge_steepness_for_upper_bound(unsigned p);
T pricing_for_row(unsigned i);
void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows);
bool advance_on_known_p();
int define_sign_of_alpha_r();
bool can_be_breakpoint(unsigned j);
void fill_breakpoint_set();
void DSE_FTran();
T get_delta();
void restore_d();
bool d_is_correct();
void xb_minus_delta_p_pivot_column();
void update_betas();
void apply_flips();
void snap_xN_column_to_bounds(unsigned j);
void snap_xN_to_bounds();
void init_beta_precisely(unsigned i);
void init_betas_precisely();
// step 7 of the algorithm from Progress
bool basis_change_and_update();
void revert_to_previous_basis();
non_basic_column_value_position m_entering_boundary_position;
bool update_basis_and_x_local(int entering, int leaving, X const & tt);
void recover_leaving();
bool problem_is_dual_feasible() const;
bool snap_runaway_nonbasic_column(unsigned);
bool snap_runaway_nonbasic_columns();
unsigned get_number_of_rows_to_try_for_leaving();
void update_a_wave(const T & del, unsigned j) {
this->m_A.add_column_to_vector(del, j, & m_a_wave[0]);
}
bool delta_keeps_the_sign(int initial_delta_sign, const T & delta);
void set_status_to_tentative_dual_unbounded_or_dual_unbounded();
// it is positive if going from low bound to upper bound and negative if going from upper bound to low bound
T signed_span_of_boxed(unsigned j) {
return this->x_is_at_low_bound(j)? this->bound_span(j): - this->bound_span(j);
}
void add_tight_breakpoints_and_q_to_flipped_set();
T delta_lost_on_flips_of_tight_breakpoints();
bool tight_breakpoinst_are_all_boxed();
T calculate_harris_delta_on_breakpoint_set();
void fill_tight_set_on_harris_delta(const T & harris_delta );
void find_q_on_tight_set();
void find_q_and_tight_set();
void erase_tight_breakpoints_and_q_from_breakpoint_set();
bool ratio_test();
void process_flipped();
void update_d_and_xB();
void calculate_beta_r_precisely();
// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms"
void update_xb_after_bound_flips();
void one_iteration();
void solve();
bool low_bounds_are_set() const { return true; }
};
}

View file

@ -0,0 +1,743 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <algorithm>
#include <string>
#include "util/vector.h"
#include "util/lp/lp_dual_core_solver.h"
namespace lean {
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_a_wave_by_zeros() {
unsigned j = this->m_m();
while (j--) {
m_a_wave[j] = numeric_traits<T>::zero();
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_non_basis() {
auto & nb = this->m_nbasis;
nb.reset();
unsigned j = this->m_n();
while (j--) {
if (this->m_basis_heading[j] >= 0 ) continue;
if (m_can_enter_basis[j]) {
lean_assert(std::find(nb.begin(), nb.end(), j) == nb.end());
nb.push_back(j);
this->m_basis_heading[j] = - static_cast<int>(nb.size());
}
}
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>::update_basis(int entering, int leaving) {
// the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done
if (this->m_refactor_counter++ < 200) {
this->m_factorization->replace_column(this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w);
if (this->m_factorization->get_status() == LU_status::OK) {
this->m_factorization->change_basis(entering, leaving);
return true;
}
}
// need to refactor
this->m_factorization->change_basis(entering, leaving);
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings);
this->m_refactor_counter = 0;
if (this->m_factorization->get_status() != LU_status::OK) {
LP_OUT(this->m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->total_iterations() << std::endl);
this->m_iters_with_no_cost_growing++;
return false;
}
return true;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::recalculate_xB_and_d() {
this->solve_Ax_eq_b();
recalculate_d();
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::recalculate_d() {
this->solve_yB(this->m_y);
this->fill_reduced_costs_from_m_y_by_rows();
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_betas() {
// todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation
// the current implementation is not good enough: todo
unsigned i = this->m_m();
while (i--) {
m_betas[i] = 1;
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::adjust_xb_for_changed_xn_and_init_betas() {
this->solve_Ax_eq_b();
init_betas();
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::start_with_initial_basis_and_make_it_dual_feasible() {
this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29,
// however this version does not require that m_x is the solution of Ax = 0 beforehand
adjust_xb_for_changed_xn_and_init_betas();
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>::done() {
if (this->get_status() == OPTIMAL) {
return true;
}
if (this->total_iterations() > this->m_settings.max_total_number_of_iterations) { // debug !!!!
this->set_status(ITERATIONS_EXHAUSTED);
return true;
}
return false; // todo, need to be more cases
}
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_low_bound(unsigned p) {
lean_assert(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m());
T del = this->m_x[p] - this->m_low_bounds[p];
del *= del;
return del / this->m_betas[this->m_basis_heading[p]];
}
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_upper_bound(unsigned p) {
lean_assert(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m());
T del = this->m_x[p] - this->m_upper_bounds[p];
del *= del;
return del / this->m_betas[this->m_basis_heading[p]];
}
template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(unsigned i) {
unsigned p = this->m_basis[i];
switch (this->m_column_types[p]) {
case column_type::fixed:
case column_type::boxed:
if (this->x_below_low_bound(p)) {
T del = get_edge_steepness_for_low_bound(p);
return del;
}
if (this->x_above_upper_bound(p)) {
T del = get_edge_steepness_for_upper_bound(p);
return del;
}
return numeric_traits<T>::zero();
case column_type::low_bound:
if (this->x_below_low_bound(p)) {
T del = get_edge_steepness_for_low_bound(p);
return del;
}
return numeric_traits<T>::zero();
break;
case column_type::upper_bound:
if (this->x_above_upper_bound(p)) {
T del = get_edge_steepness_for_upper_bound(p);
return del;
}
return numeric_traits<T>::zero();
break;
case column_type::free_column:
lean_assert(numeric_traits<T>::is_zero(this->m_d[p]));
return numeric_traits<T>::zero();
default:
lean_unreachable();
}
lean_unreachable();
return numeric_traits<T>::zero();
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) {
m_r = -1;
T steepest_edge_max = numeric_traits<T>::zero();
unsigned initial_offset_in_rows = offset_in_rows;
unsigned i = offset_in_rows;
unsigned rows_left = number_of_rows_to_try;
do {
if (m_forbidden_rows.find(i) != m_forbidden_rows.end()) {
if (++i == this->m_m()) {
i = 0;
}
continue;
}
T se = pricing_for_row(i);
if (se > steepest_edge_max) {
steepest_edge_max = se;
m_r = i;
if (rows_left > 0) {
rows_left--;
}
}
if (++i == this->m_m()) {
i = 0;
}
} while (i != initial_offset_in_rows && rows_left);
if (m_r == -1) {
if (this->get_status() != UNSTABLE) {
this->set_status(OPTIMAL);
}
} else {
m_p = this->m_basis[m_r];
m_delta = get_delta();
if (advance_on_known_p()){
m_forbidden_rows.clear();
return;
}
// failure in advance_on_known_p
if (this->get_status() == FLOATING_POINT_ERROR) {
return;
}
this->set_status(UNSTABLE);
m_forbidden_rows.insert(m_r);
}
}
// this calculation is needed for the steepest edge update,
// it hijackes m_pivot_row_of_B_1 for this purpose since we will need it anymore to the end of the cycle
template <typename T, typename X> void lp_dual_core_solver<T, X>::DSE_FTran() { // todo, see algorithm 7 from page 35
this->m_factorization->solve_By_for_T_indexed_only(this->m_pivot_row_of_B_1, this->m_settings);
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>::advance_on_known_p() {
if (done()) {
return true;
}
this->calculate_pivot_row_of_B_1(m_r);
this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(m_r);
if (!ratio_test()) {
return true;
}
calculate_beta_r_precisely();
this->solve_Bd(m_q); // FTRAN
int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p);
if (!pivot_compare_result){;}
else if (pivot_compare_result == 2) { // the sign is changed, cannot continue
lean_unreachable(); // not implemented yet
} else {
lean_assert(pivot_compare_result == 1);
this->init_lu();
}
DSE_FTran();
return basis_change_and_update();
}
template <typename T, typename X> int lp_dual_core_solver<T, X>::define_sign_of_alpha_r() {
switch (this->m_column_types[m_p]) {
case column_type::boxed:
case column_type::fixed:
if (this->x_below_low_bound(m_p)) {
return -1;
}
if (this->x_above_upper_bound(m_p)) {
return 1;
}
lean_unreachable();
case column_type::low_bound:
if (this->x_below_low_bound(m_p)) {
return -1;
}
lean_unreachable();
case column_type::upper_bound:
if (this->x_above_upper_bound(m_p)) {
return 1;
}
lean_unreachable();
default:
lean_unreachable();
}
lean_unreachable();
return 0;
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>::can_be_breakpoint(unsigned j) {
if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false;
switch (this->m_column_types[j]) {
case column_type::low_bound:
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bounds[j]));
return m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
case column_type::upper_bound:
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j]));
return m_sign_of_alpha_r * this->m_pivot_row[j] < 0;
case column_type::boxed:
{
bool low_bound = this->x_is_at_low_bound(j);
bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
return low_bound == grawing;
}
case column_type::fixed: // is always dual feasible so we ingore it
return false;
case column_type::free_column:
return true;
default:
return false;
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_breakpoint_set() {
m_breakpoint_set.clear();
for (unsigned j : this->non_basis()) {
if (can_be_breakpoint(j)) {
m_breakpoint_set.insert(j);
}
}
}
// template <typename T, typename X> void lp_dual_core_solver<T, X>::FTran() {
// this->solve_Bd(m_q);
// }
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_delta() {
switch (this->m_column_types[m_p]) {
case column_type::boxed:
if (this->x_below_low_bound(m_p)) {
return this->m_x[m_p] - this->m_low_bounds[m_p];
}
if (this->x_above_upper_bound(m_p)) {
return this->m_x[m_p] - this->m_upper_bounds[m_p];
}
lean_unreachable();
case column_type::low_bound:
if (this->x_below_low_bound(m_p)) {
return this->m_x[m_p] - this->m_low_bounds[m_p];
}
lean_unreachable();
case column_type::upper_bound:
if (this->x_above_upper_bound(m_p)) {
return get_edge_steepness_for_upper_bound(m_p);
}
lean_unreachable();
case column_type::fixed:
return this->m_x[m_p] - this->m_upper_bounds[m_p];
default:
lean_unreachable();
}
lean_unreachable();
return zero_of_type<T>();
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_d() {
this->m_d[m_p] = numeric_traits<T>::zero();
for (auto j : this->non_basis()) {
this->m_d[j] += m_theta_D * this->m_pivot_row[j];
}
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>::d_is_correct() {
this->solve_yB(this->m_y);
for (auto j : this->non_basis()) {
T d = this->m_costs[j] - this->m_A.dot_product_with_column(this->m_y, j);
if (numeric_traits<T>::get_double(abs(d - this->m_d[j])) >= 0.001) {
LP_OUT(this->m_settings, "total_iterations = " << this->total_iterations() << std::endl
<< "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl);
return false;
}
}
return true;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::xb_minus_delta_p_pivot_column() {
unsigned i = this->m_m();
while (i--) {
this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i];
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized
T one_over_arq = numeric_traits<T>::one() / this->m_pivot_row[m_q];
T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) * one_over_arq);
T k = -2 * one_over_arq;
unsigned i = this->m_m();
while (i--) {
if (static_cast<int>(i) == m_r) continue;
T a = this->m_ed[i];
m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]);
if (m_betas[i] < T(0.0001))
m_betas[i] = T(0.0001);
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::apply_flips() {
for (unsigned j : m_flipped_boxed) {
lean_assert(this->x_is_at_bound(j));
if (this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_upper_bounds[j];
} else {
this->m_x[j] = this->m_low_bounds[j];
}
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_column_to_bounds(unsigned j) {
switch (this->m_column_type[j]) {
case column_type::fixed:
this->m_x[j] = this->m_low_bounds[j];
break;
case column_type::boxed:
if (this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_low_bounds[j];
} else {
this->m_x[j] = this->m_upper_bounds[j];
}
break;
case column_type::low_bound:
this->m_x[j] = this->m_low_bounds[j];
break;
case column_type::upper_bound:
this->m_x[j] = this->m_upper_bounds[j];
break;
case column_type::free_column:
break;
default:
lean_unreachable();
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_to_bounds() {
for (auto j : this->non_basis()) {
snap_xN_column_to_bounds(j);
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_beta_precisely(unsigned i) {
vector<T> vec(this->m_m(), numeric_traits<T>::zero());
vec[i] = numeric_traits<T>::one();
this->m_factorization->solve_yB_with_error_check(vec, this->m_basis);
T beta = numeric_traits<T>::zero();
for (T & v : vec) {
beta += v * v;
}
this->m_betas[i] =beta;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_betas_precisely() {
unsigned i = this->m_m();
while (i--) {
init_beta_precisely(i);
}
}
// step 7 of the algorithm from Progress
template <typename T, typename X> bool lp_dual_core_solver<T, X>::basis_change_and_update() {
update_betas();
update_d_and_xB();
// m_theta_P = m_delta / this->m_ed[m_r];
m_theta_P = m_delta / this->m_pivot_row[m_q];
// xb_minus_delta_p_pivot_column();
apply_flips();
if (!this->update_basis_and_x(m_q, m_p, m_theta_P)) {
init_betas_precisely();
return false;
}
if (snap_runaway_nonbasic_column(m_p)) {
if (!this->find_x_by_solving()) {
revert_to_previous_basis();
this->m_iters_with_no_cost_growing++;
return false;
}
}
if (!problem_is_dual_feasible()) {
// todo : shift the costs!!!!
revert_to_previous_basis();
this->m_iters_with_no_cost_growing++;
return false;
}
lean_assert(d_is_correct());
return true;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::recover_leaving() {
switch (m_entering_boundary_position) {
case at_low_bound:
case at_fixed:
this->m_x[m_q] = this->m_low_bounds[m_q];
break;
case at_upper_bound:
this->m_x[m_q] = this->m_upper_bounds[m_q];
break;
case free_of_bounds:
this->m_x[m_q] = zero_of_type<X>();
default:
lean_unreachable();
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::revert_to_previous_basis() {
LP_OUT(this->m_settings, "revert to previous basis on ( " << m_p << ", " << m_q << ")" << std::endl);
this->change_basis_unconditionally(m_p, m_q);
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings);
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR); // complete failure
return;
}
recover_leaving();
if (!this->find_x_by_solving()) {
this->set_status(FLOATING_POINT_ERROR);
return;
}
recalculate_xB_and_d();
init_betas_precisely();
}
// returns true if the column has been snapped
template <typename T, typename X> bool lp_dual_core_solver<T, X>::snap_runaway_nonbasic_column(unsigned j) {
switch (this->m_column_types[j]) {
case column_type::fixed:
case column_type::low_bound:
if (!this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_low_bounds[j];
return true;
}
break;
case column_type::boxed:
{
bool closer_to_low_bound = abs(this->m_low_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]);
if (closer_to_low_bound) {
if (!this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_low_bounds[j];
return true;
}
} else {
if (!this->x_is_at_upper_bound(j)) {
this->m_x[j] = this->m_low_bounds[j];
return true;
}
}
}
break;
case column_type::upper_bound:
if (!this->x_is_at_upper_bound(j)) {
this->m_x[j] = this->m_upper_bounds[j];
return true;
}
break;
default:
break;
}
return false;
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>::problem_is_dual_feasible() const {
for (unsigned j : this->non_basis()){
if (!this->column_is_dual_feasible(j)) {
// std::cout << "column " << j << " is not dual feasible" << std::endl;
// std::cout << "m_d[" << j << "] = " << this->m_d[j] << std::endl;
// std::cout << "x[" << j << "] = " << this->m_x[j] << std::endl;
// std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl;
// std::cout << "bounds = " << this->m_low_bounds[j] << "," << this->m_upper_bounds[j] << std::endl;
// std::cout << "total_iterations = " << this->total_iterations() << std::endl;
return false;
}
}
return true;
}
template <typename T, typename X> unsigned lp_dual_core_solver<T, X>::get_number_of_rows_to_try_for_leaving() {
unsigned s = this->m_m();
if (this->m_m() > 300) {
s = (unsigned)((s / 100.0) * this->m_settings.percent_of_entering_to_check);
}
return my_random() % s + 1;
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>::delta_keeps_the_sign(int initial_delta_sign, const T & delta) {
if (numeric_traits<T>::precise())
return ((delta > numeric_traits<T>::zero()) && (initial_delta_sign == 1)) ||
((delta < numeric_traits<T>::zero()) && (initial_delta_sign == -1));
double del = numeric_traits<T>::get_double(delta);
return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) ||
((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1));
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::set_status_to_tentative_dual_unbounded_or_dual_unbounded() {
if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) {
this->set_status(DUAL_UNBOUNDED);
} else {
this->set_status(TENTATIVE_DUAL_UNBOUNDED);
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::add_tight_breakpoints_and_q_to_flipped_set() {
m_flipped_boxed.insert(m_q);
for (auto j : m_tight_set) {
m_flipped_boxed.insert(j);
}
}
template <typename T, typename X> T lp_dual_core_solver<T, X>::delta_lost_on_flips_of_tight_breakpoints() {
T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]);
for (auto j : m_tight_set) {
ret += abs(this->bound_span(j) * this->m_pivot_row[j]);
}
return ret;
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>::tight_breakpoinst_are_all_boxed() {
if (this->m_column_types[m_q] != column_type::boxed) return false;
for (auto j : m_tight_set) {
if (this->m_column_types[j] != column_type::boxed) return false;
}
return true;
}
template <typename T, typename X> T lp_dual_core_solver<T, X>::calculate_harris_delta_on_breakpoint_set() {
bool first_time = true;
T ret = zero_of_type<T>();
lean_assert(m_breakpoint_set.size() > 0);
for (auto j : m_breakpoint_set) {
T t;
if (this->x_is_at_low_bound(j)) {
t = abs((std::max(this->m_d[j], numeric_traits<T>::zero()) + m_harris_tolerance) / this->m_pivot_row[j]);
} else {
t = abs((std::min(this->m_d[j], numeric_traits<T>::zero()) - m_harris_tolerance) / this->m_pivot_row[j]);
}
if (first_time) {
ret = t;
first_time = false;
} else if (t < ret) {
ret = t;
}
}
return ret;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_tight_set_on_harris_delta(const T & harris_delta ){
m_tight_set.clear();
for (auto j : m_breakpoint_set) {
if (this->x_is_at_low_bound(j)) {
if (abs(std::max(this->m_d[j], numeric_traits<T>::zero()) / this->m_pivot_row[j]) <= harris_delta){
m_tight_set.insert(j);
}
} else {
if (abs(std::min(this->m_d[j], numeric_traits<T>::zero() ) / this->m_pivot_row[j]) <= harris_delta){
m_tight_set.insert(j);
}
}
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_on_tight_set() {
m_q = -1;
T max_pivot;
for (auto j : m_tight_set) {
T r = abs(this->m_pivot_row[j]);
if (m_q != -1) {
if (r > max_pivot) {
max_pivot = r;
m_q = j;
}
} else {
max_pivot = r;
m_q = j;
}
}
m_tight_set.erase(m_q);
lean_assert(m_q != -1);
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_and_tight_set() {
T harris_del = calculate_harris_delta_on_breakpoint_set();
fill_tight_set_on_harris_delta(harris_del);
find_q_on_tight_set();
m_entering_boundary_position = this->get_non_basic_column_value_position(m_q);
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::erase_tight_breakpoints_and_q_from_breakpoint_set() {
m_breakpoint_set.erase(m_q);
for (auto j : m_tight_set) {
m_breakpoint_set.erase(j);
}
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>::ratio_test() {
m_sign_of_alpha_r = define_sign_of_alpha_r();
fill_breakpoint_set();
m_flipped_boxed.clear();
int initial_delta_sign = m_delta >= numeric_traits<T>::zero()? 1: -1;
do {
if (m_breakpoint_set.size() == 0) {
set_status_to_tentative_dual_unbounded_or_dual_unbounded();
return false;
}
this->set_status(FEASIBLE);
find_q_and_tight_set();
if (!tight_breakpoinst_are_all_boxed()) break;
T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign;
if (!delta_keeps_the_sign(initial_delta_sign, del)) break;
if (m_tight_set.size() + 1 == m_breakpoint_set.size()) {
break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis
}
// we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set
add_tight_breakpoints_and_q_to_flipped_set();
m_delta = del;
erase_tight_breakpoints_and_q_from_breakpoint_set();
} while (true);
m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q];
return true;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::process_flipped() {
init_a_wave_by_zeros();
for (auto j : m_flipped_boxed) {
update_a_wave(signed_span_of_boxed(j), j);
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_d_and_xB() {
for (auto j : this->non_basis()) {
this->m_d[j] -= m_theta_D * this->m_pivot_row[j];
}
this->m_d[m_p] = - m_theta_D;
if (m_flipped_boxed.size() > 0) {
process_flipped();
update_xb_after_bound_flips();
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::calculate_beta_r_precisely() {
T t = numeric_traits<T>::zero();
unsigned i = this->m_m();
while (i--) {
T b = this->m_pivot_row_of_B_1[i];
t += b * b;
}
m_betas[m_r] = t;
}
// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms"
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_xb_after_bound_flips() {
this->m_factorization->solve_By(m_a_wave);
unsigned i = this->m_m();
while (i--) {
this->m_x[this->m_basis[i]] -= m_a_wave[i];
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::one_iteration() {
unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving();
unsigned offset_in_rows = my_random() % this->m_m();
if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) {
number_of_rows_to_try = this->m_m();
} else {
this->set_status(FEASIBLE);
}
pricing_loop(number_of_rows_to_try, offset_in_rows);
lean_assert(problem_is_dual_feasible());
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // see the page 35
lean_assert(d_is_correct());
lean_assert(problem_is_dual_feasible());
lean_assert(this->basis_heading_is_correct());
this->set_total_iterations(0);
this->m_iters_with_no_cost_growing = 0;
do {
if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over("", *this->m_settings.get_message_ostream())){
return;
}
one_iteration();
} while (this->get_status() != FLOATING_POINT_ERROR && this->get_status() != DUAL_UNBOUNDED && this->get_status() != OPTIMAL &&
this->m_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);
}
}

View file

@ -0,0 +1,29 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <utility>
#include <memory>
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lp_dual_core_solver.hpp"
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::start_with_initial_basis_and_make_it_dual_feasible();
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::solve();
template lean::lp_dual_core_solver<double, double>::lp_dual_core_solver(lean::static_matrix<double, double>&, vector<bool>&,
vector<double>&,
vector<double>&,
vector<unsigned int>&,
vector<unsigned> &,
vector<int> &,
vector<double>&,
vector<lean::column_type>&,
vector<double>&,
vector<double>&,
lean::lp_settings&, const lean::column_namer&);
template void lean::lp_dual_core_solver<double, double>::start_with_initial_basis_and_make_it_dual_feasible();
template void lean::lp_dual_core_solver<double, double>::solve();
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::restore_non_basis();
template void lean::lp_dual_core_solver<double, double>::restore_non_basis();
template void lean::lp_dual_core_solver<double, double>::revert_to_previous_basis();
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::revert_to_previous_basis();

View file

@ -0,0 +1,79 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/lp/lp_utils.h"
#include "util/lp/lp_solver.h"
#include "util/lp/lp_dual_core_solver.h"
namespace lean {
template <typename T, typename X>
class lp_dual_simplex: public lp_solver<T, X> {
lp_dual_core_solver<T, X> * m_core_solver = nullptr;
vector<T> m_b_copy;
vector<T> m_low_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver
vector<column_type> m_column_types_of_core_solver;
vector<column_type> m_column_types_of_logicals;
vector<bool> m_can_enter_basis;
public:
~lp_dual_simplex() {
if (m_core_solver != nullptr) {
delete m_core_solver;
}
}
void decide_on_status_after_stage1();
void fix_logical_for_stage2(unsigned j);
void fix_structural_for_stage2(unsigned j);
void unmark_boxed_and_fixed_columns_and_fix_structural_costs();
void restore_right_sides();
void solve_for_stage2();
void fill_x_with_zeros();
void stage1();
void stage2();
void fill_first_stage_solver_fields();
column_type get_column_type(unsigned j);
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j);
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j);
void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver();
void set_type_for_logical(unsigned j, column_type col_type) {
this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type;
}
void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row,
unsigned & slack_var,
unsigned & artificial);
void augment_matrix_A_and_fill_x_and_allocate_some_fields();
void copy_m_b_aside_and_set_it_to_zeros();
void find_maximal_solution();
virtual T get_column_value(unsigned column) const {
return this->get_column_value_with_core_solver(column, m_core_solver);
}
T get_current_cost() const;
};
}

View file

@ -0,0 +1,362 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/lp_dual_simplex.h"
namespace lean{
template <typename T, typename X> void lp_dual_simplex<T, X>::decide_on_status_after_stage1() {
switch (m_core_solver->get_status()) {
case OPTIMAL:
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
this->m_status = FEASIBLE;
} else {
this->m_status = UNBOUNDED;
}
break;
case DUAL_UNBOUNDED:
lean_unreachable();
case ITERATIONS_EXHAUSTED:
this->m_status = ITERATIONS_EXHAUSTED;
break;
case TIME_EXHAUSTED:
this->m_status = TIME_EXHAUSTED;
break;
case FLOATING_POINT_ERROR:
this->m_status = FLOATING_POINT_ERROR;
break;
default:
lean_unreachable();
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::fix_logical_for_stage2(unsigned j) {
lean_assert(j >= this->number_of_core_structurals());
switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) {
case column_type::low_bound:
m_low_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::low_bound;
m_can_enter_basis[j] = true;
break;
case column_type::fixed:
this->m_upper_bounds[j] = m_low_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::fixed;
m_can_enter_basis[j] = false;
break;
default:
lean_unreachable();
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::fix_structural_for_stage2(unsigned j) {
column_info<T> * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]];
switch (ci->get_column_type()) {
case column_type::low_bound:
m_low_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::low_bound;
m_can_enter_basis[j] = true;
break;
case column_type::fixed:
case column_type::upper_bound:
lean_unreachable();
case column_type::boxed:
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
m_low_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::boxed;
m_can_enter_basis[j] = true;
break;
case column_type::free_column:
m_can_enter_basis[j] = true;
m_column_types_of_core_solver[j] = column_type::free_column;
break;
default:
lean_unreachable();
}
// T cost_was = this->m_costs[j];
this->set_scaled_cost(j);
}
template <typename T, typename X> void lp_dual_simplex<T, X>::unmark_boxed_and_fixed_columns_and_fix_structural_costs() {
unsigned j = this->m_A->column_count();
while (j-- > this->number_of_core_structurals()) {
fix_logical_for_stage2(j);
}
j = this->number_of_core_structurals();
while (j--) {
fix_structural_for_stage2(j);
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::restore_right_sides() {
unsigned i = this->m_A->row_count();
while (i--) {
this->m_b[i] = m_b_copy[i];
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::solve_for_stage2() {
m_core_solver->restore_non_basis();
m_core_solver->solve_yB(m_core_solver->m_y);
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
m_core_solver->set_status(FEASIBLE);
m_core_solver->solve();
switch (m_core_solver->get_status()) {
case OPTIMAL:
this->m_status = OPTIMAL;
break;
case DUAL_UNBOUNDED:
this->m_status = INFEASIBLE;
break;
case TIME_EXHAUSTED:
this->m_status = TIME_EXHAUSTED;
break;
case FLOATING_POINT_ERROR:
this->m_status = FLOATING_POINT_ERROR;
break;
default:
lean_unreachable();
}
this->m_second_stage_iterations = m_core_solver->total_iterations();
this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations);
}
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_x_with_zeros() {
unsigned j = this->m_A->column_count();
while (j--) {
this->m_x[j] = numeric_traits<T>::zero();
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::stage1() {
lean_assert(m_core_solver == nullptr);
this->m_x.resize(this->m_A->column_count(), numeric_traits<T>::zero());
if (this->m_settings.get_message_ostream() != nullptr)
this->print_statistics_on_A(*this->m_settings.get_message_ostream());
m_core_solver = new lp_dual_core_solver<T, X>(
*this->m_A,
m_can_enter_basis,
this->m_b, // the right side vector
this->m_x,
this->m_basis,
this->m_nbasis,
this->m_heading,
this->m_costs,
this->m_column_types_of_core_solver,
this->m_low_bounds,
this->m_upper_bounds,
this->m_settings,
*this);
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
// skipping stage 1
m_core_solver->set_status(OPTIMAL);
m_core_solver->set_total_iterations(0);
} else {
m_core_solver->solve();
}
decide_on_status_after_stage1();
this->m_first_stage_iterations = m_core_solver->total_iterations();
}
template <typename T, typename X> void lp_dual_simplex<T, X>::stage2() {
unmark_boxed_and_fixed_columns_and_fix_structural_costs();
restore_right_sides();
solve_for_stage2();
}
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_solver_fields() {
unsigned slack_var = this->number_of_core_structurals();
unsigned artificial = this->number_of_core_structurals() + this->m_slacks;
for (unsigned row = 0; row < this->row_count(); row++) {
fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial);
}
fill_costs_and_bounds_and_column_types_for_the_first_stage_solver();
}
template <typename T, typename X> column_type lp_dual_simplex<T, X>::get_column_type(unsigned j) {
lean_assert(j < this->m_A->column_count());
if (j >= this->number_of_core_structurals()) {
return m_column_types_of_logicals[j - this->number_of_core_structurals()];
}
return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type();
}
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) {
// see 4.7 in the dissertation of Achim Koberstein
lean_assert(this->m_core_solver_columns_to_external_columns.find(j) !=
this->m_core_solver_columns_to_external_columns.end());
T free_bound = T(1e4); // see 4.8
unsigned jj = this->m_core_solver_columns_to_external_columns[j];
lean_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end());
column_info<T> * ci = this->m_map_from_var_index_to_column_info[jj];
switch (ci->get_column_type()) {
case column_type::upper_bound: {
std::stringstream s;
s << "unexpected bound type " << j << " "
<< column_type_to_string(get_column_type(j));
throw_exception(s.str());
break;
}
case column_type::low_bound: {
m_can_enter_basis[j] = true;
this->set_scaled_cost(j);
this->m_low_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] =numeric_traits<T>::one();
break;
}
case column_type::free_column: {
m_can_enter_basis[j] = true;
this->set_scaled_cost(j);
this->m_upper_bounds[j] = free_bound;
this->m_low_bounds[j] = -free_bound;
break;
}
case column_type::boxed:
m_can_enter_basis[j] = false;
this->m_costs[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] = this->m_low_bounds[j] = numeric_traits<T>::zero(); // is it needed?
break;
default:
lean_unreachable();
}
m_column_types_of_core_solver[j] = column_type::boxed;
}
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) {
this->m_costs[j] = 0;
lean_assert(get_column_type(j) != column_type::upper_bound);
if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::low_bound))) {
m_column_types_of_core_solver[j] = column_type::boxed;
this->m_low_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] = numeric_traits<T>::one();
} else {
m_column_types_of_core_solver[j] = column_type::fixed;
this->m_low_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] = numeric_traits<T>::zero();
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() {
unsigned j = this->m_A->column_count();
while (j-- > this->number_of_core_structurals()) { // go over logicals here
fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j);
}
j = this->number_of_core_structurals();
while (j--) {
fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j);
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row,
unsigned & slack_var,
unsigned & artificial) {
lean_assert(row < this->row_count());
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]];
// we need to bring the program to the form Ax = b
T rs = this->m_b[row];
switch (constraint.m_relation) {
case Equal: // no slack variable here
set_type_for_logical(artificial, column_type::fixed);
this->m_basis[row] = artificial;
this->m_costs[artificial] = numeric_traits<T>::zero();
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
artificial++;
break;
case Greater_or_equal:
set_type_for_logical(slack_var, column_type::low_bound);
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
if (rs > 0) {
// adding one artificial
set_type_for_logical(artificial, column_type::fixed);
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
this->m_basis[row] = artificial;
this->m_costs[artificial] = numeric_traits<T>::zero();
artificial++;
} else {
// we can put a slack_var into the basis, and avoid adding an artificial variable
this->m_basis[row] = slack_var;
this->m_costs[slack_var] = numeric_traits<T>::zero();
}
slack_var++;
break;
case Less_or_equal:
// introduce a non-negative slack variable
set_type_for_logical(slack_var, column_type::low_bound);
(*this->m_A)(row, slack_var) = numeric_traits<T>::one();
if (rs < 0) {
// adding one artificial
set_type_for_logical(artificial, column_type::fixed);
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
this->m_basis[row] = artificial;
this->m_costs[artificial] = numeric_traits<T>::zero();
artificial++;
} else {
// we can put slack_var into the basis, and avoid adding an artificial variable
this->m_basis[row] = slack_var;
this->m_costs[slack_var] = numeric_traits<T>::zero();
}
slack_var++;
break;
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::augment_matrix_A_and_fill_x_and_allocate_some_fields() {
this->count_slacks_and_artificials();
this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials);
unsigned n = this->m_A->column_count();
this->m_column_types_of_core_solver.resize(n);
m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials);
this->m_costs.resize(n);
this->m_upper_bounds.resize(n);
this->m_low_bounds.resize(n);
m_can_enter_basis.resize(n);
this->m_basis.resize(this->m_A->row_count());
}
template <typename T, typename X> void lp_dual_simplex<T, X>::copy_m_b_aside_and_set_it_to_zeros() {
for (unsigned i = 0; i < this->m_b.size(); i++) {
m_b_copy.push_back(this->m_b[i]);
this->m_b[i] = numeric_traits<T>::zero(); // preparing for the first stage
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::find_maximal_solution(){
if (this->problem_is_empty()) {
this->m_status = lp_status::EMPTY;
return;
}
this->flip_costs(); // do it for now, todo ( remove the flipping)
this->cleanup();
if (this->m_status == INFEASIBLE) {
return;
}
this->fill_matrix_A_and_init_right_side();
this->fill_m_b();
this->scale();
augment_matrix_A_and_fill_x_and_allocate_some_fields();
fill_first_stage_solver_fields();
copy_m_b_aside_and_set_it_to_zeros();
stage1();
if (this->m_status == FEASIBLE) {
stage2();
}
}
template <typename T, typename X> T lp_dual_simplex<T, X>::get_current_cost() const {
T ret = numeric_traits<T>::zero();
for (auto it : this->m_map_from_var_index_to_column_info) {
ret += this->get_column_cost_value(it.first, it.second);
}
return -ret; // we flip costs for now
}
}

View file

@ -0,0 +1,9 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/lp_dual_simplex.hpp"
template lean::mpq lean::lp_dual_simplex<lean::mpq, lean::mpq>::get_current_cost() const;
template void lean::lp_dual_simplex<lean::mpq, lean::mpq>::find_maximal_solution();
template double lean::lp_dual_simplex<double, double>::get_current_cost() const;
template void lean::lp_dual_simplex<double, double>::find_maximal_solution();

View file

@ -0,0 +1,986 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include <list>
#include <limits>
#include <unordered_map>
#include <sstream>
#include <string>
#include "util/vector.h"
#include <set>
#include <math.h>
#include <cstdlib>
#include <algorithm>
#include "util/lp/lu.h"
#include "util/lp/lp_solver.h"
#include "util/lp/static_matrix.h"
#include "util/lp/core_solver_pretty_printer.h"
#include "util/lp/lp_core_solver_base.h"
#include "util/lp/breakpoint.h"
#include "util/lp/binary_heap_priority_queue.h"
#include "util/lp/int_set.h"
#include "util/lp/iterator_on_row.h"
namespace lean {
// This core solver solves (Ax=b, low_bound_values \leq x \leq upper_bound_values, maximize costs*x )
// The right side b is given implicitly by x and the basis
template <typename T, typename X>
class lp_primal_core_solver:public lp_core_solver_base<T, X> {
public:
// m_sign_of_entering is set to 1 if the entering variable needs
// to grow and is set to -1 otherwise
unsigned m_column_norm_update_counter;
T m_enter_price_eps;
int m_sign_of_entering_delta;
vector<breakpoint<X>> m_breakpoints;
binary_heap_priority_queue<X> m_breakpoint_indices_queue;
indexed_vector<T> m_beta; // see Swietanowski working vector beta for column norms
T m_epsilon_of_reduced_cost = T(1)/T(10000000);
vector<T> m_costs_backup;
T m_converted_harris_eps;
unsigned m_inf_row_index_for_tableau;
bool m_bland_mode_tableau;
int_set m_left_basis_tableau;
unsigned m_bland_mode_threshold = 1000;
unsigned m_left_basis_repeated;
vector<unsigned> m_leaving_candidates;
// T m_converted_harris_eps = convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance);
std::list<unsigned> m_non_basis_list;
void sort_non_basis();
void sort_non_basis_rational();
int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over);
int choose_entering_column_tableau();
int choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over);
int find_leaving_and_t_with_breakpoints(unsigned entering, X & t);
// int find_inf_row() {
// // mimicing CLP : todo : use a heap
// int j = -1;
// for (unsigned k : this->m_inf_set.m_index) {
// if (k < static_cast<unsigned>(j))
// j = static_cast<int>(k);
// }
// if (j == -1)
// return -1;
// return this->m_basis_heading[j];
// #if 0
// vector<int> choices;
// unsigned len = 100000000;
// for (unsigned j : this->m_inf_set.m_index) {
// int i = this->m_basis_heading[j];
// lean_assert(i >= 0);
// unsigned row_len = this->m_A.m_rows[i].size();
// if (row_len < len) {
// choices.clear();
// choices.push_back(i);
// len = row_len;
// if (my_random() % 10) break;
// } else if (row_len == len) {
// choices.push_back(i);
// if (my_random() % 10) break;
// }
// }
// if (choices.size() == 0)
// return -1;
// if (choices.size() == 1)
// return choices[0];
// unsigned k = my_random() % choices.size();
// return choices[k];
// #endif
// }
bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const {
// sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos
// we have xbj = -aj * xj
lean_assert(this->m_basis_heading[j] < 0);
lean_assert(this->column_is_feasible(j));
switch (this->m_column_types[j]) {
case column_type::free_column: return true;
case column_type::fixed: return false;
case column_type::low_bound:
if (sign < 0)
return true;
return !this->x_is_at_low_bound(j);
case column_type::upper_bound:
if (sign > 0)
return true;
return !this->x_is_at_upper_bound(j);
case column_type::boxed:
if (sign < 0)
return !this->x_is_at_low_bound(j);
return !this->x_is_at_upper_bound(j);
}
lean_assert(false); // cannot be here
return false;
}
bool needs_to_grow(unsigned bj) const {
lean_assert(!this->column_is_feasible(bj));
switch(this->m_column_types[bj]) {
case column_type::free_column:
return false;
case column_type::fixed:
case column_type::low_bound:
case column_type::boxed:
return this-> x_below_low_bound(bj);
default:
return false;
}
lean_assert(false); // unreachable
return false;
}
int inf_sign_of_column(unsigned bj) const {
lean_assert(!this->column_is_feasible(bj));
switch(this->m_column_types[bj]) {
case column_type::free_column:
return 0;
case column_type::low_bound:
return 1;
case column_type::fixed:
case column_type::boxed:
return this->x_above_upper_bound(bj)? -1: 1;
default:
return -1;
}
lean_assert(false); // unreachable
return 0;
}
bool monoid_can_decrease(const row_cell<T> & rc) const {
unsigned j = rc.m_j;
lean_assert(this->column_is_feasible(j));
switch (this->m_column_types[j]) {
case column_type::free_column:
return true;
case column_type::fixed:
return false;
case column_type::low_bound:
if (is_pos(rc.get_val())) {
return this->x_above_low_bound(j);
}
return true;
case column_type::upper_bound:
if (is_pos(rc.get_val())) {
return true;
}
return this->x_below_upper_bound(j);
case column_type::boxed:
if (is_pos(rc.get_val())) {
return this->x_above_low_bound(j);
}
return this->x_below_upper_bound(j);
default:
return false;
}
lean_assert(false); // unreachable
return false;
}
bool monoid_can_increase(const row_cell<T> & rc) const {
unsigned j = rc.m_j;
lean_assert(this->column_is_feasible(j));
switch (this->m_column_types[j]) {
case column_type::free_column:
return true;
case column_type::fixed:
return false;
case column_type::low_bound:
if (is_neg(rc.get_val())) {
return this->x_above_low_bound(j);
}
return true;
case column_type::upper_bound:
if (is_neg(rc.get_val())) {
return true;
}
return this->x_below_upper_bound(j);
case column_type::boxed:
if (is_neg(rc.get_val())) {
return this->x_above_low_bound(j);
}
return this->x_below_upper_bound(j);
default:
return false;
}
lean_assert(false); // unreachable
return false;
}
unsigned get_number_of_basic_vars_that_might_become_inf(unsigned j) const { // consider looking at the signs here: todo
unsigned r = 0;
for (auto & cc : this->m_A.m_columns[j]) {
unsigned k = this->m_basis[cc.m_i];
if (this->m_column_types[k] != column_type::free_column)
r++;
}
return r;
}
int find_beneficial_column_in_row_tableau_rows_bland_mode(int i, T & a_ent) {
int j = -1;
unsigned bj = this->m_basis[i];
bool bj_needs_to_grow = needs_to_grow(bj);
for (const row_cell<T>& rc : this->m_A.m_rows[i]) {
if (rc.m_j == bj)
continue;
if (bj_needs_to_grow) {
if (!monoid_can_decrease(rc))
continue;
} else {
if (!monoid_can_increase(rc))
continue;
}
if (rc.m_j < static_cast<unsigned>(j) ) {
j = rc.m_j;
a_ent = rc.m_value;
}
}
if (j == -1) {
m_inf_row_index_for_tableau = i;
}
return j;
}
int find_beneficial_column_in_row_tableau_rows(int i, T & a_ent) {
if (m_bland_mode_tableau)
return find_beneficial_column_in_row_tableau_rows_bland_mode(i, a_ent);
// a short row produces short infeasibility explanation and benefits at least one pivot operation
vector<const row_cell<T>*> choices;
unsigned num_of_non_free_basics = 1000000;
unsigned len = 100000000;
unsigned bj = this->m_basis[i];
bool bj_needs_to_grow = needs_to_grow(bj);
for (const row_cell<T>& rc : this->m_A.m_rows[i]) {
unsigned j = rc.m_j;
if (j == bj)
continue;
if (bj_needs_to_grow) {
if (!monoid_can_decrease(rc))
continue;
} else {
if (!monoid_can_increase(rc))
continue;
}
unsigned damage = get_number_of_basic_vars_that_might_become_inf(j);
if (damage < num_of_non_free_basics) {
num_of_non_free_basics = damage;
len = this->m_A.m_columns[j].size();
choices.clear();
choices.push_back(&rc);
} else if (damage == num_of_non_free_basics &&
this->m_A.m_columns[j].size() <= len && (my_random() % 2)) {
choices.push_back(&rc);
len = this->m_A.m_columns[j].size();
}
}
if (choices.size() == 0) {
m_inf_row_index_for_tableau = i;
return -1;
}
const row_cell<T>* rc = choices.size() == 1? choices[0] :
choices[my_random() % choices.size()];
a_ent = rc->m_value;
return rc->m_j;
}
static X positive_infinity() {
return convert_struct<X, unsigned>::convert(std::numeric_limits<unsigned>::max());
}
bool get_harris_theta(X & theta);
void restore_harris_eps() { m_converted_harris_eps = convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance); }
void zero_harris_eps() { m_converted_harris_eps = zero_of_type<T>(); }
int find_leaving_on_harris_theta(X const & harris_theta, X & t);
bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited);
bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t);
int find_leaving_and_t(unsigned entering, X & t);
int find_leaving_and_t_precise(unsigned entering, X & t);
int find_leaving_and_t_tableau(unsigned entering, X & t);
void limit_theta(const X & lim, X & theta, bool & unlimited) {
if (unlimited) {
theta = lim;
unlimited = false;
} else {
theta = std::min(lim, theta);
}
}
void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
lean_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound);
limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited);
}
void limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
lean_assert(m < 0 && this->m_column_types[j] == column_type::low_bound);
limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited);
}
void limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
lean_assert(m > 0 && this->m_column_types[j] == column_type::low_bound);
limit_inf_on_low_bound_m_pos(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited);
}
void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
lean_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound);
limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited);
};
X harris_eps_for_bound(const X & bound) const { return ( convert_struct<X, int>::convert(1) + abs(bound)/10) * m_converted_harris_eps/3;
}
void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector<unsigned> & leavings, T m, X & t, T & abs_of_d_of_leaving);
vector<T> m_low_bounds_dummy; // needed for the base class only
X get_max_bound(vector<X> & b);
#ifdef LEAN_DEBUG
void check_Ax_equal_b();
void check_the_bounds();
void check_bound(unsigned i);
void check_correctness();
#endif
// from page 183 of Istvan Maros's book
// the basis structures have not changed yet
void update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving);
// return 0 if the reduced cost at entering is close enough to the refreshed
// 1 if it is way off, and 2 if it is unprofitable
int refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering);
void backup_and_normalize_costs();
void init_run();
void calc_working_vector_beta_for_column_norms();
void advance_on_entering_and_leaving(int entering, int leaving, X & t);
void advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t);
void advance_on_entering_equal_leaving(int entering, X & t);
void advance_on_entering_equal_leaving_tableau(int entering, X & t);
bool need_to_switch_costs() const {
if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
return false;
// lean_assert(calc_current_x_is_feasible() == current_x_is_feasible());
return this->current_x_is_feasible() == this->m_using_infeas_costs;
}
void advance_on_entering(int entering);
void advance_on_entering_tableau(int entering);
void advance_on_entering_precise(int entering);
void push_forward_offset_in_non_basis(unsigned & offset_in_nb);
unsigned get_number_of_non_basic_column_to_try_for_enter();
void print_column_norms(std::ostream & out);
// returns the number of iterations
unsigned solve();
lu<T, X> * factorization() {return this->m_factorization;}
void delete_factorization();
// according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming"
void init_column_norms();
T calculate_column_norm_exactly(unsigned j);
void update_or_init_column_norms(unsigned entering, unsigned leaving);
// following Swietanowski - A new steepest ...
void update_column_norms(unsigned entering, unsigned leaving);
T calculate_norm_of_entering_exactly();
void find_feasible_solution();
bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;}
void one_iteration();
void one_iteration_tableau();
void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, const X &theta ) {
this->update_basis_and_x_tableau(entering, leaving, theta);
this->update_column_in_inf_set(entering);
}
int find_leaving_tableau_rows(X & new_val_for_leaving) {
int j = -1;
for (unsigned k : this->m_inf_set.m_index) {
if (k < static_cast<unsigned>(j))
j = static_cast<int>(k);
}
if (j == -1)
return -1;
lean_assert(!this->column_is_feasible(j));
switch (this->m_column_types[j]) {
case column_type::fixed:
case column_type::upper_bound:
new_val_for_leaving = this->m_upper_bounds[j];
break;
case column_type::low_bound:
new_val_for_leaving = this->m_low_bounds[j];
break;
case column_type::boxed:
if (this->x_above_upper_bound(j))
new_val_for_leaving = this->m_upper_bounds[j];
else
new_val_for_leaving = this->m_low_bounds[j];
break;
default:
lean_assert(false);
}
return j;
}
void one_iteration_tableau_rows() {
X new_val_for_leaving;
int leaving = find_leaving_tableau_rows(new_val_for_leaving);
if (leaving == -1) {
this->set_status(OPTIMAL);
return;
}
if (!m_bland_mode_tableau) {
if (m_left_basis_tableau.contains(leaving)) {
if (++m_left_basis_repeated > m_bland_mode_threshold) {
m_bland_mode_tableau = true;
}
} else {
m_left_basis_tableau.insert(leaving);
}
}
T a_ent;
int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent);
if (entering == -1) {
this->set_status(INFEASIBLE);
return;
}
X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent;
advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta );
lean_assert(this->m_x[leaving] == new_val_for_leaving);
if (this->current_x_is_feasible())
this->set_status(OPTIMAL);
}
void fill_breakpoints_array(unsigned entering);
void try_add_breakpoint_in_row(unsigned i);
void clear_breakpoints();
void change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering);
void advance_on_sorted_breakpoints(unsigned entering);
void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta);
void decide_on_status_when_cannot_find_entering() {
lean_assert(!need_to_switch_costs());
this->set_status(this->current_x_is_feasible()? OPTIMAL: INFEASIBLE);
}
// void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) {
// lean_assert(m < 0);
// lean_assert(this->m_column_type[j] == low_bound || this->m_column_type[j] == boxed);
// const X & eps = harris_eps_for_bound(this->m_low_bounds[j]);
// if (this->above_bound(this->m_x[j], this->m_low_bounds[j])) {
// theta = std::min((this->m_low_bounds[j] -this->m_x[j] - eps) / m, theta);
// if (theta < zero_of_type<X>()) theta = zero_of_type<X>();
// }
// }
void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) {
lean_assert(m < 0);
const X& eps = harris_eps_for_bound(this->m_low_bounds[j]);
limit_theta((this->m_low_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited);
if (theta < zero_of_type<X>()) theta = zero_of_type<X>();
}
bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
// x gets smaller
lean_assert(m < 0);
if (numeric_traits<T>::precise()) {
if (this->below_bound(x, bound)) return false;
if (this->above_bound(x, bound)) {
limit_theta((bound - x) / m, theta, unlimited);
} else {
theta = zero_of_type<X>();
unlimited = false;
}
} else {
const X& eps = harris_eps_for_bound(bound);
if (this->below_bound(x, bound)) return false;
if (this->above_bound(x, bound)) {
limit_theta((bound - x - eps) / m, theta, unlimited);
} else {
theta = zero_of_type<X>();
unlimited = false;
}
}
return true;
}
bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
// x gets larger
lean_assert(m > 0);
if (numeric_traits<T>::precise()) {
if (this->above_bound(x, bound)) return false;
if (this->below_bound(x, bound)) {
limit_theta((bound - x) / m, theta, unlimited);
} else {
theta = zero_of_type<X>();
unlimited = false;
}
} else {
const X& eps = harris_eps_for_bound(bound);
if (this->above_bound(x, bound)) return false;
if (this->below_bound(x, bound)) {
limit_theta((bound - x + eps) / m, theta, unlimited);
} else {
theta = zero_of_type<X>();
unlimited = false;
}
}
return true;
}
void limit_inf_on_low_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
if (numeric_traits<T>::precise()) {
// x gets larger
lean_assert(m > 0);
if (this->below_bound(x, bound)) {
limit_theta((bound - x) / m, theta, unlimited);
}
}
else {
// x gets larger
lean_assert(m > 0);
const X& eps = harris_eps_for_bound(bound);
if (this->below_bound(x, bound)) {
limit_theta((bound - x + eps) / m, theta, unlimited);
}
}
}
void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
// x gets smaller
lean_assert(m < 0);
const X& eps = harris_eps_for_bound(bound);
if (this->above_bound(x, bound)) {
limit_theta((bound - x - eps) / m, theta, unlimited);
}
}
void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) {
// lean_assert(m > 0 && this->m_column_type[j] == column_type::boxed);
const X & x = this->m_x[j];
const X & lbound = this->m_low_bounds[j];
if (this->below_bound(x, lbound)) {
const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]);
limit_theta((lbound - x + eps) / m, theta, unlimited);
} else {
const X & ubound = this->m_upper_bounds[j];
if (this->below_bound(x, ubound)){
const X& eps = harris_eps_for_bound(ubound);
limit_theta((ubound - x + eps) / m, theta, unlimited);
} else if (!this->above_bound(x, ubound)) {
theta = zero_of_type<X>();
unlimited = false;
}
}
}
void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) {
// lean_assert(m < 0 && this->m_column_type[j] == column_type::boxed);
const X & x = this->m_x[j];
const X & ubound = this->m_upper_bounds[j];
if (this->above_bound(x, ubound)) {
const X& eps = harris_eps_for_bound(ubound);
limit_theta((ubound - x - eps) / m, theta, unlimited);
} else {
const X & lbound = this->m_low_bounds[j];
if (this->above_bound(x, lbound)){
const X& eps = harris_eps_for_bound(lbound);
limit_theta((lbound - x - eps) / m, theta, unlimited);
} else if (!this->below_bound(x, lbound)) {
theta = zero_of_type<X>();
unlimited = false;
}
}
}
void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) {
lean_assert(m > 0);
const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]);
if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) {
limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited);
if (theta < zero_of_type<X>()) {
theta = zero_of_type<X>();
unlimited = false;
}
}
}
void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) {
lean_assert(m > 0);
const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]);
limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited);
if (theta < zero_of_type<X>()) {
theta = zero_of_type<X>();
}
}
// j is a basic column or the entering, in any case x[j] has to stay feasible.
// m is the multiplier. updating t in a way that holds the following
// x[j] + t * m >= this->m_low_bounds[j]- harris_feasibility_tolerance ( if m < 0 )
// or
// x[j] + t * m <= this->m_upper_bounds[j] + harris_feasibility_tolerance ( if m > 0)
void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) {
switch (this->m_column_types[j]) {
case column_type::free_column: break;
case column_type::upper_bound:
if (this->current_x_is_feasible()) {
if (m > 0)
limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited);
} else { // inside of feasibility_loop
if (m > 0)
limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(j, m, theta, unlimited);
else
limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(j, m, theta, unlimited);
}
break;
case column_type::low_bound:
if (this->current_x_is_feasible()) {
if (m < 0)
limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited);
} else {
if (m < 0)
limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(j, m, theta, unlimited);
else
limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(j, m, theta, unlimited);
}
break;
// case fixed:
// if (get_this->current_x_is_feasible()) {
// theta = zero_of_type<X>();
// break;
// }
// if (m < 0)
// limit_theta_on_basis_column_for_inf_case_m_neg_fixed(j, m, theta);
// else
// limit_theta_on_basis_column_for_inf_case_m_pos_fixed(j, m, theta);
// break;
case column_type::fixed:
case column_type::boxed:
if (this->current_x_is_feasible()) {
if (m > 0) {
limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited);
} else {
limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited);
}
} else {
if (m > 0) {
limit_theta_on_basis_column_for_inf_case_m_pos_boxed(j, m, theta, unlimited);
} else {
limit_theta_on_basis_column_for_inf_case_m_neg_boxed(j, m, theta, unlimited);
}
}
break;
default:
lean_unreachable();
}
if (!unlimited && theta < zero_of_type<X>()) {
theta = zero_of_type<X>();
}
}
bool column_is_benefitial_for_entering_basis(unsigned j) const;
bool column_is_benefitial_for_entering_basis_precise(unsigned j) const;
bool column_is_benefitial_for_entering_on_breakpoints(unsigned j) const;
bool can_enter_basis(unsigned j);
bool done();
void init_infeasibility_costs();
void init_infeasibility_cost_for_column(unsigned j);
T get_infeasibility_cost_for_column(unsigned j) const;
void init_infeasibility_costs_for_changed_basis_only();
void print_column(unsigned j, std::ostream & out);
void add_breakpoint(unsigned j, X delta, breakpoint_type type);
// j is the basic column, x is the value at x[j]
// d is the coefficient before m_entering in the row with j as the basis column
void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value);
template <typename L>
bool same_sign_with_entering_delta(const L & a) {
return (a > zero_of_type<L>() && m_sign_of_entering_delta > 0) || (a < zero_of_type<L>() && m_sign_of_entering_delta < 0);
}
void init_reduced_costs();
bool low_bounds_are_set() const { return true; }
int advance_on_sorted_breakpoints(unsigned entering, X & t);
std::string break_type_to_string(breakpoint_type type);
void print_breakpoint(const breakpoint<X> * b, std::ostream & out);
void print_bound_info_and_x(unsigned j, std::ostream & out);
void init_infeasibility_after_update_x_if_inf(unsigned leaving) {
if (this->m_using_infeas_costs) {
init_infeasibility_costs_for_changed_basis_only();
this->m_costs[leaving] = zero_of_type<T>();
this->m_inf_set.erase(leaving);
}
}
void init_inf_set() {
this->m_inf_set.clear();
for (unsigned j = 0; j < this->m_n(); j++) {
if (this->m_basis_heading[j] < 0)
continue;
if (!this->column_is_feasible(j))
this->m_inf_set.insert(j);
}
}
int get_column_out_of_bounds_delta_sign(unsigned j) {
switch (this->m_column_types[j]) {
case column_type::fixed:
case column_type::boxed:
if (this->x_below_low_bound(j))
return -1;
if (this->x_above_upper_bound(j))
return 1;
break;
case column_type::low_bound:
if (this->x_below_low_bound(j))
return -1;
break;
case column_type::upper_bound:
if (this->x_above_upper_bound(j))
return 1;
break;
case column_type::free_column:
return 0;
default:
lean_assert(false);
}
return 0;
}
void init_column_row_non_zeroes() {
this->m_columns_nz.resize(this->m_A.column_count());
this->m_rows_nz.resize(this->m_A.row_count());
for (unsigned i = 0; i < this->m_A.column_count(); i++) {
if (this->m_columns_nz[i] == 0)
this->m_columns_nz[i] = this->m_A.m_columns[i].size();
}
for (unsigned i = 0; i < this->m_A.row_count(); i++) {
if (this->m_rows_nz[i] == 0)
this->m_rows_nz[i] = this->m_A.m_rows[i].size();
}
}
int x_at_bound_sign(unsigned j) {
switch (this->m_column_types[j]) {
case column_type::fixed:
return 0;
case column_type::boxed:
if (this->x_is_at_low_bound(j))
return 1;
return -1;
break;
case column_type::low_bound:
return 1;
break;
case column_type::upper_bound:
return -1;
break;
default:
lean_assert(false);
}
return 0;
}
unsigned solve_with_tableau();
bool basis_column_is_set_correctly(unsigned j) const {
return this->m_A.m_columns[j].size() == 1;
}
bool basis_columns_are_set_correctly() const {
for (unsigned j : this->m_basis)
if(!basis_column_is_set_correctly(j))
return false;
return true;
}
void init_run_tableau();
void update_x_tableau(unsigned entering, const X & delta);
void update_inf_cost_for_column_tableau(unsigned j);
// the delta is between the old and the new cost (old - new)
void update_reduced_cost_for_basic_column_cost_change(const T & delta, unsigned j) {
lean_assert(this->m_basis_heading[j] >= 0);
unsigned i = static_cast<unsigned>(this->m_basis_heading[j]);
for (const row_cell<T> & rc : this->m_A.m_rows[i]) {
unsigned k = rc.m_j;
if (k == j)
continue;
this->m_d[k] += delta * rc.get_val();
}
}
bool update_basis_and_x_tableau(int entering, int leaving, X const & tt);
void init_reduced_costs_tableau();
void init_tableau_rows() {
m_bland_mode_tableau = false;
m_left_basis_tableau.clear();
m_left_basis_tableau.resize(this->m_A.column_count());
m_left_basis_repeated = 0;
}
// stage1 constructor
lp_primal_core_solver(static_matrix<T, X> & A,
vector<X> & b, // the right side vector
vector<X> & x, // the number of elements in x needs to be at least as large as the number of columns in A
vector<unsigned> & basis,
vector<unsigned> & nbasis,
vector<int> & heading,
vector<T> & costs,
const vector<column_type> & column_type_array,
const vector<X> & low_bound_values,
const vector<X> & upper_bound_values,
lp_settings & settings,
const column_namer& column_names):
lp_core_solver_base<T, X>(A, b,
basis,
nbasis,
heading,
x,
costs,
settings,
column_names,
column_type_array,
low_bound_values,
upper_bound_values),
m_beta(A.row_count()) {
if (!(numeric_traits<T>::precise())) {
m_converted_harris_eps = convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance);
} else {
m_converted_harris_eps = zero_of_type<T>();
}
this->set_status(UNKNOWN);
}
// constructor
lp_primal_core_solver(static_matrix<T, X> & A,
vector<X> & b, // the right side vector
vector<X> & x, // the number of elements in x needs to be at least as large as the number of columns in A
vector<unsigned> & basis,
vector<unsigned> & nbasis,
vector<int> & heading,
vector<T> & costs,
const vector<column_type> & column_type_array,
const vector<X> & upper_bound_values,
lp_settings & settings,
const column_namer& column_names):
lp_core_solver_base<T, X>(A, b,
basis,
nbasis,
heading,
x,
costs,
settings,
column_names,
column_type_array,
m_low_bounds_dummy,
upper_bound_values),
m_beta(A.row_count()),
m_converted_harris_eps(convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance)) {
lean_assert(initial_x_is_correct());
m_low_bounds_dummy.resize(A.column_count(), zero_of_type<T>());
m_enter_price_eps = numeric_traits<T>::precise() ? numeric_traits<T>::zero() : T(1e-5);
#ifdef LEAN_DEBUG
// check_correctness();
#endif
}
bool initial_x_is_correct() {
std::set<unsigned> basis_set;
for (unsigned i = 0; i < this->m_A.row_count(); i++) {
basis_set.insert(this->m_basis[i]);
}
for (unsigned j = 0; j < this->m_n(); j++) {
if (this->column_has_low_bound(j) && this->m_x[j] < numeric_traits<T>::zero()) {
LP_OUT(this->m_settings, "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl);
return false;
}
if (this->column_has_upper_bound(j) && this->m_x[j] > this->m_upper_bounds[j]) {
LP_OUT(this->m_settings, "upper bound for " << j << " does not hold: " << this->m_upper_bounds[j] << ">" << this->m_x[j] << std::endl);
return false;
}
if (basis_set.find(j) != basis_set.end()) continue;
if (this->m_column_types[j] == column_type::low_bound) {
if (numeric_traits<T>::zero() != this->m_x[j]) {
LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits<T>::zero() << " is not equal to " << this->m_x[j] << std::endl);
return false;
}
}
if (this->m_column_types[j] == column_type::boxed) {
if (this->m_upper_bounds[j] != this->m_x[j] && !numeric_traits<T>::is_zero(this->m_x[j])) {
return false;
}
}
}
return true;
}
friend core_solver_pretty_printer<T, X>;
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,24 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <utility>
#include <memory>
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lar_solver.h"
#include "util/lp/lp_primal_core_solver.hpp"
#include "util/lp/lp_primal_core_solver_tableau.hpp"
namespace lean {
template void lp_primal_core_solver<double, double>::find_feasible_solution();
template void lean::lp_primal_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::find_feasible_solution();
template unsigned lp_primal_core_solver<double, double>::solve();
template unsigned lp_primal_core_solver<double, double>::solve_with_tableau();
template unsigned lp_primal_core_solver<mpq, mpq>::solve();
template void lean::lp_primal_core_solver<double, double>::clear_breakpoints();
template bool lean::lp_primal_core_solver<lean::mpq, lean::mpq>::update_basis_and_x_tableau(int, int, lean::mpq const&);
template bool lean::lp_primal_core_solver<double, double>::update_basis_and_x_tableau(int, int, double const&);
template bool lean::lp_primal_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::update_basis_and_x_tableau(int, int, lean::numeric_pair<lean::mpq> const&);
}

View file

@ -0,0 +1,393 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
// this is a part of lp_primal_core_solver that deals with the tableau
#include "util/lp/lp_primal_core_solver.h"
namespace lean {
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);
}
lean_assert(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();
lean_assert(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 && my_random() % 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:
lean_assert(! (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->m_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));
lean_assert(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) {
lean_assert(this->A_mult_x_is_off() == false);
lean_assert(leaving >= 0 && entering >= 0);
lean_assert((this->m_settings.simplex_strategy() ==
simplex_strategy_enum::tableau_rows) ||
m_non_basis_list.back() == static_cast<unsigned>(entering));
lean_assert(this->m_using_infeas_costs || !is_neg(t));
lean_assert(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);
lean_assert(this->A_mult_x_is_off() == false);
this->m_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();
}
lean_assert(!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) {
lean_assert(!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->m_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);
lean_assert(!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);
lean_assert(!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 = my_random() % 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);
lean_assert(this->A_mult_x_is_off() == false);
lean_assert(basis_columns_are_set_correctly());
this->m_basis_sort_counter = 0; // to initiate the sort of the basis
this->set_total_iterations(0);
this->m_iters_with_no_cost_growing = 0;
lean_assert(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.m_simplex_strategy == simplex_strategy_enum::tableau_rows)
init_tableau_rows();
lean_assert(this->reduced_costs_are_correct_tableau());
lean_assert(!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) {
lean_assert(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;
lean_assert(this->column_is_feasible(entering));
lean_assert(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);
}
}
lean_assert(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) {
lean_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows);
lean_assert(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

@ -0,0 +1,96 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include <unordered_map>
#include <string>
#include <algorithm>
#include "util/lp/lp_utils.h"
#include "util/lp/column_info.h"
#include "util/lp/lp_primal_core_solver.h"
#include "util/lp/lp_solver.h"
#include "util/lp/iterator_on_row.h"
namespace lean {
template <typename T, typename X>
class lp_primal_simplex: public lp_solver<T, X> {
lp_primal_core_solver<T, X> * m_core_solver = nullptr;
vector<X> m_low_bounds;
private:
unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); }
void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns);
void init_buffer(unsigned k, vector<T> & r);
void refactor();
void set_scaled_costs();
public:
lp_primal_simplex() {}
column_info<T> * get_or_create_column_info(unsigned column);
void set_status(lp_status status) {
this->m_status = status;
}
lp_status get_status() {
return this->m_status;
}
void fill_acceptable_values_for_x();
void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i);
void fill_costs_and_x_for_first_stage_solver_for_row(
int row,
unsigned & slack_var,
unsigned & artificial);
void set_core_solver_bounds();
void update_time_limit_from_starting_time(int start_time) {
this->m_settings.time_limit -= (get_millisecond_span(start_time) / 1000.);
}
void find_maximal_solution();
void fill_A_x_and_basis_for_stage_one_total_inf();
void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row);
void solve_with_total_inf();
~lp_primal_simplex();
bool bounds_hold(std::unordered_map<std::string, T> const & solution);
T get_row_value(unsigned i, std::unordered_map<std::string, T> const & solution, std::ostream * out);
bool row_constraint_holds(unsigned i, std::unordered_map<std::string, T> const & solution, std::ostream * out);
bool row_constraints_hold(std::unordered_map<std::string, T> const & solution);
T * get_array_from_map(std::unordered_map<std::string, T> const & solution);
bool solution_is_feasible(std::unordered_map<std::string, T> const & solution) {
return bounds_hold(solution) && row_constraints_hold(solution);
}
virtual T get_column_value(unsigned column) const {
return this->get_column_value_with_core_solver(column, m_core_solver);
}
T get_current_cost() const;
};
}

View file

@ -0,0 +1,355 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <string>
#include "util/vector.h"
#include "util/lp/lp_primal_simplex.h"
namespace lean {
template <typename T, typename X> void lp_primal_simplex<T, X>::fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) {
unsigned slack_var = original_number_of_columns;
unsigned artificial = original_number_of_columns + this->m_slacks;
for (unsigned row = 0; row < this->row_count(); row++) {
fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial);
}
}
template <typename T, typename X> void lp_primal_simplex<T, X>::init_buffer(unsigned k, vector<T> & r) {
for (unsigned i = 0; i < k; i++) {
r[i] = 0;
}
r[k] = 1;
for (unsigned i = this->row_count() -1; i > k; i--) {
r[i] = 0;
}
}
template <typename T, typename X> void lp_primal_simplex<T, X>::refactor() {
m_core_solver->init_lu();
if (m_core_solver->factorization()->get_status() != LU_status::OK) {
throw_exception("cannot refactor");
}
}
template <typename T, typename X> void lp_primal_simplex<T, X>::set_scaled_costs() {
unsigned j = this->number_of_core_structurals();
while (j-- > 0) {
this->set_scaled_cost(j);
}
}
template <typename T, typename X> column_info<T> * lp_primal_simplex<T, X>::get_or_create_column_info(unsigned column) {
auto it = this->m_columns.find(column);
return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info<T>) : it->second;
}
template <typename T, typename X> void lp_primal_simplex<T, X>::fill_acceptable_values_for_x() {
for (auto t : this->m_core_solver_columns_to_external_columns) {
this->m_x[t.first] = numeric_traits<T>::zero();
}
}
template <typename T, typename X> void lp_primal_simplex<T, X>::set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) {
bound_is_set[i] = true;
bounds[i] = numeric_traits<T>::zero();
}
template <typename T, typename X> void lp_primal_simplex<T, X>::fill_costs_and_x_for_first_stage_solver_for_row(
int row,
unsigned & slack_var,
unsigned & artificial) {
lean_assert(row >= 0 && row < this->row_count());
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]];
// we need to bring the program to the form Ax = b
T rs = this->m_b[row];
T artificial_cost = - numeric_traits<T>::one();
switch (constraint.m_relation) {
case Equal: // no slack variable here
this->m_column_types[artificial] = column_type::low_bound;
this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero
this->m_basis[row] = artificial;
if (rs >= 0) {
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
this->m_x[artificial] = rs;
} else {
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
this->m_x[artificial] = - rs;
}
artificial++;
break;
case Greater_or_equal:
this->m_column_types[slack_var] = column_type::low_bound;
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
if (rs > 0) {
lean_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
// adding one artificial
this->m_column_types[artificial] = column_type::low_bound;
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
this->m_costs[artificial] = artificial_cost;
this->m_basis[row] = artificial;
this->m_x[artificial] = rs;
artificial++;
} else {
// we can put a slack_var into the basis, and atemplate <typename T, typename X> void lp_primal_simplex<T, X>::adding an artificial variable
this->m_basis[row] = slack_var;
this->m_x[slack_var] = - rs;
}
slack_var++;
break;
case Less_or_equal:
// introduce a non-negative slack variable
this->m_column_types[slack_var] = column_type::low_bound;
(*this->m_A)(row, slack_var) = numeric_traits<T>::one();
if (rs < 0) {
// adding one artificial
lean_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
this->m_column_types[artificial] = column_type::low_bound;
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
this->m_costs[artificial] = artificial_cost;
this->m_x[artificial] = - rs;
this->m_basis[row] = artificial++;
} else {
// we can put slack_var into the basis, and atemplate <typename T, typename X> void lp_primal_simplex<T, X>::adding an artificial variable
this->m_basis[row] = slack_var;
this->m_x[slack_var] = rs;
}
slack_var++;
break;
}
}
template <typename T, typename X> void lp_primal_simplex<T, X>::set_core_solver_bounds() {
unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials;
this->m_column_types.resize(total_vars);
this->m_upper_bounds.resize(total_vars);
for (auto cit : this->m_map_from_var_index_to_column_info) {
column_info<T> * ci = cit.second;
unsigned j = ci->get_column_index();
if (!is_valid(j))
continue; // the variable is not mapped to a column
switch (this->m_column_types[j] = ci->get_column_type()){
case column_type::fixed:
this->m_upper_bounds[j] = numeric_traits<T>::zero();
break;
case column_type::boxed:
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
break;
default: break; // do nothing
}
}
}
template <typename T, typename X> void lp_primal_simplex<T, X>::find_maximal_solution() {
int preprocessing_start_time = get_millisecond_count();
if (this->problem_is_empty()) {
this->m_status = lp_status::EMPTY;
return;
}
this->cleanup();
this->fill_matrix_A_and_init_right_side();
if (this->m_status == lp_status::INFEASIBLE) {
return;
}
this->m_x.resize(this->m_A->column_count());
this->fill_m_b();
this->scale();
fill_acceptable_values_for_x();
this->count_slacks_and_artificials();
set_core_solver_bounds();
update_time_limit_from_starting_time(preprocessing_start_time);
solve_with_total_inf();
}
template <typename T, typename X> void lp_primal_simplex<T, X>::fill_A_x_and_basis_for_stage_one_total_inf() {
for (unsigned row = 0; row < this->row_count(); row++)
fill_A_x_and_basis_for_stage_one_total_inf_for_row(row);
}
template <typename T, typename X> void lp_primal_simplex<T, X>::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) {
lean_assert(row < this->row_count());
auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row);
lean_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end());
unsigned ext_row = ext_row_it->second;
auto constr_it = this->m_constraints.find(ext_row);
lean_assert(constr_it != this->m_constraints.end());
auto & constraint = constr_it->second;
unsigned j = this->m_A->column_count(); // j is a slack variable
this->m_A->add_column();
// we need to bring the program to the form Ax = b
this->m_basis[row] = j;
switch (constraint.m_relation) {
case Equal:
this->m_x[j] = this->m_b[row];
(*this->m_A)(row, j) = numeric_traits<T>::one();
this->m_column_types[j] = column_type::fixed;
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>();
break;
case Greater_or_equal:
this->m_x[j] = - this->m_b[row];
(*this->m_A)(row, j) = - numeric_traits<T>::one();
this->m_column_types[j] = column_type::low_bound;
this->m_upper_bounds[j] = zero_of_type<X>();
break;
case Less_or_equal:
this->m_x[j] = this->m_b[row];
(*this->m_A)(row, j) = numeric_traits<T>::one();
this->m_column_types[j] = column_type::low_bound;
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>();
break;
default:
lean_unreachable();
}
}
template <typename T, typename X> void lp_primal_simplex<T, X>::solve_with_total_inf() {
int total_vars = this->m_A->column_count() + this->row_count();
if (total_vars == 0) {
this->m_status = OPTIMAL;
return;
}
m_low_bounds.clear();
m_low_bounds.resize(total_vars, zero_of_type<X>()); // low bounds are shifted ot zero
this->m_x.resize(total_vars, numeric_traits<T>::zero());
this->m_basis.resize(this->row_count());
this->m_costs.clear();
this->m_costs.resize(total_vars, zero_of_type<T>());
fill_A_x_and_basis_for_stage_one_total_inf();
if (this->m_settings.get_message_ostream() != nullptr)
this->print_statistics_on_A(*this->m_settings.get_message_ostream());
set_scaled_costs();
m_core_solver = new lp_primal_core_solver<T, X>(*this->m_A,
this->m_b,
this->m_x,
this->m_basis,
this->m_nbasis,
this->m_heading,
this->m_costs,
this->m_column_types,
m_low_bounds,
this->m_upper_bounds,
this->m_settings, *this);
m_core_solver->solve();
this->set_status(m_core_solver->get_status());
this->m_total_iterations = m_core_solver->total_iterations();
}
template <typename T, typename X> lp_primal_simplex<T, X>::~lp_primal_simplex() {
if (m_core_solver != nullptr) {
delete m_core_solver;
}
}
template <typename T, typename X> bool lp_primal_simplex<T, X>::bounds_hold(std::unordered_map<std::string, T> const & solution) {
for (auto it : this->m_map_from_var_index_to_column_info) {
auto sol_it = solution.find(it.second->get_name());
if (sol_it == solution.end()) {
std::stringstream s;
s << "cannot find column " << it.first << " in solution";
throw_exception(s.str() );
}
if (!it.second->bounds_hold(sol_it->second)) {
// std::cout << "bounds do not hold for " << it.second->get_name() << std::endl;
it.second->bounds_hold(sol_it->second);
return false;
}
}
return true;
}
template <typename T, typename X> T lp_primal_simplex<T, X>::get_row_value(unsigned i, std::unordered_map<std::string, T> const & solution, std::ostream * out) {
auto it = this->m_A_values.find(i);
if (it == this->m_A_values.end()) {
std::stringstream s;
s << "cannot find row " << i;
throw_exception(s.str() );
}
T ret = numeric_traits<T>::zero();
for (auto & pair : it->second) {
auto cit = this->m_map_from_var_index_to_column_info.find(pair.first);
lean_assert(cit != this->m_map_from_var_index_to_column_info.end());
column_info<T> * ci = cit->second;
auto sol_it = solution.find(ci->get_name());
lean_assert(sol_it != solution.end());
T column_val = sol_it->second;
if (out != nullptr) {
(*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") ";
}
ret += pair.second * column_val;
}
if (out != nullptr) {
(*out) << " = " << ret << std::endl;
}
return ret;
}
template <typename T, typename X> bool lp_primal_simplex<T, X>::row_constraint_holds(unsigned i, std::unordered_map<std::string, T> const & solution, std::ostream *out) {
T row_val = get_row_value(i, solution, out);
auto & constraint = this->m_constraints[i];
T rs = constraint.m_rs;
bool print = out != nullptr;
switch (constraint.m_relation) {
case Equal:
if (fabs(numeric_traits<T>::get_double(row_val - rs)) > 0.00001) {
if (print) {
(*out) << "should be = " << rs << std::endl;
}
return false;
}
return true;
case Greater_or_equal:
if (numeric_traits<T>::get_double(row_val - rs) < -0.00001) {
if (print) {
(*out) << "should be >= " << rs << std::endl;
}
return false;
}
return true;;
case Less_or_equal:
if (numeric_traits<T>::get_double(row_val - rs) > 0.00001) {
if (print) {
(*out) << "should be <= " << rs << std::endl;
}
return false;
}
return true;;
}
lean_unreachable();
return false; // it is unreachable
}
template <typename T, typename X> bool lp_primal_simplex<T, X>::row_constraints_hold(std::unordered_map<std::string, T> const & solution) {
for (auto it : this->m_A_values) {
if (!row_constraint_holds(it.first, solution, nullptr)) {
row_constraint_holds(it.first, solution, nullptr);
return false;
}
}
return true;
}
template <typename T, typename X> T lp_primal_simplex<T, X>::get_current_cost() const {
T ret = numeric_traits<T>::zero();
for (auto it : this->m_map_from_var_index_to_column_info) {
ret += this->get_column_cost_value(it.first, it.second);
}
return ret;
}
}

View file

@ -0,0 +1,20 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <utility>
#include <memory>
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lp_primal_simplex.hpp"
template bool lean::lp_primal_simplex<double, double>::bounds_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&);
template bool lean::lp_primal_simplex<double, double>::row_constraints_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&);
template double lean::lp_primal_simplex<double, double>::get_current_cost() const;
template double lean::lp_primal_simplex<double, double>::get_column_value(unsigned int) const;
template lean::lp_primal_simplex<double, double>::~lp_primal_simplex();
template lean::lp_primal_simplex<lean::mpq, lean::mpq>::~lp_primal_simplex();
template lean::mpq lean::lp_primal_simplex<lean::mpq, lean::mpq>::get_current_cost() const;
template lean::mpq lean::lp_primal_simplex<lean::mpq, lean::mpq>::get_column_value(unsigned int) const;
template void lean::lp_primal_simplex<double, double>::find_maximal_solution();
template void lean::lp_primal_simplex<lean::mpq, lean::mpq>::find_maximal_solution();

339
src/util/lp/lp_settings.h Normal file
View file

@ -0,0 +1,339 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include <string>
#include <algorithm>
#include <limits>
#include <sys/timeb.h>
#include <iomanip>
#include "util/lp/lp_utils.h"
namespace lean {
typedef unsigned var_index;
typedef unsigned constraint_index;
typedef unsigned row_index;
enum class column_type {
free_column = 0,
low_bound = 1,
upper_bound = 2,
boxed = 3,
fixed = 4
};
enum class simplex_strategy_enum {
tableau_rows = 0,
tableau_costs = 1,
no_tableau = 2
};
std::string column_type_to_string(column_type t);
enum lp_status {
UNKNOWN,
INFEASIBLE,
TENTATIVE_UNBOUNDED,
UNBOUNDED,
TENTATIVE_DUAL_UNBOUNDED,
DUAL_UNBOUNDED,
OPTIMAL,
FEASIBLE,
FLOATING_POINT_ERROR,
TIME_EXHAUSTED,
ITERATIONS_EXHAUSTED,
EMPTY,
UNSTABLE
};
// when the ratio of the vector lenth to domain size to is greater than the return value we switch to solve_By_for_T_indexed_only
template <typename X>
unsigned ratio_of_index_size_to_all_size() {
if (numeric_traits<X>::precise())
return 10;
return 120;
}
const char* lp_status_to_string(lp_status status);
inline std::ostream& operator<<(std::ostream& out, lp_status status) {
return out << lp_status_to_string(status);
}
lp_status lp_status_from_string(std::string status);
enum non_basic_column_value_position { at_low_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound };
template <typename X> bool is_epsilon_small(const X & v, const double& eps); // forward definition
int get_millisecond_count();
int get_millisecond_span(int start_time);
unsigned my_random();
void my_random_init(long unsigned seed);
class lp_resource_limit {
public:
virtual bool get_cancel_flag() = 0;
};
struct stats {
unsigned m_total_iterations;
unsigned m_iters_with_no_cost_growing;
unsigned m_num_factorizations;
unsigned m_num_of_implied_bounds;
unsigned m_need_to_solve_inf;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
struct lp_settings {
private:
class default_lp_resource_limit : public lp_resource_limit {
lp_settings& m_settings;
int m_start_time;
public:
default_lp_resource_limit(lp_settings& s): m_settings(s), m_start_time(get_millisecond_count()) {}
virtual bool get_cancel_flag() {
int span_in_mills = get_millisecond_span(m_start_time);
return (span_in_mills / 1000.0 > m_settings.time_limit);
}
};
default_lp_resource_limit m_default_resource_limit;
lp_resource_limit* m_resource_limit;
// used for debug output
std::ostream* m_debug_out = &std::cout;
// used for messages, for example, the computation progress messages
std::ostream* m_message_out = &std::cout;
stats m_stats;
public:
unsigned reps_in_scaler = 20;
// when the absolute value of an element is less than pivot_epsilon
// in pivoting, we treat it as a zero
double pivot_epsilon = 0.00000001;
// see Chatal, page 115
double positive_price_epsilon = 1e-7;
// a quatation "if some choice of the entering vairable leads to an eta matrix
// whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ...
double entering_diag_epsilon = 1e-8;
int c_partial_pivoting = 10; // this is the constant c from page 410
unsigned depth_of_rook_search = 4;
bool using_partial_pivoting = true;
// dissertation of Achim Koberstein
// if Bx - b is different at any component more that refactor_epsilon then we refactor
double refactor_tolerance = 1e-4;
double pivot_tolerance = 1e-6;
double zero_tolerance = 1e-12;
double drop_tolerance = 1e-14;
double tolerance_for_artificials = 1e-4;
double can_be_taken_to_basis_tolerance = 0.00001;
unsigned percent_of_entering_to_check = 5; // we try to find a profitable column in a percentage of the columns
bool use_scaling = true;
double scaling_maximum = 1;
double scaling_minimum = 0.5;
double harris_feasibility_tolerance = 1e-7; // page 179 of Istvan Maros
double ignore_epsilon_of_harris = 10e-5;
unsigned max_number_of_iterations_with_no_improvements = 2000000;
unsigned max_total_number_of_iterations = 20000000;
double time_limit = std::numeric_limits<double>::max(); // the maximum time limit of the total run time in seconds
// dual section
double dual_feasibility_tolerance = 1e-7; // // page 71 of the PhD thesis of Achim Koberstein
double primal_feasibility_tolerance = 1e-7; // page 71 of the PhD thesis of Achim Koberstein
double relative_primal_feasibility_tolerance = 1e-9; // page 71 of the PhD thesis of Achim Koberstein
bool m_bound_propagation = true;
bool bound_progation() const {
return m_bound_propagation;
}
bool& bound_propagation() {
return m_bound_propagation;
}
lp_settings() : m_default_resource_limit(*this), m_resource_limit(&m_default_resource_limit) {}
void set_resource_limit(lp_resource_limit& lim) { m_resource_limit = &lim; }
bool get_cancel_flag() const { return m_resource_limit->get_cancel_flag(); }
void set_debug_ostream(std::ostream* out) { m_debug_out = out; }
void set_message_ostream(std::ostream* out) { m_message_out = out; }
std::ostream* get_debug_ostream() { return m_debug_out; }
std::ostream* get_message_ostream() { return m_message_out; }
stats& st() { return m_stats; }
stats const& st() const { return m_stats; }
template <typename T> static bool is_eps_small_general(const T & t, const double & eps) {
return (!numeric_traits<T>::precise())? is_epsilon_small<T>(t, eps) : numeric_traits<T>::is_zero(t);
}
template <typename T>
bool abs_val_is_smaller_than_dual_feasibility_tolerance(T const & t) {
return is_eps_small_general<T>(t, dual_feasibility_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_primal_feasibility_tolerance(T const & t) {
return is_eps_small_general<T>(t, primal_feasibility_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_can_be_taken_to_basis_tolerance(T const & t) {
return is_eps_small_general<T>(t, can_be_taken_to_basis_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_drop_tolerance(T const & t) const {
return is_eps_small_general<T>(t, drop_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_zero_tolerance(T const & t) {
return is_eps_small_general<T>(t, zero_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_refactor_tolerance(T const & t) {
return is_eps_small_general<T>(t, refactor_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_pivot_tolerance(T const & t) {
return is_eps_small_general<T>(t, pivot_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_harris_tolerance(T const & t) {
return is_eps_small_general<T>(t, harris_feasibility_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_ignore_epslilon_for_harris(T const & t) {
return is_eps_small_general<T>(t, ignore_epsilon_of_harris);
}
template <typename T>
bool abs_val_is_smaller_than_artificial_tolerance(T const & t) {
return is_eps_small_general<T>(t, tolerance_for_artificials);
}
// the method of lar solver to use
bool presolve_with_double_solver_for_lar = true;
simplex_strategy_enum m_simplex_strategy = simplex_strategy_enum::tableau_rows;
simplex_strategy_enum simplex_strategy() const {
return m_simplex_strategy;
}
simplex_strategy_enum & simplex_strategy() {
return m_simplex_strategy;
}
bool use_tableau() const {
return m_simplex_strategy != simplex_strategy_enum::no_tableau;
}
bool use_tableau_rows() const {
return m_simplex_strategy == simplex_strategy_enum::tableau_rows;
}
int report_frequency = 1000;
bool print_statistics = false;
unsigned column_norms_update_frequency = 12000;
bool scale_with_ratio = true;
double density_threshold = 0.7; // need to tune it up, todo
#ifdef LEAN_DEBUG
static unsigned ddd; // used for debugging
#endif
bool use_breakpoints_in_feasibility_search = false;
unsigned random_seed = 1;
static unsigned long random_next;
unsigned max_row_length_for_bound_propagation = 300;
bool backup_costs = true;
}; // end of lp_settings class
#define LP_OUT(_settings_, _msg_) { if (_settings_.get_debug_ostream()) { *_settings_.get_debug_ostream() << _msg_; } }
template <typename T>
std::string T_to_string(const T & t) {
std::ostringstream strs;
strs << t;
return strs.str();
}
inline std::string T_to_string(const numeric_pair<mpq> & t) {
std::ostringstream strs;
double r = (t.x + t.y / mpq(1000)).get_double();
strs << r;
return strs.str();
}
inline std::string T_to_string(const mpq & t) {
std::ostringstream strs;
strs << t.get_double();
return strs.str();
}
template <typename T>
bool val_is_smaller_than_eps(T const & t, double const & eps) {
if (!numeric_traits<T>::precise()) {
return numeric_traits<T>::get_double(t) < eps;
}
return t <= numeric_traits<T>::zero();
}
template <typename T>
bool vectors_are_equal(T * a, vector<T> &b, unsigned n);
template <typename T>
bool vectors_are_equal(const vector<T> & a, const buffer<T> &b);
template <typename T>
bool vectors_are_equal(const vector<T> & a, const vector<T> &b);
template <typename T>
T abs (T const & v) { return v >= zero_of_type<T>() ? v : -v; }
template <typename X>
X max_abs_in_vector(vector<X>& t){
X r(zero_of_type<X>());
for (auto & v : t)
r = std::max(abs(v) , r);
return r;
}
inline void print_blanks(int n, std::ostream & out) {
while (n--) {out << ' '; }
}
// after a push of the last element we ensure that the vector increases
// we also suppose that before the last push the vector was increasing
inline void ensure_increasing(vector<unsigned> & v) {
lean_assert(v.size() > 0);
unsigned j = v.size() - 1;
for (; j > 0; j-- )
if (v[j] <= v[j - 1]) {
// swap
unsigned t = v[j];
v[j] = v[j-1];
v[j-1] = t;
} else {
break;
}
}
#if LEAN_DEBUG
bool D();
#endif
}

133
src/util/lp/lp_settings.hpp Normal file
View file

@ -0,0 +1,133 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <cmath>
#include <string>
#include "util/vector.h"
#include "util/lp/lp_settings.h"
namespace lean {
std::string column_type_to_string(column_type t) {
switch (t) {
case column_type::fixed: return "fixed";
case column_type::boxed: return "boxed";
case column_type::low_bound: return "low_bound";
case column_type::upper_bound: return "upper_bound";
case column_type::free_column: return "free_column";
default: lean_unreachable();
}
return "unknown"; // it is unreachable
}
const char* lp_status_to_string(lp_status status) {
switch (status) {
case UNKNOWN: return "UNKNOWN";
case INFEASIBLE: return "INFEASIBLE";
case UNBOUNDED: return "UNBOUNDED";
case TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED";
case DUAL_UNBOUNDED: return "DUAL_UNBOUNDED";
case OPTIMAL: return "OPTIMAL";
case FEASIBLE: return "FEASIBLE";
case FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR";
case TIME_EXHAUSTED: return "TIME_EXHAUSTED";
case ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED";
case EMPTY: return "EMPTY";
case UNSTABLE: return "UNSTABLE";
default:
lean_unreachable();
}
return "UNKNOWN"; // it is unreachable
}
lp_status lp_status_from_string(std::string status) {
if (status == "UNKNOWN") return lp_status::UNKNOWN;
if (status == "INFEASIBLE") return lp_status::INFEASIBLE;
if (status == "UNBOUNDED") return lp_status::UNBOUNDED;
if (status == "OPTIMAL") return lp_status::OPTIMAL;
if (status == "FEASIBLE") return lp_status::FEASIBLE;
if (status == "FLOATING_POINT_ERROR") return lp_status::FLOATING_POINT_ERROR;
if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED;
if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED;
if (status == "EMPTY") return lp_status::EMPTY;
lean_unreachable();
return lp_status::UNKNOWN; // it is unreachable
}
int get_millisecond_count() {
timeb tb;
ftime(&tb);
return tb.millitm + (tb.time & 0xfffff) * 1000;
}
int get_millisecond_span(int start_time) {
int span = get_millisecond_count() - start_time;
if (span < 0)
span += 0x100000 * 1000;
return span;
}
void my_random_init(long unsigned seed) {
lp_settings::random_next = seed;
}
unsigned my_random() {
lp_settings::random_next = lp_settings::random_next * 1103515245 + 12345;
return((unsigned)(lp_settings::random_next/65536) % 32768);
}
template <typename T>
bool vectors_are_equal(T * a, vector<T> &b, unsigned n) {
if (numeric_traits<T>::precise()) {
for (unsigned i = 0; i < n; i ++){
if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
// std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false;
}
}
} else {
for (unsigned i = 0; i < n; i ++){
if (std::abs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) {
// std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false;
}
}
}
return true;
}
template <typename T>
bool vectors_are_equal(const vector<T> & a, const vector<T> &b) {
unsigned n = static_cast<unsigned>(a.size());
if (n != b.size()) return false;
if (numeric_traits<T>::precise()) {
for (unsigned i = 0; i < n; i ++){
if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
// std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false;
}
}
} else {
for (unsigned i = 0; i < n; i ++){
double da = numeric_traits<T>::get_double(a[i]);
double db = numeric_traits<T>::get_double(b[i]);
double amax = std::max(fabs(da), fabs(db));
if (amax > 1) {
da /= amax;
db /= amax;
}
if (fabs(da - db) > 0.000001) {
// std::cout << "a[" << i <<"] = " << a[i] << ", but " << "b[" << i <<"] = " << b[i] << std::endl;
return false;
}
}
}
return true;
}
unsigned long lp_settings::random_next = 1;
#ifdef LEAN_DEBUG
unsigned lp_settings::ddd = 0;
#endif
}

View file

@ -0,0 +1,10 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/vector.h"
#include <memory>
#include "util/lp/lp_settings.hpp"
template bool lean::vectors_are_equal<double>(vector<double> const&, vector<double> const&);
template bool lean::vectors_are_equal<lean::mpq>(vector<lean::mpq > const&, vector<lean::mpq> const&);

253
src/util/lp/lp_solver.h Normal file
View file

@ -0,0 +1,253 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include <string>
#include <unordered_map>
#include <algorithm>
#include "util/vector.h"
#include "util/lp/lp_settings.h"
#include "util/lp/column_info.h"
#include "util/lp/static_matrix.h"
#include "util/lp/lp_core_solver_base.h"
#include "util/lp/scaler.h"
#include "util/lp/linear_combination_iterator.h"
#include "util/lp/bound_analyzer_on_row.h"
namespace lean {
enum lp_relation {
Less_or_equal,
Equal,
Greater_or_equal
};
template <typename T, typename X>
struct lp_constraint {
X m_rs; // right side of the constraint
lp_relation m_relation;
lp_constraint() {} // empty constructor
lp_constraint(T rs, lp_relation relation): m_rs(rs), m_relation(relation) {}
};
template <typename T, typename X>
class lp_solver : public column_namer {
column_info<T> * get_or_create_column_info(unsigned column);
protected:
T get_column_cost_value(unsigned j, column_info<T> * ci) const;
public:
unsigned m_total_iterations;
static_matrix<T, X>* m_A = nullptr; // this is the matrix of constraints
vector<T> m_b; // the right side vector
unsigned m_first_stage_iterations = 0;
unsigned m_second_stage_iterations = 0;
std::unordered_map<unsigned, lp_constraint<T, X>> m_constraints;
std::unordered_map<var_index, column_info<T>*> m_map_from_var_index_to_column_info;
std::unordered_map<unsigned, std::unordered_map<unsigned, T> > m_A_values;
std::unordered_map<std::string, unsigned> m_names_to_columns; // don't have to use it
std::unordered_map<unsigned, unsigned> m_external_rows_to_core_solver_rows;
std::unordered_map<unsigned, unsigned> m_core_solver_rows_to_external_rows;
std::unordered_map<unsigned, unsigned> m_core_solver_columns_to_external_columns;
vector<T> m_column_scale;
std::unordered_map<unsigned, std::string> m_name_map;
unsigned m_artificials = 0;
unsigned m_slacks = 0;
vector<column_type> m_column_types;
vector<T> m_costs;
vector<T> m_x;
vector<T> m_upper_bounds;
vector<unsigned> m_basis;
vector<unsigned> m_nbasis;
vector<int> m_heading;
lp_status m_status = lp_status::UNKNOWN;
lp_settings m_settings;
lp_solver() {}
unsigned row_count() const { return this->m_A->row_count(); }
void add_constraint(lp_relation relation, T right_side, unsigned row_index);
void set_cost_for_column(unsigned column, T column_cost) {
get_or_create_column_info(column)->set_cost(column_cost);
}
std::string get_column_name(unsigned j) const override;
void set_row_column_coefficient(unsigned row, unsigned column, T const & val) {
m_A_values[row][column] = val;
}
// returns the current cost
virtual T get_current_cost() const = 0;
// do not have to call it
void give_symbolic_name_to_column(std::string name, unsigned column);
virtual T get_column_value(unsigned column) const = 0;
T get_column_value_by_name(std::string name) const;
// returns -1 if not found
virtual int get_column_index_by_name(std::string name) const;
void set_low_bound(unsigned i, T bound) {
column_info<T> *ci = get_or_create_column_info(i);
ci->set_low_bound(bound);
}
void set_upper_bound(unsigned i, T bound) {
column_info<T> *ci = get_or_create_column_info(i);
ci->set_upper_bound(bound);
}
void unset_low_bound(unsigned i) {
get_or_create_column_info(i)->unset_low_bound();
}
void unset_upper_bound(unsigned i) {
get_or_create_column_info(i)->unset_upper_bound();
}
void set_fixed_value(unsigned i, T val) {
column_info<T> *ci = get_or_create_column_info(i);
ci->set_fixed_value(val);
}
void unset_fixed_value(unsigned i) {
get_or_create_column_info(i)->unset_fixed();
}
lp_status get_status() const {
return m_status;
}
void set_status(lp_status st) {
m_status = st;
}
virtual ~lp_solver();
void flip_costs();
virtual void find_maximal_solution() = 0;
void set_time_limit(unsigned time_limit_in_seconds) {
m_settings.time_limit = time_limit_in_seconds;
}
void set_max_iterations_per_stage(unsigned max_iterations) {
m_settings.max_total_number_of_iterations = max_iterations;
}
unsigned get_max_iterations_per_stage() const {
return m_settings.max_total_number_of_iterations;
}
protected:
bool problem_is_empty();
void scale();
void print_rows_scale_stats(std::ostream & out);
void print_columns_scale_stats(std::ostream & out);
void print_row_scale_stats(unsigned i, std::ostream & out);
void print_column_scale_stats(unsigned j, std::ostream & out);
void print_scale_stats(std::ostream & out);
void get_max_abs_in_row(std::unordered_map<unsigned, T> & row_map);
void pin_vars_down_on_row(std::unordered_map<unsigned, T> & row) {
pin_vars_on_row_with_sign(row, - numeric_traits<T>::one());
}
void pin_vars_up_on_row(std::unordered_map<unsigned, T> & row) {
pin_vars_on_row_with_sign(row, numeric_traits<T>::one());
}
void pin_vars_on_row_with_sign(std::unordered_map<unsigned, T> & row, T sign );
bool get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound);
bool get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound);
bool row_is_zero(std::unordered_map<unsigned, T> & row);
bool row_e_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index);
bool row_ge_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index);
bool row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index);
// analyse possible max and min values that are derived from var boundaries
// Let us say that the we have a "ge" constraint, and the min value is equal to the rs.
// Then we know what values of the variables are. For each positive coeff of the row it has to be
// the low boundary of the var and for a negative - the upper.
// this routing also pins the variables to the boundaries
bool row_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index );
void remove_fixed_or_zero_columns();
void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map<unsigned, T> & row);
unsigned try_to_remove_some_rows();
void cleanup();
void map_external_rows_to_core_solver_rows();
void map_external_columns_to_core_solver_columns();
unsigned number_of_core_structurals() {
return static_cast<unsigned>(m_core_solver_columns_to_external_columns.size());
}
void restore_column_scales_to_one() {
for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits<T>::one();
}
void unscale();
void fill_A_from_A_values();
void fill_matrix_A_and_init_right_side();
void count_slacks_and_artificials();
void count_slacks_and_artificials_for_row(unsigned i);
T low_bound_shift_for_row(unsigned i);
void fill_m_b();
T get_column_value_with_core_solver(unsigned column, lp_core_solver_base<T, X> * core_solver) const;
void set_scaled_cost(unsigned j);
void print_statistics_on_A(std::ostream & out) {
out << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl;
}
struct row_tighten_stats {
unsigned n_of_new_bounds = 0;
unsigned n_of_fixed = 0;
bool is_obsolete = false;
};
public:
lp_settings & settings() { return m_settings;}
void print_model(std::ostream & s) const {
s << "objective = " << get_current_cost() << std::endl;
s << "column values\n";
for (auto & it : m_names_to_columns) {
s << it.first << " = " << get_column_value(it.second) << std::endl;
}
}
};
}

554
src/util/lp/lp_solver.hpp Normal file
View file

@ -0,0 +1,554 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <string>
#include <algorithm>
#include "util/vector.h"
#include "util/lp/lp_solver.h"
namespace lean {
template <typename T, typename X> column_info<T> * lp_solver<T, X>::get_or_create_column_info(unsigned column) {
auto it = m_map_from_var_index_to_column_info.find(column);
return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info<T>(static_cast<unsigned>(-1))) : it->second;
}
template <typename T, typename X>
std::string lp_solver<T, X>::get_column_name(unsigned j) const { // j here is the core solver index
auto it = this->m_core_solver_columns_to_external_columns.find(j);
if (it == this->m_core_solver_columns_to_external_columns.end())
return std::string("x")+T_to_string(j);
unsigned external_j = it->second;
auto t = this->m_map_from_var_index_to_column_info.find(external_j);
if (t == this->m_map_from_var_index_to_column_info.end()) {
return std::string("x") +T_to_string(external_j);
}
return t->second->get_name();
}
template <typename T, typename X> T lp_solver<T, X>::get_column_cost_value(unsigned j, column_info<T> * ci) const {
if (ci->is_fixed()) {
return ci->get_cost() * ci->get_fixed_value();
}
return ci->get_cost() * get_column_value(j);
}
template <typename T, typename X> void lp_solver<T, X>::add_constraint(lp_relation relation, T right_side, unsigned row_index) {
lean_assert(m_constraints.find(row_index) == m_constraints.end());
lp_constraint<T, X> cs(right_side, relation);
m_constraints[row_index] = cs;
}
template <typename T, typename X> void lp_solver<T, X>::give_symbolic_name_to_column(std::string name, unsigned column) {
auto it = m_map_from_var_index_to_column_info.find(column);
column_info<T> *ci;
if (it == m_map_from_var_index_to_column_info.end()){
m_map_from_var_index_to_column_info[column] = ci = new column_info<T>;
} else {
ci = it->second;
}
ci->set_name(name);
m_names_to_columns[name] = column;
}
template <typename T, typename X> T lp_solver<T, X>::get_column_value_by_name(std::string name) const {
auto it = m_names_to_columns.find(name);
if (it == m_names_to_columns.end()) {
std::stringstream s;
s << "get_column_value_by_name " << name;
throw_exception(s.str());
}
return get_column_value(it -> second);
}
// returns -1 if not found
template <typename T, typename X> int lp_solver<T, X>::get_column_index_by_name(std::string name) const {
auto t = m_names_to_columns.find(name);
if (t == m_names_to_columns.end()) {
return -1;
}
return t->second;
}
template <typename T, typename X> lp_solver<T, X>::~lp_solver(){
if (m_A != nullptr) {
delete m_A;
}
for (auto t : m_map_from_var_index_to_column_info) {
delete t.second;
}
}
template <typename T, typename X> void lp_solver<T, X>::flip_costs() {
for (auto t : m_map_from_var_index_to_column_info) {
column_info<T> *ci = t.second;
ci->set_cost(-ci->get_cost());
}
}
template <typename T, typename X> bool lp_solver<T, X>::problem_is_empty() {
for (auto & c : m_A_values)
if (c.second.size())
return false;
return true;
}
template <typename T, typename X> void lp_solver<T, X>::scale() {
if (numeric_traits<T>::precise() || m_settings.use_scaling == false) {
m_column_scale.clear();
m_column_scale.resize(m_A->column_count(), one_of_type<T>());
return;
}
T smin = T(m_settings.scaling_minimum);
T smax = T(m_settings.scaling_maximum);
scaler<T, X> scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings);
if (!scaler.scale()) {
unscale();
}
}
template <typename T, typename X> void lp_solver<T, X>::print_rows_scale_stats(std::ostream & out) {
out << "rows max" << std::endl;
for (unsigned i = 0; i < m_A->row_count(); i++) {
print_row_scale_stats(i, out);
}
out << std::endl;
}
template <typename T, typename X> void lp_solver<T, X>::print_columns_scale_stats(std::ostream & out) {
out << "columns max" << std::endl;
for (unsigned i = 0; i < m_A->column_count(); i++) {
print_column_scale_stats(i, out);
}
out << std::endl;
}
template <typename T, typename X> void lp_solver<T, X>::print_row_scale_stats(unsigned i, std::ostream & out) {
out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " ";
out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")";
}
template <typename T, typename X> void lp_solver<T, X>::print_column_scale_stats(unsigned j, std::ostream & out) {
out << "(" << m_A->get_min_abs_in_row(j) << " ";
out << m_A->get_max_abs_in_column(j) << ")";
}
template <typename T, typename X> void lp_solver<T, X>::print_scale_stats(std::ostream & out) {
print_rows_scale_stats(out);
print_columns_scale_stats(out);
}
template <typename T, typename X> void lp_solver<T, X>::get_max_abs_in_row(std::unordered_map<unsigned, T> & row_map) {
T ret = numeric_traits<T>::zero();
for (auto jp : row_map) {
T ac = numeric_traits<T>::abs(jp->second);
if (ac > ret) {
ret = ac;
}
}
return ret;
}
template <typename T, typename X> void lp_solver<T, X>::pin_vars_on_row_with_sign(std::unordered_map<unsigned, T> & row, T sign ) {
for (auto t : row) {
unsigned j = t.first;
column_info<T> * ci = m_map_from_var_index_to_column_info[j];
T a = t.second;
if (a * sign > numeric_traits<T>::zero()) {
lean_assert(ci->upper_bound_is_set());
ci->set_fixed_value(ci->get_upper_bound());
} else {
lean_assert(ci->low_bound_is_set());
ci->set_fixed_value(ci->get_low_bound());
}
}
}
template <typename T, typename X> bool lp_solver<T, X>::get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound) {
low_bound = numeric_traits<T>::zero();
for (auto & t : row) {
T a = t.second;
column_info<T> * ci = m_map_from_var_index_to_column_info[t.first];
if (a > numeric_traits<T>::zero()) {
if (ci->low_bound_is_set()) {
low_bound += ci->get_low_bound() * a;
} else {
return false;
}
} else {
if (ci->upper_bound_is_set()) {
low_bound += ci->get_upper_bound() * a;
} else {
return false;
}
}
}
return true;
}
template <typename T, typename X> bool lp_solver<T, X>::get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound) {
low_bound = numeric_traits<T>::zero();
for (auto & t : row) {
T a = t.second;
column_info<T> * ci = m_map_from_var_index_to_column_info[t.first];
if (a < numeric_traits<T>::zero()) {
if (ci->low_bound_is_set()) {
low_bound += ci->get_low_bound() * a;
} else {
return false;
}
} else {
if (ci->upper_bound_is_set()) {
low_bound += ci->get_upper_bound() * a;
} else {
return false;
}
}
}
return true;
}
template <typename T, typename X> bool lp_solver<T, X>::row_is_zero(std::unordered_map<unsigned, T> & row) {
for (auto & t : row) {
if (!is_zero(t.second))
return false;
}
return true;
}
template <typename T, typename X> bool lp_solver<T, X>::row_e_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
T rs = m_constraints[row_index].m_rs;
if (row_is_zero(row)) {
if (!is_zero(rs))
m_status = INFEASIBLE;
return true;
}
T low_bound;
bool lb = get_minimal_row_value(row, low_bound);
if (lb) {
T diff = low_bound - rs;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
// low_bound > rs + m_settings.refactor_epsilon
m_status = INFEASIBLE;
return true;
}
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
pin_vars_down_on_row(row);
return true;
}
}
T upper_bound;
bool ub = get_maximal_row_value(row, upper_bound);
if (ub) {
T diff = rs - upper_bound;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
// upper_bound < rs - m_settings.refactor_tolerance
m_status = INFEASIBLE;
return true;
}
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
pin_vars_up_on_row(row);
return true;
}
}
return false;
}
template <typename T, typename X> bool lp_solver<T, X>::row_ge_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
T rs = m_constraints[row_index].m_rs;
if (row_is_zero(row)) {
if (rs > zero_of_type<X>())
m_status = INFEASIBLE;
return true;
}
T upper_bound;
if (get_maximal_row_value(row, upper_bound)) {
T diff = rs - upper_bound;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
// upper_bound < rs - m_settings.refactor_tolerance
m_status = INFEASIBLE;
return true;
}
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
pin_vars_up_on_row(row);
return true;
}
}
return false;
}
template <typename T, typename X> bool lp_solver<T, X>::row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
T low_bound;
T rs = m_constraints[row_index].m_rs;
if (row_is_zero(row)) {
if (rs < zero_of_type<X>())
m_status = INFEASIBLE;
return true;
}
if (get_minimal_row_value(row, low_bound)) {
T diff = low_bound - rs;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
// low_bound > rs + m_settings.refactor_tolerance
m_status = lp_status::INFEASIBLE;
return true;
}
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
pin_vars_down_on_row(row);
return true;
}
}
return false;
}
// analyse possible max and min values that are derived from var boundaries
// Let us say that the we have a "ge" constraint, and the min value is equal to the rs.
// Then we know what values of the variables are. For each positive coeff of the row it has to be
// the low boundary of the var and for a negative - the upper.
// this routing also pins the variables to the boundaries
template <typename T, typename X> bool lp_solver<T, X>::row_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index ) {
auto & constraint = m_constraints[row_index];
switch (constraint.m_relation) {
case lp_relation::Equal:
return row_e_is_obsolete(row, row_index);
case lp_relation::Greater_or_equal:
return row_ge_is_obsolete(row, row_index);
case lp_relation::Less_or_equal:
return row_le_is_obsolete(row, row_index);
}
lean_unreachable();
return false; // it is unreachable
}
template <typename T, typename X> void lp_solver<T, X>::remove_fixed_or_zero_columns() {
for (auto & i_row : m_A_values) {
remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second);
}
}
template <typename T, typename X> void lp_solver<T, X>::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map<unsigned, T> & row) {
auto & constraint = m_constraints[i];
vector<unsigned> removed;
for (auto & col : row) {
unsigned j = col.first;
lean_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end());
column_info<T> * ci = m_map_from_var_index_to_column_info[j];
if (ci->is_fixed()) {
removed.push_back(j);
T aj = col.second;
constraint.m_rs -= aj * ci->get_fixed_value();
} else {
if (numeric_traits<T>::is_zero(col.second)){
removed.push_back(j);
}
}
}
for (auto j : removed) {
row.erase(j);
}
}
template <typename T, typename X> unsigned lp_solver<T, X>::try_to_remove_some_rows() {
vector<unsigned> rows_to_delete;
for (auto & t : m_A_values) {
if (row_is_obsolete(t.second, t.first)) {
rows_to_delete.push_back(t.first);
}
if (m_status == lp_status::INFEASIBLE) {
return 0;
}
}
if (rows_to_delete.size() > 0) {
for (unsigned k : rows_to_delete) {
m_A_values.erase(k);
}
}
remove_fixed_or_zero_columns();
return static_cast<unsigned>(rows_to_delete.size());
}
template <typename T, typename X> void lp_solver<T, X>::cleanup() {
int n = 0; // number of deleted rows
int d;
while ((d = try_to_remove_some_rows() > 0))
n += d;
if (n == 1) {
LP_OUT(m_settings, "deleted one row" << std::endl);
} else if (n) {
LP_OUT(m_settings, "deleted " << n << " rows" << std::endl);
}
}
template <typename T, typename X> void lp_solver<T, X>::map_external_rows_to_core_solver_rows() {
unsigned size = 0;
for (auto & row : m_A_values) {
m_external_rows_to_core_solver_rows[row.first] = size;
m_core_solver_rows_to_external_rows[size] = row.first;
size++;
}
}
template <typename T, typename X> void lp_solver<T, X>::map_external_columns_to_core_solver_columns() {
unsigned size = 0;
for (auto & row : m_A_values) {
for (auto & col : row.second) {
if (col.second == numeric_traits<T>::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) {
throw_exception("found fixed column");
}
unsigned j = col.first;
auto column_info_it = m_map_from_var_index_to_column_info.find(j);
lean_assert(column_info_it != m_map_from_var_index_to_column_info.end());
auto j_column = column_info_it->second->get_column_index();
if (!is_valid(j_column)) { // j is a newcomer
m_map_from_var_index_to_column_info[j]->set_column_index(size);
m_core_solver_columns_to_external_columns[size++] = j;
}
}
}
}
template <typename T, typename X> void lp_solver<T, X>::unscale() {
delete m_A;
m_A = nullptr;
fill_A_from_A_values();
restore_column_scales_to_one();
fill_m_b();
}
template <typename T, typename X> void lp_solver<T, X>::fill_A_from_A_values() {
m_A = new static_matrix<T, X>(static_cast<unsigned>(m_A_values.size()), number_of_core_structurals());
for (auto & t : m_A_values) {
auto row_it = m_external_rows_to_core_solver_rows.find(t.first);
lean_assert(row_it != m_external_rows_to_core_solver_rows.end());
unsigned row = row_it->second;
for (auto k : t.second) {
auto column_info_it = m_map_from_var_index_to_column_info.find(k.first);
lean_assert(column_info_it != m_map_from_var_index_to_column_info.end());
column_info<T> *ci = column_info_it->second;
unsigned col = ci->get_column_index();
lean_assert(is_valid(col));
bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped();
if (!col_is_flipped) {
(*m_A)(row, col) = k.second;
} else {
(*m_A)(row, col) = - k.second;
}
}
}
}
template <typename T, typename X> void lp_solver<T, X>::fill_matrix_A_and_init_right_side() {
map_external_rows_to_core_solver_rows();
map_external_columns_to_core_solver_columns();
lean_assert(m_A == nullptr);
fill_A_from_A_values();
m_b.resize(m_A->row_count());
}
template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artificials() {
for (int i = row_count() - 1; i >= 0; i--) {
count_slacks_and_artificials_for_row(i);
}
}
template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artificials_for_row(unsigned i) {
lean_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]];
switch (constraint.m_relation) {
case Equal:
m_artificials++;
break;
case Greater_or_equal:
m_slacks++;
if (this->m_b[i] > 0) {
m_artificials++;
}
break;
case Less_or_equal:
m_slacks++;
if (this->m_b[i] < 0) {
m_artificials++;
}
break;
}
}
template <typename T, typename X> T lp_solver<T, X>::low_bound_shift_for_row(unsigned i) {
T ret = numeric_traits<T>::zero();
auto row = this->m_A_values.find(i);
if (row == this->m_A_values.end()) {
throw_exception("cannot find row");
}
for (auto col : row->second) {
ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift();
}
return ret;
}
template <typename T, typename X> void lp_solver<T, X>::fill_m_b() {
for (int i = this->row_count() - 1; i >= 0; i--) {
lean_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
unsigned external_i = this->m_core_solver_rows_to_external_rows[i];
auto & constraint = this->m_constraints[external_i];
this->m_b[i] = constraint.m_rs - low_bound_shift_for_row(external_i);
}
}
template <typename T, typename X> T lp_solver<T, X>::get_column_value_with_core_solver(unsigned column, lp_core_solver_base<T, X> * core_solver) const {
auto cit = this->m_map_from_var_index_to_column_info.find(column);
if (cit == this->m_map_from_var_index_to_column_info.end()) {
return numeric_traits<T>::zero();
}
column_info<T> * ci = cit->second;
if (ci->is_fixed()) {
return ci->get_fixed_value();
}
unsigned cj = ci->get_column_index();
if (cj != static_cast<unsigned>(-1)) {
T v = core_solver->get_var_value(cj) * this->m_column_scale[cj];
if (ci->is_free()) {
return v;
}
if (!ci->is_flipped()) {
return v + ci->get_low_bound();
}
// the flipped case when there is only upper bound
return -v + ci->get_upper_bound(); //
}
return numeric_traits<T>::zero(); // returns zero for out of boundary columns
}
template <typename T, typename X> void lp_solver<T, X>::set_scaled_cost(unsigned j) {
// grab original costs but modify it with the column scales
lean_assert(j < this->m_column_scale.size());
column_info<T> * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]];
T cost = ci->get_cost();
if (ci->is_flipped()){
cost *= -1;
}
lean_assert(ci->is_fixed() == false);
this->m_costs[j] = cost * this->m_column_scale[j];
}
}

View file

@ -0,0 +1,40 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <string>
#include "util/lp/lp_solver.hpp"
template void lean::lp_solver<double, double>::add_constraint(lean::lp_relation, double, unsigned int);
template void lean::lp_solver<double, double>::cleanup();
template void lean::lp_solver<double, double>::count_slacks_and_artificials();
template void lean::lp_solver<double, double>::fill_m_b();
template void lean::lp_solver<double, double>::fill_matrix_A_and_init_right_side();
template void lean::lp_solver<double, double>::flip_costs();
template double lean::lp_solver<double, double>::get_column_cost_value(unsigned int, lean::column_info<double>*) const;
template int lean::lp_solver<double, double>::get_column_index_by_name(std::string) const;
template double lean::lp_solver<double, double>::get_column_value_with_core_solver(unsigned int, lean::lp_core_solver_base<double, double>*) const;
template lean::column_info<double>* lean::lp_solver<double, double>::get_or_create_column_info(unsigned int);
template void lean::lp_solver<double, double>::give_symbolic_name_to_column(std::string, unsigned int);
template void lean::lp_solver<double, double>::print_statistics_on_A(std::ostream & out);
template bool lean::lp_solver<double, double>::problem_is_empty();
template void lean::lp_solver<double, double>::scale();
template void lean::lp_solver<double, double>::set_scaled_cost(unsigned int);
template lean::lp_solver<double, double>::~lp_solver();
template void lean::lp_solver<lean::mpq, lean::mpq>::add_constraint(lean::lp_relation, lean::mpq, unsigned int);
template void lean::lp_solver<lean::mpq, lean::mpq>::cleanup();
template void lean::lp_solver<lean::mpq, lean::mpq>::count_slacks_and_artificials();
template void lean::lp_solver<lean::mpq, lean::mpq>::fill_m_b();
template void lean::lp_solver<lean::mpq, lean::mpq>::fill_matrix_A_and_init_right_side();
template void lean::lp_solver<lean::mpq, lean::mpq>::flip_costs();
template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_cost_value(unsigned int, lean::column_info<lean::mpq>*) const;
template int lean::lp_solver<lean::mpq, lean::mpq>::get_column_index_by_name(std::string) const;
template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_value_by_name(std::string) const;
template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_value_with_core_solver(unsigned int, lean::lp_core_solver_base<lean::mpq, lean::mpq>*) const;
template lean::column_info<lean::mpq>* lean::lp_solver<lean::mpq, lean::mpq>::get_or_create_column_info(unsigned int);
template void lean::lp_solver<lean::mpq, lean::mpq>::give_symbolic_name_to_column(std::string, unsigned int);
template void lean::lp_solver<lean::mpq, lean::mpq>::print_statistics_on_A(std::ostream & out);
template bool lean::lp_solver<lean::mpq, lean::mpq>::problem_is_empty();
template void lean::lp_solver<lean::mpq, lean::mpq>::scale();
template void lean::lp_solver<lean::mpq, lean::mpq>::set_scaled_cost(unsigned int);
template lean::lp_solver<lean::mpq, lean::mpq>::~lp_solver();
template double lean::lp_solver<double, double>::get_column_value_by_name(std::string) const;

11
src/util/lp/lp_utils.cpp Normal file
View file

@ -0,0 +1,11 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/lp_utils.h"
#ifdef lp_for_z3
namespace lean {
double numeric_traits<double>::g_zero = 0.0;
double numeric_traits<double>::g_one = 1.0;
}
#endif

141
src/util/lp/lp_utils.h Normal file
View file

@ -0,0 +1,141 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
This file should be present in z3 and in Lean.
*/
#pragma once
#include <string>
#include "util/lp/numeric_pair.h"
#include "util/debug.h"
#include <unordered_map>
template <typename A, typename B>
bool try_get_val(const std::unordered_map<A,B> & map, const A& key, B & val) {
const auto it = map.find(key);
if (it == map.end()) return false;
val = it->second;
return true;
}
template <typename A, typename B>
bool contains(const std::unordered_map<A, B> & map, const A& key) {
return map.find(key) != map.end();
}
#ifdef lp_for_z3
#ifdef Z3DEBUG
#define LEAN_DEBUG 1
#endif
namespace lean {
inline void throw_exception(const std::string & str) {
throw default_exception(str);
}
typedef z3_exception exception;
#define lean_assert(_x_) { SASSERT(_x_); }
inline void lean_unreachable() { lean_assert(false); }
template <typename X> inline X zero_of_type() { return numeric_traits<X>::zero(); }
template <typename X> inline X one_of_type() { return numeric_traits<X>::one(); }
template <typename X> inline bool is_zero(const X & v) { return numeric_traits<X>::is_zero(v); }
template <typename X> inline bool is_pos(const X & v) { return numeric_traits<X>::is_pos(v); }
template <typename X> inline bool is_neg(const X & v) { return numeric_traits<X>::is_neg(v); }
template <typename X> inline bool precise() { return numeric_traits<X>::precise(); }
}
namespace std {
template<>
struct hash<rational> {
inline size_t operator()(const rational & v) const {
return v.hash();
}
};
}
template <class T>
inline void hash_combine(std::size_t & seed, const T & v) {
seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
namespace std {
template<typename S, typename T> struct hash<pair<S, T>> {
inline size_t operator()(const pair<S, T> & v) const {
size_t seed = 0;
hash_combine(seed, v.first);
hash_combine(seed, v.second);
return seed;
}
};
template<>
struct hash<lean::numeric_pair<lean::mpq>> {
inline size_t operator()(const lean::numeric_pair<lean::mpq> & v) const {
size_t seed = 0;
hash_combine(seed, v.x);
hash_combine(seed, v.y);
return seed;
}
};
}
#else // else of #if lp_for_z3
#include <utility>
#include <functional>
//include "util/numerics/mpq.h"
//include "util/numerics/numeric_traits.h"
//include "util/numerics/double.h"
#ifdef __CLANG__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif
namespace std {
template<>
struct hash<lean::mpq> {
inline size_t operator()(const lean::mpq & v) const {
return v.hash();
}
};
}
namespace lean {
template <typename X> inline bool precise() { return numeric_traits<X>::precise();}
template <typename X> inline X one_of_type() { return numeric_traits<X>::one(); }
template <typename X> inline bool is_zero(const X & v) { return numeric_traits<X>::is_zero(v); }
template <typename X> inline double get_double(const X & v) { return numeric_traits<X>::get_double(v); }
template <typename T> inline T zero_of_type() {return numeric_traits<T>::zero();}
inline void throw_exception(std::string str) { throw exception(str); }
template <typename T> inline T from_string(std::string const & ) { lean_unreachable();}
template <> double inline from_string<double>(std::string const & str) { return atof(str.c_str());}
template <> mpq inline from_string<mpq>(std::string const & str) {
return mpq(atof(str.c_str()));
}
} // closing lean
template <class T>
inline void hash_combine(std::size_t & seed, const T & v) {
seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
namespace std {
template<typename S, typename T> struct hash<pair<S, T>> {
inline size_t operator()(const pair<S, T> & v) const {
size_t seed = 0;
hash_combine(seed, v.first);
hash_combine(seed, v.second);
return seed;
}
};
template<>
struct hash<lean::numeric_pair<lean::mpq>> {
inline size_t operator()(const lean::numeric_pair<lean::mpq> & v) const {
size_t seed = 0;
hash_combine(seed, v.x);
hash_combine(seed, v.y);
return seed;
}
};
} // std
#ifdef __CLANG__
#pragma clang diagnostic pop
#endif
#endif

359
src/util/lp/lu.h Normal file
View file

@ -0,0 +1,359 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/debug.h"
#include <algorithm>
#include <set>
#include "util/lp/sparse_matrix.h"
#include "util/lp/static_matrix.h"
#include <string>
#include "util/lp/numeric_pair.h"
#include <iostream>
#include <fstream>
#include "util/lp/row_eta_matrix.h"
#include "util/lp/square_dense_submatrix.h"
#include "util/lp/dense_matrix.h"
namespace lean {
#ifdef LEAN_DEBUG
template <typename T, typename X> // print the nr x nc submatrix at the top left corner
void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc);
template<typename T, typename X>
void print_matrix(static_matrix<T, X> &m, std::ostream & out);
template <typename T, typename X>
void print_matrix(sparse_matrix<T, X>& m, std::ostream & out);
#endif
template <typename T, typename X>
X dot_product(const vector<T> & a, const vector<X> & b) {
lean_assert(a.size() == b.size());
auto r = zero_of_type<X>();
for (unsigned i = 0; i < a.size(); i++) {
r += a[i] * b[i];
}
return r;
}
template <typename T, typename X>
class one_elem_on_diag: public tail_matrix<T, X> {
unsigned m_i;
T m_val;
public:
one_elem_on_diag(unsigned i, T val) : m_i(i), m_val(val) {
#ifdef LEAN_DEBUG
m_one_over_val = numeric_traits<T>::one() / m_val;
#endif
}
bool is_dense() const { return false; }
one_elem_on_diag(const one_elem_on_diag & o);
#ifdef LEAN_DEBUG
unsigned m_m;
unsigned m_n;
virtual void set_number_of_rows(unsigned m) { m_m = m; m_n = m; }
virtual void set_number_of_columns(unsigned n) { m_m = n; m_n = n; }
T m_one_over_val;
T get_elem (unsigned i, unsigned j) const;
unsigned row_count() const { return m_m; } // not defined }
unsigned column_count() const { return m_m; } // not defined }
#endif
void apply_from_left(vector<X> & w, lp_settings &) {
w[m_i] /= m_val;
}
void apply_from_right(vector<T> & w) {
w[m_i] /= m_val;
}
void apply_from_right(indexed_vector<T> & w) {
if (is_zero(w.m_data[m_i]))
return;
auto & v = w.m_data[m_i] /= m_val;
if (lp_settings::is_eps_small_general(v, 1e-14)) {
w.erase_from_index(m_i);
v = zero_of_type<T>();
}
}
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings);
void conjugate_by_permutation(permutation_matrix<T, X> & p) {
// this = p * this * p(-1)
#ifdef LEAN_DEBUG
// auto rev = p.get_reverse();
// auto deb = ((*this) * rev);
// deb = p * deb;
#endif
m_i = p.apply_reverse(m_i);
#ifdef LEAN_DEBUG
// lean_assert(*this == deb);
#endif
}
}; // end of one_elem_on_diag
enum class LU_status { OK, Degenerated};
// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
template <typename T, typename X>
class lu {
LU_status m_status = LU_status::OK;
public:
// the fields
unsigned m_dim;
static_matrix<T, X> const &m_A;
permutation_matrix<T, X> m_Q;
permutation_matrix<T, X> m_R;
permutation_matrix<T, X> m_r_wave;
sparse_matrix<T, X> m_U;
square_dense_submatrix<T, X>* m_dense_LU;
vector<tail_matrix<T, X> *> m_tail;
lp_settings & m_settings;
bool m_failure = false;
indexed_vector<T> m_row_eta_work_vector;
indexed_vector<T> m_w_for_extension;
indexed_vector<T> m_y_copy;
indexed_vector<unsigned> m_ii; //to optimize the work with the m_index fields
unsigned m_refactor_counter = 0;
// constructor
// if A is an m by n matrix then basis has length m and values in [0,n); the values are all different
// they represent the set of m columns
lu(static_matrix<T, X> const & A,
vector<unsigned>& basis,
lp_settings & settings);
void debug_test_of_basis(static_matrix<T, X> const & A, vector<unsigned> & basis);
void solve_Bd_when_w_is_ready(vector<T> & d, indexed_vector<T>& w );
void solve_By(indexed_vector<X> & y);
void solve_By(vector<X> & y);
void solve_By_for_T_indexed_only(indexed_vector<T>& y, const lp_settings &);
template <typename L>
void solve_By_when_y_is_ready(indexed_vector<L> & y);
void solve_By_when_y_is_ready_for_X(vector<X> & y);
void solve_By_when_y_is_ready_for_T(vector<T> & y, vector<unsigned> & index);
void print_indexed_vector(indexed_vector<T> & w, std::ofstream & f);
void print_matrix_compact(std::ostream & f);
void print(indexed_vector<T> & w, const vector<unsigned>& basis);
void solve_Bd(unsigned a_column, vector<T> & d, indexed_vector<T> & w);
void solve_Bd(unsigned a_column, indexed_vector<T> & d, indexed_vector<T> & w);
void solve_Bd_faster(unsigned a_column, indexed_vector<T> & d); // d is the right side on the input and the solution at the exit
void solve_yB(vector<T>& y);
void solve_yB_indexed(indexed_vector<T>& y);
void add_delta_to_solution_indexed(indexed_vector<T>& y);
void add_delta_to_solution(const vector<T>& yc, vector<T>& y);
void find_error_of_yB(vector<T>& yc, const vector<T>& y,
const vector<unsigned>& basis);
void find_error_of_yB_indexed(const indexed_vector<T>& y,
const vector<int>& heading, const lp_settings& settings);
void solve_yB_with_error_check(vector<T> & y, const vector<unsigned>& basis);
void solve_yB_with_error_check_indexed(indexed_vector<T> & y, const vector<int>& heading, const vector<unsigned> & basis, const lp_settings &);
void apply_Q_R_to_U(permutation_matrix<T, X> & r_wave);
LU_status get_status() { return m_status; }
void set_status(LU_status status) {
m_status = status;
}
~lu();
void init_vector_y(vector<X> & y);
void perform_transformations_on_w(indexed_vector<T>& w);
void init_vector_w(unsigned entering, indexed_vector<T> & w);
void apply_lp_list_to_w(indexed_vector<T> & w);
void apply_lp_list_to_y(vector<X>& y);
void swap_rows(int j, int k);
void swap_columns(int j, int pivot_column);
void push_matrix_to_tail(tail_matrix<T, X>* tm) {
m_tail.push_back(tm);
}
bool pivot_the_row(int row);
eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j);
// we're processing the column j now
eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j, sparse_matrix<T, X>& copy_of_U);
// see page 407 of Chvatal
unsigned transform_U_to_V_by_replacing_column(indexed_vector<T> & w, unsigned leaving_column_of_U);
#ifdef LEAN_DEBUG
void check_vector_w(unsigned entering);
void check_apply_matrix_to_vector(matrix<T, X> *lp, T *w);
void check_apply_lp_lists_to_w(T * w);
// provide some access operators for testing
permutation_matrix<T, X> & Q() { return m_Q; }
permutation_matrix<T, X> & R() { return m_R; }
matrix<T, X> & U() { return m_U; }
unsigned tail_size() { return m_tail.size(); }
tail_matrix<T, X> * get_lp_matrix(unsigned i) {
return m_tail[i];
}
T B_(unsigned i, unsigned j, const vector<unsigned>& basis) {
return m_A.get_elem(i, basis[j]);
}
unsigned dimension() { return m_dim; }
#endif
unsigned get_number_of_nonzeroes() {
return m_U.get_number_of_nonzeroes();
}
void process_column(int j);
bool is_correct(const vector<unsigned>& basis);
#ifdef LEAN_DEBUG
dense_matrix<T, X> tail_product();
dense_matrix<T, X> get_left_side(const vector<unsigned>& basis);
dense_matrix<T, X> get_right_side();
#endif
// needed for debugging purposes
void copy_w(T *buffer, indexed_vector<T> & w);
// needed for debugging purposes
void restore_w(T *buffer, indexed_vector<T> & w);
bool all_columns_and_rows_are_active();
bool too_dense(unsigned j) const;
void pivot_in_dense_mode(unsigned i);
void create_initial_factorization();
void calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave);
void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump);
bool diagonal_element_is_off(T /* diag_element */) { return false; }
void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump);
// see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last
// row at the same time
row_eta_matrix<T, X> *get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking);
void replace_column(T pivot_elem, indexed_vector<T> & w, unsigned leaving_column_of_U);
void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump);
void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element);
void prepare_entering(unsigned entering, indexed_vector<T> & w) {
init_vector_w(entering, w);
}
bool need_to_refactor() { return m_refactor_counter >= 200; }
void adjust_dimension_with_matrix_A() {
lean_assert(m_A.row_count() >= m_dim);
m_dim = m_A.row_count();
m_U.resize(m_dim);
m_Q.resize(m_dim);
m_R.resize(m_dim);
m_row_eta_work_vector.resize(m_dim);
}
std::unordered_set<unsigned> get_set_of_columns_to_replace_for_add_last_rows(const vector<int> & heading) const {
std::unordered_set<unsigned> columns_to_replace;
unsigned m = m_A.row_count();
unsigned m_prev = m_U.dimension();
lean_assert(m_A.column_count() == heading.size());
for (unsigned i = m_prev; i < m; i++) {
for (const row_cell<T> & c : m_A.m_rows[i]) {
int h = heading[c.m_j];
if (h < 0) {
continue;
}
columns_to_replace.insert(c.m_j);
}
}
return columns_to_replace;
}
void add_last_rows_to_B(const vector<int> & heading, const std::unordered_set<unsigned> & columns_to_replace) {
unsigned m = m_A.row_count();
lean_assert(m_A.column_count() == heading.size());
adjust_dimension_with_matrix_A();
m_w_for_extension.resize(m);
// At this moment the LU is correct
// for B extended by only by ones at the diagonal in the lower right corner
for (unsigned j :columns_to_replace) {
lean_assert(heading[j] >= 0);
replace_column_with_only_change_at_last_rows(j, heading[j]);
if (get_status() == LU_status::Degenerated)
break;
}
}
// column j is a basis column, and there is a change in the last rows
void replace_column_with_only_change_at_last_rows(unsigned j, unsigned column_to_change_in_U) {
init_vector_w(j, m_w_for_extension);
replace_column(zero_of_type<T>(), m_w_for_extension, column_to_change_in_U);
}
bool has_dense_submatrix() const {
for (auto m : m_tail)
if (m->is_dense())
return true;
return false;
}
}; // end of lu
template <typename T, typename X>
void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, vector<unsigned> & m_basis, lp_settings &m_settings);
#ifdef LEAN_DEBUG
template <typename T, typename X>
dense_matrix<T, X> get_B(lu<T, X>& f, const vector<unsigned>& basis);
#endif
}

940
src/util/lp/lu.hpp Normal file
View file

@ -0,0 +1,940 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <string>
#include <algorithm>
#include <set>
#include "util/vector.h"
#include <utility>
#include "util/debug.h"
#include "util/lp/lu.h"
namespace lean {
#ifdef LEAN_DEBUG
template <typename T, typename X> // print the nr x nc submatrix at the top left corner
void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc, std::ostream & out) {
vector<vector<std::string>> A;
vector<unsigned> widths;
for (unsigned i = 0; i < m.row_count() && i < mr ; i++) {
A.push_back(vector<std::string>());
for (unsigned j = 0; j < m.column_count() && j < nc; j++) {
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
}
}
for (unsigned j = 0; j < m.column_count() && j < nc; j++) {
widths.push_back(get_width_of_column(j, A));
}
print_matrix_with_widths(A, widths, out);
}
template<typename T, typename X>
void print_matrix(static_matrix<T, X> &m, std::ostream & out) {
vector<vector<std::string>> A;
vector<unsigned> widths;
std::set<std::pair<unsigned, unsigned>> domain = m.get_domain();
for (unsigned i = 0; i < m.row_count(); i++) {
A.push_back(vector<std::string>());
for (unsigned j = 0; j < m.column_count(); j++) {
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
}
}
for (unsigned j = 0; j < m.column_count(); j++) {
widths.push_back(get_width_of_column(j, A));
}
print_matrix_with_widths(A, widths, out);
}
template <typename T, typename X>
void print_matrix(sparse_matrix<T, X>& m, std::ostream & out) {
vector<vector<std::string>> A;
vector<unsigned> widths;
for (unsigned i = 0; i < m.row_count(); i++) {
A.push_back(vector<std::string>());
for (unsigned j = 0; j < m.column_count(); j++) {
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
}
}
for (unsigned j = 0; j < m.column_count(); j++) {
widths.push_back(get_width_of_column(j, A));
}
print_matrix_with_widths(A, widths, out);
}
#endif
template <typename T, typename X>
one_elem_on_diag<T, X>::one_elem_on_diag(const one_elem_on_diag & o) {
m_i = o.m_i;
m_val = o.m_val;
#ifdef LEAN_DEBUG
m_m = m_n = o.m_m;
m_one_over_val = numeric_traits<T>::one() / o.m_val;
#endif
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
T one_elem_on_diag<T, X>::get_elem(unsigned i, unsigned j) const {
if (i == j){
if (j == m_i) {
return m_one_over_val;
}
return numeric_traits<T>::one();
}
return numeric_traits<T>::zero();
}
#endif
template <typename T, typename X>
void one_elem_on_diag<T, X>::apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
T & t = w[m_i];
if (numeric_traits<T>::is_zero(t)) {
return;
}
t /= m_val;
if (numeric_traits<T>::precise()) return;
if (settings.abs_val_is_smaller_than_drop_tolerance(t)) {
w.erase_from_index(m_i);
t = numeric_traits<T>::zero();
}
}
// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
template <typename T, typename X>
lu<T, X>::lu(static_matrix<T, X> const & A,
vector<unsigned>& basis,
lp_settings & settings):
m_dim(A.row_count()),
m_A(A),
m_Q(m_dim),
m_R(m_dim),
m_r_wave(m_dim),
m_U(A, basis), // create the square matrix that eventually will be factorized
m_settings(settings),
m_row_eta_work_vector(A.row_count()){
lean_assert(!(numeric_traits<T>::precise() && settings.use_tableau()));
#ifdef LEAN_DEBUG
debug_test_of_basis(A, basis);
#endif
++m_settings.st().m_num_factorizations;
create_initial_factorization();
#ifdef LEAN_DEBUG
// lean_assert(check_correctness());
#endif
}
template <typename T, typename X>
void lu<T, X>::debug_test_of_basis(static_matrix<T, X> const & A, vector<unsigned> & basis) {
std::set<unsigned> set;
for (unsigned i = 0; i < A.row_count(); i++) {
lean_assert(basis[i]< A.column_count());
set.insert(basis[i]);
}
lean_assert(set.size() == A.row_count());
}
template <typename T, typename X>
void lu<T, X>::solve_By(indexed_vector<X> & y) {
lean_assert(false); // not implemented
// init_vector_y(y);
// solve_By_when_y_is_ready(y);
}
template <typename T, typename X>
void lu<T, X>::solve_By(vector<X> & y) {
init_vector_y(y);
solve_By_when_y_is_ready_for_X(y);
}
template <typename T, typename X>
void lu<T, X>::solve_By_when_y_is_ready_for_X(vector<X> & y) {
if (numeric_traits<T>::precise()) {
m_U.solve_U_y(y);
m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal
return;
}
m_U.double_solve_U_y(y);
m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal
unsigned i = m_dim;
while (i--) {
if (is_zero(y[i])) continue;
if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){
y[i] = zero_of_type<X>();
}
}
}
template <typename T, typename X>
void lu<T, X>::solve_By_when_y_is_ready_for_T(vector<T> & y, vector<unsigned> & index) {
if (numeric_traits<T>::precise()) {
m_U.solve_U_y(y);
m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal
unsigned j = m_dim;
while (j--) {
if (!is_zero(y[j]))
index.push_back(j);
}
return;
}
m_U.double_solve_U_y(y);
m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal
unsigned i = m_dim;
while (i--) {
if (is_zero(y[i])) continue;
if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){
y[i] = zero_of_type<T>();
} else {
index.push_back(i);
}
}
}
template <typename T, typename X>
void lu<T, X>::solve_By_for_T_indexed_only(indexed_vector<T> & y, const lp_settings & settings) {
if (numeric_traits<T>::precise()) {
vector<unsigned> active_rows;
m_U.solve_U_y_indexed_only(y, settings, active_rows);
m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal
return;
}
m_U.double_solve_U_y(y, m_settings);
m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal
}
template <typename T, typename X>
void lu<T, X>::print_matrix_compact(std::ostream & f) {
f << "matrix_start" << std::endl;
f << "nrows " << m_A.row_count() << std::endl;
f << "ncolumns " << m_A.column_count() << std::endl;
for (unsigned i = 0; i < m_A.row_count(); i++) {
auto & row = m_A.m_rows[i];
f << "row " << i << std::endl;
for (auto & t : row) {
f << "column " << t.m_j << " value " << t.m_value << std::endl;
}
f << "row_end" << std::endl;
}
f << "matrix_end" << std::endl;
}
template <typename T, typename X>
void lu<T, X>::print(indexed_vector<T> & w, const vector<unsigned>& basis) {
std::string dump_file_name("/tmp/lu");
remove(dump_file_name.c_str());
std::ofstream f(dump_file_name);
if (!f.is_open()) {
LP_OUT(m_settings, "cannot open file " << dump_file_name << std::endl);
return;
}
LP_OUT(m_settings, "writing lu dump to " << dump_file_name << std::endl);
print_matrix_compact(f);
print_vector(basis, f);
print_indexed_vector(w, f);
f.close();
}
template <typename T, typename X>
void lu<T, X>::solve_Bd(unsigned a_column, indexed_vector<T> & d, indexed_vector<T> & w) {
init_vector_w(a_column, w);
if (w.m_index.size() * ratio_of_index_size_to_all_size<T>() < d.m_data.size()) { // this const might need some tuning
d = w;
solve_By_for_T_indexed_only(d, m_settings);
} else {
d.m_data = w.m_data;
d.m_index.clear();
solve_By_when_y_is_ready_for_T(d.m_data, d.m_index);
}
}
template <typename T, typename X>
void lu<T, X>::solve_Bd_faster(unsigned a_column, indexed_vector<T> & d) { // puts the a_column into d
init_vector_w(a_column, d);
solve_By_for_T_indexed_only(d, m_settings);
}
template <typename T, typename X>
void lu<T, X>::solve_yB(vector<T>& y) {
// first solve yU = cb*R(-1)
m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1)
m_U.solve_y_U(y); // got y*U=cb*R(-1)
m_Q.apply_reverse_from_right_to_T(y); //
for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) {
#ifdef LEAN_DEBUG
(*e)->set_number_of_columns(m_dim);
#endif
(*e)->apply_from_right(y);
}
}
template <typename T, typename X>
void lu<T, X>::solve_yB_indexed(indexed_vector<T>& y) {
lean_assert(y.is_OK());
// first solve yU = cb*R(-1)
m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1)
lean_assert(y.is_OK());
m_U.solve_y_U_indexed(y, m_settings); // got y*U=cb*R(-1)
lean_assert(y.is_OK());
m_Q.apply_reverse_from_right_to_T(y);
lean_assert(y.is_OK());
for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) {
#ifdef LEAN_DEBUG
(*e)->set_number_of_columns(m_dim);
#endif
(*e)->apply_from_right(y);
lean_assert(y.is_OK());
}
}
template <typename T, typename X>
void lu<T, X>::add_delta_to_solution(const vector<T>& yc, vector<T>& y){
unsigned i = static_cast<unsigned>(y.size());
while (i--)
y[i]+=yc[i];
}
template <typename T, typename X>
void lu<T, X>::add_delta_to_solution_indexed(indexed_vector<T>& y) {
// the delta sits in m_y_copy, put result into y
lean_assert(y.is_OK());
lean_assert(m_y_copy.is_OK());
m_ii.clear();
m_ii.resize(y.data_size());
for (unsigned i : y.m_index)
m_ii.set_value(1, i);
for (unsigned i : m_y_copy.m_index) {
y.m_data[i] += m_y_copy[i];
if (m_ii[i] == 0)
m_ii.set_value(1, i);
}
lean_assert(m_ii.is_OK());
y.m_index.clear();
for (unsigned i : m_ii.m_index) {
T & v = y.m_data[i];
if (!lp_settings::is_eps_small_general(v, 1e-14))
y.m_index.push_back(i);
else if (!numeric_traits<T>::is_zero(v))
v = zero_of_type<T>();
}
lean_assert(y.is_OK());
}
template <typename T, typename X>
void lu<T, X>::find_error_of_yB(vector<T>& yc, const vector<T>& y, const vector<unsigned>& m_basis) {
unsigned i = m_dim;
while (i--) {
yc[i] -= m_A.dot_product_with_column(y, m_basis[i]);
}
}
template <typename T, typename X>
void lu<T, X>::find_error_of_yB_indexed(const indexed_vector<T>& y, const vector<int>& heading, const lp_settings& settings) {
#if 0 == 1
// it is a non efficient version
indexed_vector<T> yc = m_y_copy;
yc.m_index.clear();
lean_assert(!numeric_traits<T>::precise());
{
vector<unsigned> d_basis(y.m_data.size());
for (unsigned j = 0; j < heading.size(); j++) {
if (heading[j] >= 0) {
d_basis[heading[j]] = j;
}
}
unsigned i = m_dim;
while (i--) {
T & v = yc.m_data[i] -= m_A.dot_product_with_column(y.m_data, d_basis[i]);
if (settings.abs_val_is_smaller_than_drop_tolerance(v))
v = zero_of_type<T>();
else
yc.m_index.push_back(i);
}
}
#endif
lean_assert(m_ii.is_OK());
m_ii.clear();
m_ii.resize(y.data_size());
lean_assert(m_y_copy.is_OK());
// put the error into m_y_copy
for (auto k : y.m_index) {
auto & row = m_A.m_rows[k];
const T & y_k = y.m_data[k];
for (auto & c : row) {
unsigned j = c.m_j;
int hj = heading[j];
if (hj < 0) continue;
if (m_ii.m_data[hj] == 0)
m_ii.set_value(1, hj);
m_y_copy.m_data[hj] -= c.get_val() * y_k;
}
}
// add the index of m_y_copy to m_ii
for (unsigned i : m_y_copy.m_index) {
if (m_ii.m_data[i] == 0)
m_ii.set_value(1, i);
}
// there is no guarantee that m_y_copy is OK here, but its index
// is contained in m_ii index
m_y_copy.m_index.clear();
// setup the index of m_y_copy
for (auto k : m_ii.m_index) {
T& v = m_y_copy.m_data[k];
if (settings.abs_val_is_smaller_than_drop_tolerance(v))
v = zero_of_type<T>();
else {
m_y_copy.set_value(v, k);
}
}
lean_assert(m_y_copy.is_OK());
}
// solves y*B = y
// y is the input
template <typename T, typename X>
void lu<T, X>::solve_yB_with_error_check_indexed(indexed_vector<T> & y, const vector<int>& heading, const vector<unsigned> & basis, const lp_settings & settings) {
if (numeric_traits<T>::precise()) {
if (y.m_index.size() * ratio_of_index_size_to_all_size<T>() * 3 < m_A.column_count()) {
solve_yB_indexed(y);
} else {
solve_yB(y.m_data);
y.restore_index_and_clean_from_data();
}
return;
}
lean_assert(m_y_copy.is_OK());
lean_assert(y.is_OK());
if (y.m_index.size() * ratio_of_index_size_to_all_size<T>() < m_A.column_count()) {
m_y_copy = y;
solve_yB_indexed(y);
lean_assert(y.is_OK());
if (y.m_index.size() * ratio_of_index_size_to_all_size<T>() >= m_A.column_count()) {
find_error_of_yB(m_y_copy.m_data, y.m_data, basis);
solve_yB(m_y_copy.m_data);
add_delta_to_solution(m_y_copy.m_data, y.m_data);
y.restore_index_and_clean_from_data();
m_y_copy.clear_all();
} else {
find_error_of_yB_indexed(y, heading, settings); // this works with m_y_copy
solve_yB_indexed(m_y_copy);
add_delta_to_solution_indexed(y);
}
lean_assert(m_y_copy.is_OK());
} else {
solve_yB_with_error_check(y.m_data, basis);
y.restore_index_and_clean_from_data();
}
}
// solves y*B = y
// y is the input
template <typename T, typename X>
void lu<T, X>::solve_yB_with_error_check(vector<T> & y, const vector<unsigned>& basis) {
if (numeric_traits<T>::precise()) {
solve_yB(y);
return;
}
auto & yc = m_y_copy.m_data;
yc =y; // copy y aside
solve_yB(y);
find_error_of_yB(yc, y, basis);
solve_yB(yc);
add_delta_to_solution(yc, y);
m_y_copy.clear_all();
}
template <typename T, typename X>
void lu<T, X>::apply_Q_R_to_U(permutation_matrix<T, X> & r_wave) {
m_U.multiply_from_right(r_wave);
m_U.multiply_from_left_with_reverse(r_wave);
}
// Solving yB = cb to find the entering variable,
// where cb is the cost vector projected to B.
// The result is stored in cb.
// solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering
// variable
template <typename T, typename X>
lu<T, X>::~lu(){
for (auto t : m_tail) {
delete t;
}
}
template <typename T, typename X>
void lu<T, X>::init_vector_y(vector<X> & y) {
apply_lp_list_to_y(y);
m_Q.apply_reverse_from_left_to_X(y);
}
template <typename T, typename X>
void lu<T, X>::perform_transformations_on_w(indexed_vector<T>& w) {
apply_lp_list_to_w(w);
m_Q.apply_reverse_from_left(w);
// TBD does not compile: lean_assert(numeric_traits<T>::precise() || check_vector_for_small_values(w, m_settings));
}
// see Chvatal 24.3
template <typename T, typename X>
void lu<T, X>::init_vector_w(unsigned entering, indexed_vector<T> & w) {
w.clear();
m_A.copy_column_to_indexed_vector(entering, w); // w = a, the column
perform_transformations_on_w(w);
}
template <typename T, typename X>
void lu<T, X>::apply_lp_list_to_w(indexed_vector<T> & w) {
for (unsigned i = 0; i < m_tail.size(); i++) {
m_tail[i]->apply_from_left_to_T(w, m_settings);
// TBD does not compile: lean_assert(check_vector_for_small_values(w, m_settings));
}
}
template <typename T, typename X>
void lu<T, X>::apply_lp_list_to_y(vector<X>& y) {
for (unsigned i = 0; i < m_tail.size(); i++) {
m_tail[i]->apply_from_left(y, m_settings);
}
}
template <typename T, typename X>
void lu<T, X>::swap_rows(int j, int k) {
if (j != k) {
m_Q.transpose_from_left(j, k);
m_U.swap_rows(j, k);
}
}
template <typename T, typename X>
void lu<T, X>::swap_columns(int j, int pivot_column) {
if (j == pivot_column)
return;
m_R.transpose_from_right(j, pivot_column);
m_U.swap_columns(j, pivot_column);
}
template <typename T, typename X>
bool lu<T, X>::pivot_the_row(int row) {
eta_matrix<T, X> * eta_matrix = get_eta_matrix_for_pivot(row);
if (get_status() != LU_status::OK) {
return false;
}
if (eta_matrix == nullptr) {
m_U.shorten_active_matrix(row, nullptr);
return true;
}
if (!m_U.pivot_with_eta(row, eta_matrix, m_settings))
return false;
eta_matrix->conjugate_by_permutation(m_Q);
push_matrix_to_tail(eta_matrix);
return true;
}
// we're processing the column j now
template <typename T, typename X>
eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j) {
eta_matrix<T, X> *ret;
if(!m_U.fill_eta_matrix(j, &ret)) {
set_status(LU_status::Degenerated);
}
return ret;
}
// we're processing the column j now
template <typename T, typename X>
eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j, sparse_matrix<T, X>& copy_of_U) {
eta_matrix<T, X> *ret;
copy_of_U.fill_eta_matrix(j, &ret);
return ret;
}
// see page 407 of Chvatal
template <typename T, typename X>
unsigned lu<T, X>::transform_U_to_V_by_replacing_column(indexed_vector<T> & w,
unsigned leaving_column) {
unsigned column_to_replace = m_R.apply_reverse(leaving_column);
m_U.replace_column(column_to_replace, w, m_settings);
return column_to_replace;
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
void lu<T, X>::check_vector_w(unsigned entering) {
T * w = new T[m_dim];
m_A.copy_column_to_vector(entering, w);
check_apply_lp_lists_to_w(w);
delete [] w;
}
template <typename T, typename X>
void lu<T, X>::check_apply_matrix_to_vector(matrix<T, X> *lp, T *w) {
if (lp != nullptr) {
lp -> set_number_of_rows(m_dim);
lp -> set_number_of_columns(m_dim);
apply_to_vector(*lp, w);
}
}
template <typename T, typename X>
void lu<T, X>::check_apply_lp_lists_to_w(T * w) {
for (unsigned i = 0; i < m_tail.size(); i++) {
check_apply_matrix_to_vector(m_tail[i], w);
}
permutation_matrix<T, X> qr = m_Q.get_reverse();
apply_to_vector(qr, w);
for (int i = m_dim - 1; i >= 0; i--) {
lean_assert(abs(w[i] - w[i]) < 0.0000001);
}
}
#endif
template <typename T, typename X>
void lu<T, X>::process_column(int j) {
unsigned pi, pj;
bool success = m_U.get_pivot_for_column(pi, pj, m_settings.c_partial_pivoting, j);
if (!success) {
LP_OUT(m_settings, "get_pivot returned false: cannot find the pivot for column " << j << std::endl);
m_failure = true;
return;
}
if (static_cast<int>(pi) == -1) {
LP_OUT(m_settings, "cannot find the pivot for column " << j << std::endl);
m_failure = true;
return;
}
swap_columns(j, pj);
swap_rows(j, pi);
if (!pivot_the_row(j)) {
// LP_OUT(m_settings, "pivot_the_row(" << j << ") failed" << std::endl);
m_failure = true;
}
}
template <typename T, typename X>
bool lu<T, X>::is_correct(const vector<unsigned>& basis) {
#ifdef LEAN_DEBUG
if (get_status() != LU_status::OK) {
return false;
}
dense_matrix<T, X> left_side = get_left_side(basis);
dense_matrix<T, X> right_side = get_right_side();
return left_side == right_side;
#else
return true;
#endif
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
dense_matrix<T, X> lu<T, X>::tail_product() {
lean_assert(tail_size() > 0);
dense_matrix<T, X> left_side = permutation_matrix<T, X>(m_dim);
for (unsigned i = 0; i < tail_size(); i++) {
matrix<T, X>* lp = get_lp_matrix(i);
lp->set_number_of_rows(m_dim);
lp->set_number_of_columns(m_dim);
left_side = ((*lp) * left_side);
}
return left_side;
}
template <typename T, typename X>
dense_matrix<T, X> lu<T, X>::get_left_side(const vector<unsigned>& basis) {
dense_matrix<T, X> left_side = get_B(*this, basis);
for (unsigned i = 0; i < tail_size(); i++) {
matrix<T, X>* lp = get_lp_matrix(i);
lp->set_number_of_rows(m_dim);
lp->set_number_of_columns(m_dim);
left_side = ((*lp) * left_side);
}
return left_side;
}
template <typename T, typename X>
dense_matrix<T, X> lu<T, X>::get_right_side() {
auto ret = U() * R();
ret = Q() * ret;
return ret;
}
#endif
// needed for debugging purposes
template <typename T, typename X>
void lu<T, X>::copy_w(T *buffer, indexed_vector<T> & w) {
unsigned i = m_dim;
while (i--) {
buffer[i] = w[i];
}
}
// needed for debugging purposes
template <typename T, typename X>
void lu<T, X>::restore_w(T *buffer, indexed_vector<T> & w) {
unsigned i = m_dim;
while (i--) {
w[i] = buffer[i];
}
}
template <typename T, typename X>
bool lu<T, X>::all_columns_and_rows_are_active() {
unsigned i = m_dim;
while (i--) {
lean_assert(m_U.col_is_active(i));
lean_assert(m_U.row_is_active(i));
}
return true;
}
template <typename T, typename X>
bool lu<T, X>::too_dense(unsigned j) const {
unsigned r = m_dim - j;
if (r < 5)
return false;
// if (j * 5 < m_dim * 4) // start looking for dense only at the bottom of the rows
// return false;
// return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j);
return r * r * m_settings.density_threshold <= m_U.get_n_of_active_elems();
}
template <typename T, typename X>
void lu<T, X>::pivot_in_dense_mode(unsigned i) {
int j = m_dense_LU->find_pivot_column_in_row(i);
if (j == -1) {
m_failure = true;
return;
}
if (i != static_cast<unsigned>(j)) {
swap_columns(i, j);
m_dense_LU->swap_columns(i, j);
}
m_dense_LU->pivot(i, m_settings);
}
template <typename T, typename X>
void lu<T, X>::create_initial_factorization(){
m_U.prepare_for_factorization();
unsigned j;
for (j = 0; j < m_dim; j++) {
process_column(j);
if (m_failure) {
set_status(LU_status::Degenerated);
return;
}
if (too_dense(j)) {
break;
}
}
if (j == m_dim) {
// TBD does not compile: lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
// lean_assert(is_correct());
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
return;
}
j++;
m_dense_LU = new square_dense_submatrix<T, X>(&m_U, j);
for (; j < m_dim; j++) {
pivot_in_dense_mode(j);
if (m_failure) {
set_status(LU_status::Degenerated);
return;
}
}
m_dense_LU->update_parent_matrix(m_settings);
lean_assert(m_dense_LU->is_L_matrix());
m_dense_LU->conjugate_by_permutation(m_Q);
push_matrix_to_tail(m_dense_LU);
m_refactor_counter = 0;
// lean_assert(is_correct());
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
}
template <typename T, typename X>
void lu<T, X>::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave) {
if (bump_start > bump_end) {
set_status(LU_status::Degenerated);
return;
}
if (bump_start == bump_end) {
return;
}
r_wave[bump_start] = bump_end; // sending the offensive column to the end of the bump
for ( unsigned i = bump_start + 1 ; i <= bump_end; i++ ) {
r_wave[i] = i - 1;
}
m_U.multiply_from_right(r_wave);
m_U.multiply_from_left_with_reverse(r_wave);
}
template <typename T, typename X>
void lu<T, X>::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) {
vector<indexed_value<T>> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump));
for (auto & iv : last_row_vec) {
if (is_zero(iv.m_value)) continue;
lean_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value));
unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index);
if (adjusted_col < lowest_row_of_the_bump) {
m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col);
} else {
m_row_eta_work_vector.set_value(iv.m_value, adjusted_col); // preparing to calculate the real value in the matrix
}
}
}
template <typename T, typename X>
void lu<T, X>::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) {
// we have the system right side at m_row_eta_work_vector now
// solve the system column wise
for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) {
T v = m_row_eta_work_vector[j];
if (numeric_traits<T>::is_zero(v)) continue; // this column does not contribute to the solution
unsigned aj = m_U.adjust_row(j);
vector<indexed_value<T>> & row = m_U.get_row_values(aj);
for (auto & iv : row) {
unsigned col = m_U.adjust_column_inverse(iv.m_index);
lean_assert(col >= j || numeric_traits<T>::is_zero(iv.m_value));
if (col == j) continue;
if (numeric_traits<T>::is_zero(iv.m_value)) {
continue;
}
// the -v is for solving the system ( to zero the last row), and +v is for pivoting
T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value;
lean_assert(numeric_traits<T>::is_zero(delta) == false);
// m_row_eta_work_vector.add_value_at_index_with_drop_tolerance(col, delta);
if (numeric_traits<T>::is_zero(m_row_eta_work_vector[col])) {
if (!m_settings.abs_val_is_smaller_than_drop_tolerance(delta)){
m_row_eta_work_vector.set_value(delta, col);
}
} else {
T t = (m_row_eta_work_vector[col] += delta);
if (m_settings.abs_val_is_smaller_than_drop_tolerance(t)){
m_row_eta_work_vector[col] = numeric_traits<T>::zero();
auto it = std::find(m_row_eta_work_vector.m_index.begin(), m_row_eta_work_vector.m_index.end(), col);
if (it != m_row_eta_work_vector.m_index.end())
m_row_eta_work_vector.m_index.erase(it);
}
}
}
}
}
// see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last
// row at the same time
template <typename T, typename X>
row_eta_matrix<T, X> *lu<T, X>::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) {
if (replaced_column == lowest_row_of_the_bump) return nullptr;
scan_last_row_to_work_vector(lowest_row_of_the_bump);
pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump);
if (numeric_traits<T>::precise() == false && !is_zero(pivot_elem_for_checking)) {
T denom = std::max(T(1), abs(pivot_elem_for_checking));
if (
!m_settings.abs_val_is_smaller_than_pivot_tolerance((m_row_eta_work_vector[lowest_row_of_the_bump] - pivot_elem_for_checking) / denom)) {
set_status(LU_status::Degenerated);
// LP_OUT(m_settings, "diagonal element is off" << std::endl);
return nullptr;
}
}
#ifdef LEAN_DEBUG
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump, m_dim);
#else
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump);
#endif
for (auto j : m_row_eta_work_vector.m_index) {
if (j < lowest_row_of_the_bump) {
auto & v = m_row_eta_work_vector[j];
if (!is_zero(v)) {
if (!m_settings.abs_val_is_smaller_than_drop_tolerance(v)){
ret->push_back(j, v);
}
v = numeric_traits<T>::zero();
}
}
} // now the lowest_row_of_the_bump contains the rest of the row to the right of the bump with correct values
return ret;
}
template <typename T, typename X>
void lu<T, X>::replace_column(T pivot_elem_for_checking, indexed_vector<T> & w, unsigned leaving_column_of_U){
m_refactor_counter++;
unsigned replaced_column = transform_U_to_V_by_replacing_column( w, leaving_column_of_U);
unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column);
m_r_wave.init(m_dim);
calculate_r_wave_and_update_U(replaced_column, lowest_row_of_the_bump, m_r_wave);
auto row_eta = get_row_eta_matrix_and_set_row_vector(replaced_column, lowest_row_of_the_bump, pivot_elem_for_checking);
if (get_status() == LU_status::Degenerated) {
m_row_eta_work_vector.clear_all();
return;
}
m_Q.multiply_by_permutation_from_right(m_r_wave);
m_R.multiply_by_permutation_reverse_from_left(m_r_wave);
if (row_eta != nullptr) {
row_eta->conjugate_by_permutation(m_Q);
push_matrix_to_tail(row_eta);
}
calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump);
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
// lean_assert(w.is_OK() && m_row_eta_work_vector.is_OK());
}
template <typename T, typename X>
void lu<T, X>::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){
T diagonal_elem;
if (replaced_column < lowest_row_of_the_bump) {
diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump];
// lean_assert(m_row_eta_work_vector.is_OK());
m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings);
} else {
diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently
}
if (m_settings.abs_val_is_smaller_than_pivot_tolerance(diagonal_elem)) {
set_status(LU_status::Degenerated);
return;
}
calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem);
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
}
template <typename T, typename X>
void lu<T, X>::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) {
auto l = new one_elem_on_diag<T, X>(lowest_row_of_the_bump, diagonal_element);
#ifdef LEAN_DEBUG
l->set_number_of_columns(m_dim);
#endif
push_matrix_to_tail(l);
m_U.divide_row_by_constant(lowest_row_of_the_bump, diagonal_element, m_settings);
l->conjugate_by_permutation(m_Q);
}
template <typename T, typename X>
void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, vector<unsigned> & m_basis, lp_settings &m_settings) {
if (factorization != nullptr)
delete factorization;
factorization = new lu<T, X>(m_A, m_basis, m_settings);
// if (factorization->get_status() != LU_status::OK)
// LP_OUT(m_settings, "failing in init_factorization" << std::endl);
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
dense_matrix<T, X> get_B(lu<T, X>& f, const vector<unsigned>& basis) {
lean_assert(basis.size() == f.dimension());
lean_assert(basis.size() == f.m_U.dimension());
dense_matrix<T, X> B(f.dimension(), f.dimension());
for (unsigned i = 0; i < f.dimension(); i++)
for (unsigned j = 0; j < f.dimension(); j++)
B.set_elem(i, j, f.B_(i, j, basis));
return B;
}
#endif
}

View file

@ -0,0 +1,63 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <utility>
#include <memory>
#include <string>
#include "util/vector.h"
#include "util/debug.h"
#include "util/lp/lu.hpp"
template double lean::dot_product<double, double>(vector<double> const&, vector<double> const&);
template lean::lu<double, double>::lu(lean::static_matrix<double, double> const&, vector<unsigned int>&, lean::lp_settings&);
template void lean::lu<double, double>::push_matrix_to_tail(lean::tail_matrix<double, double>*);
template void lean::lu<double, double>::replace_column(double, lean::indexed_vector<double>&, unsigned);
template void lean::lu<double, double>::solve_Bd(unsigned int, lean::indexed_vector<double>&, lean::indexed_vector<double>&);
template lean::lu<double, double>::~lu();
template void lean::lu<lean::mpq, lean::mpq>::push_matrix_to_tail(lean::tail_matrix<lean::mpq, lean::mpq>*);
template void lean::lu<lean::mpq, lean::mpq>::solve_Bd(unsigned int, lean::indexed_vector<lean::mpq>&, lean::indexed_vector<lean::mpq>&);
template lean::lu<lean::mpq, lean::mpq>::~lu();
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::push_matrix_to_tail(lean::tail_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >*);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd(unsigned int, lean::indexed_vector<lean::mpq>&, lean::indexed_vector<lean::mpq>&);
template lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::~lu();
template lean::mpq lean::dot_product<lean::mpq, lean::mpq>(vector<lean::mpq > const&, vector<lean::mpq > const&);
template void lean::init_factorization<double, double>(lean::lu<double, double>*&, lean::static_matrix<double, double>&, vector<unsigned int>&, lean::lp_settings&);
template void lean::init_factorization<lean::mpq, lean::mpq>(lean::lu<lean::mpq, lean::mpq>*&, lean::static_matrix<lean::mpq, lean::mpq>&, vector<unsigned int>&, lean::lp_settings&);
template void lean::init_factorization<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >*&, lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, vector<unsigned int>&, lean::lp_settings&);
#ifdef LEAN_DEBUG
template void lean::print_matrix<double, double>(lean::sparse_matrix<double, double>&, std::ostream & out);
template void lean::print_matrix<lean::mpq, lean::mpq>(lean::static_matrix<lean::mpq, lean::mpq>&, std::ostream&);
template void lean::print_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, std::ostream&);
template void lean::print_matrix<double, double>(lean::static_matrix<double, double>&, std::ostream & out);
template bool lean::lu<double, double>::is_correct(const vector<unsigned>& basis);
template bool lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::is_correct( vector<unsigned int> const &);
template lean::dense_matrix<double, double> lean::get_B<double, double>(lean::lu<double, double>&, const vector<unsigned>& basis);
template lean::dense_matrix<lean::mpq, lean::mpq> lean::get_B<lean::mpq, lean::mpq>(lean::lu<lean::mpq, lean::mpq>&, vector<unsigned int> const&);
#endif
template bool lean::lu<double, double>::pivot_the_row(int); // NOLINT
template void lean::lu<double, double>::init_vector_w(unsigned int, lean::indexed_vector<double>&);
template void lean::lu<double, double>::solve_By(vector<double>&);
template void lean::lu<double, double>::solve_By_when_y_is_ready_for_X(vector<double>&);
template void lean::lu<double, double>::solve_yB_with_error_check(vector<double>&, const vector<unsigned>& basis);
template void lean::lu<double, double>::solve_yB_with_error_check_indexed(lean::indexed_vector<double>&, vector<int> const&, const vector<unsigned> & basis, const lp_settings&);
template void lean::lu<lean::mpq, lean::mpq>::replace_column(lean::mpq, lean::indexed_vector<lean::mpq>&, unsigned);
template void lean::lu<lean::mpq, lean::mpq>::solve_By(vector<lean::mpq >&);
template void lean::lu<lean::mpq, lean::mpq>::solve_By_when_y_is_ready_for_X(vector<lean::mpq >&);
template void lean::lu<lean::mpq, lean::mpq>::solve_yB_with_error_check(vector<lean::mpq >&, const vector<unsigned>& basis);
template void lean::lu<lean::mpq, lean::mpq>::solve_yB_with_error_check_indexed(lean::indexed_vector<lean::mpq>&, vector< int > const&, const vector<unsigned> & basis, const lp_settings&);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB_with_error_check_indexed(lean::indexed_vector<lean::mpq>&, vector< int > const&, const vector<unsigned> & basis, const lp_settings&);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::init_vector_w(unsigned int, lean::indexed_vector<lean::mpq>&);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::replace_column(lean::mpq, lean::indexed_vector<lean::mpq>&, unsigned);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd_faster(unsigned int, lean::indexed_vector<lean::mpq>&);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_By(vector<lean::numeric_pair<lean::mpq> >&);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_By_when_y_is_ready_for_X(vector<lean::numeric_pair<lean::mpq> >&);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB_with_error_check(vector<lean::mpq >&, const vector<unsigned>& basis);
template void lean::lu<lean::mpq, lean::mpq>::solve_By(lean::indexed_vector<lean::mpq>&);
template void lean::lu<double, double>::solve_By(lean::indexed_vector<double>&);
template void lean::lu<double, double>::solve_yB_indexed(lean::indexed_vector<double>&);
template void lean::lu<lean::mpq, lean::mpq>::solve_yB_indexed(lean::indexed_vector<lean::mpq>&);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB_indexed(lean::indexed_vector<lean::mpq>&);
template void lean::lu<lean::mpq, lean::mpq>::solve_By_for_T_indexed_only(lean::indexed_vector<lean::mpq>&, lean::lp_settings const&);
template void lean::lu<double, double>::solve_By_for_T_indexed_only(lean::indexed_vector<double>&, lean::lp_settings const&);

44
src/util/lp/matrix.h Normal file
View file

@ -0,0 +1,44 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#ifdef Z3DEBUG
#pragma once
#include "util/lp/numeric_pair.h"
#include "util/vector.h"
#include <string>
#include "util/lp/lp_settings.h"
namespace lean {
// used for debugging purposes only
template <typename T, typename X>
class matrix {
public:
virtual T get_elem (unsigned i, unsigned j) const = 0;
virtual unsigned row_count() const = 0;
virtual unsigned column_count() const = 0;
virtual void set_number_of_rows(unsigned m) = 0;
virtual void set_number_of_columns(unsigned n) = 0;
virtual ~matrix() {}
bool is_equal(const matrix<T, X>& other);
bool operator == (matrix<T, X> const & other) {
return is_equal(other);
}
T operator()(unsigned i, unsigned j) const { return get_elem(i, j); }
};
template <typename T, typename X>
void apply_to_vector(matrix<T, X> & m, T * w);
unsigned get_width_of_column(unsigned j, vector<vector<std::string>> & A);
void print_matrix_with_widths(vector<vector<std::string>> & A, vector<unsigned> & ws, std::ostream & out);
void print_string_matrix(vector<vector<std::string>> & A);
template <typename T, typename X>
void print_matrix(matrix<T, X> const * m, std::ostream & out);
}
#endif

105
src/util/lp/matrix.hpp Normal file
View file

@ -0,0 +1,105 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#ifdef Z3DEBUG
#include <cmath>
#include <string>
#include "util/lp/matrix.h"
namespace lean {
template <typename T, typename X>
bool matrix<T, X>::is_equal(const matrix<T, X>& other) {
if (other.row_count() != row_count() || other.column_count() != column_count())
return false;
for (unsigned i = 0; i < row_count(); i++) {
for (unsigned j = 0; j < column_count(); j++) {
auto a = get_elem(i, j);
auto b = other.get_elem(i, j);
if (numeric_traits<T>::precise()) {
if (a != b) return false;
} else if (fabs(numeric_traits<T>::get_double(a - b)) > 0.000001) {
// cout << "returning false from operator== of matrix comparison" << endl;
// cout << "this matrix is " << endl;
// print_matrix(*this);
// cout << "other matrix is " << endl;
// print_matrix(other);
return false;
}
}
}
return true;
}
template <typename T, typename X>
void apply_to_vector(matrix<T, X> & m, T * w) {
// here m is a square matrix
unsigned dim = m.row_count();
T * wc = new T[dim];
for (unsigned i = 0; i < dim; i++) {
wc[i] = w[i];
}
for (unsigned i = 0; i < dim; i++) {
T t = numeric_traits<T>::zero();
for (unsigned j = 0; j < dim; j++) {
t += m(i, j) * wc[j];
}
w[i] = t;
}
delete [] wc;
}
unsigned get_width_of_column(unsigned j, vector<vector<std::string>> & A) {
unsigned r = 0;
for (unsigned i = 0; i < A.size(); i++) {
vector<std::string> & t = A[i];
std::string str= t[j];
unsigned s = str.size();
if (r < s) {
r = s;
}
}
return r;
}
void print_matrix_with_widths(vector<vector<std::string>> & A, vector<unsigned> & ws, std::ostream & out) {
for (unsigned i = 0; i < A.size(); i++) {
for (unsigned j = 0; j < A[i].size(); j++) {
print_blanks(ws[j] - A[i][j].size(), out);
out << A[i][j] << " ";
}
out << std::endl;
}
}
void print_string_matrix(vector<vector<std::string>> & A, std::ostream & out) {
vector<unsigned> widths;
if (A.size() > 0)
for (unsigned j = 0; j < A[0].size(); j++) {
widths.push_back(get_width_of_column(j, A));
}
print_matrix_with_widths(A, widths, out);
out << std::endl;
}
template <typename T, typename X>
void print_matrix(matrix<T, X> const * m, std::ostream & out) {
vector<vector<std::string>> A(m->row_count());
for (unsigned i = 0; i < m->row_count(); i++) {
for (unsigned j = 0; j < m->column_count(); j++) {
A[i].push_back(T_to_string(m->get_elem(i, j)));
}
}
print_string_matrix(A, out);
}
}
#endif

View file

@ -0,0 +1,16 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/lp_settings.h"
#ifdef LEAN_DEBUG
#include "util/lp/matrix.hpp"
#include "util/lp/static_matrix.h"
#include <string>
template void lean::print_matrix<double, double>(lean::matrix<double, double> const*, std::ostream & out);
template bool lean::matrix<double, double>::is_equal(lean::matrix<double, double> const&);
template void lean::print_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> > const *, std::basic_ostream<char, std::char_traits<char> > &);
template void lean::print_matrix<lean::mpq, lean::mpq>(lean::matrix<lean::mpq, lean::mpq> const*, std::ostream&);
template bool lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::is_equal(lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> > const&);
template bool lean::matrix<lean::mpq, lean::mpq>::is_equal(lean::matrix<lean::mpq, lean::mpq> const&);
#endif

867
src/util/lp/mps_reader.h Normal file
View file

@ -0,0 +1,867 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
// reads an MPS file reperesenting a Mixed Integer Program
#include <functional>
#include <algorithm>
#include <string>
#include "util/vector.h"
#include <unordered_map>
#include <iostream>
#include <fstream>
#include <locale>
#include "util/lp/lp_primal_simplex.h"
#include "util/lp/lp_dual_simplex.h"
#include "util/lp/lar_solver.h"
#include "util/lp/lp_utils.h"
#include "util/lp/lp_solver.h"
namespace lean {
inline bool my_white_space(const char & a) {
return a == ' ' || a == '\t';
}
inline size_t number_of_whites(const std::string & s) {
size_t i = 0;
for(;i < s.size(); i++)
if (!my_white_space(s[i])) return i;
return i;
}
inline size_t number_of_whites_from_end(const std::string & s) {
size_t ret = 0;
for(int i = static_cast<int>(s.size()) - 1;i >= 0; i--)
if (my_white_space(s[i])) ret++;else break;
return ret;
}
// trim from start
inline std::string &ltrim(std::string &s) {
s.erase(0, number_of_whites(s));
return s;
}
// trim from end
inline std::string &rtrim(std::string &s) {
// s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
s.erase(s.end() - number_of_whites_from_end(s), s.end());
return s;
}
// trim from both ends
inline std::string &trim(std::string &s) {
return ltrim(rtrim(s));
}
inline std::string trim(std::string const &r) {
std::string s = r;
return ltrim(rtrim(s));
}
inline vector<std::string> string_split(const std::string &source, const char *delimiter, bool keep_empty) {
vector<std::string> results;
size_t prev = 0;
size_t next = 0;
while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) {
if (keep_empty || (next - prev != 0)) {
results.push_back(source.substr(prev, next - prev));
}
prev = next + 1;
}
if (prev < source.size()) {
results.push_back(source.substr(prev));
}
return results;
}
inline vector<std::string> split_and_trim(std::string line) {
auto split = string_split(line, " \t", false);
vector<std::string> ret;
for (auto s : split) {
ret.push_back(trim(s));
}
return ret;
}
template <typename T, typename X>
class mps_reader {
enum row_type { Cost, Less_or_equal, Greater_or_equal, Equal };
struct bound {
bool m_low_is_set = true;
T m_low;
bool m_upper_is_set = false;
T m_upper;
bool m_value_is_fixed = false;
T m_fixed_value;
bool m_free = false;
// constructor
bound() : m_low(numeric_traits<T>::zero()) {} // it seems all mps files I have seen have the default low value 0 on a variable
};
struct column {
std::string m_name;
bound * m_bound = nullptr;
unsigned m_index;
column(std::string name, unsigned index): m_name(name), m_index(index) {
}
};
struct row {
row_type m_type;
std::string m_name;
std::unordered_map<std::string, T> m_row_columns;
T m_right_side = numeric_traits<T>::zero();
unsigned m_index;
T m_range = numeric_traits<T>::zero();
row(row_type type, std::string name, unsigned index) : m_type(type), m_name(name), m_index(index) {
}
};
std::string m_file_name;
bool m_is_OK = true;
std::unordered_map<std::string, row *> m_rows;
std::unordered_map<std::string, column *> m_columns;
std::unordered_map<std::string, unsigned> m_names_to_var_index;
std::string m_line;
std::string m_name;
std::string m_cost_row_name;
std::ifstream m_file_stream;
// needed to adjust the index row
unsigned m_cost_line_count = 0;
unsigned m_line_number = 0;
std::ostream * m_message_stream = & std::cout;
void set_m_ok_to_false() {
*m_message_stream << "setting m_is_OK to false" << std::endl;
m_is_OK = false;
}
std::string get_string_from_position(unsigned offset) {
unsigned i = offset;
for (; i < m_line.size(); i++){
if (m_line[i] == ' ')
break;
}
lean_assert(m_line.size() >= offset);
lean_assert(m_line.size() >> i);
lean_assert(i >= offset);
return m_line.substr(offset, i - offset);
}
void set_boundary_for_column(unsigned col, bound * b, lp_solver<T, X> * solver){
if (b == nullptr) {
solver->set_low_bound(col, numeric_traits<T>::zero());
return;
}
if (b->m_free) {
return;
}
if (b->m_low_is_set) {
solver->set_low_bound(col, b->m_low);
}
if (b->m_upper_is_set) {
solver->set_upper_bound(col, b->m_upper);
}
if (b->m_value_is_fixed) {
solver->set_fixed_value(col, b->m_fixed_value);
}
}
bool all_white_space() {
for (unsigned i = 0; i < m_line.size(); i++) {
char c = m_line[i];
if (c != ' ' && c != '\t') {
return false;
}
}
return true;
}
void read_line() {
while (m_is_OK) {
if (!getline(m_file_stream, m_line)) {
m_line_number++;
set_m_ok_to_false();
*m_message_stream << "cannot read from file" << std::endl;
}
m_line_number++;
if (m_line.size() != 0 && m_line[0] != '*' && !all_white_space())
break;
}
}
void read_name() {
do {
read_line();
if (m_line.find("NAME") != 0) {
continue;
}
m_line = m_line.substr(4);
m_name = trim(m_line);
break;
} while (m_is_OK);
}
void read_rows() {
// look for start of the rows
read_line();
do {
if (static_cast<int>(m_line.find("ROWS")) >= 0) {
break;
}
} while (m_is_OK);
do {
read_line();
if (m_line.find("COLUMNS") == 0) {
break;
}
add_row();
} while (m_is_OK);
}
void read_column_by_columns(std::string column_name, std::string column_data) {
// uph, let us try to work with columns
if (column_data.size() >= 22) {
std::string ss = column_data.substr(0, 8);
std::string row_name = trim(ss);
auto t = m_rows.find(row_name);
if (t == m_rows.end()) {
*m_message_stream << "cannot find " << row_name << std::endl;
goto fail;
} else {
row * row = t->second;
row->m_row_columns[column_name] = numeric_traits<T>::from_string(column_data.substr(8));
if (column_data.size() > 24) {
column_data = column_data.substr(25);
if (column_data.size() >= 22) {
read_column_by_columns(column_name, column_data);
}
}
}
} else {
fail:
set_m_ok_to_false();
*m_message_stream << "cannot understand this line" << std::endl;
*m_message_stream << "line = " << m_line << ", line number is " << m_line_number << std::endl;
return;
}
}
void read_column(std::string column_name, std::string column_data){
auto tokens = split_and_trim(column_data);
for (unsigned i = 0; i < tokens.size() - 1; i+= 2) {
auto row_name = tokens[i];
if (row_name == "'MARKER'") return; // it is the integrality marker, no real data here
auto t = m_rows.find(row_name);
if (t == m_rows.end()) {
read_column_by_columns(column_name, column_data);
return;
}
row *r = t->second;
r->m_row_columns[column_name] = numeric_traits<T>::from_string(tokens[i + 1]);
}
}
void read_columns(){
std::string column_name;
do {
read_line();
if (m_line.find("RHS") == 0) {
// cout << "found RHS" << std::endl;
break;
}
if (m_line.size() < 22) {
(*m_message_stream) << "line is too short for a column" << std::endl;
(*m_message_stream) << m_line << std::endl;
(*m_message_stream) << "line number is " << m_line_number << std::endl;
set_m_ok_to_false();
return;
}
std::string column_name_tmp = trim(m_line.substr(4, 8));
if (!column_name_tmp.empty()) {
column_name = column_name_tmp;
}
auto col_it = m_columns.find(column_name);
mps_reader::column * col;
if (col_it == m_columns.end()) {
col = new mps_reader::column(column_name, static_cast<unsigned>(m_columns.size()));
m_columns[column_name] = col;
// (*m_message_stream) << column_name << '[' << col->m_index << ']'<< std::endl;
} else {
col = col_it->second;
}
read_column(column_name, m_line.substr(14));
} while (m_is_OK);
}
void read_rhs() {
do {
read_line();
if (m_line.find("BOUNDS") == 0 || m_line.find("ENDATA") == 0 || m_line.find("RANGES") == 0) {
break;
}
fill_rhs();
} while (m_is_OK);
}
void fill_rhs_by_columns(std::string rhsides) {
// uph, let us try to work with columns
if (rhsides.size() >= 22) {
std::string ss = rhsides.substr(0, 8);
std::string row_name = trim(ss);
auto t = m_rows.find(row_name);
if (t == m_rows.end()) {
(*m_message_stream) << "cannot find " << row_name << std::endl;
goto fail;
} else {
row * row = t->second;
row->m_right_side = numeric_traits<T>::from_string(rhsides.substr(8));
if (rhsides.size() > 24) {
rhsides = rhsides.substr(25);
if (rhsides.size() >= 22) {
fill_rhs_by_columns(rhsides);
}
}
}
} else {
fail:
set_m_ok_to_false();
(*m_message_stream) << "cannot understand this line" << std::endl;
(*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl;
return;
}
}
void fill_rhs() {
if (m_line.size() < 14) {
(*m_message_stream) << "line is too short" << std::endl;
(*m_message_stream) << m_line << std::endl;
(*m_message_stream) << "line number is " << m_line_number << std::endl;
set_m_ok_to_false();
return;
}
std::string rhsides = m_line.substr(14);
vector<std::string> splitted_line = split_and_trim(rhsides);
for (unsigned i = 0; i < splitted_line.size() - 1; i += 2) {
auto t = m_rows.find(splitted_line[i]);
if (t == m_rows.end()) {
fill_rhs_by_columns(rhsides);
return;
}
row * row = t->second;
row->m_right_side = numeric_traits<T>::from_string(splitted_line[i + 1]);
}
}
void read_bounds() {
if (m_line.find("BOUNDS") != 0) {
return;
}
do {
read_line();
if (m_line[0] != ' ') {
break;
}
create_or_update_bound();
} while (m_is_OK);
}
void read_ranges() {
if (m_line.find("RANGES") != 0) {
return;
}
do {
read_line();
auto sl = split_and_trim(m_line);
if (sl.size() < 2) {
break;
}
read_range(sl);
} while (m_is_OK);
}
void read_bound_by_columns(std::string colstr) {
if (colstr.size() < 14) {
(*m_message_stream) << "line is too short" << std::endl;
(*m_message_stream) << m_line << std::endl;
(*m_message_stream) << "line number is " << m_line_number << std::endl;
set_m_ok_to_false();
return;
}
// uph, let us try to work with columns
if (colstr.size() >= 22) {
std::string ss = colstr.substr(0, 8);
std::string column_name = trim(ss);
auto t = m_columns.find(column_name);
if (t == m_columns.end()) {
(*m_message_stream) << "cannot find " << column_name << std::endl;
goto fail;
} else {
vector<std::string> bound_string;
bound_string.push_back(column_name);
if (colstr.size() > 14) {
bound_string.push_back(colstr.substr(14));
}
mps_reader::column * col = t->second;
bound * b = col->m_bound;
if (b == nullptr) {
col->m_bound = b = new bound();
}
update_bound(b, bound_string);
}
} else {
fail:
set_m_ok_to_false();
(*m_message_stream) << "cannot understand this line" << std::endl;
(*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl;
return;
}
}
void update_bound(bound * b, vector<std::string> bound_string) {
/*
UP means an upper bound is applied to the variable. A bound of type LO means a lower bound is applied. A bound type of FX ("fixed") means that the variable has upper and lower bounds equal to a single value. A bound type of FR ("free") means the variable has neither lower nor upper bounds and so can take on negative values. A variation on that is MI for free negative, giving an upper bound of 0 but no lower bound. Bound type PL is for a free positive for zero to plus infinity, but as this is the normal default, it is seldom used. There are also bound types for use in MIP models - BV for binary, being 0 or 1. UI for upper integer and LI for lower integer. SC stands for semi-continuous and indicates that the variable may be zero, but if not must be equal to at least the value given.
*/
std::string bound_type = get_string_from_position(1);
if (bound_type == "BV") {
b->m_upper_is_set = true;
b->m_upper = 1;
return;
}
if (bound_type == "UP" || bound_type == "UI" || bound_type == "LIMITMAX") {
if (bound_string.size() <= 1){
set_m_ok_to_false();
return;
}
b->m_upper_is_set = true;
b->m_upper= numeric_traits<T>::from_string(bound_string[1]);
} else if (bound_type == "LO" || bound_type == "LI") {
if (bound_string.size() <= 1){
set_m_ok_to_false();
return;
}
b->m_low_is_set = true;
b->m_low = numeric_traits<T>::from_string(bound_string[1]);
} else if (bound_type == "FR") {
b->m_free = true;
} else if (bound_type == "FX") {
if (bound_string.size() <= 1){
set_m_ok_to_false();
return;
}
b->m_value_is_fixed = true;
b->m_fixed_value = numeric_traits<T>::from_string(bound_string[1]);
} else if (bound_type == "PL") {
b->m_low_is_set = true;
b->m_low = 0;
} else if (bound_type == "MI") {
b->m_upper_is_set = true;
b->m_upper = 0;
} else {
(*m_message_stream) << "unexpected bound type " << bound_type << " at line " << m_line_number << std::endl;
set_m_ok_to_false();
throw;
}
}
void create_or_update_bound() {
const unsigned name_offset = 14;
lean_assert(m_line.size() >= 14);
vector<std::string> bound_string = split_and_trim(m_line.substr(name_offset, m_line.size()));
if (bound_string.size() == 0) {
set_m_ok_to_false();
(*m_message_stream) << "error at line " << m_line_number << std::endl;
throw m_line;
}
std::string name = bound_string[0];
auto it = m_columns.find(name);
if (it == m_columns.end()){
read_bound_by_columns(m_line.substr(14));
return;
}
mps_reader::column * col = it->second;
bound * b = col->m_bound;
if (b == nullptr) {
col->m_bound = b = new bound();
}
update_bound(b, bound_string);
}
void read_range_by_columns(std::string rhsides) {
if (m_line.size() < 14) {
(*m_message_stream) << "line is too short" << std::endl;
(*m_message_stream) << m_line << std::endl;
(*m_message_stream) << "line number is " << m_line_number << std::endl;
set_m_ok_to_false();
return;
}
// uph, let us try to work with columns
if (rhsides.size() >= 22) {
std::string ss = rhsides.substr(0, 8);
std::string row_name = trim(ss);
auto t = m_rows.find(row_name);
if (t == m_rows.end()) {
(*m_message_stream) << "cannot find " << row_name << std::endl;
goto fail;
} else {
row * row = t->second;
row->m_range = numeric_traits<T>::from_string(rhsides.substr(8));
maybe_modify_current_row_and_add_row_for_range(row);
if (rhsides.size() > 24) {
rhsides = rhsides.substr(25);
if (rhsides.size() >= 22) {
read_range_by_columns(rhsides);
}
}
}
} else {
fail:
set_m_ok_to_false();
(*m_message_stream) << "cannot understand this line" << std::endl;
(*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl;
return;
}
}
void read_range(vector<std::string> & splitted_line){
for (unsigned i = 1; i < splitted_line.size() - 1; i += 2) {
auto it = m_rows.find(splitted_line[i]);
if (it == m_rows.end()) {
read_range_by_columns(m_line.substr(14));
return;
}
row * row = it->second;
row->m_range = numeric_traits<T>::from_string(splitted_line[i + 1]);
maybe_modify_current_row_and_add_row_for_range(row);
}
}
void maybe_modify_current_row_and_add_row_for_range(row * row_with_range) {
unsigned index= static_cast<unsigned>(m_rows.size() - m_cost_line_count);
std::string row_name = row_with_range->m_name + "_range";
row * other_bound_range_row;
switch (row_with_range->m_type) {
case row_type::Greater_or_equal:
m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index);
other_bound_range_row->m_right_side = row_with_range->m_right_side + abs(row_with_range->m_range);
break;
case row_type::Less_or_equal:
m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index);
other_bound_range_row->m_right_side = row_with_range->m_right_side - abs(row_with_range->m_range);
break;
case row_type::Equal:
if (row_with_range->m_range > 0) {
row_with_range->m_type = row_type::Greater_or_equal; // the existing row type change
m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index);
} else { // row->m_range < 0;
row_with_range->m_type = row_type::Less_or_equal; // the existing row type change
m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index);
}
other_bound_range_row->m_right_side = row_with_range->m_right_side + row_with_range->m_range;
break;
default:
(*m_message_stream) << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << std::endl;
set_m_ok_to_false();
throw;
}
for (auto s : row_with_range->m_row_columns) {
lean_assert(m_columns.find(s.first) != m_columns.end());
other_bound_range_row->m_row_columns[s.first] = s.second;
}
}
void add_row() {
if (m_line.length() < 2) {
return;
}
m_line = trim(m_line);
char c = m_line[0];
m_line = m_line.substr(1);
m_line = trim(m_line);
add_row(c);
}
void add_row(char c) {
unsigned index= static_cast<unsigned>(m_rows.size() - m_cost_line_count);
switch (c) {
case 'E':
m_rows[m_line] = new row(row_type::Equal, m_line, index);
break;
case 'L':
m_rows[m_line] = new row(row_type::Less_or_equal, m_line, index);
break;
case 'G':
m_rows[m_line] = new row(row_type::Greater_or_equal, m_line, index);
break;
case 'N':
m_rows[m_line] = new row(row_type::Cost, m_line, index);
m_cost_row_name = m_line;
m_cost_line_count++;
break;
}
}
unsigned range_count() {
unsigned ret = 0;
for (auto s : m_rows) {
if (s.second->m_range != 0) {
ret++;
}
}
return ret;
}
/*
If rhs is a constraint's right-hand-side value and range is the constraint's range value, then the range interval is defined according to the following table:
sense interval
G [rhs, rhs + |range|]
L [rhs - |range|, rhs]
E [rhs, rhs + |range|] if range ¡Ý 0 [rhs - |range|, rhs] if range < 0
where |range| is range's absolute value.
*/
lp_relation get_relation_from_row(row_type rt) {
switch (rt) {
case mps_reader::Less_or_equal: return lp_relation::Less_or_equal;
case mps_reader::Greater_or_equal: return lp_relation::Greater_or_equal;
case mps_reader::Equal: return lp_relation::Equal;
default:
(*m_message_stream) << "Unexpected rt " << rt << std::endl;
set_m_ok_to_false();
throw;
}
}
unsigned solver_row_count() {
return m_rows.size() - m_cost_line_count + range_count();
}
void fill_solver_on_row(row * row, lp_solver<T, X> *solver) {
if (row->m_name != m_cost_row_name) {
solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index);
for (auto s : row->m_row_columns) {
lean_assert(m_columns.find(s.first) != m_columns.end());
solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second);
}
} else {
set_solver_cost(row, solver);
}
}
T abs(T & t) { return t < numeric_traits<T>::zero() ? -t: t; }
void fill_solver_on_rows(lp_solver<T, X> * solver) {
for (auto row_it : m_rows) {
fill_solver_on_row(row_it.second, solver);
}
}
void fill_solver_on_columns(lp_solver<T, X> * solver){
for (auto s : m_columns) {
mps_reader::column * col = s.second;
unsigned index = col->m_index;
set_boundary_for_column(index, col->m_bound, solver);
// optional call
solver->give_symbolic_name_to_column(col->m_name, col->m_index);
}
}
void fill_solver(lp_solver<T, X> *solver) {
fill_solver_on_rows(solver);
fill_solver_on_columns(solver);
}
void set_solver_cost(row * row, lp_solver<T, X> *solver) {
for (auto s : row->m_row_columns) {
std::string name = s.first;
lean_assert(m_columns.find(name) != m_columns.end());
mps_reader::column * col = m_columns[name];
solver->set_cost_for_column(col->m_index, s.second);
}
}
public:
void set_message_stream(std::ostream * o) {
lean_assert(o != nullptr);
m_message_stream = o;
}
vector<std::string> column_names() {
vector<std::string> v;
for (auto s : m_columns) {
v.push_back(s.first);
}
return v;
}
~mps_reader() {
for (auto s : m_rows) {
delete s.second;
}
for (auto s : m_columns) {
auto col = s.second;
auto b = col->m_bound;
if (b != nullptr) {
delete b;
}
delete col;
}
}
mps_reader(std::string file_name):
m_file_name(file_name), m_file_stream(file_name) {
}
void read() {
if (!m_file_stream.is_open()){
set_m_ok_to_false();
return;
}
read_name();
read_rows();
read_columns();
read_rhs();
if (m_line.find("BOUNDS") == 0) {
read_bounds();
read_ranges();
} else if (m_line.find("RANGES") == 0) {
read_ranges();
read_bounds();
}
}
bool is_ok() {
return m_is_OK;
}
lp_solver<T, X> * create_solver(bool dual) {
lp_solver<T, X> * solver = dual? (lp_solver<T, X>*)new lp_dual_simplex<T, X>() : new lp_primal_simplex<T, X>();
fill_solver(solver);
return solver;
}
lconstraint_kind get_lar_relation_from_row(row_type rt) {
switch (rt) {
case Less_or_equal: return LE;
case Greater_or_equal: return GE;
case Equal: return EQ;
default:
(*m_message_stream) << "Unexpected rt " << rt << std::endl;
set_m_ok_to_false();
throw;
}
}
unsigned get_var_index(std::string s) {
auto it = m_names_to_var_index.find(s);
if (it != m_names_to_var_index.end())
return it->second;
unsigned ret = m_names_to_var_index.size();
m_names_to_var_index[s] = ret;
return ret;
}
void fill_lar_solver_on_row(row * row, lar_solver *solver) {
if (row->m_name != m_cost_row_name) {
auto kind = get_lar_relation_from_row(row->m_type);
vector<std::pair<mpq, var_index>> ls;
for (auto s : row->m_row_columns) {
var_index i = solver->add_var(get_var_index(s.first));
ls.push_back(std::make_pair(s.second, i));
}
solver->add_constraint(ls, kind, row->m_right_side);
} else {
// ignore the cost row
}
}
void fill_lar_solver_on_rows(lar_solver * solver) {
for (auto row_it : m_rows) {
fill_lar_solver_on_row(row_it.second, solver);
}
}
void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) {
vector<std::pair<mpq, var_index>> ls;
var_index i = solver->add_var(col->m_index);
ls.push_back(std::make_pair(numeric_traits<T>::one(), i));
solver->add_constraint(ls, GE, b->m_low);
}
void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) {
var_index i = solver->add_var(col->m_index);
vector<std::pair<mpq, var_index>> ls;
ls.push_back(std::make_pair(numeric_traits<T>::one(), i));
solver->add_constraint(ls, LE, b->m_upper);
}
void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) {
var_index i = solver->add_var(col->m_index);
vector<std::pair<mpq, var_index>> ls;
ls.push_back(std::make_pair(numeric_traits<T>::one(), i));
solver->add_constraint(ls, EQ, b->m_fixed_value);
}
void fill_lar_solver_on_columns(lar_solver * solver) {
for (auto s : m_columns) {
mps_reader::column * col = s.second;
solver->add_var(col->m_index);
auto b = col->m_bound;
if (b == nullptr) return;
if (b->m_free) continue;
if (b->m_low_is_set) {
create_low_constraint_for_var(col, b, solver);
}
if (b->m_upper_is_set) {
create_upper_constraint_for_var(col, b, solver);
}
if (b->m_value_is_fixed) {
create_equality_contraint_for_var(col, b, solver);
}
}
}
void fill_lar_solver(lar_solver * solver) {
fill_lar_solver_on_columns(solver);
fill_lar_solver_on_rows(solver);
}
lar_solver * create_lar_solver() {
lar_solver * solver = new lar_solver();
fill_lar_solver(solver);
return solver;
}
};
}

329
src/util/lp/numeric_pair.h Normal file
View file

@ -0,0 +1,329 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
The idea is that it is only one different file in Lean and z3 source inside of LP
*/
#pragma once
#define lp_for_z3
#include <string>
#include <cmath>
#include <algorithm>
#ifdef lp_for_z3
#include "../rational.h"
#include "../sstream.h"
#include "../z3_exception.h"
#else
// include "util/numerics/mpq.h"
// include "util/numerics/numeric_traits.h"
#endif
namespace lean {
#ifdef lp_for_z3 // rename rationals
typedef rational mpq;
#else
typedef lean::mpq mpq;
#endif
template <typename T>
std::string T_to_string(const T & t); // forward definition
#ifdef lp_for_z3
template <typename T> class numeric_traits {};
template <> class numeric_traits<unsigned> {
public:
static bool precise() { return true; }
static unsigned const zero() { return 0; }
static unsigned const one() { return 1; }
static bool is_zero(unsigned v) { return v == 0; }
static double const get_double(unsigned const & d) { return d; }
};
template <> class numeric_traits<double> {
public:
static bool precise() { return false; }
static double g_zero;
static double const &zero() { return g_zero; }
static double g_one;
static double const &one() { return g_one; }
static bool is_zero(double v) { return v == 0.0; }
static double const & get_double(double const & d) { return d;}
static double log(double const & d) { NOT_IMPLEMENTED_YET(); return d;}
static double from_string(std::string const & str) { return atof(str.c_str()); }
static bool is_pos(const double & d) {return d > 0.0;}
static bool is_neg(const double & d) {return d < 0.0;}
};
template<>
class numeric_traits<rational> {
public:
static bool precise() { return true; }
static rational const & zero() { return rational::zero(); }
static rational const & one() { return rational::one(); }
static bool is_zero(const rational & v) { return v.is_zero(); }
static double const get_double(const rational & d) { return d.get_double();}
static rational log(rational const& r) { UNREACHABLE(); return r; }
static rational from_string(std::string const & str) { return rational(str.c_str()); }
static bool is_pos(const rational & d) {return d.is_pos();}
static bool is_neg(const rational & d) {return d.is_neg();}
};
#endif
template <typename X, typename Y>
struct convert_struct {
static X convert(const Y & y){ return X(y);}
static bool is_epsilon_small(const X & x, const double & y) { return std::abs(numeric_traits<X>::get_double(x)) < y; }
static bool below_bound_numeric(const X &, const X &, const Y &) { /*lean_unreachable();*/ return false;}
static bool above_bound_numeric(const X &, const X &, const Y &) { /*lean_unreachable();*/ return false; }
};
template <>
struct convert_struct<double, mpq> {
static double convert(const mpq & q) {return q.get_double();}
};
template <>
struct convert_struct<mpq, unsigned> {
static mpq convert(unsigned q) {return mpq(q);}
};
template <typename T>
struct numeric_pair {
T x;
T y;
// empty constructor
numeric_pair() {}
// another constructor
numeric_pair(T xp, T yp) : x(xp), y(yp) {}
template <typename X>
numeric_pair(const X & n) : x(n), y(0) {
}
template <typename X>
numeric_pair(const numeric_pair<X> & n) : x(n.x), y(n.y) {}
template <typename X, typename Y>
numeric_pair(X xp, Y yp) : numeric_pair(convert_struct<T, X>::convert(xp), convert_struct<T, Y>::convert(yp)) {}
bool operator<(const numeric_pair& a) const {
return x < a.x || (x == a.x && y < a.y);
}
bool operator>(const numeric_pair& a) const {
return x > a.x || (x == a.x && y > a.y);
}
bool operator==(const numeric_pair& a) const {
return a.x == x && a.y == y;
}
bool operator!=(const numeric_pair& a) const {
return !(*this == a);
}
bool operator<=(const numeric_pair& a) const {
return *this < a || *this == a;
}
bool operator>=(const numeric_pair& a) const {
return *this > a || a == *this;
}
numeric_pair operator*(const T & a) const {
return numeric_pair(a * x, a * y);
}
numeric_pair operator/(const T & a) const {
T a_as_T(a);
return numeric_pair(x / a_as_T, y / a_as_T);
}
numeric_pair operator/(const numeric_pair &) const {
// lean_unreachable();
}
numeric_pair operator+(const numeric_pair & a) const {
return numeric_pair(a.x + x, a.y + y);
}
numeric_pair operator*(const numeric_pair & /*a*/) const {
// lean_unreachable();
}
numeric_pair& operator+=(const numeric_pair & a) {
x += a.x;
y += a.y;
return *this;
}
numeric_pair& operator-=(const numeric_pair & a) {
x -= a.x;
y -= a.y;
return *this;
}
numeric_pair& operator/=(const T & a) {
x /= a;
y /= a;
return *this;
}
numeric_pair& operator*=(const T & a) {
x *= a;
y *= a;
return *this;
}
numeric_pair operator-(const numeric_pair & a) const {
return numeric_pair(x - a.x, y - a.y);
}
numeric_pair operator-() const {
return numeric_pair(-x, -y);
}
static bool precize() { return lean::numeric_traits<T>::precize();}
bool is_zero() const { return x.is_zero() && y.is_zero(); }
bool is_pos() const { return x.is_pos() || (x.is_zero() && y.is_pos());}
bool is_neg() const { return x.is_neg() || (x.is_zero() && y.is_neg());}
std::string to_string() const {
return std::string("(") + T_to_string(x) + ", " + T_to_string(y) + ")";
}
};
template <typename T>
std::ostream& operator<<(std::ostream& os, numeric_pair<T> const & obj) {
os << obj.to_string();
return os;
}
template <typename T, typename X>
numeric_pair<T> operator*(const X & a, const numeric_pair<T> & r) {
return numeric_pair<T>(a * r.x, a * r.y);
}
template <typename T, typename X>
numeric_pair<T> operator*(const numeric_pair<T> & r, const X & a) {
return numeric_pair<T>(a * r.x, a * r.y);
}
template <typename T, typename X>
numeric_pair<T> operator/(const numeric_pair<T> & r, const X & a) {
return numeric_pair<T>(r.x / a, r.y / a);
}
// template <numeric_pair, typename T> bool precise() { return numeric_traits<T>::precise();}
template <typename T> double get_double(const lean::numeric_pair<T> & ) { /* lean_unreachable(); */ return 0;}
template <typename T>
class numeric_traits<lean::numeric_pair<T>> {
public:
static bool precise() { return numeric_traits<T>::precise();}
static lean::numeric_pair<T> zero() { return lean::numeric_pair<T>(numeric_traits<T>::zero(), numeric_traits<T>::zero()); }
static bool is_zero(const lean::numeric_pair<T> & v) { return numeric_traits<T>::is_zero(v.x) && numeric_traits<T>::is_zero(v.y); }
static double get_double(const lean::numeric_pair<T> & v){ return numeric_traits<T>::get_double(v.x); } // just return the double of the first coordinate
static double one() { /*lean_unreachable();*/ return 0;}
static bool is_pos(const numeric_pair<T> &p) {
return numeric_traits<T>::is_pos(p.x) ||
(numeric_traits<T>::is_zero(p.x) && numeric_traits<T>::is_pos(p.y));
}
static bool is_neg(const numeric_pair<T> &p) {
return numeric_traits<T>::is_neg(p.x) ||
(numeric_traits<T>::is_zero(p.x) && numeric_traits<T>::is_neg(p.y));
}
};
template <>
struct convert_struct<double, numeric_pair<double>> {
static double convert(const numeric_pair<double> & q) {return q.x;}
};
typedef numeric_pair<mpq> impq;
template <typename X> bool is_epsilon_small(const X & v, const double& eps); // forward definition { return convert_struct<X, double>::is_epsilon_small(v, eps);}
template <typename T>
struct convert_struct<numeric_pair<T>, double> {
static numeric_pair<T> convert(const double & q) {
return numeric_pair<T>(convert_struct<T, double>::convert(q), numeric_traits<T>::zero());
}
static bool is_epsilon_small(const numeric_pair<T> & p, const double & eps) {
return convert_struct<T, double>::is_epsilon_small(p.x, eps) && convert_struct<T, double>::is_epsilon_small(p.y, eps);
}
static bool below_bound_numeric(const numeric_pair<T> &, const numeric_pair<T> &, const double &) {
// lean_unreachable();
return false;
}
static bool above_bound_numeric(const numeric_pair<T> &, const numeric_pair<T> &, const double &) {
// lean_unreachable();
return false;
}
};
template <>
struct convert_struct<numeric_pair<double>, double> {
static numeric_pair<double> convert(const double & q) {
return numeric_pair<double>(q, 0.0);
}
static bool is_epsilon_small(const numeric_pair<double> & p, const double & eps) {
return std::abs(p.x) < eps && std::abs(p.y) < eps;
}
static int compare_on_coord(const double & x, const double & bound, const double eps) {
if (bound == 0) return (x < - eps)? -1: (x > eps? 1 : 0); // it is an important special case
double relative = (bound > 0)? - eps: eps;
return (x < bound * (1.0 + relative) - eps)? -1 : ((x > bound * (1.0 - relative) + eps)? 1 : 0);
}
static bool below_bound_numeric(const numeric_pair<double> & x, const numeric_pair<double> & bound, const double & eps) {
int r = compare_on_coord(x.x, bound.x, eps);
if (r == 1) return false;
if (r == -1) return true;
// the first coordinates are almost the same
return compare_on_coord(x.y, bound.y, eps) == -1;
}
static bool above_bound_numeric(const numeric_pair<double> & x, const numeric_pair<double> & bound, const double & eps) {
int r = compare_on_coord(x.x, bound.x, eps);
if (r == -1) return false;
if (r == 1) return true;
// the first coordinates are almost the same
return compare_on_coord(x.y, bound.y, eps) == 1;
}
};
template <>
struct convert_struct<double, double> {
static bool is_epsilon_small(const double& x, const double & eps) {
return x < eps && x > -eps;
}
static double convert(const double & y){ return y;}
static bool below_bound_numeric(const double & x, const double & bound, const double & eps) {
if (bound == 0) return x < - eps;
double relative = (bound > 0)? - eps: eps;
return x < bound * (1.0 + relative) - eps;
}
static bool above_bound_numeric(const double & x, const double & bound, const double & eps) {
if (bound == 0) return x > eps;
double relative = (bound > 0)? eps: - eps;
return x > bound * (1.0 + relative) + eps;
}
};
template <typename X> bool is_epsilon_small(const X & v, const double &eps) { return convert_struct<X, double>::is_epsilon_small(v, eps);}
template <typename X> bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct<X, double>::below_bound_numeric(x, bound, eps);}
template <typename X> bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct<X, double>::above_bound_numeric(x, bound, eps);}
}

View file

@ -0,0 +1,173 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include <algorithm>
#include "util/debug.h"
#include <string>
#include "util/lp/sparse_vector.h"
#include "util/lp/indexed_vector.h"
#include "util/lp/lp_settings.h"
#include "util/lp/matrix.h"
#include "util/lp/tail_matrix.h"
namespace lean {
#ifdef LEAN_DEBUG
inline bool is_even(int k) { return (k/2)*2 == k; }
#endif
template <typename T, typename X>
class permutation_matrix : public tail_matrix<T, X> {
vector<unsigned> m_permutation;
vector<unsigned> m_rev;
vector<unsigned> m_work_array;
vector<T> m_T_buffer;
vector<X> m_X_buffer;
class ref {
permutation_matrix & m_p;
unsigned m_i;
public:
ref(permutation_matrix & m, unsigned i):m_p(m), m_i(i) {}
ref & operator=(unsigned v) { m_p.set_val(m_i, v); return *this; }
ref & operator=(ref const & v) {
m_p.set_val(m_i, v.m_p.m_permutation[v.m_i]);
return *this;
}
operator unsigned & () const { return m_p.m_permutation[m_i]; }
};
public:
permutation_matrix() {}
permutation_matrix(unsigned length);
permutation_matrix(unsigned length, vector<unsigned> const & values);
// create a unit permutation of the given length
void init(unsigned length);
unsigned get_rev(unsigned i) { return m_rev[i]; }
bool is_dense() const { return false; }
#ifdef LEAN_DEBUG
permutation_matrix get_inverse() const {
return permutation_matrix(size(), m_rev);
}
void print(std::ostream & out) const;
#endif
ref operator[](unsigned i) { return ref(*this, i); }
unsigned operator[](unsigned i) const { return m_permutation[i]; }
void apply_from_left(vector<X> & w, lp_settings &);
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings);
void apply_from_right(vector<T> & w);
void apply_from_right(indexed_vector<T> & w);
template <typename L>
void copy_aside(vector<L> & t, vector<unsigned> & tmp_index, indexed_vector<L> & w);
template <typename L>
void clear_data(indexed_vector<L> & w);
template <typename L>
void apply_reverse_from_left(indexed_vector<L> & w);
void apply_reverse_from_left_to_T(vector<T> & w);
void apply_reverse_from_left_to_X(vector<X> & w);
void apply_reverse_from_right_to_T(vector<T> & w);
void apply_reverse_from_right_to_T(indexed_vector<T> & w);
void apply_reverse_from_right_to_X(vector<X> & w);
void set_val(unsigned i, unsigned pi) {
lean_assert(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; }
void transpose_from_left(unsigned i, unsigned j);
unsigned apply_reverse(unsigned i) const { return m_rev[i]; }
void transpose_from_right(unsigned i, unsigned j);
#ifdef LEAN_DEBUG
T get_elem(unsigned i, unsigned j) const{
return m_permutation[i] == j? numeric_traits<T>::one() : numeric_traits<T>::zero();
}
unsigned row_count() const{ return size(); }
unsigned column_count() const { return size(); }
virtual void set_number_of_rows(unsigned /*m*/) { }
virtual void set_number_of_columns(unsigned /*n*/) { }
#endif
void multiply_by_permutation_from_left(permutation_matrix<T, X> & p);
// this is multiplication in the matrix sense
void multiply_by_permutation_from_right(permutation_matrix<T, X> & p);
void multiply_by_reverse_from_right(permutation_matrix<T, X> & q);
void multiply_by_permutation_reverse_from_left(permutation_matrix<T, X> & r);
void shrink_by_one_identity();
bool is_identity() const;
unsigned size() const { return static_cast<unsigned>(m_rev.size()); }
unsigned * values() const { return m_permutation; }
void resize(unsigned size) {
unsigned old_size = m_permutation.size();
m_permutation.resize(size);
m_rev.resize(size);
m_T_buffer.resize(size);
m_X_buffer.resize(size);
for (unsigned i = old_size; i < size; i++) {
m_permutation[i] = m_rev[i] = i;
}
}
}; // end of the permutation class
#ifdef LEAN_DEBUG
template <typename T, typename X>
class permutation_generator {
unsigned m_n;
permutation_generator* m_lower;
bool m_done = false;
permutation_matrix<T, X> m_current;
unsigned m_last;
public:
permutation_generator(unsigned n);
permutation_generator(const permutation_generator & o);
bool move_next();
~permutation_generator() {
if (m_lower != nullptr) {
delete m_lower;
}
}
permutation_matrix<T, X> *current() {
return &m_current;
}
};
template <typename T, typename X>
inline unsigned number_of_inversions(permutation_matrix<T, X> & p);
template <typename T, typename X>
int sign(permutation_matrix<T, X> & p) {
return is_even(number_of_inversions(p))? 1: -1;
}
template <typename T, typename X>
T det_val_on_perm(permutation_matrix<T, X>* u, const matrix<T, X>& m);
template <typename T, typename X>
T determinant(const matrix<T, X>& m);
#endif
}

View file

@ -0,0 +1,419 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/vector.h"
#include "util/lp/permutation_matrix.h"
namespace lean {
template <typename T, typename X> permutation_matrix<T, X>::permutation_matrix(unsigned length): m_permutation(length), m_rev(length), m_T_buffer(length), m_X_buffer(length) {
for (unsigned i = 0; i < length; i++) { // do not change the direction of the loop because of the vectorization bug in clang3.3
m_permutation[i] = m_rev[i] = i;
}
}
template <typename T, typename X> permutation_matrix<T, X>::permutation_matrix(unsigned length, vector<unsigned> const & values): m_permutation(length), m_rev(length) , m_T_buffer(length), m_X_buffer(length) {
for (unsigned i = 0; i < length; i++) {
set_val(i, values[i]);
}
}
// create a unit permutation of the given length
template <typename T, typename X> void permutation_matrix<T, X>::init(unsigned length) {
m_permutation.resize(length);
m_rev.resize(length);
m_T_buffer.resize(length);
m_X_buffer.resize(length);
for (unsigned i = 0; i < length; i++) {
m_permutation[i] = m_rev[i] = i;
}
}
#ifdef LEAN_DEBUG
template <typename T, typename X> void permutation_matrix<T, X>::print(std::ostream & out) const {
out << "[";
for (unsigned i = 0; i < size(); i++) {
out << m_permutation[i];
if (i < size() - 1) {
out << ",";
} else {
out << "]";
}
}
out << std::endl;
}
#endif
template <typename T, typename X>
void permutation_matrix<T, X>::apply_from_left(vector<X> & w, lp_settings & ) {
#ifdef LEAN_DEBUG
// dense_matrix<L, X> deb(*this);
// L * deb_w = clone_vector<L>(w, row_count());
// deb.apply_from_left(deb_w);
#endif
// std::cout << " apply_from_left " << std::endl;
lean_assert(m_X_buffer.size() == w.size());
unsigned i = size();
while (i-- > 0) {
m_X_buffer[i] = w[m_permutation[i]];
}
i = size();
while (i-- > 0) {
w[i] = m_X_buffer[i];
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<L>(deb_w, w, row_count()));
// delete [] deb_w;
#endif
}
template <typename T, typename X>
void permutation_matrix<T, X>::apply_from_left_to_T(indexed_vector<T> & w, lp_settings & ) {
vector<T> t(w.m_index.size());
vector<unsigned> tmp_index(w.m_index.size());
copy_aside(t, tmp_index, w); // todo: is it too much copying
clear_data(w);
// set the new values
for (unsigned i = static_cast<unsigned>(t.size()); i > 0;) {
i--;
unsigned j = m_rev[tmp_index[i]];
w[j] = t[i];
w.m_index[i] = j;
}
}
template <typename T, typename X> void permutation_matrix<T, X>::apply_from_right(vector<T> & w) {
#ifdef LEAN_DEBUG
// dense_matrix<T, X> deb(*this);
// T * deb_w = clone_vector<T>(w, row_count());
// deb.apply_from_right(deb_w);
#endif
lean_assert(m_T_buffer.size() == w.size());
for (unsigned i = 0; i < size(); i++) {
m_T_buffer[i] = w[m_rev[i]];
}
for (unsigned i = 0; i < size(); i++) {
w[i] = m_T_buffer[i];
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(deb_w, w, row_count()));
// delete [] deb_w;
#endif
}
template <typename T, typename X> void permutation_matrix<T, X>::apply_from_right(indexed_vector<T> & w) {
#ifdef LEAN_DEBUG
vector<T> wcopy(w.m_data);
apply_from_right(wcopy);
#endif
vector<T> buffer(w.m_index.size());
vector<unsigned> index_copy(w.m_index);
for (unsigned i = 0; i < w.m_index.size(); i++) {
buffer[i] = w.m_data[w.m_index[i]];
}
w.clear();
for (unsigned i = 0; i < index_copy.size(); i++) {
unsigned j = index_copy[i];
unsigned pj = m_permutation[j];
w.set_value(buffer[i], pj);
}
lean_assert(w.is_OK());
#ifdef LEAN_DEBUG
lean_assert(vectors_are_equal(wcopy, w.m_data));
#endif
}
template <typename T, typename X> template <typename L>
void permutation_matrix<T, X>::copy_aside(vector<L> & t, vector<unsigned> & tmp_index, indexed_vector<L> & w) {
for (unsigned i = static_cast<unsigned>(t.size()); i > 0;) {
i--;
unsigned j = w.m_index[i];
t[i] = w[j]; // copy aside all non-zeroes
tmp_index[i] = j; // and the indices too
}
}
template <typename T, typename X> template <typename L>
void permutation_matrix<T, X>::clear_data(indexed_vector<L> & w) {
// clear old non-zeroes
for (unsigned i = static_cast<unsigned>(w.m_index.size()); i > 0;) {
i--;
unsigned j = w.m_index[i];
w[j] = zero_of_type<L>();
}
}
template <typename T, typename X>template <typename L>
void permutation_matrix<T, X>::apply_reverse_from_left(indexed_vector<L> & w) {
// the result will be w = p(-1) * w
#ifdef LEAN_DEBUG
// dense_matrix<L, X> deb(get_reverse());
// L * deb_w = clone_vector<L>(w.m_data, row_count());
// deb.apply_from_left(deb_w);
#endif
vector<L> t(w.m_index.size());
vector<unsigned> tmp_index(w.m_index.size());
copy_aside(t, tmp_index, w);
clear_data(w);
// set the new values
for (unsigned i = static_cast<unsigned>(t.size()); i > 0;) {
i--;
unsigned j = m_permutation[tmp_index[i]];
w[j] = t[i];
w.m_index[i] = j;
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<L>(deb_w, w.m_data, row_count()));
// delete [] deb_w;
#endif
}
template <typename T, typename X>
void permutation_matrix<T, X>::apply_reverse_from_left_to_T(vector<T> & w) {
// the result will be w = p(-1) * w
lean_assert(m_T_buffer.size() == w.size());
unsigned i = size();
while (i-- > 0) {
m_T_buffer[m_permutation[i]] = w[i];
}
i = size();
while (i-- > 0) {
w[i] = m_T_buffer[i];
}
}
template <typename T, typename X>
void permutation_matrix<T, X>::apply_reverse_from_left_to_X(vector<X> & w) {
// the result will be w = p(-1) * w
lean_assert(m_X_buffer.size() == w.size());
unsigned i = size();
while (i-- > 0) {
m_X_buffer[m_permutation[i]] = w[i];
}
i = size();
while (i-- > 0) {
w[i] = m_X_buffer[i];
}
}
template <typename T, typename X>
void permutation_matrix<T, X>::apply_reverse_from_right_to_T(vector<T> & w) {
// the result will be w = w * p(-1)
lean_assert(m_T_buffer.size() == w.size());
unsigned i = size();
while (i-- > 0) {
m_T_buffer[i] = w[m_permutation[i]];
}
i = size();
while (i-- > 0) {
w[i] = m_T_buffer[i];
}
}
template <typename T, typename X>
void permutation_matrix<T, X>::apply_reverse_from_right_to_T(indexed_vector<T> & w) {
// the result will be w = w * p(-1)
#ifdef LEAN_DEBUG
// vector<T> wcopy(w.m_data);
// apply_reverse_from_right_to_T(wcopy);
#endif
lean_assert(w.is_OK());
vector<T> tmp;
vector<unsigned> tmp_index(w.m_index);
for (auto i : w.m_index) {
tmp.push_back(w[i]);
}
w.clear();
for (unsigned k = 0; k < tmp_index.size(); k++) {
unsigned j = tmp_index[k];
w.set_value(tmp[k], m_rev[j]);
}
// lean_assert(w.is_OK());
// lean_assert(vectors_are_equal(w.m_data, wcopy));
}
template <typename T, typename X>
void permutation_matrix<T, X>::apply_reverse_from_right_to_X(vector<X> & w) {
// the result will be w = w * p(-1)
lean_assert(m_X_buffer.size() == w.size());
unsigned i = size();
while (i-- > 0) {
m_X_buffer[i] = w[m_permutation[i]];
}
i = size();
while (i-- > 0) {
w[i] = m_X_buffer[i];
}
}
template <typename T, typename X> void permutation_matrix<T, X>::transpose_from_left(unsigned i, unsigned j) {
// the result will be this = (i,j)*this
lean_assert(i < size() && j < size() && i != j);
auto pi = m_rev[i];
auto pj = m_rev[j];
set_val(pi, j);
set_val(pj, i);
}
template <typename T, typename X> void permutation_matrix<T, X>::transpose_from_right(unsigned i, unsigned j) {
// the result will be this = this * (i,j)
lean_assert(i < size() && j < size() && i != j);
auto pi = m_permutation[i];
auto pj = m_permutation[j];
set_val(i, pj);
set_val(j, pi);
}
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_from_left(permutation_matrix<T, X> & p) {
m_work_array = m_permutation;
lean_assert(p.size() == size());
unsigned i = size();
while (i-- > 0) {
set_val(i, m_work_array[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
}
}
// this is multiplication in the matrix sense
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_from_right(permutation_matrix<T, X> & p) {
m_work_array = m_permutation;
lean_assert(p.size() == size());
unsigned i = size();
while (i-- > 0)
set_val(i, p[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
}
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_reverse_from_right(permutation_matrix<T, X> & q){ // todo : condensed permutations ?
lean_assert(q.size() == size());
m_work_array = m_permutation;
// the result is this = this*q(-1)
unsigned i = size();
while (i-- > 0) {
set_val(i, q.m_rev[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
}
}
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_reverse_from_left(permutation_matrix<T, X> & r){ // todo : condensed permutations?
// the result is this = r(-1)*this
m_work_array = m_permutation;
// the result is this = this*q(-1)
unsigned i = size();
while (i-- > 0) {
set_val(i, m_work_array[r.m_rev[i]]);
}
}
template <typename T, typename X> bool permutation_matrix<T, X>::is_identity() const {
unsigned i = size();
while (i-- > 0) {
if (m_permutation[i] != i) {
return false;
}
}
return true;
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
permutation_generator<T, X>::permutation_generator(unsigned n): m_n(n), m_current(n) {
lean_assert(n > 0);
if (n > 1) {
m_lower = new permutation_generator(n - 1);
} else {
m_lower = nullptr;
}
m_last = 0;
}
template <typename T, typename X>
permutation_generator<T, X>::permutation_generator(const permutation_generator & o): m_n(o.m_n), m_done(o.m_done), m_current(o.m_current), m_last(o.m_last) {
if (m_lower != nullptr) {
m_lower = new permutation_generator(o.m_lower);
} else {
m_lower = nullptr;
}
}
template <typename T, typename X> bool
permutation_generator<T, X>::move_next() {
if (m_done) {
return false;
}
if (m_lower == nullptr) {
if (m_last == 0) {
m_last++;
return true;
} else {
m_done = true;
return false;
}
} else {
if (m_last < m_n && m_last > 0) {
m_current[m_last - 1] = m_current[m_last];
m_current[m_last] = m_n - 1;
m_last++;
return true;
} else {
if (m_lower -> move_next()) {
auto lower_curr = m_lower -> current();
for ( unsigned i = 1; i < m_n; i++ ){
m_current[i] = (*lower_curr)[i - 1];
}
m_current[0] = m_n - 1;
m_last = 1;
return true;
} else {
m_done = true;
return false;
}
}
}
}
template <typename T, typename X>
inline unsigned number_of_inversions(permutation_matrix<T, X> & p) {
unsigned ret = 0;
unsigned n = p.size();
for (unsigned i = 0; i < n; i++) {
for (unsigned j = i + 1; j < n; j++) {
if (p[i] > p[j]) {
ret++;
}
}
}
return ret;
}
template <typename T, typename X>
T det_val_on_perm(permutation_matrix<T, X>* u, const matrix<T, X>& m) {
unsigned n = m.row_count();
T ret = numeric_traits<T>::one();
for (unsigned i = 0; i < n; i++) {
unsigned j = (*u)[i];
ret *= m(i, j);
}
return ret * sign(*u);
}
template <typename T, typename X>
T determinant(const matrix<T, X>& m) {
lean_assert(m.column_count() == m.row_count());
unsigned n = m.row_count();
permutation_generator<T, X> allp(n);
T ret = numeric_traits<T>::zero();
while (allp.move_next()){
ret += det_val_on_perm(allp.current(), m);
}
return ret;
}
#endif
}

View file

@ -0,0 +1,60 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <memory>
#include "util/vector.h"
#include "util/lp/permutation_matrix.hpp"
#include "util/lp/numeric_pair.h"
template void lean::permutation_matrix<double, double>::apply_from_right(vector<double>&);
template void lean::permutation_matrix<double, double>::init(unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::init(unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq>>::init(unsigned int);
template bool lean::permutation_matrix<double, double>::is_identity() const;
template void lean::permutation_matrix<double, double>::multiply_by_permutation_from_left(lean::permutation_matrix<double, double>&);
template void lean::permutation_matrix<double, double>::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<double, double>&);
template void lean::permutation_matrix<double, double>::multiply_by_reverse_from_right(lean::permutation_matrix<double, double>&);
template lean::permutation_matrix<double, double>::permutation_matrix(unsigned int, vector<unsigned int> const&);
template void lean::permutation_matrix<double, double>::transpose_from_left(unsigned int, unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_from_right(vector<lean::mpq>&);
template bool lean::permutation_matrix<lean::mpq, lean::mpq>::is_identity() const;
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_from_left(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_from_right(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_reverse_from_right(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template lean::permutation_matrix<lean::mpq, lean::mpq>::permutation_matrix(unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::transpose_from_left(unsigned int, unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::transpose_from_right(unsigned int, unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(vector<lean::mpq>&);
template bool lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::is_identity() const;
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_from_left(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_from_right(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_reverse_from_right(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::permutation_matrix(unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_from_left(unsigned int, unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_from_right(unsigned int, unsigned int);
template void lean::permutation_matrix<double, double>::apply_reverse_from_left<double>(lean::indexed_vector<double>&);
template void lean::permutation_matrix<double, double>::apply_reverse_from_left_to_T(vector<double>&);
template void lean::permutation_matrix<double, double>::apply_reverse_from_right_to_T(vector<double>&);
template void lean::permutation_matrix<double, double>::transpose_from_right(unsigned int, unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_left<lean::mpq>(lean::indexed_vector<lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_left_to_T(vector<lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_right_to_T(vector<lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_left<lean::mpq>(lean::indexed_vector<lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_left_to_T(vector<lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_right_to_T(vector<lean::mpq >&);
template void lean::permutation_matrix<double, double>::multiply_by_permutation_from_right(lean::permutation_matrix<double, double>&);
#ifdef LEAN_DEBUG
template bool lean::permutation_generator<double, double>::move_next();
template lean::permutation_generator<double, double>::permutation_generator(unsigned int);
#endif
template lean::permutation_matrix<double, double>::permutation_matrix(unsigned int);
template void lean::permutation_matrix<double, double>::apply_reverse_from_left_to_X(vector<double> &);
template void lean::permutation_matrix< lean::mpq, lean::mpq>::apply_reverse_from_left_to_X(vector<lean::mpq> &);
template void lean::permutation_matrix< lean::mpq, lean::numeric_pair< lean::mpq> >::apply_reverse_from_left_to_X(vector<lean::numeric_pair< lean::mpq>> &);
template void lean::permutation_matrix<double, double>::apply_reverse_from_right_to_T(lean::indexed_vector<double>&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_right_to_T(lean::indexed_vector<lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_right_to_T(lean::indexed_vector<lean::mpq>&);

View file

@ -0,0 +1,138 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/lar_solver.h"
namespace lean {
quick_xplain::quick_xplain(vector<std::pair<mpq, constraint_index>> & explanation, const lar_solver & ls, lar_solver & qsol) :
m_explanation(explanation),
m_parent_solver(ls),
m_qsol(qsol) {
}
void quick_xplain::add_constraint_to_qsol(unsigned j) {
auto & lar_c = m_constraints_in_local_vars[j];
auto ls = lar_c.get_left_side_coefficients();
auto ci = m_qsol.add_constraint(ls, lar_c.m_kind, lar_c.m_right_side);
m_local_ci_to_constraint_offsets[ci] = j;
}
void quick_xplain::copy_constraint_and_add_constraint_vars(const lar_constraint& lar_c) {
vector < std::pair<mpq, unsigned>> ls;
for (auto & p : lar_c.get_left_side_coefficients()) {
unsigned j = p.second;
unsigned lj = m_qsol.add_var(j);
ls.push_back(std::make_pair(p.first, lj));
}
m_constraints_in_local_vars.push_back(lar_constraint(ls, lar_c.m_kind, lar_c.m_right_side));
}
bool quick_xplain::infeasible() {
m_qsol.solve();
return m_qsol.get_status() == INFEASIBLE;
}
// u - unexplored constraints
// c and x are assumed, in other words, all constrains of x and c are already added to m_qsol
void quick_xplain::minimize(const vector<unsigned>& u) {
unsigned k = 0;
unsigned initial_stack_size = m_qsol.constraint_stack_size();
for (; k < u.size();k++) {
m_qsol.push();
add_constraint_to_qsol(u[k]);
if (infeasible())
break;
}
m_x.insert(u[k]);
unsigned m = k / 2; // the split
if (m < k) {
m_qsol.pop(k + 1 - m);
add_constraint_to_qsol(u[k]);
if (!infeasible()) {
vector<unsigned> un;
for (unsigned j = m; j < k; j++)
un.push_back(u[j]);
minimize(un);
}
}
if (m > 0) {
lean_assert(m_qsol.constraint_stack_size() >= initial_stack_size);
m_qsol.pop(m_qsol.constraint_stack_size() - initial_stack_size);
for (auto j : m_x)
add_constraint_to_qsol(j);
if (!infeasible()) {
vector<unsigned> un;
for (unsigned j = 0; j < m; j++)
un.push_back(u[j]);
minimize(un);
}
}
}
void quick_xplain::run(vector<std::pair<mpq, constraint_index>> & explanation, const lar_solver & ls){
if (explanation.size() <= 2) return;
lar_solver qsol;
lean_assert(ls.explanation_is_correct(explanation));
quick_xplain q(explanation, ls, qsol);
q.solve();
}
void quick_xplain::copy_constraints_to_local_constraints() {
for (auto & p : m_explanation) {
const auto & lar_c = m_parent_solver.get_constraint(p.second);
m_local_constraint_offset_to_external_ci.push_back(p.second);
copy_constraint_and_add_constraint_vars(lar_c);
}
}
bool quick_xplain::is_feasible(const vector<unsigned> & x, unsigned k) const {
lar_solver l;
for (unsigned i : x) {
if (i == k)
continue;
vector < std::pair<mpq, unsigned>> ls;
const lar_constraint & c = m_constraints_in_local_vars[i];
for (auto & p : c.get_left_side_coefficients()) {
unsigned lj = l.add_var(p.second);
ls.push_back(std::make_pair(p.first, lj));
}
l.add_constraint(ls, c.m_kind, c.m_right_side);
}
l.solve();
return l.get_status() != INFEASIBLE;
}
bool quick_xplain::x_is_minimal() const {
vector<unsigned> x;
for (auto j : m_x)
x.push_back(j);
for (unsigned k = 0; k < x.size(); k++) {
lean_assert(is_feasible(x, x[k]));
}
return true;
}
void quick_xplain::solve() {
copy_constraints_to_local_constraints();
m_qsol.push();
lean_assert(m_qsol.constraint_count() == 0)
vector<unsigned> u;
for (unsigned k = 0; k < m_constraints_in_local_vars.size(); k++)
u.push_back(k);
minimize(u);
while (m_qsol.constraint_count() > 0)
m_qsol.pop();
for (unsigned i : m_x)
add_constraint_to_qsol(i);
m_qsol.solve();
lean_assert(m_qsol.get_status() == INFEASIBLE);
m_qsol.get_infeasibility_explanation(m_explanation);
lean_assert(m_qsol.explanation_is_correct(m_explanation));
lean_assert(x_is_minimal());
for (auto & p : m_explanation) {
p.second = this->m_local_constraint_offset_to_external_ci[m_local_ci_to_constraint_offsets[p.second]];
}
}
}

View file

@ -0,0 +1,33 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include <unordered_set>
namespace lean {
class lar_solver; // forward definition
class quick_xplain {
std::unordered_set<unsigned> m_x; // the minimal set of constraints, the core - it is empty at the begining
vector<lar_constraint> m_constraints_in_local_vars;
vector<std::pair<mpq, constraint_index>> & m_explanation;
const lar_solver& m_parent_solver;
lar_solver & m_qsol;
vector<constraint_index> m_local_constraint_offset_to_external_ci;
std::unordered_map<constraint_index, unsigned> m_local_ci_to_constraint_offsets;
quick_xplain(vector<std::pair<mpq, constraint_index>> & explanation, const lar_solver & parent_lar_solver, lar_solver & qsol);
void minimize(const vector<unsigned> & u);
void add_constraint_to_qsol(unsigned j);
void copy_constraint_and_add_constraint_vars(const lar_constraint& lar_c);
void copy_constraints_to_local_constraints();
bool infeasible();
bool is_feasible(const vector<unsigned> & x, unsigned k) const;
bool x_is_minimal() const;
public:
static void run(vector<std::pair<mpq, constraint_index>> & explanation,const lar_solver & ls);
void solve();
};
}

View file

@ -0,0 +1,77 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include <set>
#include "util/vector.h"
#include <unordered_map>
#include <string>
#include <algorithm>
#include "util/lp/lp_settings.h"
#include "util/lp/linear_combination_iterator.h"
// see http://research.microsoft.com/projects/z3/smt07.pdf
// The class searches for a feasible solution with as many different values of variables as it can find
namespace lean {
template <typename T> struct numeric_pair; // forward definition
class lar_core_solver; // forward definition
class random_updater {
unsigned range = 100000;
struct interval {
bool upper_bound_is_set = false;
numeric_pair<mpq> upper_bound;
bool low_bound_is_set = false;
numeric_pair<mpq> low_bound;
void set_low_bound(const numeric_pair<mpq> & v) {
if (low_bound_is_set) {
low_bound = std::max(v, low_bound);
} else {
low_bound = v;
low_bound_is_set = true;
}
}
void set_upper_bound(const numeric_pair<mpq> & v) {
if (upper_bound_is_set) {
upper_bound = std::min(v, upper_bound);
} else {
upper_bound = v;
upper_bound_is_set = true;
}
}
bool is_empty() const {return
upper_bound_is_set && low_bound_is_set && low_bound >= upper_bound;
}
bool low_bound_holds(const numeric_pair<mpq> & a) const {
return low_bound_is_set == false || a >= low_bound;
}
bool upper_bound_holds(const numeric_pair<mpq> & a) const {
return upper_bound_is_set == false || a <= upper_bound;
}
bool contains(const numeric_pair<mpq> & a) const {
return low_bound_holds(a) && upper_bound_holds(a);
}
std::string lbs() { return low_bound_is_set ? T_to_string(low_bound):std::string("inf");}
std::string rbs() { return upper_bound_is_set? T_to_string(upper_bound):std::string("inf");}
std::string to_str() { return std::string("[")+ lbs() + ", " + rbs() + "]";}
};
std::set<var_index> m_var_set;
lar_core_solver & m_core_solver;
linear_combination_iterator<mpq>* m_column_j; // the actual column
interval find_shift_interval(unsigned j);
interval get_interval_of_non_basic_var(unsigned j);
void add_column_to_sets(unsigned j);
void random_shift_var(unsigned j);
std::unordered_map<numeric_pair<mpq>, unsigned> m_values; // it maps a value to the number of time it occurs
void diminish_interval_to_leave_basic_vars_feasible(numeric_pair<mpq> &nb_x, interval & inter);
void shift_var(unsigned j, interval & r);
void diminish_interval_for_basic_var(numeric_pair<mpq> &nb_x, unsigned j, mpq & a, interval & r);
numeric_pair<mpq> get_random_from_interval(interval & r);
void add_value(numeric_pair<mpq>& v);
void remove_value(numeric_pair<mpq> & v);
public:
random_updater(lar_core_solver & core_solver, const vector<unsigned> & column_list);
void update();
};
}

View file

@ -0,0 +1,205 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/random_updater.h"
#include "util/lp/static_matrix.h"
#include "util/lp/lar_solver.h"
#include "util/vector.h"
namespace lean {
random_updater::random_updater(
lar_core_solver & lar_core_solver,
const vector<unsigned> & column_indices) : m_core_solver(lar_core_solver) {
for (unsigned j : column_indices)
add_column_to_sets(j);
}
random_updater::interval random_updater::get_interval_of_non_basic_var(unsigned j) {
interval ret;
switch (m_core_solver.get_column_type(j)) {
case column_type::free_column:
break;
case column_type::low_bound:
ret.set_low_bound(m_core_solver.m_r_low_bounds[j]);
break;
case column_type::upper_bound:
ret.set_upper_bound(m_core_solver.m_r_upper_bounds[j]);
break;
case column_type::boxed:
case column_type::fixed:
ret.set_low_bound(m_core_solver.m_r_low_bounds[j]);
ret.set_upper_bound(m_core_solver.m_r_upper_bounds[j]);
break;
default:
lean_assert(false);
}
return ret;
}
void random_updater::diminish_interval_for_basic_var(numeric_pair<mpq>& nb_x, unsigned j,
mpq & a,
interval & r) {
lean_assert(m_core_solver.m_r_heading[j] >= 0);
numeric_pair<mpq> delta;
lean_assert(a != zero_of_type<mpq>());
switch (m_core_solver.get_column_type(j)) {
case column_type::free_column:
break;
case column_type::low_bound:
delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j];
lean_assert(delta >= zero_of_type<numeric_pair<mpq>>());
if (a > 0) {
r.set_upper_bound(nb_x + delta / a);
} else {
r.set_low_bound(nb_x + delta / a);
}
break;
case column_type::upper_bound:
delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j];
lean_assert(delta >= zero_of_type<numeric_pair<mpq>>());
if (a > 0) {
r.set_low_bound(nb_x - delta / a);
} else {
r.set_upper_bound(nb_x - delta / a);
}
break;
case column_type::boxed:
if (a > 0) {
delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j];
lean_assert(delta >= zero_of_type<numeric_pair<mpq>>());
r.set_upper_bound(nb_x + delta / a);
delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j];
lean_assert(delta >= zero_of_type<numeric_pair<mpq>>());
r.set_low_bound(nb_x - delta / a);
} else { // a < 0
delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j];
lean_assert(delta >= zero_of_type<numeric_pair<mpq>>());
r.set_upper_bound(nb_x - delta / a);
delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j];
lean_assert(delta >= zero_of_type<numeric_pair<mpq>>());
r.set_low_bound(nb_x + delta / a);
}
break;
case column_type::fixed:
r.set_low_bound(nb_x);
r.set_upper_bound(nb_x);
break;
default:
lean_assert(false);
}
}
void random_updater::diminish_interval_to_leave_basic_vars_feasible(numeric_pair<mpq> &nb_x, interval & r) {
m_column_j->reset();
unsigned i;
mpq a;
while (m_column_j->next(a, i)) {
diminish_interval_for_basic_var(nb_x, m_core_solver.m_r_basis[i], a, r);
if (r.is_empty())
break;
}
}
random_updater::interval random_updater::find_shift_interval(unsigned j) {
interval ret = get_interval_of_non_basic_var(j);
diminish_interval_to_leave_basic_vars_feasible(m_core_solver.m_r_x[j], ret);
return ret;
}
void random_updater::shift_var(unsigned j, interval & r) {
lean_assert(r.contains(m_core_solver.m_r_x[j]));
lean_assert(m_core_solver.m_r_solver.column_is_feasible(j));
auto old_x = m_core_solver.m_r_x[j];
remove_value(old_x);
auto new_val = m_core_solver.m_r_x[j] = get_random_from_interval(r);
add_value(new_val);
lean_assert(r.contains(m_core_solver.m_r_x[j]));
lean_assert(m_core_solver.m_r_solver.column_is_feasible(j));
auto delta = m_core_solver.m_r_x[j] - old_x;
unsigned i;
m_column_j->reset();
mpq a;
while(m_column_j->next(a, i)) {
unsigned bj = m_core_solver.m_r_basis[i];
m_core_solver.m_r_x[bj] -= a * delta;
lean_assert(m_core_solver.m_r_solver.column_is_feasible(bj));
}
lean_assert(m_core_solver.m_r_solver.A_mult_x_is_off() == false);
}
numeric_pair<mpq> random_updater::get_random_from_interval(interval & r) {
unsigned rand = my_random();
if ((!r.low_bound_is_set) && (!r.upper_bound_is_set))
return numeric_pair<mpq>(rand % range, 0);
if (r.low_bound_is_set && (!r.upper_bound_is_set))
return r.low_bound + numeric_pair<mpq>(rand % range, 0);
if ((!r.low_bound_is_set) && r.upper_bound_is_set)
return r.upper_bound - numeric_pair<mpq>(rand % range, 0);
lean_assert(r.low_bound_is_set && r.upper_bound_is_set);
return r.low_bound + (rand % range) * (r.upper_bound - r.low_bound)/ range;
}
void random_updater::random_shift_var(unsigned j) {
m_column_j = m_core_solver.get_column_iterator(j);
if (m_column_j->size() >= 50) {
delete m_column_j;
return;
}
interval interv = find_shift_interval(j);
if (interv.is_empty()) {
delete m_column_j;
return;
}
shift_var(j, interv);
delete m_column_j;
}
void random_updater::update() {
for (auto j : m_var_set) {
if (m_var_set.size() <= m_values.size()) {
break; // we are done
}
random_shift_var(j);
}
}
void random_updater::add_value(numeric_pair<mpq>& v) {
auto it = m_values.find(v);
if (it == m_values.end()) {
m_values[v] = 1;
} else {
it->second++;
}
}
void random_updater::remove_value(numeric_pair<mpq>& v) {
auto it = m_values.find(v);
lean_assert(it != m_values.end());
it->second--;
if (it->second == 0)
m_values.erase(it);
}
void random_updater::add_column_to_sets(unsigned j) {
if (m_core_solver.m_r_heading[j] < 0) {
m_var_set.insert(j);
add_value(m_core_solver.m_r_x[j]);
} else {
unsigned row = m_core_solver.m_r_heading[j];
for (auto row_c : m_core_solver.m_r_A.m_rows[row]) {
unsigned cj = row_c.m_j;
if (m_core_solver.m_r_heading[cj] < 0) {
m_var_set.insert(cj);
add_value(m_core_solver.m_r_x[cj]);
}
}
}
}
}

View file

@ -0,0 +1,5 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/random_updater.hpp"

View file

@ -0,0 +1,74 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/debug.h"
#include <string>
#include "util/lp/sparse_vector.h"
#include "util/lp/indexed_vector.h"
#include "util/lp/permutation_matrix.h"
namespace lean {
// This is the sum of a unit matrix and a lower triangular matrix
// with non-zero elements only in one row
template <typename T, typename X>
class row_eta_matrix
: public tail_matrix<T, X> {
#ifdef LEAN_DEBUG
unsigned m_dimension;
#endif
unsigned m_row_start;
unsigned m_row;
sparse_vector<T> m_row_vector;
public:
#ifdef LEAN_DEBUG
row_eta_matrix(unsigned row_start, unsigned row, unsigned dim):
#else
row_eta_matrix(unsigned row_start, unsigned row):
#endif
#ifdef LEAN_DEBUG
m_dimension(dim),
#endif
m_row_start(row_start), m_row(row) {
}
bool is_dense() const { return false; }
void print(std::ostream & out) {
print_matrix(*this, out);
}
const T & get_diagonal_element() const {
return m_row_vector.m_data[m_row];
}
void apply_from_left(vector<X> & w, lp_settings &);
void apply_from_left_local_to_T(indexed_vector<T> & w, lp_settings & settings);
void apply_from_left_local_to_X(indexed_vector<X> & w, lp_settings & settings);
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
apply_from_left_local_to_T(w, settings);
}
void push_back(unsigned row_index, T val ) {
lean_assert(row_index != m_row);
m_row_vector.push_back(row_index, val);
}
void apply_from_right(vector<T> & w);
void apply_from_right(indexed_vector<T> & w);
void conjugate_by_permutation(permutation_matrix<T, X> & p);
#ifdef LEAN_DEBUG
T get_elem(unsigned row, unsigned col) const;
unsigned row_count() const { return m_dimension; }
unsigned column_count() const { return m_dimension; }
void set_number_of_rows(unsigned m) { m_dimension = m; }
void set_number_of_columns(unsigned n) { m_dimension = n; }
#endif
}; // end of row_eta_matrix
}

View file

@ -0,0 +1,171 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/vector.h"
#include "util/lp/row_eta_matrix.h"
namespace lean {
template <typename T, typename X>
void row_eta_matrix<T, X>::apply_from_left(vector<X> & w, lp_settings &) {
// #ifdef LEAN_DEBUG
// dense_matrix<T> deb(*this);
// auto clone_w = clone_vector<T>(w, m_dimension);
// deb.apply_from_left(clone_w, settings);
// #endif
auto & w_at_row = w[m_row];
for (auto & it : m_row_vector.m_data) {
w_at_row += w[it.first] * it.second;
}
// w[m_row] = w_at_row;
// #ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
// delete [] clone_w;
// #endif
}
template <typename T, typename X>
void row_eta_matrix<T, X>::apply_from_left_local_to_T(indexed_vector<T> & w, lp_settings & settings) {
auto w_at_row = w[m_row];
bool was_zero_at_m_row = is_zero(w_at_row);
for (auto & it : m_row_vector.m_data) {
w_at_row += w[it.first] * it.second;
}
if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){
if (was_zero_at_m_row) {
w.m_index.push_back(m_row);
}
w[m_row] = w_at_row;
} else if (!was_zero_at_m_row){
w[m_row] = zero_of_type<T>();
auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row);
w.m_index.erase(it);
}
// TBD: lean_assert(check_vector_for_small_values(w, settings));
}
template <typename T, typename X>
void row_eta_matrix<T, X>::apply_from_left_local_to_X(indexed_vector<X> & w, lp_settings & settings) {
auto w_at_row = w[m_row];
bool was_zero_at_m_row = is_zero(w_at_row);
for (auto & it : m_row_vector.m_data) {
w_at_row += w[it.first] * it.second;
}
if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){
if (was_zero_at_m_row) {
w.m_index.push_back(m_row);
}
w[m_row] = w_at_row;
} else if (!was_zero_at_m_row){
w[m_row] = zero_of_type<X>();
auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row);
w.m_index.erase(it);
}
// TBD: does not compile lean_assert(check_vector_for_small_values(w, settings));
}
template <typename T, typename X>
void row_eta_matrix<T, X>::apply_from_right(vector<T> & w) {
const T & w_row = w[m_row];
if (numeric_traits<T>::is_zero(w_row)) return;
#ifdef LEAN_DEBUG
// dense_matrix<T> deb(*this);
// auto clone_w = clone_vector<T>(w, m_dimension);
// deb.apply_from_right(clone_w);
#endif
for (auto & it : m_row_vector.m_data) {
w[it.first] += w_row * it.second;
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
// delete clone_w;
#endif
}
template <typename T, typename X>
void row_eta_matrix<T, X>::apply_from_right(indexed_vector<T> & w) {
lean_assert(w.is_OK());
const T & w_row = w[m_row];
if (numeric_traits<T>::is_zero(w_row)) return;
#ifdef LEAN_DEBUG
// vector<T> wcopy(w.m_data);
// apply_from_right(wcopy);
#endif
if (numeric_traits<T>::precise()) {
for (auto & it : m_row_vector.m_data) {
unsigned j = it.first;
bool was_zero = numeric_traits<T>::is_zero(w[j]);
const T & v = w[j] += w_row * it.second;
if (was_zero) {
if (!numeric_traits<T>::is_zero(v))
w.m_index.push_back(j);
} else {
if (numeric_traits<T>::is_zero(v))
w.erase_from_index(j);
}
}
} else { // the non precise version
const double drop_eps = 1e-14;
for (auto & it : m_row_vector.m_data) {
unsigned j = it.first;
bool was_zero = numeric_traits<T>::is_zero(w[j]);
T & v = w[j] += w_row * it.second;
if (was_zero) {
if (!lp_settings::is_eps_small_general(v, drop_eps))
w.m_index.push_back(j);
else
v = zero_of_type<T>();
} else {
if (lp_settings::is_eps_small_general(v, drop_eps)) {
w.erase_from_index(j);
v = zero_of_type<T>();
}
}
}
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal(wcopy, w.m_data));
#endif
}
template <typename T, typename X>
void row_eta_matrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & p) {
// this = p * this * p(-1)
#ifdef LEAN_DEBUG
// auto rev = p.get_reverse();
// auto deb = ((*this) * rev);
// deb = p * deb;
#endif
m_row = p.apply_reverse(m_row);
// copy aside the column indices
vector<unsigned> columns;
for (auto & it : m_row_vector.m_data)
columns.push_back(it.first);
for (unsigned i = static_cast<unsigned>(columns.size()); i-- > 0;)
m_row_vector.m_data[i].first = p.get_rev(columns[i]);
#ifdef LEAN_DEBUG
// lean_assert(deb == *this);
#endif
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
T row_eta_matrix<T, X>::get_elem(unsigned row, unsigned col) const {
if (row == m_row){
if (col == row) {
return numeric_traits<T>::one();
}
return m_row_vector[col];
}
return col == row ? numeric_traits<T>::one() : numeric_traits<T>::zero();
}
#endif
}

View file

@ -0,0 +1,33 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/vector.h"
#include <memory>
#include "util/lp/row_eta_matrix.hpp"
#include "util/lp/lu.h"
namespace lean {
template void row_eta_matrix<double, double>::conjugate_by_permutation(permutation_matrix<double, double>&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::conjugate_by_permutation(permutation_matrix<mpq, numeric_pair<mpq> >&);
template void row_eta_matrix<mpq, mpq>::conjugate_by_permutation(permutation_matrix<mpq, mpq>&);
#ifdef LEAN_DEBUG
template mpq row_eta_matrix<mpq, mpq>::get_elem(unsigned int, unsigned int) const;
template mpq row_eta_matrix<mpq, numeric_pair<mpq> >::get_elem(unsigned int, unsigned int) const;
template double row_eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
#endif
template void row_eta_matrix<mpq, mpq>::apply_from_left(vector<mpq>&, lp_settings&);
template void row_eta_matrix<mpq, mpq>::apply_from_right(vector<mpq>&);
template void row_eta_matrix<mpq, mpq>::apply_from_right(indexed_vector<mpq>&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left(vector<numeric_pair<mpq>>&, lp_settings&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_right(vector<mpq>&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_right(indexed_vector<mpq>&);
template void row_eta_matrix<double, double>::apply_from_left(vector<double>&, lp_settings&);
template void row_eta_matrix<double, double>::apply_from_right(vector<double>&);
template void row_eta_matrix<double, double>::apply_from_right(indexed_vector<double>&);
template void row_eta_matrix<mpq, mpq>::apply_from_left_to_T(indexed_vector<mpq>&, lp_settings&);
template void row_eta_matrix<mpq, mpq>::apply_from_left_local_to_T(indexed_vector<mpq>&, lp_settings&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left_to_T(indexed_vector<mpq>&, lp_settings&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left_local_to_T(indexed_vector<mpq>&, lp_settings&);
template void row_eta_matrix<double, double>::apply_from_left_to_T(indexed_vector<double>&, lp_settings&);
template void row_eta_matrix<double, double>::apply_from_left_local_to_T(indexed_vector<double>&, lp_settings&);
}

79
src/util/lp/scaler.h Normal file
View file

@ -0,0 +1,79 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include <math.h>
#include <algorithm>
#include <stdio.h> /* printf, fopen */
#include <stdlib.h> /* exit, EXIT_FAILURE */
#include "util/lp/lp_utils.h"
#include "util/lp/static_matrix.h"
namespace lean {
// for scaling an LP
template <typename T, typename X>
class scaler {
vector<X> & m_b; // right side
static_matrix<T, X> &m_A; // the constraint matrix
const T & m_scaling_minimum;
const T & m_scaling_maximum;
vector<T>& m_column_scale;
lp_settings & m_settings;
public:
// constructor
scaler(vector<X> & b, static_matrix<T, X> &A, const T & scaling_minimum, const T & scaling_maximum, vector<T> & column_scale,
lp_settings & settings):
m_b(b),
m_A(A),
m_scaling_minimum(scaling_minimum),
m_scaling_maximum(scaling_maximum),
m_column_scale(column_scale),
m_settings(settings) {
lean_assert(m_column_scale.size() == 0);
m_column_scale.resize(m_A.column_count(), numeric_traits<T>::one());
}
T right_side_balance();
T get_balance() { return m_A.get_balance(); }
T A_min() const;
T A_max() const;
T get_A_ratio() const;
T get_max_ratio_on_rows() const;
T get_max_ratio_on_columns() const;
void scale_rows_with_geometric_mean();
void scale_columns_with_geometric_mean();
void scale_once_for_ratio();
bool scale_with_ratio();
void bring_row_maximums_to_one();
void bring_column_maximums_to_one();
void bring_rows_and_columns_maximums_to_one();
bool scale_with_log_balance();
// Returns true if and only if the scaling was successful.
// It is the caller responsibility to restore the matrix
bool scale();
void scale_rows();
void scale_row(unsigned i);
void scale_column(unsigned i);
void scale_columns();
};
}

254
src/util/lp/scaler.hpp Normal file
View file

@ -0,0 +1,254 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include <algorithm>
#include "util/lp/scaler.h"
#include "util/lp/numeric_pair.h"
namespace lean {
// for scaling an LP
template <typename T, typename X> T scaler<T, X>::right_side_balance() {
T ret = zero_of_type<T>();
unsigned i = m_A.row_count();
while (i--) {
T rs = abs(convert_struct<T, X>::convert(m_b[i]));
if (!is_zero<T>(rs)) {
numeric_traits<T>::log(rs);
ret += rs * rs;
}
}
return ret;
}
template <typename T, typename X> T scaler<T, X>::A_min() const {
T min = zero_of_type<T>();
for (unsigned i = 0; i < m_A.row_count(); i++) {
T t = m_A.get_min_abs_in_row(i);
min = i == 0 ? t : std::min(t, min);
}
return min;
}
template <typename T, typename X> T scaler<T, X>::A_max() const {
T max = zero_of_type<T>();
for (unsigned i = 0; i < m_A.row_count(); i++) {
T t = m_A.get_max_abs_in_row(i);
max = i == 0? t : std::max(t, max);
}
return max;
}
template <typename T, typename X> T scaler<T, X>::get_A_ratio() const {
T min = A_min();
T max = A_max();
lean_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(min));
T ratio = max / min;
return ratio;
}
template <typename T, typename X> T scaler<T, X>::get_max_ratio_on_rows() const {
T ret = T(1);
unsigned i = m_A.row_count();
while (i--) {
T den = m_A.get_min_abs_in_row(i);
lean_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(den));
T t = m_A.get_max_abs_in_row(i)/ den;
if (t > ret)
ret = t;
}
return ret;
}
template <typename T, typename X> T scaler<T, X>::get_max_ratio_on_columns() const {
T ret = T(1);
unsigned i = m_A.column_count();
while (i--) {
T den = m_A.get_min_abs_in_column(i);
if (m_settings.abs_val_is_smaller_than_zero_tolerance(den))
continue; // got a zero column
T t = m_A.get_max_abs_in_column(i)/den;
if (t > ret)
ret = t;
}
return ret;
}
template <typename T, typename X> void scaler<T, X>::scale_rows_with_geometric_mean() {
unsigned i = m_A.row_count();
while (i--) {
T max = m_A.get_max_abs_in_row(i);
T min = m_A.get_min_abs_in_row(i);
lean_assert(max > zero_of_type<T>() && min > zero_of_type<T>());
if (is_zero(max) || is_zero(min))
continue;
T gm = T(sqrt(numeric_traits<T>::get_double(max*min)));
if (m_settings.is_eps_small_general(gm, 0.01)) {
continue;
}
m_A.multiply_row(i, one_of_type<T>() / gm);
m_b[i] /= gm;
}
}
template <typename T, typename X> void scaler<T, X>::scale_columns_with_geometric_mean() {
unsigned i = m_A.column_count();
while (i--) {
T max = m_A.get_max_abs_in_column(i);
T min = m_A.get_min_abs_in_column(i);
T den = T(sqrt(numeric_traits<T>::get_double(max*min)));
if (m_settings.is_eps_small_general(den, 0.01))
continue; // got a zero column
T gm = T(1)/ den;
T cs = m_column_scale[i] * gm;
if (m_settings.is_eps_small_general(cs, 0.1))
continue;
m_A.multiply_column(i, gm);
m_column_scale[i] = cs;
}
}
template <typename T, typename X> void scaler<T, X>::scale_once_for_ratio() {
T max_ratio_on_rows = get_max_ratio_on_rows();
T max_ratio_on_columns = get_max_ratio_on_columns();
bool scale_rows_first = max_ratio_on_rows > max_ratio_on_columns;
// if max_ratio_on_columns is the largerst then the rows are in worser shape then columns
if (scale_rows_first) {
scale_rows_with_geometric_mean();
scale_columns_with_geometric_mean();
} else {
scale_columns_with_geometric_mean();
scale_rows_with_geometric_mean();
}
}
template <typename T, typename X> bool scaler<T, X>::scale_with_ratio() {
T ratio = get_A_ratio();
// The ratio is greater than or equal to one. We would like to diminish it and bring it as close to 1 as possible
unsigned reps = m_settings.reps_in_scaler;
do {
scale_once_for_ratio();
T new_r = get_A_ratio();
if (new_r >= T(0.9) * ratio)
break;
} while (reps--);
bring_rows_and_columns_maximums_to_one();
return true;
}
template <typename T, typename X> void scaler<T, X>::bring_row_maximums_to_one() {
unsigned i = m_A.row_count();
while (i--) {
T t = m_A.get_max_abs_in_row(i);
if (m_settings.abs_val_is_smaller_than_zero_tolerance(t)) continue;
m_A.multiply_row(i, one_of_type<T>() / t);
m_b[i] /= t;
}
}
template <typename T, typename X> void scaler<T, X>::bring_column_maximums_to_one() {
unsigned i = m_A.column_count();
while (i--) {
T max = m_A.get_max_abs_in_column(i);
if (m_settings.abs_val_is_smaller_than_zero_tolerance(max)) continue;
T t = T(1) / max;
m_A.multiply_column(i, t);
m_column_scale[i] *= t;
}
}
template <typename T, typename X> void scaler<T, X>::bring_rows_and_columns_maximums_to_one() {
if (get_max_ratio_on_rows() > get_max_ratio_on_columns()) {
bring_row_maximums_to_one();
bring_column_maximums_to_one();
} else {
bring_column_maximums_to_one();
bring_row_maximums_to_one();
}
}
template <typename T, typename X> bool scaler<T, X>::scale_with_log_balance() {
T balance = get_balance();
T balance_before_scaling = balance;
// todo : analyze the scale order : rows-columns, or columns-rows. Iterate if needed
for (int i = 0; i < 10; i++) {
scale_rows();
scale_columns();
T nb = get_balance();
if (nb < T(0.9) * balance) {
balance = nb;
} else {
balance = nb;
break;
}
}
return balance <= balance_before_scaling;
}
// Returns true if and only if the scaling was successful.
// It is the caller responsibility to restore the matrix
template <typename T, typename X> bool scaler<T, X>::scale() {
if (numeric_traits<T>::precise()) return true;
if (m_settings.scale_with_ratio)
return scale_with_ratio();
return scale_with_log_balance();
}
template <typename T, typename X> void scaler<T, X>::scale_rows() {
for (unsigned i = 0; i < m_A.row_count(); i++)
scale_row(i);
}
template <typename T, typename X> void scaler<T, X>::scale_row(unsigned i) {
T row_max = std::max(m_A.get_max_abs_in_row(i), abs(convert_struct<T, X>::convert(m_b[i])));
T alpha = numeric_traits<T>::one();
if (numeric_traits<T>::is_zero(row_max)) {
return;
}
if (numeric_traits<T>::get_double(row_max) < m_scaling_minimum) {
do {
alpha *= 2;
row_max *= 2;
} while (numeric_traits<T>::get_double(row_max) < m_scaling_minimum);
m_A.multiply_row(i, alpha);
m_b[i] *= alpha;
} else if (numeric_traits<T>::get_double(row_max) > m_scaling_maximum) {
do {
alpha /= 2;
row_max /= 2;
} while (numeric_traits<T>::get_double(row_max) > m_scaling_maximum);
m_A.multiply_row(i, alpha);
m_b[i] *= alpha;
}
}
template <typename T, typename X> void scaler<T, X>::scale_column(unsigned i) {
T column_max = m_A.get_max_abs_in_column(i);
T alpha = numeric_traits<T>::one();
if (numeric_traits<T>::is_zero(column_max)){
return; // the column has zeros only
}
if (numeric_traits<T>::get_double(column_max) < m_scaling_minimum) {
do {
alpha *= 2;
column_max *= 2;
} while (numeric_traits<T>::get_double(column_max) < m_scaling_minimum);
} else if (numeric_traits<T>::get_double(column_max) > m_scaling_maximum) {
do {
alpha /= 2;
column_max /= 2;
} while (numeric_traits<T>::get_double(column_max) > m_scaling_maximum);
} else {
return;
}
m_A.multiply_column(i, alpha);
m_column_scale[i] = alpha;
}
template <typename T, typename X> void scaler<T, X>::scale_columns() {
for (unsigned i = 0; i < m_A.column_count(); i++) {
scale_column(i);
}
}
}

View file

@ -0,0 +1,7 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/scaler.hpp"
template bool lean::scaler<double, double>::scale();
template bool lean::scaler<lean::mpq, lean::mpq>::scale();

View file

@ -0,0 +1,23 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/lp_settings.h"
#include "util/lp/lar_constraints.h"
namespace lean {
struct bound_signature {
unsigned m_i;
bool m_at_low;
bound_signature(unsigned i, bool at_low) :m_i(i), m_at_low(m_at_low) {}
bool at_upper_bound() const { return !m_at_low_bound;}
bool at_low_bound() const { return m_at_low;}
};
template <typename X>
struct signature_bound_evidence {
vector<bound_signature> m_evidence;
unsigned m_j; // found new bound
bool m_low_bound;
X m_bound;
};
}

417
src/util/lp/sparse_matrix.h Normal file
View file

@ -0,0 +1,417 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include "util/lp/permutation_matrix.h"
#include <unordered_map>
#include "util/lp/static_matrix.h"
#include <set>
#include <utility>
#include <string>
#include <algorithm>
#include <queue>
#include "util/lp/indexed_value.h"
#include "util/lp/indexed_vector.h"
#include <functional>
#include "util/lp/lp_settings.h"
#include "util/lp/eta_matrix.h"
#include "util/lp/binary_heap_upair_queue.h"
#include "util/lp/numeric_pair.h"
#include "util/lp/int_set.h"
namespace lean {
// it is a square matrix
template <typename T, typename X>
class sparse_matrix
#ifdef LEAN_DEBUG
: public matrix<T, X>
#endif
{
struct col_header {
unsigned m_shortened_markovitz = 0;
vector<indexed_value<T>> m_values; // the actual column values
col_header() {}
void shorten_markovich_by_one() {
m_shortened_markovitz++;
}
void zero_shortened_markovitz() {
m_shortened_markovitz = 0;
}
};
unsigned m_n_of_active_elems = 0;
binary_heap_upair_queue<unsigned> m_pivot_queue;
public:
vector<vector<indexed_value<T>>> m_rows;
vector<col_header> m_columns;
permutation_matrix<T, X> m_row_permutation;
permutation_matrix<T, X> m_column_permutation;
// m_work_pivot_vector[j] = offset of elementh of j-th column in the row we are pivoting to
// if the column is not present then m_work_pivot_vector[j] is -1
vector<int> m_work_pivot_vector;
vector<bool> m_processed;
unsigned get_n_of_active_elems() const { return m_n_of_active_elems; }
#ifdef LEAN_DEBUG
// dense_matrix<T> m_dense;
#endif
/*
the rule is: row i is mapped to m_row_permutation[i] and
column j is mapped to m_column_permutation.apply_reverse(j)
*/
unsigned adjust_row(unsigned row) const{
return m_row_permutation[row];
}
unsigned adjust_column(unsigned col) const{
return m_column_permutation.apply_reverse(col);
}
unsigned adjust_row_inverse(unsigned row) const{
return m_row_permutation.apply_reverse(row);
}
unsigned adjust_column_inverse(unsigned col) const{
return m_column_permutation[col];
}
void copy_column_from_static_matrix(unsigned col, static_matrix<T, X> const &A, unsigned col_index_in_the_new_matrix);
void copy_B(static_matrix<T, X> const &A, vector<unsigned> & basis);
public:
// constructor that copies columns of the basis from A
sparse_matrix(static_matrix<T, X> const &A, vector<unsigned> & basis);
class ref_matrix_element {
sparse_matrix & m_matrix;
unsigned m_row;
unsigned m_col;
public:
ref_matrix_element(sparse_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {}
ref_matrix_element & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; }
ref_matrix_element & operator=(ref_matrix_element const & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; }
operator T () const { return m_matrix.get(m_row, m_col); }
};
class ref_row {
sparse_matrix & m_matrix;
unsigned m_row;
public:
ref_row(sparse_matrix & m, unsigned row) : m_matrix(m), m_row(row) {}
ref_matrix_element operator[](unsigned col) const { return ref_matrix_element(m_matrix, m_row, col); }
};
void set_with_no_adjusting_for_row(unsigned row, unsigned col, T val);
void set_with_no_adjusting_for_col(unsigned row, unsigned col, T val);
void set_with_no_adjusting(unsigned row, unsigned col, T val);
void set(unsigned row, unsigned col, T val);
T const & get_not_adjusted(unsigned row, unsigned col) const;
T const & get(unsigned row, unsigned col) const;
ref_row operator[](unsigned row) { return ref_row(*this, row); }
ref_matrix_element operator()(unsigned row, unsigned col) { return ref_matrix_element(*this, row, col); }
T operator() (unsigned row, unsigned col) const { return get(row, col); }
vector<indexed_value<T>> & get_row_values(unsigned row) {
return m_rows[row];
}
vector<indexed_value<T>> const & get_row_values(unsigned row) const {
return m_rows[row];
}
vector<indexed_value<T>> & get_column_values(unsigned col) {
return m_columns[col].m_values;
}
vector<indexed_value<T>> const & get_column_values(unsigned col) const {
return m_columns[col].m_values;
}
// constructor creating a zero matrix of dim*dim
sparse_matrix(unsigned dim);
unsigned dimension() const {return static_cast<unsigned>(m_row_permutation.size());}
#ifdef LEAN_DEBUG
unsigned row_count() const {return dimension();}
unsigned column_count() const {return dimension();}
#endif
void init_row_headers();
void init_column_headers();
unsigned lowest_row_in_column(unsigned j);
indexed_value<T> & column_iv_other(indexed_value<T> & iv) {
return m_rows[iv.m_index][iv.m_other];
}
indexed_value<T> & row_iv_other(indexed_value<T> & iv) {
return m_columns[iv.m_index].m_values[iv.m_other];
}
void remove_element(vector<indexed_value<T>> & row_vals, unsigned row_offset, vector<indexed_value<T>> & column_vals, unsigned column_offset);
void remove_element(vector<indexed_value<T>> & row_chunk, indexed_value<T> & row_el_iv);
void put_max_index_to_0(vector<indexed_value<T>> & row_vals, unsigned max_index);
void set_max_in_row(unsigned row) {
set_max_in_row(m_rows[row]);
}
void set_max_in_row(vector<indexed_value<T>> & row_vals);
bool pivot_with_eta(unsigned i, eta_matrix<T, X> *eta_matrix, lp_settings & settings);
void scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column);
// This method pivots row i to row i0 by muliplying row i by
// alpha and adding it to row i0.
// After pivoting the row i0 has a max abs value set correctly at the beginning of m_start,
// Returns false if the resulting row is all zeroes, and true otherwise
bool pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings );
// set the max val as well
// returns false if the resulting row is all zeroes, and true otherwise
bool set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector<T> & work_vec,
lp_settings & settings);
// set the max val as well
// returns false if the resulting row is all zeroes, and true otherwise
bool set_row_from_work_vector_and_clean_work_vector(unsigned i0);
void remove_zero_elements_and_set_data_on_existing_elements(unsigned row);
// work_vec here has not adjusted column indices
void remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector<T> & work_vec, lp_settings & settings);
void multiply_from_right(permutation_matrix<T, X>& p) {
// m_dense = m_dense * p;
m_column_permutation.multiply_by_permutation_from_right(p);
// lean_assert(*this == m_dense);
}
void multiply_from_left(permutation_matrix<T, X>& p) {
// m_dense = p * m_dense;
m_row_permutation.multiply_by_permutation_from_left(p);
// lean_assert(*this == m_dense);
}
void multiply_from_left_with_reverse(permutation_matrix<T, X>& p) {
// m_dense = p * m_dense;
m_row_permutation.multiply_by_permutation_reverse_from_left(p);
// lean_assert(*this == m_dense);
}
// adding delta columns at the end of the matrix
void add_columns_at_the_end(unsigned delta);
void delete_column(int i);
void swap_columns(unsigned a, unsigned b) {
// cout << "swaapoiiin" << std::endl;
// dense_matrix<T, X> d(*this);
m_column_permutation.transpose_from_left(a, b);
// d.swap_columns(a, b);
// lean_assert(*this == d);
}
void swap_rows(unsigned a, unsigned b) {
m_row_permutation.transpose_from_right(a, b);
// m_dense.swap_rows(a, b);
// lean_assert(*this == m_dense);
}
void divide_row_by_constant(unsigned i, const T & t, lp_settings & settings);
bool close(T a, T b) {
return // (numeric_traits<T>::precise() && numeric_traits<T>::is_zero(a - b))
// ||
fabs(numeric_traits<T>::get_double(a - b)) < 0.0000001;
}
// solving x * this = y, and putting the answer into y
// the matrix here has to be upper triangular
void solve_y_U(vector<T> & y) const;
// solving x * this = y, and putting the answer into y
// the matrix here has to be upper triangular
void solve_y_U_indexed(indexed_vector<T> & y, const lp_settings &);
// fills the indices for such that y[i] can be not a zero
// sort them so the smaller indices come first
void fill_reachable_indices(std::set<unsigned> & rset, T *y);
template <typename L>
void find_error_in_solution_U_y(vector<L>& y_orig, vector<L> & y);
template <typename L>
void find_error_in_solution_U_y_indexed(indexed_vector<L>& y_orig, indexed_vector<L> & y, const vector<unsigned>& sorted_active_rows);
template <typename L>
void add_delta_to_solution(const vector<L>& del, vector<L> & y);
template <typename L>
void add_delta_to_solution(const indexed_vector<L>& del, indexed_vector<L> & y);
template <typename L>
void double_solve_U_y(indexed_vector<L>& y, const lp_settings & settings);
template <typename L>
void double_solve_U_y(vector<L>& y);
// solving this * x = y, and putting the answer into y
// the matrix here has to be upper triangular
template <typename L>
void solve_U_y(vector<L> & y);
// solving this * x = y, and putting the answer into y
// the matrix here has to be upper triangular
template <typename L>
void solve_U_y_indexed_only(indexed_vector<L> & y, const lp_settings&, vector<unsigned> & sorted_active_rows );
#ifdef LEAN_DEBUG
T get_elem(unsigned i, unsigned j) const { return get(i, j); }
unsigned get_number_of_rows() const { return dimension(); }
unsigned get_number_of_columns() const { return dimension(); }
virtual void set_number_of_rows(unsigned /*m*/) { }
virtual void set_number_of_columns(unsigned /*n*/) { }
#endif
template <typename L>
L dot_product_with_row (unsigned row, const vector<L> & y) const;
template <typename L>
L dot_product_with_row (unsigned row, const indexed_vector<L> & y) const;
unsigned get_number_of_nonzeroes() const;
bool get_non_zero_column_in_row(unsigned i, unsigned *j) const;
void remove_element_that_is_not_in_w(vector<indexed_value<T>> & column_vals, indexed_value<T> & col_el_iv);
// w contains the new column
// the old column inside of the matrix has not been changed yet
void remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector<T> & w);
void add_new_element(unsigned row, unsigned col, const T& val);
// w contains the "rest" of the new column; all common elements of w and the old column has been zeroed
// the old column inside of the matrix has not been changed yet
void add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector<T> & w, lp_settings & settings);
void replace_column(unsigned column_to_replace, indexed_vector<T> & w, lp_settings &settings);
unsigned pivot_score(unsigned i, unsigned j);
void enqueue_domain_into_pivot_queue();
void set_max_in_rows();
void zero_shortened_markovitz_numbers();
void prepare_for_factorization();
void recover_pivot_queue(vector<upair> & rejected_pivots);
int elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting);
bool remove_row_from_active_pivots_and_shorten_columns(unsigned row);
void remove_pivot_column(unsigned row);
void update_active_pivots(unsigned row);
bool shorten_active_matrix(unsigned row, eta_matrix<T, X> *eta_matrix);
unsigned pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k);
#ifdef LEAN_DEBUG
bool can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k);
bool really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k);
void print_active_matrix(unsigned k, std::ostream & out);
#endif
bool pivot_queue_is_correct_for_row(unsigned i, unsigned k);
bool pivot_queue_is_correct_after_pivoting(int k);
bool get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k);
bool elem_is_too_small(vector<indexed_value<T>> & row_chunk, indexed_value<T> & iv, int c_partial_pivoting);
unsigned number_of_non_zeroes_in_row(unsigned row) const {
return static_cast<unsigned>(m_rows[row].size());
}
unsigned number_of_non_zeroes_in_column(unsigned col) const {
return m_columns[col].m_values.size();
}
bool shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column);
bool col_is_active(unsigned j, unsigned pivot) {
return adjust_column_inverse(j) > pivot;
}
bool row_is_active(unsigned i, unsigned pivot) {
return adjust_row_inverse(i) > pivot;
}
bool fill_eta_matrix(unsigned j, eta_matrix<T, X> ** eta);
#ifdef LEAN_DEBUG
bool is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const;
bool is_upper_triangular_until(unsigned k) const;
void check_column_vs_rows(unsigned col);
void check_row_vs_columns(unsigned row);
void check_rows_vs_columns();
void check_columns_vs_rows();
void check_matrix();
#endif
void create_graph_G(const vector<unsigned> & active_rows, vector<unsigned> & sorted_active_rows);
void process_column_recursively(unsigned i, vector<unsigned> & sorted_rows);
void extend_and_sort_active_rows(const vector<unsigned> & active_rows, vector<unsigned> & sorted_active_rows);
void process_index_recursively_for_y_U(unsigned j, vector<unsigned> & sorted_rows);
void resize(unsigned new_dim) {
unsigned old_dim = dimension();
lean_assert(new_dim >= old_dim);
for (unsigned j = old_dim; j < new_dim; j++) {
m_rows.push_back(vector<indexed_value<T>>());
m_columns.push_back(col_header());
}
m_pivot_queue.resize(new_dim);
m_row_permutation.resize(new_dim);
m_column_permutation.resize(new_dim);
m_work_pivot_vector.resize(new_dim);
m_processed.resize(new_dim);
for (unsigned j = old_dim; j < new_dim; j++) {
add_new_element(j, j, numeric_traits<T>::one());
}
}
#ifdef LEAN_DEBUG
vector<T> get_full_row(unsigned i) const;
#endif
unsigned pivot_queue_size() const { return m_pivot_queue.size(); }
};
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,101 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/vector.h"
#include <memory>
#include "util/lp/lp_settings.h"
#include "util/lp/lu.h"
#include "util/lp/sparse_matrix.hpp"
#include "util/lp/dense_matrix.h"
namespace lean {
template double sparse_matrix<double, double>::dot_product_with_row<double>(unsigned int, vector<double> const&) const;
template void sparse_matrix<double, double>::add_new_element(unsigned int, unsigned int, const double&);
template void sparse_matrix<double, double>::divide_row_by_constant(unsigned int, const double&, lp_settings&);
template bool sparse_matrix<double, double>::fill_eta_matrix(unsigned int, eta_matrix<double, double>**);
template const double & sparse_matrix<double, double>::get(unsigned int, unsigned int) const;
template unsigned sparse_matrix<double, double>::get_number_of_nonzeroes() const;
template bool sparse_matrix<double, double>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int);
template unsigned sparse_matrix<double, double>::lowest_row_in_column(unsigned int);
template bool sparse_matrix<double, double>::pivot_row_to_row(unsigned int, const double&, unsigned int, lp_settings&);
template bool sparse_matrix<double, double>::pivot_with_eta(unsigned int, eta_matrix<double, double>*, lp_settings&);
template void sparse_matrix<double, double>::prepare_for_factorization();
template void sparse_matrix<double, double>::remove_element(vector<indexed_value<double> >&, indexed_value<double>&);
template void sparse_matrix<double, double>::replace_column(unsigned int, indexed_vector<double>&, lp_settings&);
template void sparse_matrix<double, double>::set(unsigned int, unsigned int, double);
template void sparse_matrix<double, double>::set_max_in_row(vector<indexed_value<double> >&);
template bool sparse_matrix<double, double>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector<double>&, lp_settings&);
template bool sparse_matrix<double, double>::shorten_active_matrix(unsigned int, eta_matrix<double, double>*);
template void sparse_matrix<double, double>::solve_y_U(vector<double>&) const;
template sparse_matrix<double, double>::sparse_matrix(static_matrix<double, double> const&, vector<unsigned int>&);
template sparse_matrix<double, double>::sparse_matrix(unsigned int);
template void sparse_matrix<mpq, mpq>::add_new_element(unsigned int, unsigned int, const mpq&);
template void sparse_matrix<mpq, mpq>::divide_row_by_constant(unsigned int, const mpq&, lp_settings&);
template bool sparse_matrix<mpq, mpq>::fill_eta_matrix(unsigned int, eta_matrix<mpq, mpq>**);
template mpq const & sparse_matrix<mpq, mpq>::get(unsigned int, unsigned int) const;
template unsigned sparse_matrix<mpq, mpq>::get_number_of_nonzeroes() const;
template bool sparse_matrix<mpq, mpq>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int);
template unsigned sparse_matrix<mpq, mpq>::lowest_row_in_column(unsigned int);
template bool sparse_matrix<mpq, mpq>::pivot_with_eta(unsigned int, eta_matrix<mpq, mpq>*, lp_settings&);
template void sparse_matrix<mpq, mpq>::prepare_for_factorization();
template void sparse_matrix<mpq, mpq>::remove_element(vector<indexed_value<mpq>> &, indexed_value<mpq>&);
template void sparse_matrix<mpq, mpq>::replace_column(unsigned int, indexed_vector<mpq>&, lp_settings&);
template void sparse_matrix<mpq, mpq>::set_max_in_row(vector<indexed_value<mpq>>&);
template bool sparse_matrix<mpq, mpq>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector<mpq>&, lp_settings&);
template bool sparse_matrix<mpq, mpq>::shorten_active_matrix(unsigned int, eta_matrix<mpq, mpq>*);
template void sparse_matrix<mpq, mpq>::solve_y_U(vector<mpq>&) const;
template sparse_matrix<mpq, mpq>::sparse_matrix(static_matrix<mpq, mpq> const&, vector<unsigned int>&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::add_new_element(unsigned int, unsigned int, const mpq&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::divide_row_by_constant(unsigned int, const mpq&, lp_settings&);
template bool sparse_matrix<mpq, numeric_pair<mpq>>::fill_eta_matrix(unsigned int, eta_matrix<mpq, numeric_pair<mpq> >**);
template const mpq & sparse_matrix<mpq, numeric_pair<mpq>>::get(unsigned int, unsigned int) const;
template unsigned sparse_matrix<mpq, numeric_pair<mpq>>::get_number_of_nonzeroes() const;
template bool sparse_matrix<mpq, numeric_pair<mpq>>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int);
template unsigned sparse_matrix<mpq, numeric_pair<mpq>>::lowest_row_in_column(unsigned int);
template bool sparse_matrix<mpq, numeric_pair<mpq>>::pivot_with_eta(unsigned int, eta_matrix<mpq, numeric_pair<mpq> >*, lp_settings&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::prepare_for_factorization();
template void sparse_matrix<mpq, numeric_pair<mpq>>::remove_element(vector<indexed_value<mpq>>&, indexed_value<mpq>&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::replace_column(unsigned int, indexed_vector<mpq>&, lp_settings&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::set_max_in_row(vector<indexed_value<mpq>>&);
template bool sparse_matrix<mpq, numeric_pair<mpq>>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector<mpq>&, lp_settings&);
template bool sparse_matrix<mpq, numeric_pair<mpq>>::shorten_active_matrix(unsigned int, eta_matrix<mpq, numeric_pair<mpq> >*);
template void sparse_matrix<mpq, numeric_pair<mpq>>::solve_y_U(vector<mpq>&) const;
template sparse_matrix<mpq, numeric_pair<mpq>>::sparse_matrix(static_matrix<mpq, numeric_pair<mpq> > const&, vector<unsigned int>&);
template void sparse_matrix<double, double>::double_solve_U_y<double>(indexed_vector<double>&, const lp_settings &);
template void sparse_matrix<mpq, mpq>::double_solve_U_y<mpq>(indexed_vector<mpq>&, const lp_settings&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::double_solve_U_y<mpq>(indexed_vector<mpq>&, const lp_settings&);
template void sparse_matrix<mpq, numeric_pair<mpq> >::double_solve_U_y<numeric_pair<mpq> >(indexed_vector<numeric_pair<mpq>>&, const lp_settings&);
template void lean::sparse_matrix<double, double>::solve_U_y_indexed_only<double>(lean::indexed_vector<double>&, const lp_settings&, vector<unsigned> &);
template void lean::sparse_matrix<lean::mpq, lean::mpq>::solve_U_y_indexed_only<lean::mpq>(lean::indexed_vector<lean::mpq>&, const lp_settings &, vector<unsigned> &);
#ifdef LEAN_DEBUG
template bool sparse_matrix<double, double>::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const;
template bool sparse_matrix<mpq, mpq>::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const;
template bool sparse_matrix<mpq, numeric_pair<mpq> >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const;
#endif
}
template void lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_U_y_indexed_only<lean::mpq>(lean::indexed_vector<lean::mpq>&, const lp_settings &, vector<unsigned> &);
template void lean::sparse_matrix<lean::mpq, lean::mpq>::solve_U_y<lean::mpq>(vector<lean::mpq>&);
template void lean::sparse_matrix<lean::mpq, lean::mpq>::double_solve_U_y<lean::mpq>(vector<lean::mpq >&);
template void lean::sparse_matrix<double, double>::solve_U_y<double>(vector<double>&);
template void lean::sparse_matrix<double, double>::double_solve_U_y<double>(vector<double>&);
template void lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_U_y<lean::numeric_pair<lean::mpq> >(vector<lean::numeric_pair<lean::mpq> >&);
template void lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::double_solve_U_y<lean::numeric_pair<lean::mpq> >(vector<lean::numeric_pair<lean::mpq> >&);
template void lean::sparse_matrix<double, double>::find_error_in_solution_U_y_indexed<double>(lean::indexed_vector<double>&, lean::indexed_vector<double>&, const vector<unsigned> &);
template double lean::sparse_matrix<double, double>::dot_product_with_row<double>(unsigned int, lean::indexed_vector<double> const&) const;
template void lean::sparse_matrix<lean::mpq, lean::mpq>::find_error_in_solution_U_y_indexed<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::indexed_vector<lean::mpq>&, const vector<unsigned> &);
template lean::mpq lean::sparse_matrix<lean::mpq, lean::mpq>::dot_product_with_row<lean::mpq>(unsigned int, lean::indexed_vector<lean::mpq> const&) const;
template void lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::find_error_in_solution_U_y_indexed<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::indexed_vector<lean::mpq>&, const vector<unsigned> &);
template lean::mpq lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::dot_product_with_row<lean::mpq>(unsigned int, lean::indexed_vector<lean::mpq> const&) const;
template void lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::find_error_in_solution_U_y_indexed<lean::numeric_pair<lean::mpq> >(lean::indexed_vector<lean::numeric_pair<lean::mpq> >&, lean::indexed_vector<lean::numeric_pair<lean::mpq> >&, const vector<unsigned> &);
template lean::numeric_pair<lean::mpq> lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::dot_product_with_row<lean::numeric_pair<lean::mpq> >(unsigned int, lean::indexed_vector<lean::numeric_pair<lean::mpq> > const&) const;
template void lean::sparse_matrix<lean::mpq, lean::mpq>::extend_and_sort_active_rows(vector<unsigned int> const&, vector<unsigned int>&);
template void lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::extend_and_sort_active_rows(vector<unsigned int> const&, vector<unsigned int>&);
template void lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_U_y<lean::mpq>(vector<lean::mpq >&);
template void lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::double_solve_U_y<lean::mpq>(vector<lean::mpq >&);
template void lean::sparse_matrix< lean::mpq,lean::numeric_pair< lean::mpq> >::set(unsigned int,unsigned int, lean::mpq);
template void lean::sparse_matrix<double, double>::solve_y_U_indexed(lean::indexed_vector<double>&, const lp_settings & );
template void lean::sparse_matrix<lean::mpq, lean::mpq>::solve_y_U_indexed(lean::indexed_vector<lean::mpq>&, const lp_settings &);
template void lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_y_U_indexed(lean::indexed_vector<lean::mpq>&, const lp_settings &);

View file

@ -0,0 +1,38 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/vector.h"
#include <utility>
#include "util/debug.h"
#include "util/lp/lp_utils.h"
#include "util/lp/lp_settings.h"
namespace lean {
template <typename T>
class sparse_vector {
public:
vector<std::pair<unsigned, T>> m_data;
void push_back(unsigned index, T val) {
m_data.push_back(std::make_pair(index, val));
}
#ifdef LEAN_DEBUG
T operator[] (unsigned i) const {
for (auto t : m_data) {
if (t.first == i) return t.second;
}
return numeric_traits<T>::zero();
}
#endif
void divide(T const & a) {
lean_assert(!lp_settings::is_eps_small_general(a, 1e-12));
for (auto & t : m_data) { t.second /= a; }
}
unsigned size() const {
return m_data.size();
}
};
}

Some files were not shown because too many files have changed in this diff Show more