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

mv util/lp to math/lp

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
This commit is contained in:
Lev Nachmanson 2019-06-03 16:46:19 -07:00
parent b6513b8e2d
commit 33cbd29ed0
150 changed files with 524 additions and 479 deletions

View file

@ -1,51 +0,0 @@
z3_add_component(lp
SOURCES
binary_heap_priority_queue.cpp
binary_heap_upair_queue.cpp
lp_bound_propagator.cpp
core_solver_pretty_printer.cpp
dense_matrix.cpp
eta_matrix.cpp
emonomials.cpp
factorization.cpp
factorization_factory_imp.cpp
gomory.cpp
indexed_vector.cpp
int_solver.cpp
lar_solver.cpp
lar_core_solver.cpp
lp_core_solver_base.cpp
lp_dual_core_solver.cpp
lp_dual_simplex.cpp
lp_primal_core_solver.cpp
lp_primal_simplex.cpp
lp_settings.cpp
lp_solver.cpp
lu.cpp
lp_utils.cpp
matrix.cpp
mon_eq.cpp
nla_basics_lemmas.cpp
nla_common.cpp
nla_core.cpp
nla_monotone_lemmas.cpp
nla_order_lemmas.cpp
nla_solver.cpp
nla_tangent_lemmas.cpp
nra_solver.cpp
permutation_matrix.cpp
random_updater.cpp
row_eta_matrix.cpp
scaler.cpp
square_dense_submatrix.cpp
square_sparse_matrix.cpp
static_matrix.cpp
COMPONENT_DEPENDENCIES
util
polynomial
nlsat
PYG_FILES
lp_params.pyg
)
include_directories(${src_SOURCE_DIR})

View file

@ -1,76 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/binary_heap_priority_queue.h"
namespace lp {
class active_set {
std::unordered_set<constraint*, constraint_hash, constraint_equal> m_cs;
binary_heap_priority_queue<int> m_q;
std::unordered_map<unsigned, constraint *> m_id_to_constraint;
public:
std::unordered_set<constraint*, constraint_hash, constraint_equal> cs() const { return m_cs;}
bool contains(const constraint* c) const {
return m_id_to_constraint.find(c->id()) != m_id_to_constraint.end();
}
bool is_empty() const { return m_cs.size() == 0; }
// low priority will be dequeued first
void add_constraint(constraint* c, int priority) {
if (contains(c))
return;
m_cs.insert(c);
m_id_to_constraint[c->id()] = c;
m_q.enqueue(c->id(), priority);
}
void clear() {
m_cs.clear();
m_id_to_constraint.clear();
m_q.clear();
}
constraint* remove_constraint() {
if (m_cs.size() == 0)
return nullptr;
unsigned id = m_q.dequeue();
auto it = m_id_to_constraint.find(id);
lp_assert(it != m_id_to_constraint.end());
constraint* c = it->second;
m_cs.erase(c);
m_id_to_constraint.erase(it);
return c;
}
unsigned size() const {
return static_cast<unsigned>(m_cs.size());
}
void remove_constraint(constraint * c) {
if (! contains(c)) return;
m_cs.erase(c);
m_id_to_constraint.erase(c->id());
m_q.remove(c->id());
}
};
}

View file

@ -1,41 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/numeric_pair.h"
#include "util/lp/binary_heap_priority_queue_def.h"
namespace lp {
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 lp::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 lp::binary_heap_priority_queue<mpq>::resize(unsigned int);
}

View file

@ -1,83 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
#include "util/debug.h"
#include "util/lp/lp_utils.h"
namespace lp {
// 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;
// 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 Z3DEBUG
bool is_consistent() const;
#endif
public:
void remove(unsigned o);
unsigned size() const { return m_heap_size; }
binary_heap_priority_queue(): m_heap(1), m_heap_size(0) {} // the empty constructror
// n is the initial queue capacity.
// The capacity will be enlarged each time twice 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 {
lp_assert(m_heap_size > 0);
return m_heap[1];
}
void print(std::ostream & out);
};
}

View file

@ -1,212 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/vector.h"
#include "util/lp/binary_heap_priority_queue.h"
namespace lp {
// "i" is the child's place in the 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 Z3DEBUG
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];
lp_assert(i_index <= static_cast<int>(m_heap_size));
lp_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
}
lp_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 propagate 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 {
lp_assert(static_cast<unsigned>(o_in_heap) == m_heap_size);
m_heap_size--;
}
m_heap_inverse[o] = -1;
// lp_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),
m_heap_size(0)
{ }
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;
lp_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()) {
if (o == 0)
resize(2);
else
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) {
lp_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() {
lp_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;
}
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]);
}
}

View file

@ -1,32 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/binary_heap_upair_queue_def.h"
namespace lp {
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

@ -1,65 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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 lp {
template <typename T>
class binary_heap_upair_queue {
binary_heap_priority_queue<T> m_q;
std::unordered_map<upair, unsigned> m_pairs_to_index;
svector<upair> m_pairs; // inverse to index
svector<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 Z3DEBUG
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

@ -1,125 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <set>
#include "util/lp/lp_utils.h"
#include "util/lp/binary_heap_upair_queue.h"
namespace lp {
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() {
lp_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();
// lp_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) {
lp_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 Z3DEBUG
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

@ -1,349 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
#include "implied_bound.h"
#include "test_bound_analyzer.h"
#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 lp {
template <typename C> // C plays a role of a container
class bound_analyzer_on_row {
const C& m_row;
bound_propagator & m_bp;
unsigned m_row_or_term_index;
int m_column_of_u; // 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; // index of an unlimited from below monoid
impq m_rs;
public :
// constructor
bound_analyzer_on_row(
const C & it,
unsigned bj, // basis column for the row
const numeric_pair<mpq>& rs,
unsigned row_or_term_index,
bound_propagator & bp
)
:
m_row(it),
m_bp(bp),
m_row_or_term_index(row_or_term_index),
m_column_of_u(-1),
m_column_of_l(-1),
m_rs(rs)
{}
unsigned j;
void analyze() {
for (const auto & c : m_row) {
if ((m_column_of_l == -2) && (m_column_of_u == -2))
break;
analyze_bound_on_var_on_coeff(c.var(), c.coeff());
}
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 lower_bound) {
return (lower_bound && lower_bound_is_available(j)) ||
(!lower_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 lower_bound_is_available(unsigned j) const {
switch (m_bp.get_column_type(j))
{
case column_type::fixed:
case column_type::boxed:
case column_type::lower_bound:
return true;
default:
return false;
}
}
const impq & ub(unsigned j) const {
lp_assert(upper_bound_is_available(j));
return m_bp.get_upper_bound(j);
}
const impq & lb(unsigned j) const {
lp_assert(lower_bound_is_available(j));
return m_bp.get_lower_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;
lp_assert(is_zero(total));
for (const auto& p : m_row) {
bool str;
total -= monoid_min(p.coeff(), p.var(), str);
if (str)
strict++;
}
mpq bound;
for (const auto &p : m_row) {
bool str;
bool a_is_pos = is_pos(p.coeff());
bound = total;
bound /= p.coeff();
bound += monoid_min_no_mult(a_is_pos, p.var(), str);
if (a_is_pos) {
limit_j(p.var(), bound, true, false, strict - static_cast<int>(str) > 0);
}
else {
limit_j(p.var(), bound, false, true, strict - static_cast<int>(str) > 0);
}
}
}
void limit_all_monoids_from_below() {
int strict = 0;
mpq total;
lp_assert(is_zero(total));
for (const auto &p : m_row) {
bool str;
total -= monoid_max(p.coeff(), p.var(), str);
if (str)
strict++;
}
for (const auto& p : m_row) {
bool str;
bool a_is_pos = is_pos(p.coeff());
mpq bound = total / p.coeff() + monoid_max_no_mult(a_is_pos, p.var(), str);
bool astrict = strict - static_cast<int>(str) > 0;
if (a_is_pos) {
limit_j(p.var(), bound, true, true, astrict);
}
else {
limit_j(p.var(), 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;
unsigned j;
mpq bound = -m_rs.x;
bool strict = false;
for (const auto& p : m_row) {
j = p.var();
if (j == static_cast<unsigned>(m_column_of_u)) {
u_coeff = p.coeff();
continue;
}
bool str;
bound -= monoid_max(p.coeff(), j, str);
if (str)
strict = true;
}
bound /= u_coeff;
if (u_coeff.is_pos()) {
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;
unsigned j;
mpq bound = -m_rs.x;
bool strict = false;
for (const auto &p : m_row) {
j = p.var();
if (j == static_cast<unsigned>(m_column_of_l)) {
l_coeff = p.coeff();
continue;
}
bool str;
bound -= monoid_min(p.coeff(), 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 coefficient before the bounded column
// void provide_evidence(bool coeff_is_pos) {
// /*
// auto & be = m_ibounds.back();
// bool lower_bound = be.m_lower_bound;
// if (!coeff_is_pos)
// lower_bound = !lower_bound;
// auto it = m_row.clone();
// mpq a; unsigned j;
// while (it->next(a, j)) {
// if (be.m_j == j) continue;
// lp_assert(bound_is_available(j, is_neg(a) ? lower_bound : !lower_bound));
// be.m_vector_of_bound_signatures.emplace_back(a, j, numeric_traits<impq>::
// is_neg(a)? lower_bound: !lower_bound);
// }
// delete it;
// */
// }
void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){
m_bp.try_add_bound(u, j, is_lower_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::lower_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(const C & row,
unsigned bj, // basis column for the row
const numeric_pair<mpq>& rs,
unsigned row_or_term_index,
bound_propagator & bp
) {
bound_analyzer_on_row a(row, bj, rs, row_or_term_index, bp);
a.analyze();
}
};
}

View file

@ -1,27 +0,0 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/lp_settings.h"
namespace lp {
class lar_solver;
class bound_propagator {
std::unordered_map<unsigned, unsigned> m_improved_lower_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_lower_bound(unsigned) const;
const impq & get_upper_bound(unsigned) const;
void try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict);
virtual bool bound_is_interesting(unsigned vi,
lp::lconstraint_kind kind,
const rational & bval) {return true;}
unsigned number_of_found_bounds() const { return m_ibounds.size(); }
virtual void consume(mpq const& v, lp::constraint_index j) = 0;
};
}

View file

@ -1,35 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
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) {}
};
}

View file

@ -1,255 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
#include <unordered_map>
#include <string>
#include <algorithm>
#include "util/lp/lp_settings.h"
namespace lp {
inline bool is_valid(unsigned j) { return static_cast<int>(j) >= 0;}
template <typename T>
class column_info {
std::string m_name;
bool m_lower_bound_is_set;
bool m_lower_bound_is_strict;
bool m_upper_bound_is_set;
bool m_upper_bound_is_strict;
T m_lower_bound;
T m_upper_bound;
T m_fixed_value;
bool m_is_fixed;
T m_cost;
unsigned m_column_index;
public:
bool operator==(const column_info & c) const {
return m_name == c.m_name &&
m_lower_bound_is_set == c.m_lower_bound_is_set &&
m_lower_bound_is_strict == c.m_lower_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_lower_bound_is_set || m_lower_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():
m_lower_bound_is_set(false),
m_lower_bound_is_strict(false),
m_upper_bound_is_set (false),
m_upper_bound_is_strict (false),
m_is_fixed(false),
m_cost(numeric_traits<T>::zero()),
m_column_index(static_cast<unsigned>(-1))
{}
column_info(const column_info & ci) {
m_name = ci.m_name;
m_lower_bound_is_set = ci.m_lower_bound_is_set;
m_lower_bound_is_strict = ci.m_lower_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_lower_bound = ci.m_lower_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_lower_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::lower_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_lower_bound_is_set) {
return m_upper_bound_is_set? column_type::boxed: column_type::lower_bound;
}
// we are flipping the bounds!
return m_upper_bound_is_set? column_type::upper_bound
: column_type::free_column;
}
T get_lower_bound() const {
lp_assert(m_lower_bound_is_set);
return m_lower_bound;
}
T get_upper_bound() const {
lp_assert(m_upper_bound_is_set);
return m_upper_bound;
}
bool lower_bound_is_set() const {
return m_lower_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_lower_bound_is_set? m_lower_bound : numeric_traits<T>::zero();
}
bool is_flipped() {
return m_upper_bound_is_set && !m_lower_bound_is_set;
}
bool adjusted_lower_bound_is_set() {
return !is_flipped()? lower_bound_is_set(): upper_bound_is_set();
}
bool adjusted_upper_bound_is_set() {
return !is_flipped()? upper_bound_is_set(): lower_bound_is_set();
}
T get_adjusted_upper_bound() {
return get_upper_bound() - get_lower_bound();
}
bool is_fixed() const {
return m_is_fixed;
}
bool is_free() {
return !m_lower_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 {
lp_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_lower_bound(T const & l) {
m_lower_bound = l;
m_lower_bound_is_set = true;
}
void set_upper_bound(T const & l) {
m_upper_bound = l;
m_upper_bound_is_set = true;
}
void unset_lower_bound() {
m_lower_bound_is_set = false;
}
void unset_upper_bound() {
m_upper_bound_is_set = false;
}
void unset_fixed() {
m_is_fixed = false;
}
bool lower_bound_holds(T v) {
return !lower_bound_is_set() || v >= m_lower_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 lower_bound_holds(v) && upper_bound_holds(v);
}
bool adjusted_bounds_hold(T v) {
return adjusted_lower_bound_holds(v) && adjusted_upper_bound_holds(v);
}
bool adjusted_lower_bound_holds(T v) {
return !adjusted_lower_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()) || (!lower_bound_is_set()))
return false;
// ok, both bounds are set
bool at_least_one_is_strict = upper_bound_is_strict() || lower_bound_is_strict();
if (!at_least_one_is_strict)
return get_upper_bound() < get_lower_bound();
// at least on bound is strict
return get_upper_bound() <= get_lower_bound(); // the equality is impossible
}
bool lower_bound_is_strict() const {
return m_lower_bound_is_strict;
}
void set_lower_bound_strict(bool val) {
m_lower_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

@ -1,61 +0,0 @@
#pragma once
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <string>
#include "util/lp/static_matrix.h"
namespace lp {
class column_namer {
public:
virtual std::string get_variable_name(unsigned j) const = 0;
template <typename T>
void print_row(const row_strip<T> & row, std::ostream & out) const {
vector<std::pair<T, unsigned>> coeff;
for (auto & p : row) {
coeff.push_back(std::make_pair(p.coeff(), p.var()));
}
print_linear_combination_of_column_indices(coeff, out);
}
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_variable_name(it.second);
}
}
};
}

View file

@ -1,99 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
class constraint; // forward definition
struct constraint_hash {
size_t operator() (const constraint* c) const;
};
struct constraint_equal {
bool operator() (const constraint * a, const constraint * b) const;
};
class constraint { // we only have less or equal for the inequality sign, which is enough for integral variables
int m_id;
bool m_is_ineq;
polynomial m_poly;
mpq m_d; // the divider for the case of a divisibility constraint
std::unordered_set<constraint_index> m_assert_origins; // these indices come from the client and get collected during tightening
public :
unsigned id() const { return m_id; }
const polynomial & poly() const { return m_poly; }
polynomial & poly() { return m_poly; }
std::unordered_set<constraint_index> & assert_origins() { return m_assert_origins;}
const std::unordered_set<constraint_index> & assert_origins() const { return m_assert_origins;}
bool is_lemma() const { return !is_assert(); }
bool is_assert() const { return m_assert_origins.size() == 1; }
bool is_ineq() const { return m_is_ineq; }
const mpq & divider() const { return m_d; }
public:
constraint(
unsigned id,
constraint_index assert_origin,
const polynomial & p,
bool is_ineq):
m_id(id),
m_is_ineq(is_ineq),
m_poly(p)
{ // creates an assert
m_assert_origins.insert(assert_origin);
}
constraint(
unsigned id,
const std::unordered_set<constraint_index>& origins,
const polynomial & p,
bool is_ineq):
m_id(id),
m_is_ineq(is_ineq),
m_poly(p),
m_assert_origins(origins)
{}
constraint(
unsigned id,
const polynomial & p,
bool is_ineq):
m_id(id),
m_is_ineq(is_ineq),
m_poly(p) { // creates a lemma
}
public:
constraint() {}
const mpq & coeff(var_index j) const {
return m_poly.coeff(j);
}
const vector<monomial>& coeffs() const { return m_poly.m_coeffs;}
bool is_tight(unsigned j) const {
const mpq & a = m_poly.coeff(j);
return a == 1 || a == -1;
}
void add_predecessor(const constraint* p) {
lp_assert(p != nullptr);
for (auto m : p->assert_origins())
m_assert_origins.insert(m); }
};
}

View file

@ -1,58 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
template <typename V>
struct conversion_helper {
static V get_lower_bound(const column_info<mpq> & ci) {
return V(ci.get_lower_bound(), ci.lower_bound_is_strict()? 1 : 0);
}
static V get_upper_bound(const column_info<mpq> & ci) {
return V(ci.get_upper_bound(), ci.upper_bound_is_strict()? -1 : 0);
}
};
template<>
struct conversion_helper <double> {
static double get_upper_bound(const column_info<mpq> & ci) {
if (!ci.upper_bound_is_strict())
return ci.get_upper_bound().get_double();
double eps = 0.00001;
if (!ci.lower_bound_is_set())
return ci.get_upper_bound().get_double() - eps;
eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps);
return ci.get_upper_bound().get_double() - eps;
}
static double get_lower_bound(const column_info<mpq> & ci) {
if (!ci.lower_bound_is_strict())
return ci.get_lower_bound().get_double();
double eps = 0.00001;
if (!ci.upper_bound_is_set())
return ci.get_lower_bound().get_double() + eps;
eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps);
return ci.get_lower_bound().get_double() + eps;
}
};
}

View file

@ -1,30 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/numeric_pair.h"
#include "util/lp/core_solver_pretty_printer_def.h"
template lp::core_solver_pretty_printer<double, double>::core_solver_pretty_printer(lp::lp_core_solver_base<double, double> &, std::ostream & out);
template void lp::core_solver_pretty_printer<double, double>::print();
template lp::core_solver_pretty_printer<double, double>::~core_solver_pretty_printer();
template lp::core_solver_pretty_printer<lp::mpq, lp::mpq>::core_solver_pretty_printer(lp::lp_core_solver_base<lp::mpq, lp::mpq> &, std::ostream & out);
template void lp::core_solver_pretty_printer<lp::mpq, lp::mpq>::print();
template lp::core_solver_pretty_printer<lp::mpq, lp::mpq>::~core_solver_pretty_printer();
template lp::core_solver_pretty_printer<lp::mpq, lp::numeric_pair<lp::mpq> >::core_solver_pretty_printer(lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> > &, std::ostream & out);
template lp::core_solver_pretty_printer<lp::mpq, lp::numeric_pair<lp::mpq> >::~core_solver_pretty_printer();
template void lp::core_solver_pretty_printer<lp::mpq, lp::numeric_pair<lp::mpq> >::print();

View file

@ -1,132 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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 lp {
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;
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_lower_bounds_title;
std::string m_upp_bounds_title;
std::string m_exact_norm_title;
std::string m_approx_norm_title;
unsigned ncols() { return m_core_solver.m_A.column_count(); }
unsigned nrows() { return m_core_solver.m_A.row_count(); }
unsigned m_artificial_start;
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_lower_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, const 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_lower_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

@ -1,400 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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 lp {
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_lower_bounds_title = "low";
m_upp_bounds_title = "upp";
m_exact_norm_title = "exact cn";
m_approx_norm_title = "approx cn";
m_artificial_start = std::numeric_limits<unsigned>::max();
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.var()] = m_core_solver.m_A.get_val(c);
}
string name = m_core_solver.column_name(column);
for (unsigned row = 0; row < nrows(); row ++) {
m_A[row].resize(ncols(), "");
m_signs[row].resize(ncols(),"");
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_lower_bound(unsigned column, unsigned & w) {
if (!m_core_solver.lower_bounds_are_set()) return;
w = std::max(w, (unsigned)T_to_string(m_core_solver.lower_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_lower_bound(column, w);
adjust_width_with_upper_bound(column, w);
break;
case column_type::lower_bound:
adjust_width_with_lower_bound(column, w);
break;
case column_type::upper_bound:
adjust_width_with_upper_bound(column, w);
break;
case column_type::free_column:
break;
default:
lp_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.empty())
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_lower_bound_string(unsigned j) {
switch (m_core_solver.get_column_type(j)){
case column_type::boxed:
case column_type::lower_bound:
case column_type::fixed:
if (m_core_solver.lower_bounds_are_set())
return T_to_string(m_core_solver.lower_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_lower_bounds_title.size());
m_out << m_lower_bounds_title;
print_blanks(blanks, m_out);
for (unsigned i = 0; i < ncols(); i++) {
string s = get_lower_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.empty())
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());
lp_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());
lp_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

@ -1,40 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/lp_settings.h"
#include "util/lp/dense_matrix_def.h"
#ifdef Z3DEBUG
#include "util/vector.h"
template lp::dense_matrix<double, double> lp::operator*<double, double>(lp::matrix<double, double>&, lp::matrix<double, double>&);
template void lp::dense_matrix<double, double>::apply_from_left(vector<double> &);
template lp::dense_matrix<double, double>::dense_matrix(lp::matrix<double, double> const*);
template lp::dense_matrix<double, double>::dense_matrix(unsigned int, unsigned int);
template lp::dense_matrix<double, double>& lp::dense_matrix<double, double>::operator=(lp::dense_matrix<double, double> const&);
template lp::dense_matrix<lp::mpq, lp::mpq>::dense_matrix(unsigned int, unsigned int);
template lp::dense_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::dense_matrix(lp::matrix<lp::mpq, lp::numeric_pair<lp::mpq> > const*);
template void lp::dense_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::apply_from_left(vector<lp::mpq>&);
template lp::dense_matrix<lp::mpq, lp::mpq> lp::operator*<lp::mpq, lp::mpq>(lp::matrix<lp::mpq, lp::mpq>&, lp::matrix<lp::mpq, lp::mpq>&);
template lp::dense_matrix<lp::mpq, lp::mpq> & lp::dense_matrix<lp::mpq, lp::mpq>::operator=(lp::dense_matrix<lp::mpq, lp::mpq> const&);
template lp::dense_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::dense_matrix(unsigned int, unsigned int);
template lp::dense_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >& lp::dense_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::operator=(lp::dense_matrix<lp::mpq, lp::numeric_pair<lp::mpq> > const&);
template lp::dense_matrix<lp::mpq, lp::numeric_pair<lp::mpq> > lp::operator*<lp::mpq, lp::numeric_pair<lp::mpq> >(lp::matrix<lp::mpq, lp::numeric_pair<lp::mpq> >&, lp::matrix<lp::mpq, lp::numeric_pair<lp::mpq> >&);
template void lp::dense_matrix<lp::mpq, lp::numeric_pair< lp::mpq> >::apply_from_right( vector< lp::mpq> &);
template void lp::dense_matrix<double,double>::apply_from_right(vector<double> &);
template void lp::dense_matrix<lp::mpq, lp::mpq>::apply_from_left(vector<lp::mpq>&);
#endif

View file

@ -1,108 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#ifdef Z3DEBUG
#include "util/vector.h"
#include "util/lp/matrix.h"
namespace lp {
// 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) {
lp_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 & );
void set_number_of_rows(unsigned /*m*/) override {}
void set_number_of_columns(unsigned /*n*/) override {}
#ifdef Z3DEBUG
T get_elem(unsigned i, unsigned j) const override { return m_values[i * m_n + j]; }
#endif
unsigned row_count() const override { return m_m; }
unsigned column_count() const override { 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

@ -1,200 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/lp_settings.h"
#ifdef Z3DEBUG
#include "util/vector.h"
#include "util/lp/numeric_pair.h"
#include "util/lp/dense_matrix.h"
namespace lp {
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){
lp_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

@ -1,432 +0,0 @@
/*++
Copyright (c) 2019 Microsoft Corporation
Module Name:
emonomials.cpp
Abstract:
table that associate monomials to congruence class representatives modulo a union find structure.
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
to replace rooted_mons.h and rooted_mon, rooted_mon_tabled
--*/
#include "util/lp/emonomials.h"
#include "util/lp/nla_defs.h"
#include "util/lp/nla_core.h"
namespace nla {
void emonomials::inc_visited() const {
++m_visited;
if (m_visited == 0) {
for (auto& svt : m_monomials) {
svt.visited() = 0;
}
++m_visited;
}
}
void emonomials::push() {
m_u_f_stack.push_scope();
m_lim.push_back(m_monomials.size());
m_region.push_scope();
m_ve.push();
SASSERT(monomials_are_canonized());
}
void emonomials::pop(unsigned n) {
m_ve.pop(n);
unsigned old_sz = m_lim[m_lim.size() - n];
for (unsigned i = m_monomials.size(); i-- > old_sz; ) {
monomial & m = m_monomials[i];
remove_cg_mon(m);
m_var2index[m.var()] = UINT_MAX;
lpvar last_var = UINT_MAX;
for (lpvar v : m.vars()) {
if (v != last_var) {
remove_cell(m_use_lists[v], i);
last_var = v;
}
}
}
m_monomials.shrink(old_sz);
m_monomials.shrink(old_sz);
m_region.pop_scope(n);
m_lim.shrink(m_lim.size() - n);
SASSERT(monomials_are_canonized());
m_u_f_stack.pop_scope(n);
}
void emonomials::remove_cell(head_tail& v, unsigned mIndex) {
cell*& cur_head = v.m_head;
cell*& cur_tail = v.m_tail;
cell* old_head = cur_head->m_next;
if (old_head == cur_head) {
cur_head = nullptr;
cur_tail = nullptr;
}
else {
cur_head = old_head;
cur_tail->m_next = old_head;
}
}
void emonomials::insert_cell(head_tail& v, unsigned mIndex) {
cell*& cur_head = v.m_head;
cell*& cur_tail = v.m_tail;
cell* new_head = new (m_region) cell(mIndex, cur_head);
cur_head = new_head;
if (!cur_tail) cur_tail = new_head;
cur_tail->m_next = new_head;
}
void emonomials::merge_cells(head_tail& root, head_tail& other) {
if (&root == &other) return;
cell*& root_head = root.m_head;
cell*& root_tail = root.m_tail;
cell* other_head = other.m_head;
cell* other_tail = other.m_tail;
if (root_head == nullptr) {
root_head = other_head;
root_tail = other_tail;
}
else if (other_head) {
// other_head -> other_tail -> root_head --> root_tail -> other_head.
root_tail->m_next = other_head;
other_tail->m_next = root_head;
root_head = other_head;
}
else {
// other_head = other_tail = nullptr
}
}
void emonomials::unmerge_cells(head_tail& root, head_tail& other) {
if (&root == &other) return;
cell*& root_head = root.m_head;
cell*& root_tail = root.m_tail;
cell* other_head = other.m_head;
cell* other_tail = other.m_tail;
if (other_head == nullptr) {
// no-op
}
else if (root_tail == other_tail) {
root_head = nullptr;
root_tail = nullptr;
}
else {
root_head = other_tail->m_next;
root_tail->m_next = root_head;
other_tail->m_next = other_head;
}
}
emonomials::cell* emonomials::head(lpvar v) const {
v = m_ve.find(v).var();
m_use_lists.reserve(v + 1);
return m_use_lists[v].m_head;
}
monomial const* emonomials::find_canonical(svector<lpvar> const& vars) const {
SASSERT(m_ve.is_root(vars));
m_find_key = vars;
std::sort(m_find_key.begin(), m_find_key.end());
monomial const* result = nullptr;
lpvar w;
if (m_cg_table.find(UINT_MAX, w)) {
result = &m_monomials[m_var2index[w]];
}
return result;
}
void emonomials::remove_cg(lpvar v) {
cell* c = m_use_lists[v].m_head;
if (c == nullptr) {
return;
}
cell* first = c;
inc_visited();
do {
unsigned idx = c->m_index;
c = c->m_next;
monomial & m = m_monomials[idx];
if (!is_visited(m)) {
set_visited(m);
remove_cg_mon(m);
}
}
while (c != first);
}
void emonomials::remove_cg_mon(const monomial& m) {
lpvar u = m.var(), w;
// equivalence class of u:
if (m_cg_table.find(u, w)) {
TRACE("nla_solver", tout << "erase << " << m << "\n";);
m_cg_table.erase(u);
}
}
/**
\brief insert canonized monomials using v into a congruence table.
Prior to insertion, the monomials are canonized according to the current
variable equivalences. The canonized monomials (monomial) are considered
in the same equivalence class if they have the same set of representative
variables. Their signs may differ.
*/
void emonomials::insert_cg(lpvar v) {
cell* c = m_use_lists[v].m_head;
if (c == nullptr) {
return;
}
cell* first = c;
inc_visited();
do {
unsigned idx = c->m_index;
c = c->m_next;
monomial & m = m_monomials[idx];
if (!is_visited(m)) {
set_visited(m);
insert_cg_mon(m);
}
}
while (c != first);
}
bool emonomials::elists_are_consistent(std::unordered_map<unsigned_vector, std::unordered_set<lpvar>, hash_svector>& lists) const {
for (auto const & m : m_monomials) {
auto it = lists.find(m.rvars());
if (it == lists.end()) {
std::unordered_set<lpvar> v;
v.insert(m.var());
lists[m.rvars()] = v;
} else {
it->second.insert(m.var());
}
}
for (auto const & m : m_monomials) {
SASSERT(is_canonized(m));
if (!is_canonical_monomial(m.var()))
continue;
std::unordered_set<lpvar> c;
for (const monomial& e : enum_sign_equiv_monomials(m))
c.insert(e.var());
auto it = lists.find(m.rvars());
CTRACE("nla_solver", it->second != c,
tout << "m = " << m << "\n";
tout << "c = " ; print_vector(c, tout); tout << "\n";
if (it == lists.end()) {
tout << "m.rvars are not found\n";
}
else {
tout << "it->second = "; print_vector(it->second, tout); tout << "\n";
for (unsigned j : it->second) {
tout << (*this)[j] << "\n";
}
});
SASSERT(c == it->second);
}
return true;
}
void emonomials::insert_cg_mon(monomial & m) {
do_canonize(m);
lpvar v = m.var(), w;
if (m_cg_table.find(v, w)) {
if (v == w) {
TRACE("nla_solver", tout << "found " << v << "\n";);
return;
}
unsigned v_idx = m_var2index[v];
unsigned w_idx = m_var2index[w];
unsigned max_i = std::max(v_idx, w_idx);
while (m_u_f.get_num_vars() <= max_i)
m_u_f.mk_var();
TRACE("nla_solver", tout << "merge " << v << " idx " << v_idx << ", and " << w << " idx " << w_idx << "\n";);
m_u_f.merge(v_idx, w_idx);
}
else {
TRACE("nla_solver", tout << "insert " << m << "\n";);
m_cg_table.insert(v);
}
}
void emonomials::set_visited(monomial& m) const {
m_monomials[m_var2index[m.var()]].visited() = m_visited;
}
bool emonomials::is_visited(monomial const& m) const {
return m_visited == m_monomials[m_var2index[m.var()]].visited();
}
/**
\brief insert a new monomial.
Assume that the variables are canonical, that is, not equal in current
context to another variable. The monomial is inserted into a congruence
class of equal up-to var_eqs monomials.
*/
void emonomials::add(lpvar v, unsigned sz, lpvar const* vs) {
unsigned idx = m_monomials.size();
m_monomials.push_back(monomial(v, sz, vs, idx));
lpvar last_var = UINT_MAX;
for (unsigned i = 0; i < sz; ++i) {
lpvar w = vs[i];
SASSERT(m_ve.is_root(w));
if (w != last_var) {
m_use_lists.reserve(w + 1);
insert_cell(m_use_lists[w], idx);
last_var = w;
}
}
SASSERT(m_ve.is_root(v));
m_var2index.setx(v, idx, UINT_MAX);
insert_cg_mon(m_monomials[idx]);
}
void emonomials::do_canonize(monomial & m) const {
m.reset_rfields();
for (lpvar v : m.vars()) {
m.push_rvar(m_ve.find(v));
}
m.sort_rvars();
}
bool emonomials::is_canonized(const monomial & m) const {
monomial mm(m);
do_canonize(mm);
return mm.rvars() == m.rvars();
}
bool emonomials:: monomials_are_canonized() const {
for (auto & m: m_monomials) {
if (! is_canonized(m)) {
return false;
}
}
return true;
}
bool emonomials::canonize_divides(monomial& m, monomial & n) const {
if (m.size() > n.size()) return false;
unsigned ms = m.size(), ns = n.size();
unsigned i = 0, j = 0;
while (true) {
if (i == ms) {
return true;
}
else if (j == ns) {
return false;
}
else if (m.rvars()[i] == n.rvars()[j]) {
++i; ++j;
}
else if (m.rvars()[i] < n.rvars()[j]) {
return false;
}
else {
++j;
}
}
}
// yes, assume that monomials are non-empty.
emonomials::pf_iterator::pf_iterator(emonomials const& m, monomial & mon, bool at_end):
m_em(m), m_mon(&mon), m_it(iterator(m, m.head(mon.vars()[0]), at_end)), m_end(iterator(m, m.head(mon.vars()[0]), true)) {
fast_forward();
}
emonomials::pf_iterator::pf_iterator(emonomials const& m, lpvar v, bool at_end):
m_em(m), m_mon(nullptr), m_it(iterator(m, m.head(v), at_end)), m_end(iterator(m, m.head(v), true)) {
fast_forward();
}
void emonomials::pf_iterator::fast_forward() {
for (; m_it != m_end; ++m_it) {
if (m_mon && m_mon->var() != (*m_it).var() && m_em.canonize_divides(*m_mon, *m_it) && !m_em.is_visited(*m_it)) {
m_em.set_visited(*m_it);
break;
}
if (!m_mon && !m_em.is_visited(*m_it)) {
m_em.set_visited(*m_it);
break;
}
}
}
void emonomials::merge_eh(signed_var r2, signed_var r1, signed_var v2, signed_var v1) {
// no-op
}
void emonomials::after_merge_eh(signed_var r2, signed_var r1, signed_var v2, signed_var v1) {
TRACE("nla_solver", tout << r2 << " <- " << r1 << "\n";);
if (m_ve.find(~r1) == m_ve.find(~r2)) { // the other sign has also been merged
m_use_lists.reserve(std::max(r2.var(), r1.var()) + 1);
TRACE("nla_solver", tout << "rehasing " << r1.var() << "\n";);
merge_cells(m_use_lists[r2.var()], m_use_lists[r1.var()]);
rehash_cg(r1.var());
}
}
void emonomials::unmerge_eh(signed_var r2, signed_var r1) {
TRACE("nla_solver", tout << r2 << " -> " << r1 << "\n";);
if (m_ve.find(~r1) != m_ve.find(~r2)) { // the other sign has also been unmerged
unmerge_cells(m_use_lists[r2.var()], m_use_lists[r1.var()]);
rehash_cg(r1.var());
}
}
std::ostream& emonomials::display(const core& cr, std::ostream& out) const {
out << "monomials\n";
unsigned idx = 0;
for (auto const& m : m_monomials) {
out << "m" << (idx++) << ": " << pp_rmon(cr, m) << "\n";
}
return display_use(out);
}
std::ostream& emonomials::display(std::ostream& out) const {
out << "monomials\n";
unsigned idx = 0;
for (auto const& m : m_monomials) {
out << "m" << (idx++) << ": " << m << "\n";
}
return display_use(out);
}
std::ostream& emonomials::display_use(std::ostream& out) const {
out << "use lists\n";
unsigned idx = 0;
for (auto const& ht : m_use_lists) {
cell* c = ht.m_head;
if (c) {
out << idx << ": ";
do {
out << "m" << c->m_index << " ";
c = c->m_next;
}
while (c != ht.m_head);
out << "\n";
}
++idx;
}
return out;
}
}

View file

@ -1,342 +0,0 @@
/*++
Copyright (c) 2019 Microsoft Corporation
Module Name:
emonomials.h
Abstract:
table that associate monomials to congruence class representatives modulo a union find structure.
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
to replace rooted_mons.h and rooted_mon, rooted_mon_tabled
--*/
#pragma once
#include "util/lp/lp_utils.h"
#include "util/lp/var_eqs.h"
#include "util/lp/monomial.h"
#include "util/region.h"
namespace nla {
struct hash_svector {
size_t operator()(const unsigned_vector & v) const {
return svector_hash<unsigned_hash>()(v);
}
};
class core;
class emonomials {
/**
\brief singly-lined cyclic list of monomial indices where variable occurs.
Each variable points to the head and tail of the cyclic list.
Initially, head and tail are nullptr.
New elements are inserted in the beginning of the list.
Two lists are merged when equivalence class representatives are merged,
and the merge is undone when the representative variables are unmerged.
*/
struct cell {
cell(unsigned mIndex, cell* c): m_next(c), m_index(mIndex) {}
cell* m_next;
unsigned m_index;
};
struct head_tail {
head_tail(): m_head(nullptr), m_tail(nullptr) {}
cell* m_head;
cell* m_tail;
};
struct hash_canonical {
emonomials& em;
hash_canonical(emonomials& em): em(em) {}
unsigned operator()(lpvar v) const {
auto const& vec = v != UINT_MAX? em.m_monomials[em.m_var2index[v]].rvars() : em.m_find_key;
return string_hash(reinterpret_cast<char const*>(vec.c_ptr()), sizeof(lpvar)*vec.size(), 10);
}
};
/**
\brief private fields used by emonomials for maintaining state of canonized monomials.
*/
struct eq_canonical {
emonomials& em;
eq_canonical(emonomials& em): em(em) {}
bool operator()(lpvar u, lpvar v) const {
auto const& uvec = u != UINT_MAX? em.m_monomials[em.m_var2index[u]].rvars(): em.m_find_key;
auto const& vvec = v != UINT_MAX? em.m_monomials[em.m_var2index[v]].rvars(): em.m_find_key;
return uvec == vvec;
}
};
union_find<emonomials> m_u_f;
trail_stack<emonomials> m_u_f_stack;
mutable svector<lpvar> m_find_key; // the key used when looking for a monomial with the specific variables
var_eqs<emonomials>& m_ve;
mutable vector<monomial> m_monomials; // set of monomials
mutable unsigned_vector m_var2index; // var_mIndex -> mIndex
unsigned_vector m_lim; // backtracking point
mutable unsigned m_visited; // timestamp of visited monomials during pf_iterator
region m_region; // region for allocating linked lists
mutable svector<head_tail> m_use_lists; // use list of monomials where variables occur.
hash_canonical m_cg_hash;
eq_canonical m_cg_eq;
hashtable<lpvar, hash_canonical, eq_canonical> m_cg_table; // congruence (canonical) table.
void inc_visited() const;
void remove_cell(head_tail& v, unsigned mIndex);
void insert_cell(head_tail& v, unsigned mIndex);
void merge_cells(head_tail& root, head_tail& other);
void unmerge_cells(head_tail& root, head_tail& other);
void remove_cg(lpvar v);
void insert_cg(lpvar v);
void insert_cg_mon(monomial & m);
void remove_cg_mon(const monomial & m);
void rehash_cg(lpvar v) { remove_cg(v); insert_cg(v); }
void do_canonize(monomial& m) const;
cell* head(lpvar v) const;
void set_visited(monomial& m) const;
bool is_visited(monomial const& m) const;
std::ostream& display_use(std::ostream& out) const;
public:
/**
\brief emonomials builds on top of var_eqs.
push and pop on emonomials calls push/pop on var_eqs, so no
other calls to push/pop to the var_eqs should take place.
*/
emonomials(var_eqs<emonomials>& ve):
m_u_f(*this),
m_u_f_stack(*this),
m_ve(ve),
m_visited(0),
m_cg_hash(*this),
m_cg_eq(*this),
m_cg_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_cg_hash, m_cg_eq) {
m_ve.set_merge_handler(this);
}
void unmerge_eh(unsigned i, unsigned j) {
TRACE("nla_solver", tout << "unmerged " << i << " and " << j << "\n";);
}
void merge_eh(unsigned r2, unsigned r1, unsigned v2, unsigned v1) {}
void after_merge_eh(unsigned r2, unsigned r1, unsigned v2, unsigned v1) {}
// this method is required by union_find
trail_stack<emonomials> & get_trail_stack() { return m_u_f_stack; }
/**
\brief push/pop scopes.
The life-time of a merge is local within a scope.
*/
void push();
void pop(unsigned n);
/**
\brief create a monomial from an equality v := vs
*/
void add(lpvar v, unsigned sz, lpvar const* vs);
void add(lpvar v, svector<lpvar> const& vs) { add(v, vs.size(), vs.c_ptr()); }
void add(lpvar v, lpvar x, lpvar y) { lpvar vs[2] = { x, y }; add(v, 2, vs); }
void add(lpvar v, lpvar x, lpvar y, lpvar z) { lpvar vs[3] = { x, y, z }; add(v, 3, vs); }
/**
\brief retrieve monomial corresponding to variable v from definition v := vs
*/
monomial const& operator[](lpvar v) const { return m_monomials[m_var2index[v]]; }
monomial & operator[](lpvar v) { return m_monomials[m_var2index[v]]; }
bool is_canonized(const monomial&) const;
bool monomials_are_canonized() const;
/**
\brief obtain the representative canonized monomial
*/
monomial const& rep(monomial const& sv) const {
unsigned j = -1;
m_cg_table.find(sv.var(), j);
return m_monomials[m_var2index[j]];
}
/**
\brief determine if m1 divides m2 over the canonization obtained from merged variables.
*/
bool canonize_divides(monomial & m1, monomial& m2) const;
/**
\brief iterator over monomials that are declared.
*/
vector<monomial>::const_iterator begin() const { return m_monomials.begin(); }
vector<monomial>::const_iterator end() const { return m_monomials.end(); }
/**
\brief iterators over monomials where a variable is used
*/
class iterator {
emonomials const& m;
cell* m_cell;
bool m_touched;
public:
iterator(emonomials const& m, cell* c, bool at_end): m(m), m_cell(c), m_touched(at_end || c == nullptr) {}
monomial & operator*() { return m.m_monomials[m_cell->m_index]; }
iterator& operator++() { m_touched = true; m_cell = m_cell->m_next; return *this; }
iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
bool operator==(iterator const& other) const { return m_cell == other.m_cell && m_touched == other.m_touched; }
bool operator!=(iterator const& other) const { return m_cell != other.m_cell || m_touched != other.m_touched; }
};
class use_list {
emonomials const& m;
lpvar m_var;
cell* head() { return m.head(m_var); }
public:
use_list(emonomials const& m, lpvar v): m(m), m_var(v) {}
iterator begin() { return iterator(m, head(), false); }
iterator end() { return iterator(m, head(), true); }
};
use_list get_use_list(lpvar v) const { return use_list(*this, v); }
/**
\brief retrieve monomials m' where m is a proper factor of modulo current equalities.
*/
class pf_iterator {
emonomials const& m_em;
monomial * m_mon; // monomial
iterator m_it; // iterator over the first variable occurs list, ++ filters out elements that do not have m as a factor
iterator m_end;
void fast_forward();
public:
pf_iterator(emonomials const& m, monomial& mon, bool at_end);
pf_iterator(emonomials const& m, lpvar v, bool at_end);
monomial & operator*() {
return *m_it;
}
pf_iterator& operator++() { ++m_it; fast_forward(); return *this; }
pf_iterator operator++(int) { pf_iterator tmp = *this; ++*this; return tmp; }
bool operator==(pf_iterator const& other) const { return m_it == other.m_it; }
bool operator!=(pf_iterator const& other) const { return m_it != other.m_it; }
};
class products_of {
emonomials const& m;
monomial * mon;
lpvar m_var;
public:
products_of(emonomials const& m, monomial & mon): m(m), mon(&mon), m_var(UINT_MAX) {}
products_of(emonomials const& m, lpvar v): m(m), mon(nullptr), m_var(v) {}
pf_iterator begin() { if (mon) return pf_iterator(m, *mon, false); return pf_iterator(m, m_var, false); }
pf_iterator end() { if (mon) return pf_iterator(m, *mon, true); return pf_iterator(m, m_var, true); }
};
products_of get_products_of(monomial& m) const { inc_visited(); return products_of(*this, m); }
products_of get_products_of(lpvar v) const { inc_visited(); return products_of(*this, v); }
monomial const* find_canonical(svector<lpvar> const& vars) const;
bool is_canonical_monomial(lpvar j) const {
SASSERT(is_monomial_var(j));
unsigned idx = m_var2index[j];
if (idx >= m_u_f.get_num_vars())
return true;
return m_u_f.find(idx) == idx;
}
/**
\brief iterator over sign equivalent monomials.
These are monomials that are equivalent modulo m_var_eqs amd modulo signs.
*/
class sign_equiv_monomials_it {
emonomials const& m;
unsigned m_index;
bool m_touched;
public:
sign_equiv_monomials_it(emonomials const& m, unsigned idx, bool at_end):
m(m), m_index(idx), m_touched(at_end) {}
monomial const& operator*() { return m.m_monomials[m_index]; }
sign_equiv_monomials_it& operator++() {
m_touched = true;
if (m_index < m.m_u_f.get_num_vars())
m_index = m.m_u_f.next(m_index);
return *this;
}
sign_equiv_monomials_it operator++(int) {
sign_equiv_monomials_it tmp = *this;
++*this;
return tmp;
}
bool operator==(sign_equiv_monomials_it const& other) const {
return m_index == other.m_index && m_touched == other.m_touched;
}
bool operator!=(sign_equiv_monomials_it const& other) const {
return m_index != other.m_index || m_touched != other.m_touched;
}
};
class sign_equiv_monomials {
const emonomials& em;
monomial const& m;
unsigned index() const { return em.m_var2index[m.var()]; }
public:
sign_equiv_monomials(const emonomials & em, monomial const& m): em(em), m(m) {}
sign_equiv_monomials_it begin() { return sign_equiv_monomials_it(em, index(), false); }
sign_equiv_monomials_it end() { return sign_equiv_monomials_it(em, index(), true); }
};
sign_equiv_monomials enum_sign_equiv_monomials(monomial const& m) const { return sign_equiv_monomials(*this, m); }
sign_equiv_monomials enum_sign_equiv_monomials(lpvar v) { return enum_sign_equiv_monomials((*this)[v]); }
/**
\brief display state of emonomials
*/
std::ostream& display(const core&, std::ostream& out) const;
std::ostream& display(std::ostream& out) const;
/**
\brief
these are merge event handlers to interect the union-find handlers.
r2 becomes the new root. r2 is the root of v2, r1 is the old root of v1
*/
void merge_eh(signed_var r2, signed_var r1, signed_var v2, signed_var v1);
void after_merge_eh(signed_var r2, signed_var r1, signed_var v2, signed_var v1);
void unmerge_eh(signed_var r2, signed_var r1);
bool is_monomial_var(lpvar v) const { return m_var2index.get(v, UINT_MAX) != UINT_MAX; }
bool elists_are_consistent(std::unordered_map<unsigned_vector, std::unordered_set<lpvar>, hash_svector> &lists) const;
}; // end of emonomials
struct pp_emons {
const core& m_c;
const emonomials& m_em;
pp_emons(const core& c, const emonomials& e): m_c(c), m_em(e) {}
inline std::ostream& display(std::ostream& out) const {
return m_em.display(m_c, out);
}
};
inline std::ostream& operator<<(std::ostream& out, pp_emons const& p) { return p.display(out); }
}

View file

@ -1,43 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <memory>
#include "util/vector.h"
#include "util/lp/numeric_pair.h"
#include "util/lp/eta_matrix_def.h"
#ifdef Z3DEBUG
template double lp::eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
template lp::mpq lp::eta_matrix<lp::mpq, lp::mpq>::get_elem(unsigned int, unsigned int) const;
template lp::mpq lp::eta_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::get_elem(unsigned int, unsigned int) const;
#endif
template void lp::eta_matrix<double, double>::apply_from_left(vector<double>&, lp::lp_settings&);
template void lp::eta_matrix<double, double>::apply_from_right(vector<double>&);
template void lp::eta_matrix<double, double>::conjugate_by_permutation(lp::permutation_matrix<double, double>&);
template void lp::eta_matrix<lp::mpq, lp::mpq>::apply_from_left(vector<lp::mpq>&, lp::lp_settings&);
template void lp::eta_matrix<lp::mpq, lp::mpq>::apply_from_right(vector<lp::mpq>&);
template void lp::eta_matrix<lp::mpq, lp::mpq>::conjugate_by_permutation(lp::permutation_matrix<lp::mpq, lp::mpq>&);
template void lp::eta_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::apply_from_left(vector<lp::numeric_pair<lp::mpq> >&, lp::lp_settings&);
template void lp::eta_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::apply_from_right(vector<lp::mpq>&);
template void lp::eta_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::conjugate_by_permutation(lp::permutation_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >&);
template void lp::eta_matrix<double, double>::apply_from_left_local<double>(lp::indexed_vector<double>&, lp::lp_settings&);
template void lp::eta_matrix<lp::mpq, lp::mpq>::apply_from_left_local<lp::mpq>(lp::indexed_vector<lp::mpq>&, lp::lp_settings&);
template void lp::eta_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::apply_from_left_local<lp::mpq>(lp::indexed_vector<lp::mpq>&, lp::lp_settings&);
template void lp::eta_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::apply_from_right(lp::indexed_vector<lp::mpq>&);
template void lp::eta_matrix<lp::mpq, lp::mpq>::apply_from_right(lp::indexed_vector<lp::mpq>&);
template void lp::eta_matrix<double, double>::apply_from_right(lp::indexed_vector<double>&);

View file

@ -1,98 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
#include "util/lp/tail_matrix.h"
#include "util/lp/permutation_matrix.h"
namespace lp {
// 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 Z3DEBUG
unsigned m_length;
#endif
unsigned m_column_index;
public:
sparse_vector<T> m_column_vector;
T m_diagonal_element;
#ifdef Z3DEBUG
eta_matrix(unsigned column_index, unsigned length):
#else
eta_matrix(unsigned column_index):
#endif
#ifdef Z3DEBUG
m_length(length),
#endif
m_column_index(column_index) {}
bool is_dense() const override { 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 & ) override;
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) override {
apply_from_left_local(w, settings);
}
void push_back(unsigned row_index, T val ) {
lp_assert(row_index != m_column_index);
m_column_vector.push_back(row_index, val);
}
void apply_from_right(vector<T> & w) override;
void apply_from_right(indexed_vector<T> & w) override;
#ifdef Z3DEBUG
T get_elem(unsigned i, unsigned j) const override;
unsigned row_count() const override { return m_length; }
unsigned column_count() const override { return m_length; }
void set_number_of_rows(unsigned m) override { m_length = m; }
void set_number_of_columns(unsigned n) override { 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);
};
}

View file

@ -1,151 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
#include "util/lp/eta_matrix.h"
namespace lp {
// 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 Z3DEBUG
// 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 Z3DEBUG
// lp_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.empty())
return;
#ifdef Z3DEBUG
// 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 Z3DEBUG
// lp_assert(w.is_OK());
// lp_assert(vectors_are_equal<T>(wcopy, w.m_data));
#endif
}
#ifdef Z3DEBUG
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 Z3DEBUG
// 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 Z3DEBUG
// lp_assert(deb == *this);
#endif
}
}

View file

@ -1,49 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include <unordered_set>
#include "util/lp/lp_utils.h"
namespace lp {
class explanation {
vector<std::pair<mpq, constraint_index>> m_explanation;
std::unordered_set<unsigned> m_set_of_ci;
public:
void clear() { m_explanation.clear(); m_set_of_ci.clear(); }
vector<std::pair<mpq, constraint_index>>::const_iterator begin() const { return m_explanation.begin(); }
vector<std::pair<mpq, constraint_index>>::const_iterator end() const { return m_explanation.end(); }
void push_justification(constraint_index j, const mpq& v) {
m_explanation.push_back(std::make_pair(v, j));
}
void push_justification(constraint_index j) {
if (m_set_of_ci.find(j) != m_set_of_ci.end()) return;
m_set_of_ci.insert(j);
m_explanation.push_back(std::make_pair(one_of_type<mpq>(), j));
}
void add(const explanation& e) { for (auto j: e.m_set_of_ci) add(j); }
void add(unsigned ci) { push_justification(ci); }
void add(const std::pair<mpq, constraint_index>& j) { push_justification(j.second, j.first); }
bool empty() const { return m_explanation.empty(); }
size_t size() const { return m_explanation.size(); }
};
}

View file

@ -1,117 +0,0 @@
#include "util/vector.h"
#include "util/lp/factorization.h"
namespace nla {
void const_iterator_mon::init_vars_by_the_mask(unsigned_vector & k_vars, unsigned_vector & j_vars) const {
// the last element for m_factorization.m_rooted_vars goes to k_vars
SASSERT(m_mask.size() + 1 == m_ff->m_vars.size());
k_vars.push_back(m_ff->m_vars.back());
for (unsigned j = 0; j < m_mask.size(); j++) {
if (m_mask[j]) {
k_vars.push_back(m_ff->m_vars[j]);
} else {
j_vars.push_back(m_ff->m_vars[j]);
}
}
}
// todo : do we need the sign?
bool const_iterator_mon::get_factors(factor& k, factor& j, rational& sign) const {
unsigned_vector k_vars;
unsigned_vector j_vars;
init_vars_by_the_mask(k_vars, j_vars);
SASSERT(!k_vars.empty() && !j_vars.empty());
std::sort(k_vars.begin(), k_vars.end());
std::sort(j_vars.begin(), j_vars.end());
if (k_vars.size() == 1) {
k.set(k_vars[0], factor_type::VAR);
} else {
unsigned i;
if (!m_ff->find_canonical_monomial_of_vars(k_vars, i)) {
return false;
}
k.set(i, factor_type::MON);
}
if (j_vars.size() == 1) {
j.set(j_vars[0], factor_type::VAR);
} else {
unsigned i;
if (!m_ff->find_canonical_monomial_of_vars(j_vars, i)) {
return false;
}
j.set(i, factor_type::MON);
}
return true;
}
factorization const_iterator_mon::operator*() const {
if (m_full_factorization_returned == false) {
return create_full_factorization(m_ff->m_monomial);
}
factor j, k; rational sign;
if (!get_factors(j, k, sign))
return factorization(nullptr);
return create_binary_factorization(j, k);
}
void const_iterator_mon::advance_mask() {
if (!m_full_factorization_returned) {
m_full_factorization_returned = true;
return;
}
for (bool& m : m_mask) {
if (m) {
m = false;
}
else {
m = true;
break;
}
}
}
const_iterator_mon::self_type const_iterator_mon::operator++() { self_type i = *this; operator++(1); return i; }
const_iterator_mon::self_type const_iterator_mon::operator++(int) { advance_mask(); return *this; }
const_iterator_mon::const_iterator_mon(const svector<bool>& mask, const factorization_factory *f) :
m_mask(mask),
m_ff(f) ,
m_full_factorization_returned(false)
{}
bool const_iterator_mon::operator==(const const_iterator_mon::self_type &other) const {
return
m_full_factorization_returned == other.m_full_factorization_returned &&
m_mask == other.m_mask;
}
bool const_iterator_mon::operator!=(const const_iterator_mon::self_type &other) const { return !(*this == other); }
factorization const_iterator_mon::create_binary_factorization(factor j, factor k) const {
factorization f(nullptr);
f.push_back(j);
f.push_back(k);
// Let m by *m_ff->m_monomial, the monomial we factorize
// We have canonize_sign(m)*m.vars() = m.rvars()
// Let s = canonize_sign(f). Then we have f[0]*f[1] = s*m.rvars()
// s*canonize_sign(m)*val(m).
// Therefore val(m) = sign*val((f[0])*val(f[1]), where sign = canonize_sign(f)*canonize_sign(m).
// We apply this sign to the first factor.
f[0].sign() ^= (m_ff->canonize_sign(f)^m_ff->canonize_sign(*m_ff->m_monomial));
return f;
}
factorization const_iterator_mon::create_full_factorization(const monomial* m) const {
if (m != nullptr)
return factorization(m);
factorization f(nullptr);
for (lpvar j : m_ff->m_vars) {
f.push_back(factor(j, factor_type::VAR));
}
return f;
}
}

View file

@ -1,150 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/rational.h"
#include "util/lp/monomial.h"
#include "util/lp/nla_defs.h"
namespace nla {
struct factorization_factory;
enum class factor_type { VAR, MON };
class factor {
lpvar m_var;
factor_type m_type;
bool m_sign;
public:
factor(): factor(false) {}
factor(bool sign): m_var(UINT_MAX), m_type(factor_type::VAR), m_sign(sign) {}
explicit factor(lpvar v, factor_type t) : m_var(v), m_type(t), m_sign(false) {}
unsigned var() const { return m_var; }
factor_type type() const { return m_type; }
void set(lpvar v, factor_type t) { m_var = v; m_type = t; }
bool is_var() const { return m_type == factor_type::VAR; }
bool operator==(factor const& other) const {
return m_var == other.var() && m_type == other.type();
}
bool operator!=(factor const& other) const {
return m_var != other.var() || m_type != other.type();
}
bool sign() const { return m_sign; }
bool& sign() { return m_sign; }
rational rat_sign() const { return m_sign? rational(-1) : rational(1); }
};
class factorization {
svector<factor> m_factors;
const monomial* m_mon;
public:
factorization(const monomial* m): m_mon(m) {
if (m != nullptr) {
for (lpvar j : m->vars())
m_factors.push_back(factor(j, factor_type::VAR));
}
}
bool is_mon() const {
return m_mon != nullptr;
}
bool is_empty() const { return m_factors.empty(); }
const factor& operator[](unsigned k) const { return m_factors[k]; }
factor& operator[](unsigned k) { return m_factors[k]; }
size_t size() const { return m_factors.size(); }
const factor* begin() const { return m_factors.begin(); }
const factor* end() const { return m_factors.end(); }
void push_back(factor const& v) {
m_factors.push_back(v);
}
const monomial& mon() const { return *m_mon; }
void set_mon(const monomial* m) { m_mon = m; }
};
struct const_iterator_mon {
// fields
svector<bool> m_mask;
const factorization_factory * m_ff;
bool m_full_factorization_returned;
// typedefs
typedef const_iterator_mon self_type;
typedef factorization value_type;
typedef int difference_type;
typedef std::forward_iterator_tag iterator_category;
void init_vars_by_the_mask(unsigned_vector & k_vars, unsigned_vector & j_vars) const;
bool get_factors(factor& k, factor& j, rational& sign) const;
factorization operator*() const;
void advance_mask();
self_type operator++();
self_type operator++(int);
const_iterator_mon(const svector<bool>& mask, const factorization_factory *f);
bool operator==(const self_type &other) const;
bool operator!=(const self_type &other) const;
factorization create_binary_factorization(factor j, factor k) const;
factorization create_full_factorization(const monomial*) const;
};
struct factorization_factory {
const svector<lpvar>& m_vars;
const monomial* m_monomial;
// returns true if found
virtual bool find_canonical_monomial_of_vars(const svector<lpvar>& vars, unsigned& i) const = 0;
virtual bool canonize_sign(const monomial& m) const = 0;
virtual bool canonize_sign(const factorization& m) const = 0;
factorization_factory(const svector<lpvar>& vars, const monomial* m) :
m_vars(vars), m_monomial(m) {
}
svector<bool> get_mask() const {
// we keep the last element always in the first factor to avoid
// repeating a pair twice, that is why m_mask is shorter by one then m_vars
return
m_vars.size() != 2 ?
svector<bool>(m_vars.size() - 1, false)
:
svector<bool>(1, true); // init mask as in the end() since the full iteration will do the job
}
const_iterator_mon begin() const {
return const_iterator_mon(get_mask(), this);
}
const_iterator_mon end() const {
svector<bool> mask(m_vars.size() - 1, true);
auto it = const_iterator_mon(mask, this);
it.m_full_factorization_returned = true;
return it;
}
};
}

View file

@ -1,38 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/factorization_factory_imp.h"
#include "util/lp/nla_core.h"
namespace nla {
factorization_factory_imp::factorization_factory_imp(const monomial& rm, const core& s) :
factorization_factory(rm.rvars(), &s.m_emons[rm.var()]),
m_core(s), m_mon(s.m_emons[rm.var()]), m_rm(rm) { }
bool factorization_factory_imp::find_canonical_monomial_of_vars(const svector<lpvar>& vars, unsigned & i) const {
return m_core.find_canonical_monomial_of_vars(vars, i);
}
bool factorization_factory_imp::canonize_sign(const monomial& m) const {
return m_core.canonize_sign(m);
}
bool factorization_factory_imp::canonize_sign(const factorization& f) const {
return m_core.canonize_sign(f);
}
}

View file

@ -1,35 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/factorization.h"
namespace nla {
class core;
struct factorization_factory_imp: factorization_factory {
const core& m_core;
const monomial & m_mon;
const monomial& m_rm;
factorization_factory_imp(const monomial& rm, const core& s);
bool find_canonical_monomial_of_vars(const svector<lpvar>& vars, unsigned & i) const;
virtual bool canonize_sign(const monomial& m) const;
virtual bool canonize_sign(const factorization& m) const;
};
}

View file

@ -1,256 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include <functional>
namespace lp {
class general_matrix {
// fields
permutation_matrix<mpq, mpq> m_row_permutation;
permutation_matrix<mpq, mpq> m_column_permutation;
vector<vector<mpq>> m_data;
public:
unsigned adjust_row(unsigned row) const{
return m_row_permutation[row];
}
void push_row(vector<mpq> & v) {
m_data.push_back(v);
m_row_permutation.resize(m_data.size());
m_column_permutation.resize(v.size());
}
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];
}
unsigned row_count() const { return m_data.size(); }
unsigned column_count() const { return !m_data.empty()? m_data[0].size() : 0; }
class ref_row {
general_matrix& m_matrix;
vector<mpq>& m_row_data;
public:
ref_row(general_matrix& m, vector<mpq>& row_data) : m_matrix(m), m_row_data(row_data) {}
mpq & operator[](unsigned col) { return m_row_data[m_matrix.adjust_column(col)]; }
};
class ref_row_const {
const general_matrix& m_matrix;
const vector<mpq>& m_row_data;
public:
ref_row_const(const general_matrix& m, const vector<mpq>& row_data) : m_matrix(m), m_row_data(row_data) {}
const mpq& operator[](unsigned col) const { return m_row_data[m_matrix.adjust_column(col)]; }
};
ref_row operator[](unsigned i) { return ref_row(*this, m_data[adjust_row(i)]); }
ref_row_const operator[](unsigned i) const { return ref_row_const(*this, m_data[adjust_row(i)]); }
void print(std::ostream & out, unsigned blanks = 0) const {
unsigned m = row_count();
unsigned n = column_count();
general_matrix g(m, n);
for (unsigned i = 0; i < m; i++)
for (unsigned j = 0; j < n; j++)
g[i][j] = (*this)[i][j];
print_matrix<mpq>(g.m_data, out, blanks);
}
void print(std::ostream & out, const char * ss) const {
std::string s(ss);
out << s;
print(out, static_cast<unsigned>(s.size()));
}
void print_submatrix(std::ostream & out, unsigned k, unsigned blanks = 0) const {
general_matrix m(row_count() - k, column_count() - k);
for (unsigned i = k; i < row_count(); i++) {
for (unsigned j = k; j < column_count(); j++)
m[i-k][j-k] = (*this)[i][j];
}
print_matrix<mpq>(m.m_data, out, blanks);
}
void clear() { m_data.clear(); }
bool row_is_initialized_correctly(const vector<mpq>& row) {
lp_assert(row.size() == column_count());
for (unsigned j = 0; j < row.size(); j ++)
lp_assert(is_zero(row[j]));
return true;
}
template <typename T>
void init_row_from_container(int i, const T & c, std::function<unsigned (unsigned)> column_fix, const mpq& sign) {
auto & row = m_data[adjust_row(i)];
lp_assert(row_is_initialized_correctly(row));
for (const auto & p : c) {
unsigned j = adjust_column(column_fix(p.var()));
row[j] = sign * p.coeff();
}
}
void copy_column_to_indexed_vector(unsigned entering, indexed_vector<mpq> &w ) const {
lp_assert(false); // not implemented
}
general_matrix operator*(const general_matrix & m) const {
lp_assert(m.row_count() == column_count());
general_matrix ret(row_count(), m.column_count());
for (unsigned i = 0; i < row_count(); i ++) {
for (unsigned j = 0; j < m.column_count(); j++) {
mpq a(0);
for (unsigned k = 0; k < column_count(); k++)
a += ((*this)[i][k])*m[k][j];
ret[i][j] = a;
}
}
return ret;
}
bool elements_are_equal(const general_matrix& m) const {
for (unsigned i = 0; i < row_count(); i++)
for (unsigned j = 0; j < column_count(); j++)
if ( (*this)[i][j] != m[i][j])
return false;
return true;
}
bool elements_are_equal_modulo(const general_matrix& m, const mpq & d) const {
for (unsigned i = 0; i < row_count(); i++)
for (unsigned j = 0; j < column_count(); j++)
if (!is_zero(((*this)[i][j] - m[i][j]) % d))
return false;
return true;
}
bool operator==(const general_matrix& m) const {
return row_count() == m.row_count() && column_count() == m.column_count() && elements_are_equal(m);
}
bool operator!=(const general_matrix& m) const {
return !(*this == m);
}
bool equal_modulo(const general_matrix& m, const mpq & d) const {
return row_count() == m.row_count() && column_count() == m.column_count() && elements_are_equal_modulo(m, d);
}
vector<mpq> operator*(const vector<mpq> & x) const {
vector<mpq> r;
lp_assert(x.size() == column_count());
for (unsigned i = 0; i < row_count(); i++) {
mpq v(0);
for (unsigned j = 0; j < column_count(); j++) {
v += (*this)[i][j] * x[j];
}
r.push_back(v);
}
return r;
}
// bool create_upper_triangle(general_matrix& m, vector<mpq>& x) {
// for (unsigned i = 1; i < m.row_count(); i++) {
// lp_assert(false); // to be continued
// }
// }
// bool solve_A_x_equal_b(const general_matrix& m, vector<mpq>& x, const vector<mpq>& b) const {
// auto m_copy = m;
// // for square matrices
// lp_assert(row_count() == b.size());
// lp_assert(x.size() == column_count());
// lp_assert(row_count() == column_count());
// x = b;
// create_upper_triangle(copy_of_m, x);
// solve_on_triangle(copy_of_m, x);
// }
//
void transpose_rows(unsigned i, unsigned l) {
lp_assert(i != l);
m_row_permutation.transpose_from_right(i, l);
}
void transpose_columns(unsigned j, unsigned k) {
lp_assert(j != k);
m_column_permutation.transpose_from_left(j, k);
}
general_matrix(){}
general_matrix(unsigned n) :
m_row_permutation(n),
m_column_permutation(n),
m_data(n)
{
for (auto& v : m_data){
v.resize(n);
}
}
general_matrix(unsigned m, unsigned n) :
m_row_permutation(m),
m_column_permutation(n),
m_data(m) {
for (auto& v : m_data){
v.resize(n);
}
}
void shrink_to_rank(const svector<unsigned>& basis_rows) {
if (basis_rows.size() == row_count()) return;
vector<vector<mpq>> data; // todo : not efficient code
for (unsigned i : basis_rows)
data.push_back(m_data[i]);
m_data = data;
}
// used for debug only
general_matrix take_first_n_columns(unsigned n) const {
lp_assert(n <= column_count());
if (n == column_count())
return *this;
general_matrix ret(row_count(), n);
for (unsigned i = 0; i < row_count(); i++)
for (unsigned j = 0; j < n; j++)
ret[i][j] = (*this)[i][j];
return ret;
}
inline
friend vector<mpq> operator*(const vector<mpq> & f, const general_matrix& a) {
vector<mpq> r(a.column_count());
for (unsigned j = 0; j < a.column_count(); j ++) {
mpq t = zero_of_type<mpq>();
for (unsigned i = 0; i < a.row_count(); i++) {
t += f[i] * a[i][j];
}
r[j] = t;
}
return r;
}
};
}

View file

@ -1,340 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/gomory.h"
#include "util/lp/int_solver.h"
#include "util/lp/lar_solver.h"
#include "util/lp/lp_utils.h"
namespace lp {
class gomory::imp {
lar_term & m_t; // the term to return in the cut
mpq & m_k; // the right side of the cut
explanation* m_ex; // the conflict explanation
unsigned m_inf_col; // a basis column which has to be an integer but has a non integral value
const row_strip<mpq>& m_row;
const int_solver& m_int_solver;
mpq m_lcm_den;
mpq m_f;
mpq m_one_minus_f;
mpq m_fj;
mpq m_one_minus_fj;
const impq & get_value(unsigned j) const { return m_int_solver.get_value(j); }
bool is_real(unsigned j) const { return m_int_solver.is_real(j); }
bool at_lower(unsigned j) const { return m_int_solver.at_lower(j); }
bool at_upper(unsigned j) const { return m_int_solver.at_upper(j); }
const impq & lower_bound(unsigned j) const { return m_int_solver.lower_bound(j); }
const impq & upper_bound(unsigned j) const { return m_int_solver.upper_bound(j); }
constraint_index column_lower_bound_constraint(unsigned j) const { return m_int_solver.column_lower_bound_constraint(j); }
constraint_index column_upper_bound_constraint(unsigned j) const { return m_int_solver.column_upper_bound_constraint(j); }
bool column_is_fixed(unsigned j) const { return m_int_solver.m_lar_solver->column_is_fixed(j); }
void int_case_in_gomory_cut(unsigned j) {
lp_assert(m_int_solver.column_is_int(j) && m_fj.is_pos());
TRACE("gomory_cut_detail",
tout << " k = " << m_k;
tout << ", fj: " << m_fj << ", ";
tout << (at_lower(j)?"at_lower":"at_upper")<< std::endl;
);
mpq new_a;
if (at_lower(j)) {
new_a = m_fj <= m_one_minus_f ? m_fj / m_one_minus_f : ((1 - m_fj) / m_f);
lp_assert(new_a.is_pos());
m_k.addmul(new_a, lower_bound(j).x);
m_ex->push_justification(column_lower_bound_constraint(j));
}
else {
lp_assert(at_upper(j));
// the upper terms are inverted: therefore we have the minus
new_a = - (m_fj <= m_f ? m_fj / m_f : ((1 - m_fj) / m_one_minus_f));
lp_assert(new_a.is_neg());
m_k.addmul(new_a, upper_bound(j).x);
m_ex->push_justification(column_upper_bound_constraint(j));
}
m_t.add_coeff_var(new_a, j);
m_lcm_den = lcm(m_lcm_den, denominator(new_a));
TRACE("gomory_cut_detail", tout << "new_a = " << new_a << ", k = " << m_k << ", lcm_den = " << m_lcm_den << "\n";);
}
void real_case_in_gomory_cut(const mpq & a, unsigned j) {
TRACE("gomory_cut_detail_real", tout << "real\n";);
mpq new_a;
if (at_lower(j)) {
if (a.is_pos()) {
new_a = a / m_one_minus_f;
}
else {
new_a = - a / m_f;
}
m_k.addmul(new_a, lower_bound(j).x); // is it a faster operation than
// k += lower_bound(j).x * new_a;
m_ex->push_justification(column_lower_bound_constraint(j));
}
else {
lp_assert(at_upper(j));
if (a.is_pos()) {
new_a = - a / m_f;
}
else {
new_a = a / m_one_minus_f;
}
m_k.addmul(new_a, upper_bound(j).x); // k += upper_bound(j).x * new_a;
m_ex->push_justification(column_upper_bound_constraint(j));
}
TRACE("gomory_cut_detail_real", tout << a << "*v" << j << " k: " << m_k << "\n";);
m_t.add_coeff_var(new_a, j);
}
lia_move report_conflict_from_gomory_cut() {
lp_assert(m_k.is_pos());
// conflict 0 >= k where k is positive
m_k.neg(); // returning 0 <= -k
return lia_move::conflict;
}
void adjust_term_and_k_for_some_ints_case_gomory() {
lp_assert(!m_t.is_empty());
// k = 1 + sum of m_t at bounds
auto pol = m_t.coeffs_as_vector();
m_t.clear();
if (pol.size() == 1) {
TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;);
unsigned v = pol[0].second;
lp_assert(m_int_solver.column_is_int(v));
const mpq& a = pol[0].first;
m_k /= a;
if (a.is_pos()) { // we have av >= k
if (!m_k.is_int())
m_k = ceil(m_k);
// switch size
m_t.add_coeff_var(- mpq(1), v);
m_k.neg();
} else {
if (!m_k.is_int())
m_k = floor(m_k);
m_t.add_coeff_var(mpq(1), v);
}
} else {
m_lcm_den = lcm(m_lcm_den, denominator(m_k));
lp_assert(m_lcm_den.is_pos());
TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << m_lcm_den << std::endl;);
if (!m_lcm_den.is_one()) {
// normalize coefficients of integer parameters to be integers.
for (auto & pi: pol) {
pi.first *= m_lcm_den;
SASSERT(!m_int_solver.column_is_int(pi.second) || pi.first.is_int());
}
m_k *= m_lcm_den;
}
// negate everything to return -pol <= -m_k
for (const auto & pi: pol)
m_t.add_coeff_var(-pi.first, pi.second);
m_k.neg();
}
TRACE("gomory_cut_detail", tout << "k = " << m_k << std::endl;);
lp_assert(m_k.is_int());
}
std::string var_name(unsigned j) const {
return std::string("x") + std::to_string(j);
}
std::ostream& dump_coeff_val(std::ostream & out, const mpq & a) const {
if (a.is_int()) {
out << a;
}
else if ( a >= zero_of_type<mpq>())
out << "(/ " << numerator(a) << " " << denominator(a) << ")";
else {
out << "(- ( / " << numerator(-a) << " " << denominator(-a) << "))";
}
return out;
}
template <typename T>
void dump_coeff(std::ostream & out, const T& c) const {
out << "( * ";
dump_coeff_val(out, c.coeff());
out << " " << var_name(c.var()) << ")";
}
std::ostream& dump_row_coefficients(std::ostream & out) const {
mpq lc(1);
for (const auto& p : m_row) {
lc = lcm(lc, denominator(p.coeff()));
}
for (const auto& p : m_row) {
dump_coeff_val(out << " (* ", p.coeff()*lc) << " " << var_name(p.var()) << ")";
}
return out;
}
void dump_the_row(std::ostream& out) const {
out << "; the row, excluding fixed vars\n";
out << "(assert ( = ( +";
dump_row_coefficients(out) << ") 0))\n";
}
void dump_declaration(std::ostream& out, unsigned v) const {
out << "(declare-const " << var_name(v) << (m_int_solver.column_is_int(v) ? " Int" : " Real") << ")\n";
}
void dump_declarations(std::ostream& out) const {
// for a column j the var name is vj
for (const auto & p : m_row) {
dump_declaration(out, p.var());
}
for (const auto& p : m_t) {
unsigned v = p.var();
if (m_int_solver.m_lar_solver->is_term(v)) {
dump_declaration(out, v);
}
}
}
void dump_lower_bound_expl(std::ostream & out, unsigned j) const {
out << "(assert (>= " << var_name(j) << " " << lower_bound(j).x << "))\n";
}
void dump_upper_bound_expl(std::ostream & out, unsigned j) const {
out << "(assert (<= " << var_name(j) << " " << upper_bound(j).x << "))\n";
}
void dump_explanations(std::ostream& out) const {
for (const auto & p : m_row) {
unsigned j = p.var();
if (j == m_inf_col || (!is_real(j) && p.coeff().is_int())) {
continue;
}
else if (at_lower(j)) {
dump_lower_bound_expl(out, j);
} else {
lp_assert(at_upper(j));
dump_upper_bound_expl(out, j);
}
}
}
std::ostream& dump_term_coefficients(std::ostream & out) const {
for (const auto& p : m_t) {
dump_coeff(out, p);
}
return out;
}
std::ostream& dump_term_sum(std::ostream & out) const {
return dump_term_coefficients(out << "(+ ") << ")";
}
std::ostream& dump_term_le_k(std::ostream & out) const {
return dump_term_sum(out << "(<= ") << " " << m_k << ")";
}
void dump_the_cut_assert(std::ostream & out) const {
dump_term_le_k(out << "(assert (not ") << "))\n";
}
void dump_cut_and_constraints_as_smt_lemma(std::ostream& out) const {
dump_declarations(out);
dump_the_row(out);
dump_explanations(out);
dump_the_cut_assert(out);
out << "(check-sat)\n";
}
public:
lia_move create_cut() {
TRACE("gomory_cut",
print_linear_combination_of_column_indices_only(m_row, tout << "applying cut at:\n"); tout << std::endl;
for (auto & p : m_row) {
m_int_solver.m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(p.var(), tout);
}
tout << "inf_col = " << m_inf_col << std::endl;
);
// gomory will be t <= k and the current solution has a property t > k
m_k = 1;
m_t.clear();
mpq m_lcm_den(1);
bool some_int_columns = false;
mpq m_f = fractional_part(get_value(m_inf_col));
TRACE("gomory_cut_detail", tout << "m_f: " << m_f << ", ";
tout << "1 - m_f: " << 1 - m_f << ", get_value(m_inf_col).x - m_f = " << get_value(m_inf_col).x - m_f << "\n";);
lp_assert(m_f.is_pos() && (get_value(m_inf_col).x - m_f).is_int());
mpq one_min_m_f = 1 - m_f;
for (const auto & p : m_row) {
unsigned j = p.var();
if (j == m_inf_col) {
lp_assert(p.coeff() == one_of_type<mpq>());
TRACE("gomory_cut_detail", tout << "seeing basic var\n";);
continue;
}
// use -p.coeff() to make the format compatible with the format used in: Integrating Simplex with DPLL(T)
if (is_real(j)) {
real_case_in_gomory_cut(- p.coeff(), j);
} else {
if (p.coeff().is_int()) {
// m_fj will be zero and no monomial will be added
continue;
}
some_int_columns = true;
m_fj = fractional_part(-p.coeff());
m_one_minus_fj = 1 - m_fj;
int_case_in_gomory_cut(j);
}
}
if (m_t.is_empty())
return report_conflict_from_gomory_cut();
if (some_int_columns)
adjust_term_and_k_for_some_ints_case_gomory();
lp_assert(m_int_solver.current_solution_is_inf_on_cut());
TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout););
m_int_solver.m_lar_solver->subs_term_columns(m_t);
TRACE("gomory_cut", print_linear_combination_of_column_indices_only(m_t, tout << "gomory cut:"); tout << " <= " << m_k << std::endl;);
return lia_move::cut;
}
imp(lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip<mpq>& row, const int_solver& int_slv ) :
m_t(t),
m_k(k),
m_ex(ex),
m_inf_col(basic_inf_int_j),
m_row(row),
m_int_solver(int_slv),
m_lcm_den(1),
m_f(fractional_part(get_value(basic_inf_int_j).x)),
m_one_minus_f(1 - m_f) {}
};
lia_move gomory::create_cut() {
return m_imp->create_cut();
}
gomory::gomory(lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip<mpq>& row, const int_solver& s) {
m_imp = alloc(imp, t, k, ex, basic_inf_int_j, row, s);
}
gomory::~gomory() { dealloc(m_imp); }
}

View file

@ -1,34 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/lar_term.h"
#include "util/lp/lia_move.h"
#include "util/lp/explanation.h"
#include "util/lp/static_matrix.h"
namespace lp {
class int_solver;
class gomory {
class imp;
imp *m_imp;
public :
gomory(lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip<mpq>& row, const int_solver& s);
lia_move create_cut();
~gomory();
};
}

View file

@ -1,620 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Creates the Hermite Normal Form of a matrix in place.
We suppose that $A$ is an integral $m$ by $n$ matrix or rank $m$, where $n >= m$.
The paragraph below is applicable to the usage of HNF.
We have $H = AU$ where $H$ is in Hermite Normal Form
and $U$ is a unimodular matrix. We do not have an explicit
representation of $U$. For a given $i$ we need to find the $i$-th
row of $U^{-1}$.
Let $e_i$ be a vector of length $m$ with all elements equal to $0$ and
$1$ at $i$-th position. Then we need to find the row vector $e_iU^{-1}=t$. Noticing that $U^{-1} = H^{-1}A$, we have $e_iH^{-1}A=t$.
We find $e_iH^{-1} = f$ by solving $e_i = fH$ and then $fA$ gives us $t$.
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/numeric_pair.h"
#include "util/ext_gcd.h"
namespace lp {
namespace hnf_calc {
// d = u * a + v * b and the sum of abs(u) + abs(v) is minimal, d is positive
inline
void extended_gcd_minimal_uv(const mpq & a, const mpq & b, mpq & d, mpq & u, mpq & v) {
if (is_zero(a)) {
u = zero_of_type<mpq>();
v = one_of_type<mpq>();
d = b;
return;
}
if (is_zero(b)) {
u = one_of_type<mpq>();
v = zero_of_type<mpq>();
d = a;
return;
}
#if 1
d = gcd(a, b, u, v);
#else
extended_gcd(a, b, d, u, v);
#endif
if (is_neg(d)) {
d = -d;
u = -u;
v = -v;
}
if (d == a) {
u = one_of_type<mpq>();
v = zero_of_type<mpq>();
return;
}
if (d == -a) {
u = - one_of_type<mpq>();
v = zero_of_type<mpq>();
return;
}
mpq a_over_d = abs(a) / d;
mpq r;
mpq k = machine_div_rem(v, a_over_d, r);
if (is_neg(r)) {
r += a_over_d;
k -= one_of_type<mpq>();
}
lp_assert(v == k * a_over_d + r);
if (is_pos(b)) {
v = r - a_over_d; // v -= (k + 1) * a_over_d;
lp_assert(- a_over_d < v && v <= zero_of_type<mpq>());
if (is_pos(a)) {
u += (k + 1) * (b / d);
lp_assert( one_of_type<mpq>() <= u && u <= abs(b)/d);
} else {
u -= (k + 1) * (b / d);
lp_assert( one_of_type<mpq>() <= -u && -u <= abs(b)/d);
}
} else {
v = r; // v -= k * a_over_d;
lp_assert(- a_over_d < -v && -v <= zero_of_type<mpq>());
if (is_pos(a)) {
u += k * (b / d);
lp_assert( one_of_type<mpq>() <= u && u <= abs(b)/d);
} else {
u -= k * (b / d);
lp_assert( one_of_type<mpq>() <= -u && -u <= abs(b)/d);
}
}
lp_assert(d == u * a + v * b);
}
template <typename M>
bool prepare_pivot_for_lower_triangle(M &m, unsigned r) {
for (unsigned i = r; i < m.row_count(); i++) {
for (unsigned j = r; j < m.column_count(); j++) {
if (!is_zero(m[i][j])) {
if (i != r) {
m.transpose_rows(i, r);
}
if (j != r) {
m.transpose_columns(j, r);
}
return true;
}
}
}
return false;
}
template <typename M>
void pivot_column_non_fractional(M &m, unsigned r, bool & overflow, const mpq & big_number) {
lp_assert(!is_zero(m[r][r]));
for (unsigned j = r + 1; j < m.column_count(); j++) {
for (unsigned i = r + 1; i < m.row_count(); i++) {
if (
(m[i][j] = (r > 0) ? (m[r][r]*m[i][j] - m[i][r]*m[r][j]) / m[r-1][r-1] :
(m[r][r]*m[i][j] - m[i][r]*m[r][j]))
>= big_number) {
overflow = true;
return;
}
lp_assert(is_integer(m[i][j]));
}
}
}
// returns the rank of the matrix
template <typename M>
unsigned to_lower_triangle_non_fractional(M &m, bool & overflow, const mpq& big_number) {
unsigned i = 0;
for (; i < m.row_count(); i++) {
if (!prepare_pivot_for_lower_triangle(m, i)) {
return i;
}
pivot_column_non_fractional(m, i, overflow, big_number);
if (overflow)
return 0;
}
lp_assert(i == m.row_count());
return i;
}
// returns gcd of values below diagonal i,i
template <typename M>
mpq gcd_of_row_starting_from_diagonal(const M& m, unsigned i) {
mpq g = zero_of_type<mpq>();
unsigned j = i;
for (; j < m.column_count() && is_zero(g); j++) {
const auto & t = m[i][j];
if (!is_zero(t))
g = abs(t);
}
lp_assert(!is_zero(g));
for (; j < m.column_count(); j++) {
const auto & t = m[i][j];
if (!is_zero(t))
g = gcd(g, t);
}
return g;
}
// It fills "r" - the basic rows of m.
// The plan is to transform m to the lower triangular form by using non-fractional Gaussian Elimination by columns.
// Then the trailing after the diagonal elements of the following elements of the last non-zero row of the matrix,
// namely, m[r-1][r-1], m[r-1][r], ..., m[r-1]m[m.column_count() - 1] give the determinants of all minors of rank r.
// The gcd of these minors is the return value.
template <typename M>
mpq determinant_of_rectangular_matrix(const M& m, svector<unsigned> & basis_rows, const mpq& big_number) {
auto m_copy = m;
bool overflow = false;
unsigned rank = to_lower_triangle_non_fractional(m_copy, overflow, big_number);
if (overflow)
return big_number;
if (rank == 0)
return one_of_type<mpq>();
for (unsigned i = 0; i < rank; i++) {
basis_rows.push_back(m_copy.adjust_row(i));
}
TRACE("hnf_calc", tout << "basis_rows = "; print_vector(basis_rows, tout); m_copy.print(tout, "m_copy = "););
return gcd_of_row_starting_from_diagonal(m_copy, rank - 1);
}
} // end of namespace hnf_calc
template <typename M> // M is the matrix type
class hnf {
// fields
#ifdef Z3DEBUG
M m_H;
M m_U;
M m_U_reverse;
M m_A_orig;
#endif
M m_W;
vector<mpq> m_buffer;
unsigned m_m;
unsigned m_n;
mpq m_d; // it is a positive number and a multiple of gcd of r-minors of m_A_orig, where r is the rank of m_A_orig
// we suppose that the rank of m_A is equal to row_count(), and that row_count() <= column_count(), that is m_A has the full rank
unsigned m_i;
unsigned m_j;
mpq m_R;
mpq m_half_R;
mpq mod_R_balanced(const mpq & a) const {
mpq t = a % m_R;
return t > m_half_R? t - m_R : (t < - m_half_R? t + m_R : t);
}
mpq mod_R(const mpq & a) const {
mpq t = a % m_R;
t = is_neg(t) ? t + m_R : t;
CTRACE("hnf", is_neg(t), tout << "a=" << a << ", m_R= " << m_R << std::endl;);
return t;
}
#ifdef Z3DEBUG
void buffer_p_col_i_plus_q_col_j_H(const mpq & p, unsigned i, const mpq & q, unsigned j) {
for (unsigned k = i; k < m_m; k++) {
m_buffer[k] = p * m_H[k][i] + q * m_H[k][j];
}
}
#endif
bool zeros_in_column_W_above(unsigned i) {
for (unsigned k = 0; k < i; k++)
if (!is_zero(m_W[k][i]))
return false;
return true;
}
void buffer_p_col_i_plus_q_col_j_W_modulo(const mpq & p, const mpq & q) {
lp_assert(zeros_in_column_W_above(m_i));
for (unsigned k = m_i; k < m_m; k++) {
m_buffer[k] = mod_R_balanced(mod_R_balanced(p * m_W[k][m_i]) + mod_R_balanced(q * m_W[k][m_j]));
}
}
#ifdef Z3DEBUG
void buffer_p_col_i_plus_q_col_j_U(const mpq & p, unsigned i, const mpq & q, unsigned j) {
for (unsigned k = 0; k < m_n; k++) {
m_buffer[k] = p * m_U[k][i] + q * m_U[k][j];
}
}
void pivot_column_i_to_column_j_H(mpq u, unsigned i, mpq v, unsigned j) {
lp_assert(is_zero(u * m_H[i][i] + v * m_H[i][j]));
m_H[i][j] = zero_of_type<mpq>();
for (unsigned k = i + 1; k < m_m; k ++)
m_H[k][j] = u * m_H[k][i] + v * m_H[k][j];
}
#endif
void pivot_column_i_to_column_j_W_modulo(mpq u, mpq v) {
lp_assert(is_zero((u * m_W[m_i][m_i] + v * m_W[m_i][m_j]) % m_R));
m_W[m_i][m_j] = zero_of_type<mpq>();
for (unsigned k = m_i + 1; k < m_m; k ++)
m_W[k][m_j] = mod_R_balanced(mod_R_balanced(u * m_W[k][m_i]) + mod_R_balanced(v * m_W[k][m_j]));
}
#ifdef Z3DEBUG
void pivot_column_i_to_column_j_U(mpq u, unsigned i, mpq v, unsigned j) {
for (unsigned k = 0; k < m_n; k ++)
m_U[k][j] = u * m_U[k][i] + v * m_U[k][j];
}
void copy_buffer_to_col_i_H(unsigned i) {
for (unsigned k = i; k < m_m; k++) {
m_H[k][i] = m_buffer[k];
}
}
void copy_buffer_to_col_i_U(unsigned i) {
for (unsigned k = 0; k < m_n; k++)
m_U[k][i] = m_buffer[k];
}
// multiply by (a, b)
// (c, d)
// from the left where i and j are the modified columns
// the [i][i] = a, and [i][j] = b for the matrix we multiply by
void multiply_U_reverse_from_left_by(unsigned i, unsigned j, const mpq & a, const mpq & b, const mpq & c, const mpq d) {
// the new i-th row goes to the buffer
for (unsigned k = 0; k < m_n; k++) {
m_buffer[k] = a * m_U_reverse[i][k] + b * m_U_reverse[j][k];
}
// calculate the new j-th row in place
for (unsigned k = 0; k < m_n; k++) {
m_U_reverse[j][k] = c * m_U_reverse[i][k] + d * m_U_reverse[j][k];
}
// copy the buffer into i-th row
for (unsigned k = 0; k < m_n; k++) {
m_U_reverse[i][k] = m_buffer[k];
}
}
void handle_column_ij_in_row_i(unsigned i, unsigned j) {
const mpq& aii = m_H[i][i];
const mpq& aij = m_H[i][j];
mpq p,q,r;
extended_gcd(aii, aij, r, p, q);
mpq aii_over_r = aii / r;
mpq aij_over_r = aij / r;
buffer_p_col_i_plus_q_col_j_H(p, i, q, j);
pivot_column_i_to_column_j_H(- aij_over_r, i, aii_over_r, j);
copy_buffer_to_col_i_H(i);
buffer_p_col_i_plus_q_col_j_U(p, i, q, j);
pivot_column_i_to_column_j_U(- aij_over_r, i, aii_over_r, j);
copy_buffer_to_col_i_U(i);
// U was multiplied from the right by (p, - aij_over_r)
// (q, aii_over_r )
// We need to multiply U_reverse by (aii_over_r, aij_over_r)
// (-q , p)
// from the left
multiply_U_reverse_from_left_by(i, j, aii_over_r, aij_over_r, -q, p);
CASSERT("hnf_test", is_correct_modulo());
}
void switch_sign_for_column(unsigned i) {
for (unsigned k = i; k < m_m; k++)
m_H[k][i].neg();
for (unsigned k = 0; k < m_n; k++)
m_U[k][i].neg();
// switch sign for the i-th row in the reverse m_U_reverse
for (unsigned k = 0; k < m_n; k++)
m_U_reverse[i][k].neg();
}
void process_row_column(unsigned i, unsigned j){
if (is_zero(m_H[i][j]))
return;
handle_column_ij_in_row_i(i, j);
}
void replace_column_j_by_j_minus_u_col_i_H(unsigned i, unsigned j, const mpq & u) {
lp_assert(j < i);
for (unsigned k = i; k < m_m; k++) {
m_H[k][j] -= u * m_H[k][i];
}
}
void replace_column_j_by_j_minus_u_col_i_U(unsigned i, unsigned j, const mpq & u) {
lp_assert(j < i);
for (unsigned k = 0; k < m_n; k++) {
m_U[k][j] -= u * m_U[k][i];
}
// Here we multiply from m_U from the right by the matrix ( 1, 0)
// ( -u, 1).
// To adjust the reverse we multiply it from the left by (1, 0)
// (u, 1)
for (unsigned k = 0; k < m_n; k++) {
m_U_reverse[i][k] += u * m_U_reverse[j][k];
}
}
void work_on_columns_less_than_i_in_the_triangle(unsigned i) {
const mpq & mii = m_H[i][i];
if (is_zero(mii)) return;
for (unsigned j = 0; j < i; j++) {
const mpq & mij = m_H[i][j];
if (!is_pos(mij) && - mij < mii)
continue;
mpq u = ceil(mij / mii);
replace_column_j_by_j_minus_u_col_i_H(i, j, u);
replace_column_j_by_j_minus_u_col_i_U(i, j, u);
}
}
void process_row(unsigned i) {
for (unsigned j = i + 1; j < m_n; j++) {
process_row_column(i, j);
}
if (i >= m_n) {
lp_assert(m_H == m_A_orig * m_U);
return;
}
if (is_neg(m_H[i][i]))
switch_sign_for_column(i);
work_on_columns_less_than_i_in_the_triangle(i);
CASSERT("hnf_test", is_correct_modulo());
}
void calculate() {
for (unsigned i = 0; i < m_m; i++) {
process_row(i);
}
}
void prepare_U_and_U_reverse() {
m_U = M(m_H.column_count());
for (unsigned i = 0; i < m_U.column_count(); i++)
m_U[i][i] = 1;
m_U_reverse = m_U;
lp_assert(m_H == m_A_orig * m_U);
}
bool row_is_correct_form(unsigned i) const {
if (i >= m_n)
return true;
const mpq& hii = m_H[i][i];
if (is_neg(hii))
return false;
for (unsigned j = 0; j < i; j++) {
const mpq & hij = m_H[i][j];
if (is_pos(hij))
return false;
if (!is_zero(hii) && - hij >= hii)
return false;
}
return true;
}
bool is_correct_form() const {
for (unsigned i = 0; i < m_m; i++)
if (!row_is_correct_form(i))
return false;
return true;
}
bool is_correct() const {
return m_H == m_A_orig * m_U && is_unit_matrix(m_U * m_U_reverse);
}
bool is_correct_modulo() const {
return m_H.equal_modulo(m_A_orig * m_U, m_d) && is_unit_matrix(m_U * m_U_reverse);
}
bool is_correct_final() const {
if (!is_correct()) {
TRACE("hnf_calc",
tout << "m_H = "; m_H.print(tout, 17);
tout << "\nm_A_orig * m_U = "; (m_A_orig * m_U).print(tout, 17);
tout << "is_correct() does not hold" << std::endl;);
return false;
}
if (!is_correct_form()) {
TRACE("hnf_calc", tout << "is_correct_form() does not hold" << std::endl;);
return false;
}
return true;
}
public:
const M& H() const { return m_H;}
const M& U() const { return m_U;}
const M& U_reverse() const { return m_U_reverse; }
private:
#endif
void copy_buffer_to_col_i_W_modulo() {
for (unsigned k = m_i; k < m_m; k++) {
m_W[k][m_i] = m_buffer[k];
}
}
void replace_column_j_by_j_minus_u_col_i_W(unsigned j, const mpq & u) {
lp_assert(j < m_i);
for (unsigned k = m_i; k < m_m; k++) {
m_W[k][j] -= u * m_W[k][m_i];
// m_W[k][j] = mod_R_balanced(m_W[k][j]);
}
}
bool is_unit_matrix(const M& u) const {
unsigned m = u.row_count();
unsigned n = u.column_count();
if (m != n) return false;
for (unsigned i = 0; i < m; i ++)
for (unsigned j = 0; j < n; j++) {
if (i == j) {
if (one_of_type<mpq>() != u[i][j])
return false;
} else {
if (!is_zero(u[i][j]))
return false;
}
}
return true;
}
// follows Algorithm 2.4.8 of Henri Cohen's "A course on computational algebraic number theory",
// with some changes related to that we create a low triangle matrix
// with non-positive elements under the diagonal
void process_column_in_row_modulo() {
const mpq& aii = m_W[m_i][m_i];
const mpq& aij = m_W[m_i][m_j];
mpq d, p,q;
hnf_calc::extended_gcd_minimal_uv(aii, aij, d, p, q);
if (is_zero(d))
return;
mpq aii_over_d = mod_R(aii / d);
mpq aij_over_d = mod_R(aij / d);
buffer_p_col_i_plus_q_col_j_W_modulo(p, q);
pivot_column_i_to_column_j_W_modulo(- aij_over_d, aii_over_d);
copy_buffer_to_col_i_W_modulo();
}
void fix_row_under_diagonal_W_modulo() {
mpq d, u, v;
if (is_zero(m_W[m_i][m_i])) {
m_W[m_i][m_i] = m_R;
u = one_of_type<mpq>();
d = m_R;
} else {
hnf_calc::extended_gcd_minimal_uv(m_W[m_i][m_i], m_R, d, u, v);
}
auto & mii = m_W[m_i][m_i];
mii *= u;
mii = mod_R(mii);
if (is_zero(mii))
mii = d;
lp_assert(is_pos(mii));
// adjust column m_i
for (unsigned k = m_i + 1; k < m_m; k++) {
m_W[k][m_i] *= u;
m_W[k][m_i] = mod_R_balanced(m_W[k][m_i]);
}
lp_assert(is_pos(mii));
for (unsigned j = 0; j < m_i; j++) {
const mpq & mij = m_W[m_i][j];
if (!is_pos(mij) && - mij < mii)
continue;
mpq q = ceil(mij / mii);
replace_column_j_by_j_minus_u_col_i_W(j, q);
}
}
void process_row_modulo() {
for (m_j = m_i + 1; m_j < m_n; m_j++) {
process_column_in_row_modulo();
}
fix_row_under_diagonal_W_modulo();
}
void calculate_by_modulo() {
for (m_i = 0; m_i < m_m; m_i ++) {
process_row_modulo();
lp_assert(is_pos(m_W[m_i][m_i]));
m_R /= m_W[m_i][m_i];
lp_assert(is_integer(m_R));
m_half_R = floor(m_R / 2);
}
}
public:
hnf(M & A, const mpq & d) :
#ifdef Z3DEBUG
m_H(A),
m_A_orig(A),
#endif
m_W(A),
m_buffer(std::max(A.row_count(), A.column_count())),
m_m(A.row_count()),
m_n(A.column_count()),
m_d(d),
m_R(m_d),
m_half_R(floor(m_R / 2))
{
if (m_m == 0 || m_n == 0 || is_zero(m_d))
return;
#ifdef Z3DEBUG
prepare_U_and_U_reverse();
calculate();
CASSERT("hnf_test", is_correct_final());
#endif
calculate_by_modulo();
#ifdef Z3DEBUG
CTRACE("hnf_calc", m_H != m_W,
tout << "A = "; m_A_orig.print(tout, 4); tout << std::endl;
tout << "H = "; m_H.print(tout, 4); tout << std::endl;
tout << "W = "; m_W.print(tout, 4); tout << std::endl;);
lp_assert (m_H == m_W);
#endif
}
const M & W() const { return m_W; }
};
}

View file

@ -1,236 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/lar_term.h"
#include "util/lp/hnf.h"
#include "util/lp/general_matrix.h"
#include "util/lp/var_register.h"
#include "util/lp/lia_move.h"
#include "util/lp/explanation.h"
namespace lp {
class hnf_cutter {
general_matrix m_A;
vector<const lar_term*> m_terms;
vector<bool> m_terms_upper;
svector<constraint_index> m_constraints_for_explanation;
vector<mpq> m_right_sides;
lp_settings & m_settings;
mpq m_abs_max;
bool m_overflow;
var_register m_var_register;
public:
const mpq & abs_max() const { return m_abs_max; }
hnf_cutter(lp_settings & settings) : m_settings(settings),
m_abs_max(zero_of_type<mpq>()),
m_var_register(0) {}
unsigned terms_count() const {
return m_terms.size();
}
const vector<const lar_term*>& terms() const { return m_terms; }
const svector<unsigned>& constraints_for_explanation() const {
return m_constraints_for_explanation;
}
const vector<mpq> & right_sides() const { return m_right_sides; }
void clear() {
// m_A will be filled from scratch in init_matrix_A
m_var_register.clear();
m_terms.clear();
m_terms_upper.clear();
m_constraints_for_explanation.clear();
m_right_sides.clear();
m_abs_max = zero_of_type<mpq>();
m_overflow = false;
}
void add_term(const lar_term* t, const mpq &rs, constraint_index ci, bool upper_bound) {
m_terms.push_back(t);
m_terms_upper.push_back(upper_bound);
if (upper_bound)
m_right_sides.push_back(rs);
else
m_right_sides.push_back(-rs);
m_constraints_for_explanation.push_back(ci);
for (const auto &p : *t) {
m_var_register.add_var(p.var());
mpq t = abs(ceil(p.coeff()));
if (t > m_abs_max)
m_abs_max = t;
}
}
void print(std::ostream & out) {
out << "terms = " << m_terms.size() << ", var = " << m_var_register.size() << std::endl;
}
void initialize_row(unsigned i) {
mpq sign = m_terms_upper[i]? one_of_type<mpq>(): - one_of_type<mpq>();
m_A.init_row_from_container(i, * m_terms[i], [this](unsigned j) { return m_var_register.add_var(j);}, sign);
}
void init_matrix_A() {
m_A = general_matrix(terms_count(), vars().size());
for (unsigned i = 0; i < terms_count(); i++)
initialize_row(i);
}
// todo: as we need only one row i with non integral b[i] need to optimize later
void find_h_minus_1_b(const general_matrix& H, vector<mpq> & b) {
// the solution will be put into b
for (unsigned i = 0; i < H.row_count() ;i++) {
for (unsigned j = 0; j < i; j++) {
b[i] -= H[i][j]*b[j];
}
b[i] /= H[i][i];
// consider return from here if b[i] is not an integer and return i
}
}
vector<mpq> create_b(const svector<unsigned> & basis_rows) {
if (basis_rows.size() == m_right_sides.size())
return m_right_sides;
vector<mpq> b;
for (unsigned i : basis_rows) {
b.push_back(m_right_sides[i]);
}
return b;
}
int find_cut_row_index(const vector<mpq> & b) {
int ret = -1;
int n = 0;
for (int i = 0; i < static_cast<int>(b.size()); i++) {
if (is_integer(b[i])) continue;
if (n == 0 ) {
lp_assert(ret == -1);
n = 1;
ret = i;
} else {
if (m_settings.random_next() % (++n) == 0) {
ret = i;
}
}
}
return ret;
}
// fills e_i*H_minus_1
void get_ei_H_minus_1(unsigned i, const general_matrix& H, vector<mpq> & row) {
// we solve x = ei * H_min_1
// or x * H = ei
unsigned m = H.row_count();
for (unsigned k = i + 1; k < m; k++) {
row[k] = zero_of_type<mpq>();
}
row[i] = one_of_type<mpq>() / H[i][i];
for(int k = i - 1; k >= 0; k--) {
mpq t = zero_of_type<mpq>();
for (unsigned l = k + 1; l <= i; l++) {
t += H[l][k]*row[l];
}
row[k] = -t / H[k][k];
}
// // test region
// vector<mpq> ei(H.row_count(), zero_of_type<mpq>());
// ei[i] = one_of_type<mpq>();
// vector<mpq> pr = row * H;
// pr.shrink(ei.size());
// lp_assert(ei == pr);
// // end test region
}
void fill_term(const vector<mpq> & row, lar_term& t) {
for (unsigned j = 0; j < row.size(); j++) {
if (!is_zero(row[j]))
t.add_coeff_var(row[j], m_var_register.local_to_external(j));
}
}
#ifdef Z3DEBUG
vector<mpq> transform_to_local_columns(const vector<impq> & x) const {
vector<mpq> ret;
for (unsigned j = 0; j < vars().size(); j++) {
ret.push_back(x[m_var_register.local_to_external(j)].x);
}
return ret;
}
#endif
void shrink_explanation(const svector<unsigned>& basis_rows) {
svector<unsigned> new_expl;
for (unsigned i : basis_rows) {
new_expl.push_back(m_constraints_for_explanation[i]);
}
m_constraints_for_explanation = new_expl;
}
bool overflow() const { return m_overflow; }
lia_move create_cut(lar_term& t, mpq& k, explanation* ex, bool & upper, const vector<mpq> & x0) {
// we suppose that x0 has at least one non integer element
(void)x0;
init_matrix_A();
svector<unsigned> basis_rows;
mpq big_number = m_abs_max.expt(3);
mpq d = hnf_calc::determinant_of_rectangular_matrix(m_A, basis_rows, big_number);
if (d >= big_number) {
return lia_move::undef;
}
if (m_settings.get_cancel_flag()) {
return lia_move::undef;
}
if (basis_rows.size() < m_A.row_count()) {
m_A.shrink_to_rank(basis_rows);
shrink_explanation(basis_rows);
}
hnf<general_matrix> h(m_A, d);
vector<mpq> b = create_b(basis_rows);
lp_assert(m_A * x0 == b);
find_h_minus_1_b(h.W(), b);
int cut_row = find_cut_row_index(b);
if (cut_row == -1) {
return lia_move::undef;
}
// the matrix is not square - we can get
// all integers in b's projection
vector<mpq> row(m_A.column_count());
get_ei_H_minus_1(cut_row, h.W(), row);
vector<mpq> f = row * m_A;
fill_term(f, t);
k = floor(b[cut_row]);
upper = true;
return lia_move::cut;
}
svector<unsigned> vars() const { return m_var_register.vars(); }
};
}

View file

@ -1,57 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/lp_settings.h"
#include "util/lp/lar_constraints.h"
namespace lp {
struct implied_bound {
mpq m_bound;
unsigned m_j; // the column for which the bound has been found
bool m_is_lower_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_lower_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_lower_bound == o.m_is_lower_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 lower_bound,
bool coeff_before_j_is_pos,
unsigned row_or_term_index,
bool strict):
m_bound(a),
m_j(j),
m_is_lower_bound(lower_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

@ -1,73 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
namespace lp {
template <typename B> class incremental_vector {
vector<unsigned> m_stack_of_vector_sizes;
vector<B> m_vector;
public:
const B & operator[](unsigned a) const {
return m_vector[a];
}
unsigned size() const {
return m_vector.size();
}
void push_scope() {
m_stack_of_vector_sizes.push_back(m_vector.size());
}
void pop_scope() {
pop_scope(1);
}
template <typename T>
void pop_tail(vector<T> & v, unsigned k) {
lp_assert(v.size() >= k);
v.shrink(v.size() - k);
}
template <typename T>
void resize(vector<T> & v, unsigned new_size) {
v.resize(new_size);
}
void pop_scope(unsigned k) {
lp_assert(m_stack_of_vector_sizes.size() >= k);
lp_assert(k > 0);
m_vector.shrink(peek_size(k));
unsigned new_st_size = m_stack_of_vector_sizes.size() - k;
m_stack_of_vector_sizes.shrink(new_st_size);
}
void push_back(const B & b) {
m_vector.push_back(b);
}
unsigned peek_size(unsigned k) const {
lp_assert(k > 0 && k <= m_stack_of_vector_sizes.size());
return m_stack_of_vector_sizes[m_stack_of_vector_sizes.size() - k];
}
};
}

View file

@ -1,70 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
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 Z3DEBUG
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

@ -1,53 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/vector.h"
#include "util/lp/indexed_vector_def.h"
namespace lp {
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 Z3DEBUG
template bool indexed_vector<unsigned>::is_OK() const;
template bool indexed_vector<double>::is_OK() const;
template bool indexed_vector<mpq>::is_OK() const;
template bool indexed_vector<lp::numeric_pair<mpq> >::is_OK() const;
#endif
template void lp::indexed_vector< lp::mpq>::print(std::basic_ostream<char,struct std::char_traits<char> > &);
template void lp::indexed_vector<double>::print(std::basic_ostream<char,struct std::char_traits<char> > &);
template void lp::indexed_vector<lp::numeric_pair<lp::mpq> >::print(std::ostream&);
}
// template void lp::print_vector<double, vectro>(vector<double> const&, std::ostream&);
// template void lp::print_vector<unsigned int>(vector<unsigned int> const&, std::ostream&);
// template void lp::print_vector<std::string>(vector<std::string> const&, std::ostream&);
// template void lp::print_vector<lp::numeric_pair<lp::mpq> >(vector<lp::numeric_pair<lp::mpq>> const&, std::ostream&);
template void lp::indexed_vector<double>::resize(unsigned int);
// template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream<char, std::char_traits<char> > &);
// template void lp::print_vector<std::pair<lp::mpq, unsigned int> >(vector<std::pair<lp::mpq, unsigned int>> const&, std::ostream&);
template void lp::indexed_vector<lp::numeric_pair<lp::mpq> >::erase_from_index(unsigned int);

View file

@ -1,223 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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 lp {
template <typename T> void print_sparse_vector(const vector<T> & t, std::ostream & out);
void print_vector_as_doubles(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 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);
}
}
}
struct ival {
unsigned m_var;
const T & m_coeff;
ival(unsigned var, const T & val) : m_var(var), m_coeff(val) {
}
unsigned var() const { return m_var;}
const T & coeff() const { return m_coeff; }
bool dead() const { return false; }
};
struct const_iterator {
// fields
const unsigned *m_i;
const indexed_vector& m_v;
//typedefs
typedef const_iterator self_type;
typedef ival value_type;
typedef const ival reference;
// typedef const column_cell* pointer;
typedef int difference_type;
typedef std::forward_iterator_tag iterator_category;
reference operator*() const {
return ival(*m_i, m_v[*m_i]);
}
self_type operator++() { self_type i = *this; m_i++; return i; }
self_type operator++(int) { m_i++; return *this; }
const_iterator(const unsigned* it, const indexed_vector& v) :
m_i(it),
m_v(v)
{}
bool operator==(const self_type &other) const {
return m_i == other.m_i;
}
bool operator!=(const self_type &other) const { return !(*this == other); }
};
const_iterator begin() const {
return const_iterator(m_index.begin(), *this);
}
const_iterator end() const {
return const_iterator(m_index.end(), *this);
}
#ifdef Z3DEBUG
bool is_OK() const;
#endif
void print(std::ostream & out);
};
}

View file

@ -1,110 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/vector.h"
#include "util/lp/indexed_vector.h"
#include "util/lp/lp_settings.h"
namespace lp {
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_as_doubles(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());
lp_assert(is_OK());
}
template <typename T>
void indexed_vector<T>::set_value(const T& value, unsigned index) {
m_data[index] = value;
lp_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 Z3DEBUG
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;
}
#endif
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);
}
}

View file

@ -1,45 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/binary_heap_priority_queue.h"
namespace lp {
class indexer_of_constraints {
binary_heap_priority_queue<unsigned> m_queue_of_released_indices;
unsigned m_max;
public:
indexer_of_constraints() :m_max(0) {}
unsigned get_new_index() {
unsigned ret;
if (m_queue_of_released_indices.is_empty()) {
ret = m_max++;
}
else {
ret = m_queue_of_released_indices.dequeue();
}
return ret;
};
void release_index(unsigned i) {
m_queue_of_released_indices.enqueue(i, i);
};
unsigned max() const { return m_max; }
};
}

View file

@ -1,81 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
#include "util/lp/indexed_vector.h"
#include <ostream>
namespace lp {
// 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) {
lp_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;
}
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,151 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/lp_settings.h"
#include "util/lp/static_matrix.h"
#include "util/lp/int_set.h"
#include "util/lp/lar_term.h"
#include "util/lp/lar_constraints.h"
#include "util/lp/hnf_cutter.h"
#include "util/lp/lia_move.h"
#include "util/lp/explanation.h"
namespace lp {
class lar_solver;
template <typename T, typename X>
struct lp_constraint;
class int_solver {
public:
// fields
lar_solver *m_lar_solver;
unsigned m_number_of_calls;
lar_term m_t; // the term to return in the cut
mpq m_k; // the right side of the cut
explanation *m_ex; // the conflict explanation
bool m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise
hnf_cutter m_hnf_cutter;
// methods
int_solver(lar_solver* lp);
// main function to check that the solution provided by lar_solver is valid for integral values,
// or provide a way of how it can be adjusted.
lia_move check(explanation *);
lar_term const& get_term() const { return m_t; }
mpq const& get_offset() const { return m_k; }
bool is_upper() const { return m_upper; }
lia_move check_wrapper(lar_term& t, mpq& k, explanation& ex);
bool is_base(unsigned j) const;
bool is_real(unsigned j) const;
const impq & lower_bound(unsigned j) const;
const impq & upper_bound(unsigned j) const;
bool column_is_int(unsigned j) const;
const impq & get_value(unsigned j) const;
bool at_lower(unsigned j) const;
bool at_upper(unsigned j) const;
private:
// how to tighten bounds for integer variables.
bool gcd_test_for_row(static_matrix<mpq, numeric_pair<mpq>> & A, unsigned i);
// gcd test
// 5*x + 3*y + 6*z = 5
// suppose x is fixed at 2.
// so we have 10 + 3(y + 2z) = 5
// 5 = -3(y + 2z)
// this is unsolvable because 5/3 is not an integer.
// so we create a lemma that rules out this condition.
//
bool gcd_test(); // returns false in case of failure. Creates a theory lemma in case of failure.
bool branch(const lp_constraint<mpq, mpq> & new_inequality);
bool ext_gcd_test(const row_strip<mpq>& row,
mpq const & least_coeff,
mpq const & lcm_den,
mpq const & consts);
void fill_explanation_from_fixed_columns(const row_strip<mpq> & row);
void add_to_explanation_from_fixed_or_boxed_column(unsigned j);
lia_move patch_nbasic_columns();
bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m);
private:
bool is_boxed(unsigned j) const;
bool is_fixed(unsigned j) const;
bool is_free(unsigned j) const;
bool value_is_int(unsigned j) const;
void set_value_for_nbasic_column_ignore_old_values(unsigned j, const impq & new_val);
bool non_basic_columns_are_at_bounds() const;
bool is_feasible() const;
bool column_is_int_inf(unsigned j) const;
void trace_inf_rows() const;
lia_move branch_or_sat();
int find_any_inf_int_column_basis_first();
int find_inf_int_base_column();
int find_inf_int_boxed_base_column_with_smallest_range(unsigned&);
int get_kth_inf_int(unsigned) const;
lp_settings& settings();
const lp_settings& settings() const;
void branch_infeasible_int_var(unsigned);
lia_move mk_gomory_cut(unsigned inf_col, const row_strip<mpq>& row);
lia_move proceed_with_gomory_cut(unsigned j);
bool is_gomory_cut_target(const row_strip<mpq>&);
bool at_bound(unsigned j) const;
bool has_low(unsigned j) const;
bool has_upper(unsigned j) const;
unsigned row_of_basic_column(unsigned j) const;
public:
void display_column(std::ostream & out, unsigned j) const;
constraint_index column_upper_bound_constraint(unsigned j) const;
constraint_index column_lower_bound_constraint(unsigned j) const;
bool current_solution_is_inf_on_cut() const;
bool shift_var(unsigned j, unsigned range);
private:
void display_row_info(std::ostream & out, unsigned row_index) const;
unsigned random();
bool has_inf_int() const;
lia_move create_branch_on_column(int j);
public:
bool is_term(unsigned j) const;
bool left_branch_is_more_narrow_than_right(unsigned);
lia_move find_cube();
bool tighten_terms_for_cube();
bool tighten_term_for_cube(unsigned);
unsigned column_count() const;
bool all_columns_are_bounded() const;
impq get_cube_delta_for_term(const lar_term&) const;
void find_feasible_solution();
int find_inf_int_nbasis_column() const;
lia_move run_gcd_test();
lia_move gomory_cut();
lia_move hnf_cut();
lia_move make_hnf_cut();
bool init_terms_for_hnf_cut();
bool hnf_matrix_is_empty() const;
void try_add_term_to_A_for_hnf(unsigned term_index);
bool hnf_has_var_with_non_integral_value() const;
bool hnf_cutter_is_full() const;
void patch_nbasic_column(unsigned j, bool patch_only_int_vals);
};
}

View file

@ -1,100 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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 lp {
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("=");
case NE: return std::string("!=");
}
lp_unreachable();
return std::string(); // it is unreachable
}
struct lar_base_constraint {
lconstraint_kind m_kind;
mpq m_right_side;
virtual vector<std::pair<mpq, var_index>> coeffs() 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>> coeffs() const override {
vector<std::pair<mpq, var_index>> ret;
ret.push_back(std::make_pair(one_of_type<mpq>(), m_j));
return ret;
}
unsigned size() const override { 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>> coeffs() const override {
return m_term->coeffs_as_vector();
}
unsigned size() const override { 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) { }
// mpq get_free_coeff_of_left_side() const override { return m_term->m_v;}
};
class lar_constraint : public lar_base_constraint {
public:
vector<std::pair<mpq, var_index>> m_coeffs;
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) {
lp_assert(false); // should not be called : todo!
}
unsigned size() const override {
return static_cast<unsigned>(m_coeffs.size());
}
vector<std::pair<mpq, var_index>> coeffs() const override { return m_coeffs; }
};
}

View file

@ -1,25 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <utility>
#include <memory>
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lar_core_solver_def.h"

View file

@ -1,831 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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/lp_primal_core_solver.h"
#include "util/lp/stacked_vector.h"
#include "util/lp/lar_solution_signature.h"
#include "util/lp/stacked_value.h"
namespace lp {
class lar_core_solver {
// m_sign_of_entering is set to 1 if the entering variable needs
// to grow and is set to -1 otherwise
int m_sign_of_entering_delta;
vector<std::pair<mpq, unsigned>> m_infeasible_linear_combination;
int m_infeasible_sum_sign; // 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_lower_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_lower_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 {
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);
unsigned get_number_of_non_ints() const;
void solve();
bool lower_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() {
lp_assert(m_r_solver.basis_heading_is_correct());
lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct());
lp_assert(m_column_types.size() == m_r_A.column_count());
m_stacked_simplex_strategy = settings().simplex_strategy();
m_stacked_simplex_strategy.push();
m_column_types.push();
// rational
if (!settings().use_tableau())
m_r_A.push();
m_r_lower_bounds.push();
m_r_upper_bounds.push();
if (!settings().use_tableau()) {
push_vector(m_r_pushed_basis, m_r_basis);
push_vector(m_r_columns_nz, m_r_solver.m_columns_nz);
push_vector(m_r_rows_nz, m_r_solver.m_rows_nz);
}
m_d_A.push();
if (!settings().use_tableau())
push_vector(m_d_pushed_basis, m_d_basis);
}
template <typename K>
void push_vector(stacked_vector<K> & pushed_vector, const vector<K> & vector) {
lp_assert(pushed_vector.size() <= vector.size());
for (unsigned i = 0; i < vector.size();i++) {
if (i == pushed_vector.size()) {
pushed_vector.push_back(vector[i]);
} else {
pushed_vector[i] = vector[i];
}
}
pushed_vector.push();
}
void pop_markowitz_counts(unsigned k) {
m_r_columns_nz.pop(k);
m_r_rows_nz.pop(k);
m_r_solver.m_columns_nz.resize(m_r_columns_nz.size());
m_r_solver.m_rows_nz.resize(m_r_rows_nz.size());
for (unsigned i = 0; i < m_r_columns_nz.size(); i++)
m_r_solver.m_columns_nz[i] = m_r_columns_nz[i];
for (unsigned i = 0; i < m_r_rows_nz.size(); i++)
m_r_solver.m_rows_nz[i] = m_r_rows_nz[i];
}
void pop(unsigned k) {
// rationals
if (!settings().use_tableau())
m_r_A.pop(k);
m_r_lower_bounds.pop(k);
m_r_upper_bounds.pop(k);
m_column_types.pop(k);
delete m_r_solver.m_factorization;
m_r_solver.m_factorization = nullptr;
m_r_x.resize(m_r_A.column_count());
m_r_solver.m_costs.resize(m_r_A.column_count());
m_r_solver.m_d.resize(m_r_A.column_count());
if(!settings().use_tableau())
pop_markowitz_counts(k);
m_d_A.pop(k);
// doubles
delete m_d_solver.m_factorization;
m_d_solver.m_factorization = nullptr;
m_d_x.resize(m_d_A.column_count());
pop_basis(k);
m_stacked_simplex_strategy.pop(k);
settings().simplex_strategy() = m_stacked_simplex_strategy;
lp_assert(m_r_solver.basis_heading_is_correct());
lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct());
}
bool need_to_presolve_with_double_solver() const {
return settings().simplex_strategy() == simplex_strategy_enum::lu;
}
template <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_lower_bound:
if (x == m_r_solver.m_lower_bounds[j])
return false;
delta = m_r_solver.m_lower_bounds[j] - x;
m_r_solver.m_x[j] = m_r_solver.m_lower_bounds[j];
break;
case at_fixed:
case at_upper_bound:
if (x == m_r_solver.m_upper_bounds[j])
return false;
delta = m_r_solver.m_upper_bounds[j] - x;
x = m_r_solver.m_upper_bounds[j];
break;
case free_of_bounds: {
return false;
}
case not_at_bound:
switch (m_column_types[j]) {
case column_type::free_column:
return false;
case column_type::upper_bound:
delta = m_r_solver.m_upper_bounds[j] - x;
x = m_r_solver.m_upper_bounds[j];
break;
case column_type::lower_bound:
delta = m_r_solver.m_lower_bounds[j] - x;
x = m_r_solver.m_lower_bounds[j];
break;
case column_type::boxed:
if (x > m_r_solver.m_upper_bounds[j]) {
delta = m_r_solver.m_upper_bounds[j] - x;
x += m_r_solver.m_upper_bounds[j];
} else {
delta = m_r_solver.m_lower_bounds[j] - x;
x = m_r_solver.m_lower_bounds[j];
}
break;
case column_type::fixed:
delta = m_r_solver.m_lower_bounds[j] - x;
x = m_r_solver.m_lower_bounds[j];
break;
default:
lp_assert(false);
}
break;
default:
lp_unreachable();
}
m_r_solver.remove_column_from_inf_set(j);
return true;
}
void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) {
lp_assert(m_r_solver.inf_set_is_correct());
for (auto &t : signature) {
unsigned j = t.first;
if (m_r_heading[j] >= 0)
continue;
auto pos_type = t.second;
numeric_pair<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.var();
unsigned jb = m_r_solver.m_basis[i];
m_r_solver.update_x_with_delta_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc));
}
CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false);
}
lp_assert(m_r_solver.inf_set_is_correct());
}
template <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;
lp_assert(m_r_heading[j] < 0);
auto pos_type = t.second;
switch (pos_type) {
case at_lower_bound:
s.m_x[j] = s.m_lower_bounds[j];
break;
case at_fixed:
case at_upper_bound:
s.m_x[j] = s.m_upper_bounds[j];
break;
case free_of_bounds: {
s.m_x[j] = zero_of_type<K>();
continue;
}
case not_at_bound:
switch (m_column_types[j]) {
case column_type::free_column:
lp_assert(false); // unreachable
case column_type::upper_bound:
s.m_x[j] = s.m_upper_bounds[j];
break;
case column_type::lower_bound:
s.m_x[j] = s.m_lower_bounds[j];
break;
case column_type::boxed:
if (settings().random_next() % 2) {
s.m_x[j] = s.m_lower_bounds[j];
} else {
s.m_x[j] = s.m_upper_bounds[j];
}
break;
case column_type::fixed:
s.m_x[j] = s.m_lower_bounds[j];
break;
default:
lp_assert(false);
}
break;
default:
lp_unreachable();
}
}
lp_assert(is_zero_vector(s.m_b));
s.solve_Ax_eq_b();
}
template <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;
lp_assert(l->get_status() == LU_status::OK);
for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) {
unsigned entering = trace_of_basis_change[i];
unsigned leaving = trace_of_basis_change[i+1];
bool good_e = basis_heading[entering] >= 0 && cs.m_basis_heading[entering] < 0;
bool good_l = basis_heading[leaving] < 0 && cs.m_basis_heading[leaving] >= 0;
if (!good_e && !good_l) continue;
if (good_e && !good_l) {
while (!leav_q.empty() && cs.m_basis_heading[leav_q.front()] < 0)
leav_q.pop();
if (!leav_q.empty()) {
leaving = leav_q.front();
leav_q.pop();
} else {
entr_q.push(entering);
continue;
}
} else if (!good_e && good_l) {
while (!entr_q.empty() && cs.m_basis_heading[entr_q.front()] >= 0)
entr_q.pop();
if (!entr_q.empty()) {
entering = entr_q.front();
entr_q.pop();
} else {
leav_q.push(leaving);
continue;
}
}
lp_assert(cs.m_basis_heading[entering] < 0);
lp_assert(cs.m_basis_heading[leaving] >= 0);
if (l->get_status() == LU_status::OK) {
l->prepare_entering(entering, w); // to init vector w
l->replace_column(zero_of_type<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();
lp_assert(settings().use_tableau());
bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading);
if (!r) { // it is the case where m_d_solver gives a degenerated basis
prepare_solver_x_with_signature_tableau(signature); // still are going to use the signature partially
m_r_solver.find_feasible_solution();
m_d_basis = m_r_basis;
m_d_heading = m_r_heading;
m_d_nbasis = m_r_nbasis;
delete m_d_solver.m_factorization;
m_d_solver.m_factorization = nullptr;
} else {
prepare_solver_x_with_signature_tableau(signature);
m_r_solver.start_tracing_basis_changes();
m_r_solver.find_feasible_solution();
if (settings().get_cancel_flag())
return;
m_r_solver.stop_tracing_basis_changes();
// and now catch up in the double solver
lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2);
catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver);
}
lp_assert(r_basis_is_OK());
}
bool adjust_x_of_column(unsigned j) {
/*
if (m_r_solver.m_basis_heading[j] >= 0) {
return false;
}
if (m_r_solver.column_is_feasible(j)) {
return false;
}
m_r_solver.snap_column_to_bound_tableau(j);
lp_assert(m_r_solver.column_is_feasible(j));
m_r_solver.m_inf_set.erase(j);
*/
lp_assert(false);
return true;
}
bool catch_up_in_lu_tableau(const vector<unsigned> & trace_of_basis_change, const vector<int> & basis_heading) {
lp_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;
}
}
lp_assert(m_r_solver.m_basis_heading[entering] < 0);
lp_assert(m_r_solver.m_basis_heading[leaving] >= 0);
m_r_solver.change_basis_unconditionally(entering, leaving);
if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) {
// unroll the last step
m_r_solver.change_basis_unconditionally(leaving, entering);
#ifdef Z3DEBUG
bool t =
#endif
m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]);
#ifdef Z3DEBUG
lp_assert(t);
#endif
return false;
}
}
lp_assert(r_basis_is_OK());
return true;
}
bool r_basis_is_OK() const {
#ifdef Z3DEBUG
if (!m_r_solver.m_settings.use_tableau())
return true;
for (unsigned j : m_r_solver.m_basis) {
lp_assert(m_r_solver.m_A.m_columns[j].size() == 1);
}
for (unsigned j =0; j < m_r_solver.m_basis_heading.size(); j++) {
if (m_r_solver.m_basis_heading[j] >= 0) continue;
if (m_r_solver.m_column_types[j] == column_type::fixed) continue;
lp_assert(static_cast<unsigned>(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size());
lp_assert( m_r_solver.m_basis_heading[j] <= -1);
}
#endif
return true;
}
void solve_on_signature(const lar_solution_signature & signature, const vector<unsigned> & changes_of_basis) {
SASSERT(!settings().use_tableau());
if (m_r_solver.m_factorization == nullptr) {
for (unsigned j = 0; j < changes_of_basis.size(); j+=2) {
unsigned entering = changes_of_basis[j];
unsigned leaving = changes_of_basis[j + 1];
m_r_solver.change_basis_unconditionally(entering, leaving);
}
init_factorization(m_r_solver.m_factorization, m_r_A, m_r_basis, settings());
} else {
catch_up_in_lu(changes_of_basis, m_d_solver.m_basis_heading, m_r_solver);
}
if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back
catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver);
m_r_solver.find_feasible_solution();
m_d_basis = m_r_basis;
m_d_heading = m_r_heading;
m_d_nbasis = m_r_nbasis;
delete m_d_solver.m_factorization;
m_d_solver.m_factorization = nullptr;
} else {
prepare_solver_x_with_signature(signature, m_r_solver);
m_r_solver.start_tracing_basis_changes();
m_r_solver.find_feasible_solution();
if (settings().get_cancel_flag())
return;
m_r_solver.stop_tracing_basis_changes();
// and now catch up in the double solver
lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2);
catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver);
}
}
void create_double_matrix(static_matrix<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.add_new_element(i, c.var(), 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();
lp_assert(signature.size() == 0);
for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) {
if (solver.m_basis_heading[j] < 0) {
signature[j] = solver.get_non_basic_column_value_position(j);
}
}
}
void get_bounds_for_double_solver() {
unsigned n = m_n();
m_d_lower_bounds.resize(n);
m_d_upper_bounds.resize(n);
double delta = find_delta_for_strict_boxed_bounds().get_double();
if (delta > 0.000001)
delta = 0.000001;
for (unsigned j = 0; j < n; j++) {
if (lower_bound_is_set(j)) {
const auto & lb = m_r_solver.m_lower_bounds[j];
m_d_lower_bounds[j] = lb.x.get_double() + delta * lb.y.get_double();
}
if (upper_bound_is_set(j)) {
const auto & ub = m_r_solver.m_upper_bounds[j];
m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double();
lp_assert(!lower_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_lower_bounds[j]));
}
}
}
void scale_problem_for_doubles(
static_matrix<double, double>& A,
vector<double> & lower_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_lower_bound(j)) {
lower_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 lower_bound_is_set(unsigned j) const {
switch (m_column_types[j]) {
case column_type::free_column:
case column_type::upper_bound:
return false;
case column_type::lower_bound:
case column_type::boxed:
case column_type::fixed:
return true;
default:
lp_assert(false);
}
return false;
}
bool upper_bound_is_set(unsigned j) const {
switch (m_column_types[j]) {
case column_type::free_column:
case column_type::lower_bound:
return false;
case column_type::upper_bound:
case column_type::boxed:
case column_type::fixed:
return true;
default:
lp_assert(false);
}
return false;
}
void update_delta(mpq& delta, numeric_pair<mpq> const& l, numeric_pair<mpq> const& u) const {
lp_assert(l <= u);
if (l.x < u.x && l.y > u.y) {
mpq delta1 = (u.x - l.x) / (l.y - u.y);
if (delta1 < delta) {
delta = delta1;
}
}
lp_assert(l.x + delta * l.y <= u.x + delta * u.y);
}
mpq find_delta_for_strict_boxed_bounds() const{
mpq delta = numeric_traits<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_lower_bounds[j], m_r_upper_bounds[j]);
}
return delta;
}
mpq find_delta_for_strict_bounds(const mpq & initial_delta) const{
mpq delta = initial_delta;
for (unsigned j = 0; j < m_r_A.column_count(); j++ ) {
if (lower_bound_is_set(j))
update_delta(delta, m_r_lower_bounds[j], m_r_x[j]);
if (upper_bound_is_set(j))
update_delta(delta, m_r_x[j], m_r_upper_bounds[j]);
}
return delta;
}
void init_column_row_nz_for_r_solver() {
m_r_solver.init_column_row_non_zeroes();
}
bool column_is_fixed(unsigned j) const {
return m_column_types()[j] == column_type::fixed ||
( m_column_types()[j] == column_type::boxed &&
m_r_solver.m_lower_bounds[j] == m_r_solver.m_upper_bounds[j]);
}
const impq & lower_bound(unsigned j) const {
lp_assert(m_column_types()[j] == column_type::fixed ||
m_column_types()[j] == column_type::boxed ||
m_column_types()[j] == column_type::lower_bound);
return m_r_lower_bounds[j];
}
const impq & upper_bound(unsigned j) const {
lp_assert(m_column_types()[j] == column_type::fixed ||
m_column_types()[j] == column_type::boxed ||
m_column_types()[j] == column_type::upper_bound);
return m_r_upper_bounds[j];
}
bool column_is_bounded(unsigned j) const {
switch(m_column_types()[j]) {
case column_type::fixed:
case column_type::boxed:
return true;
default:
return false;
}
}
const vector<unsigned>& r_basis() const { return m_r_basis; }
const vector<unsigned>& r_nbasis() const { return m_r_nbasis; }
};
}

View file

@ -1,324 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <string>
#include "util/vector.h"
#include "util/lp/lar_core_solver.h"
#include "util/lp/lar_solution_signature.h"
namespace lp {
lar_core_solver::lar_core_solver(
lp_settings & settings,
const column_namer & column_names
):
m_infeasible_sum_sign(0),
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_lower_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_lower_bounds,
m_d_upper_bounds,
settings,
column_names){}
void lar_core_solver::init_costs(bool first_time) {
lp_assert(false); // should not be called
// lp_assert(this->m_x.size() >= this->m_n());
// lp_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);
// lp_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_lower_bounds[j]) {
this->m_infeasibility += this->m_lower_bounds[j] - x;
this->m_costs[j] = -1;
} else {
this->m_costs[j] = numeric_traits<T>::zero();
}
break;
case lower_bound:
if (x < this->m_lower_bounds[j]) {
this->m_costs[j] = -1;
this->m_infeasibility += this->m_lower_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:
lp_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 lower_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;
}*/
lp_assert(false);
return true;
}
void lar_core_solver::calculate_pivot_row(unsigned i) {
m_r_solver.calculate_pivot_row(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() {
CASSERT("A_off", 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.var()));
}
}
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));
}
}
}
unsigned lar_core_solver::get_number_of_non_ints() const {
unsigned n = 0;
for (auto & x : m_r_solver.m_x) {
if (x.is_int() == false)
n++;
}
return n;
}
void lar_core_solver::solve() {
TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";);
lp_assert(m_r_solver.non_basic_columns_are_set_correctly());
lp_assert(m_r_solver.inf_set_is_correct());
TRACE("find_feas_stats", tout << "infeasibles = " << m_r_solver.m_inf_set.size() << ", int_infs = " << get_number_of_non_ints() << std::endl;);
if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) {
m_r_solver.set_status(lp_status::OPTIMAL);
TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";);
return;
}
++settings().st().m_need_to_solve_inf;
CASSERT("A_off", !m_r_solver.A_mult_x_is_off());
lp_assert((!settings().use_tableau()) || r_basis_is_OK());
if (need_to_presolve_with_double_solver()) {
TRACE("lar_solver", tout << "presolving\n";);
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() == lp_status::TIME_EXHAUSTED) {
m_r_solver.set_status(lp_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);
lp_assert(!settings().use_tableau() || r_basis_is_OK());
} else {
if (!settings().use_tableau()) {
TRACE("lar_solver", tout << "no tablau\n";);
bool snapped = m_r_solver.snap_non_basic_x_to_bound();
lp_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) //todo : should it be set?
m_r_solver.find_feasible_solution();
else {
m_r_solver.solve();
}
lp_assert(!settings().use_tableau() || r_basis_is_OK());
}
if (m_r_solver.get_status() == lp_status::INFEASIBLE) {
fill_not_improvable_zero_sum();
} else if (m_r_solver.get_status() != lp_status::UNBOUNDED) {
m_r_solver.set_status(lp_status::OPTIMAL);
}
lp_assert(r_basis_is_OK());
lp_assert(m_r_solver.non_basic_columns_are_set_correctly());
lp_assert(m_r_solver.inf_set_is_correct());
TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";);
}
}

View file

@ -1,28 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
#include "util/debug.h"
#include "util/lp/lp_settings.h"
#include <unordered_map>
namespace lp {
typedef std::unordered_map<unsigned, non_basic_column_value_position> lar_solution_signature;
}

File diff suppressed because it is too large Load diff

View file

@ -1,651 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
#include <utility>
#include "util/debug.h"
#include "util/buffer.h"
#include <unordered_map>
#include <unordered_set>
#include <string>
#include "util/lp/lar_constraints.h"
#include <functional>
#include "util/lp/lar_core_solver.h"
#include <algorithm>
#include "util/lp/numeric_pair.h"
#include "util/lp/scaler.h"
#include "util/lp/lp_primal_core_solver.h"
#include "util/lp/random_updater.h"
#include <stack>
#include "util/lp/stacked_value.h"
#include "util/lp/stacked_vector.h"
#include "util/lp/implied_bound.h"
#include "util/lp/bound_analyzer_on_row.h"
#include "util/lp/conversion_helper.h"
#include "util/lp/int_solver.h"
#include "util/lp/nra_solver.h"
#include "util/lp/bound_propagator.h"
namespace lp {
class lar_solver : public column_namer {
#if Z3DEBUG_CHECK_UNIQUE_TERMS
struct term_hasher {
std::size_t operator()(const lar_term *t) const
{
using std::size_t;
using std::hash;
using std::string;
size_t seed = 0;
for (const auto& p : t->m_coeffs) {
hash_combine(seed, p);
}
return seed;
}
};
struct term_ls_comparer {
bool operator()(const lar_term *a, const lar_term* b) const
{
// a is contained in b
for (auto & p : a->m_coeffs) {
auto t = b->m_coeffs.find(p.first);
if (t == b->m_coeffs.end())
return false;
if (p.second != t->second)
return false;
}
// zz is contained in b
for (auto & p : b->m_coeffs) {
auto t = a->m_coeffs.find(p.first);
if (t == a->m_coeffs.end())
return false;
if (p.second != t->second)
return false;
}
return true;
}
};
std::unordered_set<lar_term*, term_hasher, term_ls_comparer> m_set_of_terms;
#endif
//////////////////// fields //////////////////////////
std::unordered_set<unsigned> m_cube_rounded_columns;
std::unordered_set<unsigned> m_cube_rounded_rows;
lp_settings m_settings;
lp_status m_status;
stacked_value<simplex_strategy_enum> m_simplex_strategy;
stacked_value<int> m_infeasible_column; // such can be found at the initialization step
public:
lar_core_solver m_mpq_lar_core_solver;
private:
int_solver * m_int_solver;
public:
const var_index m_terms_start_index;
var_register m_var_register;
var_register m_term_register;
stacked_vector<ul_pair> m_columns_to_ul_pairs;
vector<lar_base_constraint*> m_constraints;
stacked_value<unsigned> m_constraint_count;
// the set of column indices j such that bounds have changed for j
int_set m_columns_with_changed_bound;
int_set m_rows_with_changed_bounds;
int_set m_basic_columns_with_changed_cost;
// these are basic columns with the value changed, so the the corresponding row in the tableau
// does not sum to zero anymore
int_set m_incorrect_columns;
stacked_value<int> m_infeasible_column_index; // such can be found at the initialization step
stacked_value<unsigned> m_term_count;
vector<lar_term*> m_terms;
indexed_vector<mpq> m_column_buffer;
// end of fields
unsigned terms_start_index() const { return m_terms_start_index; }
const vector<lar_term*> & terms() const { return m_terms; }
lar_term const& term(unsigned i) const { return *m_terms[i]; }
const vector<lar_base_constraint*>& constraints() const {
return m_constraints;
}
void set_int_solver(int_solver * int_slv) {
m_int_solver = int_slv;
}
int_solver * get_int_solver() {
return m_int_solver;
}
unsigned constraint_count() const;
const lar_base_constraint& get_constraint(unsigned ci) const;
////////////////// methods ////////////////////////////////
static_matrix<mpq, numeric_pair<mpq>> & A_r();
static_matrix<mpq, numeric_pair<mpq>> const & A_r() const;
static_matrix<double, double> & A_d();
static_matrix<double, double > const & A_d() const;
static bool valid_index(unsigned j){ return static_cast<int>(j) >= 0;}
bool column_is_int(unsigned j) const;
bool column_value_is_int(unsigned j) const {
return m_mpq_lar_core_solver.m_r_x[j].is_int();
}
const impq& get_column_value(unsigned j) const {
return m_mpq_lar_core_solver.m_r_x[j];
}
void set_column_value(unsigned j, const impq& v) {
m_mpq_lar_core_solver.m_r_x[j] = v;
}
const mpq& get_column_value_rational(unsigned j) const {
return m_mpq_lar_core_solver.m_r_x[j].x;
}
bool is_term(var_index j) const;
bool column_is_fixed(unsigned j) const;
public:
// init region
bool strategy_is_undecided() const;
var_index add_var(unsigned ext_j, bool is_integer);
var_index add_named_var(unsigned ext_j, bool is_integer, const std::string&);
void register_new_ext_var_index(unsigned ext_v, bool is_int);
bool external_is_used(unsigned) const;
bool term_is_int(const lar_term * t) const;
bool var_is_int(var_index v) const;
void add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int);
void add_new_var_to_core_fields_for_doubles(bool register_in_basis);
void add_new_var_to_core_fields_for_mpq(bool register_in_basis);
// terms
bool all_vars_are_registered(const vector<std::pair<mpq, var_index>> & coeffs);
var_index add_term(const vector<std::pair<mpq, var_index>> & coeffs, unsigned ext_i);
var_index add_term_undecided(const vector<std::pair<mpq, var_index>> & coeffs);
bool term_coeffs_are_ok(const vector<std::pair<mpq, var_index>> & coeffs);
void push_and_register_term(lar_term* t);
void add_row_for_term(const lar_term * term, unsigned term_ext_index);
void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index);
void add_basic_var_to_core_fields();
constraint_index add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) ;
bool compare_values(var_index j, lconstraint_kind kind, const mpq & right_side);
bool compare_values(impq const& lhs, lconstraint_kind k, const mpq & rhs);
void update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index);
void update_column_type_and_bound_with_ub(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index);
void update_column_type_and_bound_with_no_ub(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index);
void update_bound_with_ub_lb(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index);
void update_bound_with_no_ub_lb(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index);
void update_bound_with_ub_no_lb(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index);
void update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index);
void add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci);
void set_infeasible_column(unsigned j) {
set_status(lp_status::INFEASIBLE);
m_infeasible_column = j;
}
void add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term,
lconstraint_kind kind, const mpq & right_side);
unsigned row_of_basic_column(unsigned) const;
void decide_on_strategy_and_adjust_initial_state();
void adjust_initial_state();
void adjust_initial_state_for_lu();
void adjust_initial_state_for_tableau_rows();
// this fills the last row of A_d and sets the basis column: -1 in the last column of the row
void fill_last_row_of_A_d(static_matrix<double, double> & A, const lar_term* ls);
//end of init region
lp_settings & settings();
lp_settings const & settings() const;
void clear();
lar_solver();
void set_track_pivoted_rows(bool v);
bool get_track_pivoted_rows() const;
virtual ~lar_solver();
unsigned adjust_term_index(unsigned j) const;
bool use_lu() const;
bool sizes_are_correct() const;
bool implied_bound_is_correctly_explained(implied_bound const & be, const vector<std::pair<mpq, unsigned>> & explanation) const;
void analyze_new_bounds_on_row(
unsigned row_index,
bound_propagator & bp);
void analyze_new_bounds_on_row_tableau(
unsigned row_index,
bound_propagator & bp);
void substitute_basis_var_in_terms_for_row(unsigned i);
void calculate_implied_bounds_for_row(unsigned i, bound_propagator & bp);
unsigned adjust_column_index_to_term_index(unsigned j) const;
unsigned map_term_index_to_column_index(unsigned j) const;
var_index local_to_external(var_index idx) const { return is_term(idx)?
m_term_register.local_to_external(idx) : m_var_register.local_to_external(idx); }
unsigned number_of_vars() const { return m_var_register.size(); }
var_index external_to_local(unsigned j) const {
var_index local_j;
if (
m_var_register.external_is_used(j, local_j) ||
m_term_register.external_is_used(j, local_j))
{
return local_j;
}
else
return -1;
}
bool column_has_upper_bound(unsigned j) const {
return m_mpq_lar_core_solver.m_r_solver.column_has_upper_bound(j);
}
bool column_has_lower_bound(unsigned j) const {
return m_mpq_lar_core_solver.m_r_solver.column_has_lower_bound(j);
}
const impq& get_upper_bound(unsigned j) const {
return m_mpq_lar_core_solver.m_r_solver.m_upper_bounds[j];
}
const impq& get_lower_bound(unsigned j) const {
return m_mpq_lar_core_solver.m_r_solver.m_lower_bounds[j];
}
void propagate_bounds_on_a_term(const lar_term& t, bound_propagator & bp, unsigned term_offset);
void explain_implied_bound(implied_bound & ib, bound_propagator & bp);
bool term_is_used_as_row(unsigned term) const;
void propagate_bounds_on_terms(bound_propagator & bp);
// goes over touched rows and tries to induce bounds
void propagate_bounds_for_touched_rows(bound_propagator & bp);
lp_status get_status() const;
void set_status(lp_status s);
lp_status find_feasible_solution();
lp_status solve();
void fill_explanation_from_infeasible_column(explanation & evidence) const;
unsigned get_total_iterations() const;
// see http://research.microsoft.com/projects/z3/smt07.pdf
// This method searches for a feasible solution with as many different values of variables, reverenced in vars, as it can find
// Attention, after a call to this method the non-basic variables don't necesserarly stick to their bounds anymore
vector<unsigned> get_list_of_all_var_indices() const;
void push();
static void clean_popped_elements(unsigned n, int_set& set);
static void shrink_inf_set_after_pop(unsigned n, int_set & set);
void pop(unsigned k);
vector<constraint_index> get_all_constraint_indices() const;
bool maximize_term_on_tableau(const lar_term & term,
impq &term_max);
bool costs_are_zeros_for_r_solver() const;
bool reduced_costs_are_zeroes_for_r_solver() const;
void set_costs_to_zero(const lar_term & term);
void prepare_costs_for_r_solver(const lar_term & term);
bool maximize_term_on_corrected_r_solver(lar_term & term,
impq &term_max);
// starting from a given feasible state look for the maximum of the term
// return true if found and false if unbounded
lp_status maximize_term(unsigned j_or_term,
impq &term_max);
const lar_term & get_term(unsigned j) const;
void pop_core_solver_params();
void pop_core_solver_params(unsigned k);
void set_upper_bound_witness(var_index j, constraint_index ci);
void set_lower_bound_witness(var_index j, constraint_index ci);
void substitute_terms_in_linear_expression( const vector<std::pair<mpq, var_index>>& left_side_with_terms,
vector<std::pair<mpq, var_index>> &left_side) const;
void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j);
void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j);
bool use_tableau() const;
bool use_tableau_costs() const;
void detect_rows_of_column_with_bound_change(unsigned j);
void adjust_x_of_column(unsigned j);
bool row_is_correct(unsigned i) const;
bool ax_is_correct() const;
bool tableau_with_costs() const;
bool costs_are_used() const;
void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair<mpq> & delta);
void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j);
void detect_rows_with_changed_bounds_for_column(unsigned j);
void detect_rows_with_changed_bounds();
inline bool is_base(unsigned j) const {
return m_mpq_lar_core_solver.m_r_heading[j] >= 0;
}
bool move_non_basic_columns_to_bounds();
bool move_non_basic_column_to_bounds(unsigned j);
void set_value_for_nbasic_column(unsigned j, const impq & new_val);
void update_x_and_inf_costs_for_columns_with_changed_bounds();
void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau();
void restore_rounded_columns();
void solve_with_core_solver();
numeric_pair<mpq> get_basic_var_value_from_row(unsigned i);
template <typename K, typename L>
void add_last_rows_to_lu(lp_primal_core_solver<K,L> & s);
bool x_is_correct() const;
bool var_is_registered(var_index vj) const;
unsigned constraint_stack_size() const;
void fill_last_row_of_A_r(static_matrix<mpq, numeric_pair<mpq>> & A, const lar_term * ls);
template <typename U, typename V>
void create_matrix_A(static_matrix<U, V> & matr);
template <typename U, typename V>
void copy_from_mpq_matrix(static_matrix<U, V> & matr);
bool try_to_set_fixed(column_info<mpq> & ci);
column_type get_column_type(unsigned j) const;
bool all_constrained_variables_are_registered(const vector<std::pair<mpq, var_index>>& left_side);
constraint_index add_constraint(const vector<std::pair<mpq, var_index>>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm);
bool all_constraints_hold() const;
bool constraint_holds(const lar_base_constraint & constr, std::unordered_map<var_index, mpq> & var_map) const;
bool the_relations_are_of_same_type(const vector<std::pair<mpq, unsigned>> & evidence, lconstraint_kind & the_kind_of_sum) const;
static void register_in_map(std::unordered_map<var_index, mpq> & coeffs, const lar_base_constraint & cn, const mpq & a);
static void register_monoid_in_map(std::unordered_map<var_index, mpq> & coeffs, const mpq & a, unsigned j);
bool the_left_sides_sum_to_zero(const vector<std::pair<mpq, unsigned>> & evidence) const;
bool the_right_sides_do_not_sum_to_zero(const vector<std::pair<mpq, unsigned>> & evidence);
bool explanation_is_correct(explanation&) const;
bool inf_explanation_is_correct() const;
mpq sum_of_right_sides_of_explanation(explanation &) const;
bool has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const;
bool has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const;
bool has_value(var_index var, mpq& value) const;
void get_infeasibility_explanation(explanation &) const;
void get_infeasibility_explanation_for_inf_sign(
explanation & exp,
const vector<std::pair<mpq, unsigned>> & inf_row,
int inf_sign) const;
void get_model(std::unordered_map<var_index, mpq> & variable_values) const;
void get_model_do_not_care_about_diff_vars(std::unordered_map<var_index, mpq> & variable_values) const;
std::string get_variable_name(var_index vi) const;
void set_variable_name(var_index vi, std::string);
// ********** print region start
std::ostream& print_constraint(constraint_index ci, std::ostream & out) const;
std::ostream& print_constraints(std::ostream& out) const ;
std::ostream& print_constraint_indices_only(constraint_index ci, std::ostream & out) const;
std::ostream& print_terms(std::ostream& out) const;
std::ostream& print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const;
std::ostream& print_left_side_of_constraint_indices_only(const lar_base_constraint * c, std::ostream & out) const;
std::ostream& print_term(lar_term const& term, std::ostream & out) const;
std::ostream& print_term_as_indices(lar_term const& term, std::ostream & out) const;
std::ostream& print_constraint(const lar_base_constraint * c, std::ostream & out) const;
std::ostream& print_constraint_indices_only(const lar_base_constraint * c, std::ostream & out) const;
std::ostream& print_implied_bound(const implied_bound& be, std::ostream & out) const;
std::ostream& print_values(std::ostream& out) const;
mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map<var_index, mpq> & var_map) const;
void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector<unsigned>& column_list);
void random_update(unsigned sz, var_index const * vars);
void pivot_fixed_vars_from_basis();
void pop();
bool column_represents_row_in_tableau(unsigned j);
void make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j);
void remove_last_row_and_column_from_tableau(unsigned j);
void remove_last_column_from_A();
void remove_last_column_from_basis_tableau(unsigned j);
void remove_last_column_from_tableau();
void pop_tableau();
void clean_inf_set_of_r_solver_after_pop();
void shrink_explanation_to_minimum(vector<std::pair<mpq, constraint_index>> & explanation) const;
bool column_value_is_integer(unsigned j) const {
return get_column_value(j).is_int();
}
bool column_is_real(unsigned j) const {
return !column_is_int(j);
}
bool model_is_int_feasible() const;
const impq & column_lower_bound(unsigned j) const {
return m_mpq_lar_core_solver.lower_bound(j);
}
const impq & column_upper_bound(unsigned j) const {
return m_mpq_lar_core_solver.upper_bound(j);
}
bool column_is_bounded(unsigned j) const {
return m_mpq_lar_core_solver.column_is_bounded(j);
}
void get_bound_constraint_witnesses_for_column(unsigned j, constraint_index & lc, constraint_index & uc) const {
const ul_pair & ul = m_columns_to_ul_pairs[j];
lc = ul.lower_bound_witness();
uc = ul.upper_bound_witness();
}
indexed_vector<mpq> & get_column_in_lu_mode(unsigned j) {
m_column_buffer.clear();
m_column_buffer.resize(A_r().row_count());
m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer);
return m_column_buffer;
}
bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const;
const row_strip<mpq> & get_row(unsigned i) {
return A_r().m_rows[i];
}
unsigned get_base_column_in_row(unsigned row_index) const {
return m_mpq_lar_core_solver.m_r_solver.get_base_column_in_row(row_index);
}
constraint_index get_column_upper_bound_witness(unsigned j) const {
return m_columns_to_ul_pairs()[j].upper_bound_witness();
}
constraint_index get_column_lower_bound_witness(unsigned j) const {
return m_columns_to_ul_pairs()[j].lower_bound_witness();
}
void subs_term_columns(lar_term& t) {
vector<std::pair<unsigned,unsigned>> columns_to_subs;
for (const auto & m : t) {
unsigned tj = adjust_column_index_to_term_index(m.var());
if (tj == m.var()) continue;
columns_to_subs.push_back(std::make_pair(m.var(), tj));
}
for (const auto & p : columns_to_subs) {
t.subst_index(p.first, p.second);
}
}
std::ostream& print_column_info(unsigned j, std::ostream& out) const {
return m_mpq_lar_core_solver.m_r_solver.print_column_info(j, out);
}
bool has_int_var() const;
bool has_inf_int() const {
for (unsigned j = 0; j < column_count(); j++) {
if (column_is_int(j) && ! column_value_is_int(j))
return true;
}
return false;
}
bool r_basis_has_inf_int() const {
for (unsigned j : r_basis()) {
if (column_is_int(j) && ! column_value_is_int(j))
return true;
}
return false;
}
lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; }
bool column_corresponds_to_term(unsigned) const;
void catch_up_in_updating_int_solver();
var_index to_column(unsigned ext_j) const;
bool tighten_term_bounds_by_delta(unsigned, const impq&);
void round_to_integer_solution();
void update_delta_for_terms(const impq & delta, unsigned j, const vector<unsigned>&);
void fill_vars_to_terms(vector<vector<unsigned>> & vars_to_terms);
unsigned column_count() const { return A_r().column_count(); }
const vector<unsigned> & r_basis() const { return m_mpq_lar_core_solver.r_basis(); }
const vector<unsigned> & r_nbasis() const { return m_mpq_lar_core_solver.r_nbasis(); }
bool get_equality_and_right_side_for_term_on_current_x(unsigned i, mpq &rs, constraint_index& ci, bool &upper_bound) const;
bool remove_from_basis(unsigned);
lar_term get_term_to_maximize(unsigned ext_j) const;
void set_cut_strategy(unsigned cut_frequency);
bool sum_first_coords(const lar_term& t, mpq & val) const;
void fix_Ax_b_on_rounded_rows();
void fix_Ax_b_on_rounded_row(unsigned);
void collect_rounded_rows_to_fix();
};
}

View file

@ -1,156 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/indexed_vector.h"
#include "util/map.h"
namespace lp {
class lar_term {
// the term evaluates to sum of m_coeffs
u_map<mpq> m_coeffs;
public:
lar_term() {}
void add_monomial(const mpq& c, unsigned j) {
if (c.is_zero())
return;
auto *e = m_coeffs.find_core(j);
if (e == nullptr) {
m_coeffs.insert(j, c);
} else {
e->get_data().m_value += c;
if (e->get_data().m_value.is_zero())
m_coeffs.erase(j);
}
}
void add_var(unsigned j) {
rational c(1);
add_monomial(c, j);
}
bool is_empty() const {
return m_coeffs.empty(); // && is_zero(m_v);
}
unsigned size() const { return static_cast<unsigned>(m_coeffs.size()); }
template <typename T>
const T & coeffs() const {
return m_coeffs;
}
lar_term(const vector<std::pair<mpq, unsigned>>& coeffs) {
for (const auto & p : coeffs) {
add_monomial(p.first, p.second);
}
}
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.m_value, p.m_key));
}
return ret;
}
// j is the basic variable to substitute
void subst(unsigned j, indexed_vector<mpq> & li) {
auto* it = m_coeffs.find_core(j);
if (it == nullptr) return;
const mpq & b = it->get_data().m_value;
for (unsigned it_j :li.m_index) {
add_monomial(- b * li.m_data[it_j], it_j);
}
m_coeffs.erase(j);
}
// the monomial ax[j] is substituted by ax[k]
void subst_index(unsigned j, unsigned k) {
auto* it = m_coeffs.find_core(j);
if (it == nullptr) return;
mpq b = it->get_data().m_value;
m_coeffs.erase(j);
m_coeffs.insert(k, b);
}
bool contains(unsigned j) const {
return m_coeffs.contains(j);
}
void negate() {
for (auto & t : m_coeffs)
t.m_value.neg();
}
template <typename T>
T apply(const vector<T>& x) const {
T ret(0);
for (const auto & t : m_coeffs) {
ret += t.m_value * x[t.m_key];
}
return ret;
}
void clear() {
m_coeffs.reset();
}
struct ival {
unsigned m_var;
const mpq & m_coeff;
ival(unsigned var, const mpq & val) : m_var(var), m_coeff(val) {
}
unsigned var() const { return m_var;}
const mpq & coeff() const { return m_coeff; }
};
struct const_iterator {
//fields
u_map< mpq>::iterator m_it;
typedef const_iterator self_type;
typedef ival value_type;
typedef ival reference;
// typedef std::pair<const unsigned, mpq>* pointer;
typedef int difference_type;
typedef std::forward_iterator_tag iterator_category;
reference operator*() const {
return ival(m_it->m_key, m_it->m_value);
}
self_type operator++() { self_type i = *this; m_it++; return i; }
self_type operator++(int) { m_it++; return *this; }
const_iterator(u_map<mpq>::iterator it) : m_it(it) {}
bool operator==(const self_type &other) const {
return m_it == other.m_it;
}
bool operator!=(const self_type &other) const { return !(*this == other); }
};
const_iterator begin() const { return m_coeffs.begin();}
const_iterator end() const { return m_coeffs.end(); }
};
}

View file

@ -1,52 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
enum class lia_move {
sat,
branch,
cut,
conflict,
continue_with_check,
undef,
unsat
};
inline std::string lia_move_to_string(lia_move m) {
switch (m) {
case lia_move::sat:
return "sat";
case lia_move::branch:
return "branch";
case lia_move::cut:
return "cut";
case lia_move::conflict:
return "conflict";
case lia_move::continue_with_check:
return "continue_with_check";
case lia_move::undef:
return "undef";
case lia_move::unsat:
return "unsat";
default:
lp_assert(false);
};
return "strange";
}
}

View file

@ -1,54 +0,0 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/lar_solver.h"
namespace lp {
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_lower_bound(unsigned j) const {
return m_lar_solver.m_mpq_lar_core_solver.m_r_lower_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(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_value(m_improved_lower_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);
TRACE("try_add_bound", m_lar_solver.print_implied_bound(found_bound, tout););
}
} else {
m_improved_lower_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));
TRACE("try_add_bound", m_lar_solver.print_implied_bound(m_ibounds.back(), tout););
}
} else { // the upper bound case
if (try_get_value(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);
TRACE("try_add_bound", m_lar_solver.print_implied_bound(found_bound, tout););
}
} 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));
TRACE("try_add_bound", m_lar_solver.print_implied_bound(m_ibounds.back(), tout););
}
}
}
}

View file

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

View file

@ -1,740 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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 lp {
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;
unsigned m_iters_with_no_cost_growing;
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 {
TRACE("feas",
if (m_inf_set.size()) {
tout << "column " << m_inf_set.m_index[0] << " is infeasible" << std::endl;
print_column_info(m_inf_set.m_index[0], tout);
} else {
tout << "x is feasible\n";
}
);
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;
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<static_matrix<T, X>> * m_factorization;
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
const vector<column_type> & m_column_types;
const vector<X> & m_lower_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;
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;
int_set* m_pivoted_rows;
bool m_look_for_feasible_solution_only;
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> & lower_bound_values,
const vector<X> & upper_bound_values);
void allocate_basis_heading();
void init();
virtual ~lp_core_solver_base() {
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 {
unsigned m = m_A.row_count();
for (unsigned i = 0; i < m; i++) {
unsigned bj = m_basis[i];
lp_assert(m_A.m_columns[bj].size() > 0);
if (m_A.m_columns[bj].size() > 1)
return true;
for (const auto & c : m_A.m_columns[bj]) {
if (m_A.get_val(c) != one_of_type<T>())
return true;
else
break;
}
}
return false;
}
bool reduced_costs_are_correct_tableau() const {
if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
return true;
CASSERT("check_static_matrix", m_A.is_correct());
if (m_using_infeas_costs) {
if (infeasibility_costs_are_correct() == false) {
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])) {
return false;
}
} else {
auto d = m_costs[j];
for (const auto & cc : this->m_A.m_columns[j]) {
d -= this->m_costs[this->m_basis[cc.var()]] * this->m_A.get_val(cc);
}
if (m_d[j] != d) {
return false;
}
}
}
return true;
}
bool below_bound(const X & x, const X & bound) const {
return precise()? x < bound : below_bound_numeric<X>(x, bound, m_settings.primal_feasibility_tolerance);
}
bool above_bound(const X & x, const X & bound) const {
return precise()? x > bound : 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_lower_bounds[p]);
}
bool infeasibility_costs_are_correct() const;
bool infeasibility_cost_is_correct_for_column(unsigned j) const;
bool x_above_lower_bound(unsigned p) const {
return above_bound(m_x[p], m_lower_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_lower_bound(unsigned j) const {
return at_bound(m_x[j], m_lower_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_lower_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 lower_bounds_are_set() const { return false; }
X lower_bound_value(unsigned j) const { return m_lower_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_lower_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_lower_bounds[j];
return true;
case column_type::boxed:
if (x_is_at_bound(j))
break; // we should preserve x if possible
// snap randomly
if (m_settings.random_next() % 2 == 1)
m_x[j] = m_lower_bounds[j];
else
m_x[j] = m_upper_bounds[j];
return true;
case column_type::lower_bound:
if (x_is_at_lower_bound(j))
break;
m_x[j] = m_lower_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) {
bool ret = false;
lp_assert(m_basis_heading[j] < 0);
const auto & x = m_x[j];
switch (m_column_types[j]) {
case column_type::fixed:
lp_assert(m_lower_bounds[j] == m_upper_bounds[j]);
if (x != m_lower_bounds[j]) {
delta = m_lower_bounds[j] - x;
ret = true;;
}
break;
case column_type::boxed:
if (x < m_lower_bounds[j]) {
delta = m_lower_bounds[j] - x;
ret = true;;
}
if (x > m_upper_bounds[j]) {
delta = m_upper_bounds[j] - x;
ret = true;
}
break;
case column_type::lower_bound:
if (x < m_lower_bounds[j]) {
delta = m_lower_bounds[j] - x;
ret = true;
}
break;
case column_type::upper_bound:
if (x > m_upper_bounds[j]) {
delta = m_upper_bounds[j] - x;
ret = true;
}
break;
default:
break;
}
if (ret)
add_delta_to_x_and_do_not_track_feasibility(j, delta);
return ret;
}
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 remove_from_basis(unsigned j);
bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector<T> & w);
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) {
TRACE("lar_solver", tout << "entering = " << entering << ", leaving = " << leaving << "\n";);
lp_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) {
TRACE("lar_solver", tout << "entering = " << entering << ", leaving = " << leaving << "\n";);
lp_assert(m_basis_heading[entering] < 0);
lp_assert(m_basis_heading[leaving] >= 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::lower_bound:
if (!this->x_is_at_lower_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:
lp_assert(false);
break;
}
return true;
}
bool non_basic_columns_are_set_correctly() const {
for (unsigned j : this->m_nbasis)
if (!column_is_feasible(j)) {
TRACE("lp_core", tout << "inf col "; print_column_info(j, tout) << "\n";);
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_lower_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl;
break;
case column_type::lower_bound:
out << m_lower_bounds[j] << std::endl;
break;
case column_type::upper_bound:
out << m_upper_bounds[j] << std::endl;
break;
default:
break;
}
}
std::ostream& print_column_info(unsigned j, std::ostream & out) const {
if (j >= m_lower_bounds.size()) {
out << "[" << j << "] is not present\n";
return out;
}
if (m_settings.m_print_external_var_name)
out << "[" << j << "],\tname = "<< column_name(j) << "\t";
else
out << "v" << j << "= \t";
switch (m_column_types[j]) {
case column_type::fixed:
case column_type::boxed:
out << " [" << m_lower_bounds[j] << ", " << m_upper_bounds[j] << "]";
break;
case column_type::lower_bound:
out << " [" << m_lower_bounds[j] << "," << "oo" << "]";
break;
case column_type::upper_bound:
out << " [-oo, " << m_upper_bounds[j] << ']';
break;
case column_type::free_column:
out << " [-oo, oo]";
break;
default:
lp_assert(false);
}
// out << "basis heading = " << m_basis_heading[j] << std::endl;
out << "\tx = " << m_x[j];
if (m_basis_heading[j] >= 0)
out << " base\n";
else
out << " \n";
return out;
}
bool column_is_free(unsigned j) const { return this->m_column_type[j] == free; }
bool column_has_upper_bound(unsigned j) const {
switch(m_column_types[j]) {
case column_type::free_column:
case column_type::lower_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_lower_bounds[j] > m_upper_bounds[j])
return false;
}
return true;
}
bool column_has_lower_bound(unsigned j) const {
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;
}
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_x_with_feasibility_tracking(unsigned j, const X & v) {
m_x[j] = v;
track_column_feasibility(j);
}
void update_x_with_delta_and_track_feasibility(unsigned j, const X & del) {
m_x[j] += del;
track_column_feasibility(j);
}
void update_x_and_call_tracker(unsigned j, const X & v) {
TRACE("lar_solver", tout << "j = " << j << "\n";);
m_x[j] = v;
}
void add_delta_to_x_and_do_not_track_feasibility(unsigned j, const X & delta) {
m_x[j] += delta;
}
void track_column_feasibility(unsigned j) {
if (column_is_feasible(j))
remove_column_from_inf_set(j);
else
insert_column_into_inf_set(j);
}
void insert_column_into_inf_set(unsigned j) {
m_inf_set.insert(j);
lp_assert(!column_is_feasible(j));
}
void remove_column_from_inf_set(unsigned j) {
m_inf_set.erase(j);
lp_assert(column_is_feasible(j));
}
bool costs_on_nbasis_are_zeros() const {
lp_assert(this->basis_heading_is_correct());
for (unsigned j = 0; j < this->m_n(); j++) {
if (this->m_basis_heading[j] < 0)
lp_assert(is_zero(this->m_costs[j]));
}
return true;
}
unsigned & iters_with_no_cost_growing() {
return m_iters_with_no_cost_growing;
}
const unsigned & iters_with_no_cost_growing() const {
return m_iters_with_no_cost_growing;
}
void calculate_pivot_row(unsigned i);
unsigned get_base_column_in_row(unsigned row_index) const {
return m_basis[row_index];
}
};
}

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,212 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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 lp {
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> & lower_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,
lower_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_lower_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_lower_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 lower_bounds_are_set() const override { return true; }
};
}

View file

@ -1,752 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <algorithm>
#include <string>
#include "util/vector.h"
#include "util/lp/lp_dual_core_solver.h"
namespace lp {
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]) {
lp_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() == lp_status::OPTIMAL) {
return true;
}
if (this->total_iterations() > this->m_settings.max_total_number_of_iterations) { // debug !!!!
this->set_status(lp_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_lower_bound(unsigned p) {
lp_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_lower_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) {
lp_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_lower_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::lower_bound:
if (this->x_below_low_bound(p)) {
T del = get_edge_steepness_for_lower_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:
lp_assert(numeric_traits<T>::is_zero(this->m_d[p]));
return numeric_traits<T>::zero();
default:
lp_unreachable();
}
lp_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() != lp_status::UNSTABLE) {
this->set_status(lp_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() == lp_status::FLOATING_POINT_ERROR) {
return;
}
this->set_status(lp_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
lp_unreachable(); // not implemented yet
} else {
lp_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;
}
lp_unreachable();
case column_type::lower_bound:
if (this->x_below_low_bound(m_p)) {
return -1;
}
lp_unreachable();
case column_type::upper_bound:
if (this->x_above_upper_bound(m_p)) {
return 1;
}
lp_unreachable();
default:
lp_unreachable();
}
lp_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::lower_bound:
lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_lower_bounds[j]));
return m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
case column_type::upper_bound:
lp_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 lower_bound = this->x_is_at_lower_bound(j);
bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
return lower_bound == grawing;
}
case column_type::fixed: // is always dual feasible so we ignore 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_lower_bounds[m_p];
}
if (this->x_above_upper_bound(m_p)) {
return this->m_x[m_p] - this->m_upper_bounds[m_p];
}
lp_unreachable();
case column_type::lower_bound:
if (this->x_below_low_bound(m_p)) {
return this->m_x[m_p] - this->m_lower_bounds[m_p];
}
lp_unreachable();
case column_type::upper_bound:
if (this->x_above_upper_bound(m_p)) {
return get_edge_steepness_for_upper_bound(m_p);
}
lp_unreachable();
case column_type::fixed:
return this->m_x[m_p] - this->m_upper_bounds[m_p];
default:
lp_unreachable();
}
lp_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) {
lp_assert(this->x_is_at_bound(j));
if (this->x_is_at_lower_bound(j)) {
this->m_x[j] = this->m_upper_bounds[j];
} else {
this->m_x[j] = this->m_lower_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_lower_bounds[j];
break;
case column_type::boxed:
if (this->x_is_at_lower_bound(j)) {
this->m_x[j] = this->m_lower_bounds[j];
} else {
this->m_x[j] = this->m_upper_bounds[j];
}
break;
case column_type::lower_bound:
this->m_x[j] = this->m_lower_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:
lp_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->iters_with_no_cost_growing()++;
return false;
}
}
if (!problem_is_dual_feasible()) {
// todo : shift the costs!!!!
revert_to_previous_basis();
this->iters_with_no_cost_growing()++;
return false;
}
lp_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_lower_bound:
case at_fixed:
this->m_x[m_q] = this->m_lower_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:
lp_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(lp_status::FLOATING_POINT_ERROR); // complete failure
return;
}
recover_leaving();
if (!this->find_x_by_solving()) {
this->set_status(lp_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::lower_bound:
if (!this->x_is_at_lower_bound(j)) {
this->m_x[j] = this->m_lower_bounds[j];
return true;
}
break;
case column_type::boxed:
{
bool closer_to_lower_bound = abs(this->m_lower_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]);
if (closer_to_lower_bound) {
if (!this->x_is_at_lower_bound(j)) {
this->m_x[j] = this->m_lower_bounds[j];
return true;
}
} else {
if (!this->x_is_at_upper_bound(j)) {
this->m_x[j] = this->m_lower_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)) {
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 this->m_settings.random_next() % 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() == lp_status::TENTATIVE_DUAL_UNBOUNDED) {
this->set_status(lp_status::DUAL_UNBOUNDED);
} else {
this->set_status(lp_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>();
lp_assert(m_breakpoint_set.size() > 0);
for (auto j : m_breakpoint_set) {
T t;
if (this->x_is_at_lower_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_lower_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);
lp_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.empty()) {
set_status_to_tentative_dual_unbounded_or_dual_unbounded();
return false;
}
this->set_status(lp_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.empty()) {
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 = this->m_settings.random_next() % this->m_m();
if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) {
number_of_rows_to_try = this->m_m();
} else {
this->set_status(lp_status::FEASIBLE);
}
pricing_loop(number_of_rows_to_try, offset_in_rows);
lp_assert(problem_is_dual_feasible());
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // see the page 35
lp_assert(d_is_correct());
lp_assert(problem_is_dual_feasible());
lp_assert(this->basis_heading_is_correct());
//this->set_total_iterations(0);
this->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() != lp_status::FLOATING_POINT_ERROR && this->get_status() != lp_status::DUAL_UNBOUNDED && this->get_status() != lp_status::OPTIMAL &&
this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements
&& this->total_iterations() <= this->m_settings.max_total_number_of_iterations);
}
}

View file

@ -1,24 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/lp_dual_simplex_def.h"
template lp::mpq lp::lp_dual_simplex<lp::mpq, lp::mpq>::get_current_cost() const;
template void lp::lp_dual_simplex<lp::mpq, lp::mpq>::find_maximal_solution();
template double lp::lp_dual_simplex<double, double>::get_current_cost() const;
template void lp::lp_dual_simplex<double, double>::find_maximal_solution();

View file

@ -1,93 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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 lp {
template <typename T, typename X>
class lp_dual_simplex: public lp_solver<T, X> {
lp_dual_core_solver<T, X> * m_core_solver;
vector<T> m_b_copy;
vector<T> m_lower_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() override {
delete m_core_solver;
}
lp_dual_simplex() : m_core_solver(nullptr) {}
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() override;
T get_column_value(unsigned column) const override {
return this->get_column_value_with_core_solver(column, m_core_solver);
}
T get_current_cost() const override;
};
}

View file

@ -1,377 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/lp_dual_simplex.h"
namespace lp{
template <typename T, typename X> void lp_dual_simplex<T, X>::decide_on_status_after_stage1() {
switch (m_core_solver->get_status()) {
case lp_status::OPTIMAL:
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
this->m_status = lp_status::FEASIBLE;
} else {
this->m_status = lp_status::UNBOUNDED;
}
break;
case lp_status::DUAL_UNBOUNDED:
lp_unreachable();
case lp_status::ITERATIONS_EXHAUSTED:
this->m_status = lp_status::ITERATIONS_EXHAUSTED;
break;
case lp_status::TIME_EXHAUSTED:
this->m_status = lp_status::TIME_EXHAUSTED;
break;
case lp_status::FLOATING_POINT_ERROR:
this->m_status = lp_status::FLOATING_POINT_ERROR;
break;
default:
lp_unreachable();
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::fix_logical_for_stage2(unsigned j) {
lp_assert(j >= this->number_of_core_structurals());
switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) {
case column_type::lower_bound:
m_lower_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::lower_bound;
m_can_enter_basis[j] = true;
break;
case column_type::fixed:
this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::fixed;
m_can_enter_basis[j] = false;
break;
default:
lp_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::lower_bound:
m_lower_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::lower_bound;
m_can_enter_basis[j] = true;
break;
case column_type::fixed:
case column_type::upper_bound:
lp_unreachable();
case column_type::boxed:
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
m_lower_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:
lp_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(lp_status::FEASIBLE);
m_core_solver->solve();
switch (m_core_solver->get_status()) {
case lp_status::OPTIMAL:
this->m_status = lp_status::OPTIMAL;
break;
case lp_status::DUAL_UNBOUNDED:
this->m_status = lp_status::INFEASIBLE;
break;
case lp_status::TIME_EXHAUSTED:
this->m_status = lp_status::TIME_EXHAUSTED;
break;
case lp_status::FLOATING_POINT_ERROR:
this->m_status = lp_status::FLOATING_POINT_ERROR;
break;
default:
lp_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() {
lp_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_lower_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(lp_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) {
lp_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
lp_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];
lp_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::lower_bound: {
m_can_enter_basis[j] = true;
this->set_scaled_cost(j);
this->m_lower_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_lower_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_lower_bounds[j] = numeric_traits<T>::zero(); // is it needed?
break;
default:
lp_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;
lp_assert(get_column_type(j) != column_type::upper_bound);
if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) {
m_column_types_of_core_solver[j] = column_type::boxed;
this->m_lower_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_lower_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) {
lp_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::lower_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::lower_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_lower_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 == lp_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 == lp_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

@ -1,18 +0,0 @@
def_module_params('lp',
export=True,
params=(
('rep_freq', UINT, 0, 'the report frequency, in how many iterations print the cost and other info '),
('min', BOOL, False, 'minimize cost'),
('print_stats', BOOL, False, 'print statistic'),
('simplex_strategy', UINT, 0, 'simplex strategy for the solver'),
('enable_hnf', BOOL, True, 'enable hnf cuts'),
('bprop_on_pivoted_rows', BOOL, True, 'propagate bounds on rows changed by the pivot operation'),
('nla', BOOL, False, 'call nonlinear integer solver with incremental linearization')
))
('print_ext_var_names', BOOL, False, 'print external variable names')
))

View file

@ -1,42 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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_def.h"
#include "util/lp/lp_primal_core_solver_tableau_def.h"
namespace lp {
template void lp_primal_core_solver<double, double>::find_feasible_solution();
template void lp::lp_primal_core_solver<lp::mpq, lp::numeric_pair<lp::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 unsigned lp_primal_core_solver<mpq, numeric_pair<mpq> >::solve();
template void lp::lp_primal_core_solver<double, double>::clear_breakpoints();
template bool lp::lp_primal_core_solver<lp::mpq, lp::mpq>::update_basis_and_x_tableau(int, int, lp::mpq const&);
template bool lp::lp_primal_core_solver<double, double>::update_basis_and_x_tableau(int, int, double const&);
template bool lp::lp_primal_core_solver<lp::mpq, lp::numeric_pair<lp::mpq> >::update_basis_and_x_tableau(int, int, lp::numeric_pair<lp::mpq> const&);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,418 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
// this is a part of lp_primal_core_solver that deals with the tableau
#include "util/lp/lp_primal_core_solver.h"
namespace lp {
template <typename T, typename X> void lp_primal_core_solver<T, X>::one_iteration_tableau() {
int entering = choose_entering_column_tableau();
if (entering == -1) {
decide_on_status_when_cannot_find_entering();
}
else {
advance_on_entering_tableau(entering);
}
lp_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) {
TRACE("lar_solver", tout << "nothing leaving " << entering << "\n";);
this->set_status(lp_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();
lp_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 && this->m_settings.random_next() % 2 == 0) {
entering_iter = non_basis_iter;
}
}// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb);
if (entering_iter == m_non_basis_list.end())
return -1;
unsigned entering = *entering_iter;
m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1;
if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search)
m_sign_of_entering_delta = -m_sign_of_entering_delta;
m_non_basis_list.erase(entering_iter);
m_non_basis_list.push_back(entering);
return entering;
}
template <typename T, typename X>
unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
init_run_tableau();
if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) {
this->set_status(lp_status::FEASIBLE);
return 0;
}
if ((!numeric_traits<T>::precise()) && this->A_mult_x_is_off()) {
this->set_status(lp_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();
}
TRACE("lar_solver", tout << "one iteration tableau " << this->get_status() << "\n";);
switch (this->get_status()) {
case lp_status::OPTIMAL: // double check that we are at optimum
case lp_status::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(lp_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(lp_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(lp_status::UNKNOWN);
}
}
break;
case lp_status::TENTATIVE_UNBOUNDED:
this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(lp_status::FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
break;
case lp_status::UNBOUNDED:
if (this->current_x_is_infeasible()) {
init_reduced_costs();
this->set_status(lp_status::UNKNOWN);
}
break;
case lp_status::UNSTABLE:
lp_assert(! (numeric_traits<T>::precise()));
this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(lp_status::FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
break;
default:
break; // do nothing
}
} while (this->get_status() != lp_status::FLOATING_POINT_ERROR
&&
this->get_status() != lp_status::UNBOUNDED
&&
this->get_status() != lp_status::OPTIMAL
&&
this->get_status() != lp_status::INFEASIBLE
&&
this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements
&&
this->total_iterations() <= this->m_settings.max_total_number_of_iterations
&&
!(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)
&&
this->m_settings.get_cancel_flag() == false);
if (this->m_settings.get_cancel_flag()) {
this->set_status(lp_status::CANCELLED);
}
lp_assert(
this->get_status() == lp_status::FLOATING_POINT_ERROR
||
this->get_status() == lp_status::CANCELLED
||
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) {
CASSERT("A_off", this->A_mult_x_is_off() == false);
lp_assert(leaving >= 0 && entering >= 0);
lp_assert((this->m_settings.simplex_strategy() ==
simplex_strategy_enum::tableau_rows) ||
m_non_basis_list.back() == static_cast<unsigned>(entering));
lp_assert(this->m_using_infeas_costs || !is_neg(t));
lp_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);
CASSERT("A_off", this->A_mult_x_is_off() == false);
this->iters_with_no_cost_growing() = 0;
} else {
this->pivot_column_tableau(entering, this->m_basis_heading[leaving]);
this->change_basis(entering, leaving);
}
if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
return;
if (this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) {
if (need_to_switch_costs()) {
this->init_reduced_costs_tableau();
}
lp_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) {
CASSERT("A_off", !this->A_mult_x_is_off() );
this->update_x_tableau(entering, t * m_sign_of_entering_delta);
if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
return;
if (need_to_switch_costs()) {
init_reduced_costs_tableau();
}
this->iters_with_no_cost_growing() = 0;
}
template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_and_t_tableau(unsigned entering, X & t) {
unsigned k = 0;
bool unlimited = true;
unsigned row_min_nz = this->m_n() + 1;
m_leaving_candidates.clear();
auto & col = this->m_A.m_columns[entering];
unsigned col_size = col.size();
for (;k < col_size && unlimited; k++) {
const column_cell & c = col[k];
unsigned i = c.var();
const T & ed = this->m_A.get_val(c);
lp_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.var();
const T & ed = this->m_A.get_val(c);
lp_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 = this->m_settings.random_next() % m_leaving_candidates.size();
return m_leaving_candidates[k];
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run_tableau() {
CASSERT("A_off", this->A_mult_x_is_off() == false);
lp_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->iters_with_no_cost_growing() = 0;
lp_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.simplex_strategy() == simplex_strategy_enum::tableau_rows)
init_tableau_rows();
lp_assert(this->reduced_costs_are_correct_tableau());
lp_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) {
lp_assert(this->use_tableau());
lp_assert(entering != leaving);
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) {
this->add_delta_to_x_and_do_not_track_feasibility(entering, delta);
if (!this->m_using_infeas_costs) {
for (const auto & c : this->m_A.m_columns[entering]) {
unsigned i = c.var();
this->update_x_with_delta_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c));
}
} else { // m_using_infeas_costs == true
lp_assert(this->column_is_feasible(entering));
lp_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.var();
unsigned j = this->m_basis[i];
this->add_delta_to_x_and_do_not_track_feasibility(j, -delta * this->m_A.get_val(c));
update_inf_cost_for_column_tableau(j);
if (is_zero(this->m_costs[j]))
this->remove_column_from_inf_set(j);
else
this->insert_column_into_inf_set(j);
}
}
CASSERT("A_off", 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) {
lp_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows);
lp_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.var()]] * this->m_A.get_val(cc);
}
}
}
}
}

View file

@ -1,35 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <utility>
#include <memory>
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lp_primal_simplex_def.h"
template bool lp::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 lp::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 lp::lp_primal_simplex<double, double>::get_current_cost() const;
template double lp::lp_primal_simplex<double, double>::get_column_value(unsigned int) const;
template lp::lp_primal_simplex<double, double>::~lp_primal_simplex();
template lp::lp_primal_simplex<lp::mpq, lp::mpq>::~lp_primal_simplex();
template lp::mpq lp::lp_primal_simplex<lp::mpq, lp::mpq>::get_current_cost() const;
template lp::mpq lp::lp_primal_simplex<lp::mpq, lp::mpq>::get_column_value(unsigned int) const;
template void lp::lp_primal_simplex<double, double>::find_maximal_solution();
template void lp::lp_primal_simplex<lp::mpq, lp::mpq>::find_maximal_solution();

View file

@ -1,106 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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"
namespace lp {
template <typename T, typename X>
class lp_primal_simplex: public lp_solver<T, X> {
lp_primal_core_solver<T, X> * m_core_solver;
vector<X> m_lower_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(): m_core_solver(nullptr) {}
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 find_maximal_solution() override;
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() override;
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);
}
T get_column_value(unsigned column) const override {
return this->get_column_value_with_core_solver(column, m_core_solver);
}
T get_current_cost() const override;
};
}

View file

@ -1,365 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <string>
#include "util/vector.h"
#include "util/lp/lp_primal_simplex.h"
namespace lp {
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) {
lp_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::lower_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::lower_bound;
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
if (rs > 0) {
lp_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
// adding one artificial
this->m_column_types[artificial] = column_type::lower_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::lower_bound;
(*this->m_A)(row, slack_var) = numeric_traits<T>::one();
if (rs < 0) {
// adding one artificial
lp_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
this->m_column_types[artificial] = column_type::lower_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() {
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();
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) {
lp_assert(row < this->row_count());
auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row);
lp_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);
lp_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_lower_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::lower_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::lower_bound;
this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type<X>();
break;
default:
lp_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 = lp_status::OPTIMAL;
return;
}
m_lower_bounds.clear();
m_lower_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_lower_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() {
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)) {
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);
lp_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());
lp_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;;
}
lp_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

@ -1,25 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <memory>
#include "util/vector.h"
#include "util/lp/lp_settings_def.h"
template bool lp::vectors_are_equal<double>(vector<double> const&, vector<double> const&);
template bool lp::vectors_are_equal<lp::mpq>(vector<lp::mpq > const&, vector<lp::mpq> const&);

View file

@ -1,466 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
#include <string>
#include <algorithm>
#include <limits>
#include <iomanip>
#include "util/lp/lp_utils.h"
#include "util/stopwatch.h"
#include "util/lp/lp_types.h"
namespace lp {
enum class column_type {
free_column = 0,
lower_bound = 1,
upper_bound = 2,
boxed = 3,
fixed = 4
};
enum class simplex_strategy_enum {
undecided = 3,
tableau_rows = 0,
tableau_costs = 1,
lu = 2
};
std::string column_type_to_string(column_type t);
enum class lp_status {
UNKNOWN,
INFEASIBLE,
TENTATIVE_UNBOUNDED,
UNBOUNDED,
TENTATIVE_DUAL_UNBOUNDED,
DUAL_UNBOUNDED,
OPTIMAL,
FEASIBLE,
FLOATING_POINT_ERROR,
TIME_EXHAUSTED,
ITERATIONS_EXHAUSTED,
EMPTY,
UNSTABLE,
CANCELLED
};
// when the ratio of the vector length 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_lower_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
class lp_resource_limit {
public:
virtual bool get_cancel_flag() = 0;
};
struct stats {
unsigned m_make_feasible;
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;
unsigned m_max_cols;
unsigned m_max_rows;
unsigned m_gcd_calls;
unsigned m_gcd_conflicts;
unsigned m_cube_calls;
unsigned m_cube_success;
unsigned m_patches;
unsigned m_patches_success;
unsigned m_hnf_cutter_calls;
unsigned m_hnf_cuts;
unsigned m_nla_calls;
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;
stopwatch m_sw;
public:
default_lp_resource_limit(lp_settings& s): m_settings(s) {
m_sw.start();
}
bool get_cancel_flag() override {
return (m_sw.get_current_seconds() > 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;
// used for messages, for example, the computation progress messages
std::ostream* m_message_out;
stats m_stats;
random_gen m_rand;
public:
unsigned reps_in_scaler;
// when the absolute value of an element is less than pivot_epsilon
// in pivoting, we treat it as a zero
double pivot_epsilon;
// see Chatal, page 115
double positive_price_epsilon;
// a quotation "if some choice of the entering variable 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;
int c_partial_pivoting; // this is the constant c from page 410
unsigned depth_of_rook_search;
bool using_partial_pivoting;
// dissertation of Achim Koberstein
// if Bx - b is different at any component more that refactor_epsilon then we refactor
double refactor_tolerance;
double pivot_tolerance;
double zero_tolerance;
double drop_tolerance;
double tolerance_for_artificials;
double can_be_taken_to_basis_tolerance;
unsigned percent_of_entering_to_check; // we try to find a profitable column in a percentage of the columns
bool use_scaling;
double scaling_maximum;
double scaling_minimum;
double harris_feasibility_tolerance; // page 179 of Istvan Maros
double ignore_epsilon_of_harris;
unsigned max_number_of_iterations_with_no_improvements;
unsigned max_total_number_of_iterations;
double time_limit; // the maximum time limit of the total run time in seconds
// dual section
double dual_feasibility_tolerance; // // page 71 of the PhD thesis of Achim Koberstein
double primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein
double relative_primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein
// end of dual section
bool m_bound_propagation;
bool presolve_with_double_solver_for_lar;
simplex_strategy_enum m_simplex_strategy;
int report_frequency;
bool print_statistics;
unsigned column_norms_update_frequency;
bool scale_with_ratio;
double density_threshold;
bool use_breakpoints_in_feasibility_search;
unsigned max_row_length_for_bound_propagation;
bool backup_costs;
unsigned column_number_threshold_for_using_lu_in_lar_solver;
unsigned m_int_gomory_cut_period;
unsigned m_int_find_cube_period;
private:
unsigned m_hnf_cut_period;
public:
bool m_int_run_gcd_test;
bool m_int_pivot_fixed_vars_from_basis;
bool m_int_patch_only_integer_values;
unsigned limit_on_rows_for_hnf_cutter;
unsigned limit_on_columns_for_hnf_cutter;
bool m_enable_hnf;
bool m_print_external_var_name;
#ifdef Z3DEBUG
unsigned m_counter_for_debug;
#endif
unsigned hnf_cut_period() const { return m_hnf_cut_period; }
void set_hnf_cut_period(unsigned period) { m_hnf_cut_period = period; }
unsigned random_next() { return m_rand(); }
void set_random_seed(unsigned s) { m_rand.set_seed(s); }
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),
m_debug_out(&std::cout),
m_message_out(&std::cout),
reps_in_scaler(20),
pivot_epsilon(0.00000001),
positive_price_epsilon(1e-7),
entering_diag_epsilon (1e-8),
c_partial_pivoting (10), // this is the constant c from page 410
depth_of_rook_search (4),
using_partial_pivoting (true),
// dissertation of Achim Koberstein
// if Bx - b is different at any component more that refactor_epsilon then we refactor
refactor_tolerance ( 1e-4),
pivot_tolerance ( 1e-6),
zero_tolerance ( 1e-12),
drop_tolerance ( 1e-14),
tolerance_for_artificials ( 1e-4),
can_be_taken_to_basis_tolerance ( 0.00001),
percent_of_entering_to_check ( 5),// we try to find a profitable column in a percentage of the columns
use_scaling ( true),
scaling_maximum ( 1),
scaling_minimum ( 0.5),
harris_feasibility_tolerance ( 1e-7), // page 179 of Istvan Maros
ignore_epsilon_of_harris ( 10e-5),
max_number_of_iterations_with_no_improvements ( 2000000),
max_total_number_of_iterations ( 20000000),
time_limit ( std::numeric_limits<double>::max()), // the maximum time limit of the total run time in seconds
// dual section
dual_feasibility_tolerance ( 1e-7), // // page 71 of the PhD thesis of Achim Koberstein
primal_feasibility_tolerance ( 1e-7), // page 71 of the PhD thesis of Achim Koberstein
relative_primal_feasibility_tolerance ( 1e-9), // page 71 of the PhD thesis of Achim Koberstein
m_bound_propagation ( true),
presolve_with_double_solver_for_lar(true),
m_simplex_strategy(simplex_strategy_enum::tableau_rows),
report_frequency(1000),
print_statistics(false),
column_norms_update_frequency(12000),
scale_with_ratio(true),
density_threshold(0.7),
use_breakpoints_in_feasibility_search(false),
max_row_length_for_bound_propagation(300),
backup_costs(true),
column_number_threshold_for_using_lu_in_lar_solver(4000),
m_int_gomory_cut_period(4),
m_int_find_cube_period(4),
m_hnf_cut_period(4),
m_int_run_gcd_test(true),
m_int_pivot_fixed_vars_from_basis(false),
m_int_patch_only_integer_values(true),
limit_on_rows_for_hnf_cutter(75),
limit_on_columns_for_hnf_cutter(150),
m_enable_hnf(true)
m_print_external_var_name(false),
#ifdef Z3DEBUG
, m_counter_for_debug(0)
#endif
{}
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
simplex_strategy_enum simplex_strategy() const {
return m_simplex_strategy;
}
simplex_strategy_enum & simplex_strategy() {
return m_simplex_strategy;
}
bool use_lu() const {
return m_simplex_strategy == simplex_strategy_enum::lu;
}
bool use_tableau() const {
return m_simplex_strategy == simplex_strategy_enum::tableau_rows ||
m_simplex_strategy == simplex_strategy_enum::tableau_costs;
}
bool use_tableau_rows() const {
return m_simplex_strategy == simplex_strategy_enum::tableau_rows;
}
#ifdef Z3DEBUG
static unsigned ddd; // used for debugging
#endif
}; // 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;
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, typename K >
bool vectors_are_equal_(const T & a, const K &b) {
if (a.size() != b.size())
return false;
for (unsigned i = 0; i < a.size(); i++){
if (a[i] != b[i]) {
return false;
}
}
return true;
}
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) {
lp_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;
}
}
inline static bool is_rational(const impq & n) { return is_zero(n.y); }
inline static mpq fractional_part(const impq & n) {
lp_assert(is_rational(n));
return n.x - floor(n.x);
}
inline static mpq fractional_part(const mpq & n) {
return n - floor(n);
}
#if Z3DEBUG
bool D();
#endif
}

View file

@ -1,121 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <cmath>
#include <string>
#include "util/vector.h"
#include "util/lp/lp_settings.h"
namespace lp {
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::lower_bound: return "lower_bound";
case column_type::upper_bound: return "upper_bound";
case column_type::free_column: return "free_column";
default: lp_unreachable();
}
return "unknown"; // it is unreachable
}
const char* lp_status_to_string(lp_status status) {
switch (status) {
case lp_status::UNKNOWN: return "UNKNOWN";
case lp_status::INFEASIBLE: return "INFEASIBLE";
case lp_status::UNBOUNDED: return "UNBOUNDED";
case lp_status::TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED";
case lp_status::DUAL_UNBOUNDED: return "DUAL_UNBOUNDED";
case lp_status::OPTIMAL: return "OPTIMAL";
case lp_status::FEASIBLE: return "FEASIBLE";
case lp_status::FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR";
case lp_status::TIME_EXHAUSTED: return "TIME_EXHAUSTED";
case lp_status::ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED";
case lp_status::EMPTY: return "EMPTY";
case lp_status::UNSTABLE: return "UNSTABLE";
default:
lp_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;
lp_unreachable();
return lp_status::UNKNOWN; // it is unreachable
}
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])) {
return false;
}
}
} else {
for (unsigned i = 0; i < n; i ++){
if (std::abs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) {
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])) {
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) {
return false;
}
}
}
return true;
}
#ifdef Z3DEBUG
unsigned lp_settings::ddd = 0;
#endif
}

View file

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

View file

@ -1,266 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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/bound_analyzer_on_row.h"
namespace lp {
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; // this is the matrix of constraints
vector<T> m_b; // the right side vector
unsigned m_first_stage_iterations;
unsigned m_second_stage_iterations;
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;
unsigned m_slacks;
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_settings m_settings;
lp_solver():
m_A(nullptr), // this is the matrix of constraints
m_first_stage_iterations (0),
m_second_stage_iterations (0),
m_artificials (0),
m_slacks (0),
m_status(lp_status::UNKNOWN)
{}
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_variable_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_lower_bound(unsigned i, T bound) {
column_info<T> *ci = get_or_create_column_info(i);
ci->set_lower_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_lower_bound(unsigned i) {
get_or_create_column_info(i)->unset_lower_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 & lower_bound);
bool get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & lower_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 lower_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;
}
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;
}
}
};
}

View file

@ -1,569 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <string>
#include <algorithm>
#include "util/vector.h"
#include "util/lp/lp_solver.h"
namespace lp {
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>()) : it->second;
}
template <typename T, typename X>
std::string lp_solver<T, X>::get_variable_name(unsigned j) const { // j here is the core solver index
if (!m_settings.m_print_external_var_name)
return std::string("v")+T_to_string(j);
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) {
lp_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(){
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.empty())
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()) {
lp_assert(ci->upper_bound_is_set());
ci->set_fixed_value(ci->get_upper_bound());
} else {
lp_assert(ci->lower_bound_is_set());
ci->set_fixed_value(ci->get_lower_bound());
}
}
}
template <typename T, typename X> bool lp_solver<T, X>::get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & lower_bound) {
lower_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->lower_bound_is_set()) {
lower_bound += ci->get_lower_bound() * a;
} else {
return false;
}
} else {
if (ci->upper_bound_is_set()) {
lower_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 & lower_bound) {
lower_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->lower_bound_is_set()) {
lower_bound += ci->get_lower_bound() * a;
} else {
return false;
}
} else {
if (ci->upper_bound_is_set()) {
lower_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 = lp_status::INFEASIBLE;
return true;
}
T lower_bound;
bool lb = get_minimal_row_value(row, lower_bound);
if (lb) {
T diff = lower_bound - rs;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
// lower_bound > rs + m_settings.refactor_epsilon
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;
}
}
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 = lp_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 = lp_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 = lp_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 lower_bound;
T rs = m_constraints[row_index].m_rs;
if (row_is_zero(row)) {
if (rs < zero_of_type<X>())
m_status = lp_status::INFEASIBLE;
return true;
}
if (get_minimal_row_value(row, lower_bound)) {
T diff = lower_bound - rs;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
// lower_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);
}
lp_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;
lp_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.empty()) {
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);
lp_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);
lp_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);
lp_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();
lp_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();
lp_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) {
lp_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>::lower_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--) {
lp_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 - lower_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_lower_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
lp_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 *= T(-1);
}
lp_assert(ci->is_fixed() == false);
this->m_costs[j] = cost * this->m_column_scale[j];
}
}

View file

@ -1,26 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
typedef unsigned var_index;
typedef unsigned constraint_index;
typedef unsigned row_index;
enum lconstraint_kind { LE = -2, LT = -1 , GE = 2, GT = 1, EQ = 0, NE = 3 };
}

View file

@ -1,27 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/lp_utils.h"
#ifdef lp_for_z3
namespace lp {
double numeric_traits<double>::g_zero = 0.0;
double numeric_traits<double>::g_one = 1.0;
}
#endif

View file

@ -1,256 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include <string>
#include "util/lp/numeric_pair.h"
#include "util/debug.h"
#include <unordered_map>
#include <unordered_set>
template <typename C>
std::ostream& print_vector(const C & t, std::ostream & out) {
for (const auto & p : t)
out << p << " ";
return out;
}
template <typename C, typename D>
bool contains(const C & collection, const D & key) {
return collection.find(key) != collection.end();
}
template <typename C>
std::ostream& print_vector(const C * t, unsigned size, std::ostream & out) {
for (unsigned i = 0; i < size; i++ )
out << t[i] << " ";
out << std::endl;
return out;
}
template <typename A, typename B>
bool try_get_value(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 Z3DEBUG
#define Z3DEBUG 1
#endif
namespace lp {
template <typename K>
bool is_non_decreasing(const K& v) {
auto a = v.begin();
if (a == v.end())
return true; // v is empty
auto b = v.begin();
b++;
for (; b != v.end(); a++, b++) {
if (*a > *b)
return false;
}
return true;
}
template <typename T>
void print_linear_combination_of_column_indices_only(const T & coeffs, std::ostream & out) {
bool first = true;
for (const auto & it : coeffs) {
auto val = it.coeff();
if (first) {
first = false;
} else {
if (val.is_pos()) {
out << " + ";
} else {
out << " - ";
val = -val;
}
}
if (val == 1)
out << " ";
else
out << T_to_string(val);
out << "v" << it.var();
}
}
template <typename T>
void print_linear_combination_of_column_indices_only(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) {
bool first = true;
for (const auto & it : coeffs) {
auto val = it.first;
if (first) {
first = false;
} else {
if (val.is_pos()) {
out << " + ";
} else {
out << " - ";
val = -val;
}
}
if (val == 1)
out << " ";
else
out << T_to_string(val);
out << "v" << it.second;
}
}
inline void throw_exception(std::string && str) {
throw default_exception(std::move(str));
}
typedef z3_exception exception;
#define lp_assert(_x_) { SASSERT(_x_); }
inline void lp_unreachable() { lp_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 is_integer(const X & v) { return numeric_traits<X>::is_int(v); }
template <typename X> inline X ceil_ratio(const X & a, const X & b) { return numeric_traits<X>::ceil_ratio(a, b); }
template <typename X> inline X floor_ratio(const X & a, const X & b) { return numeric_traits<X>::floor_ratio(a, b); }
template <typename X> inline bool precise() { return numeric_traits<X>::precise(); }
// returns true if a factor of b
template <typename T>
bool is_proper_factor(const T & a, const T & b) {
if (a.size() >= b.size())
return false;
SASSERT(lp::is_non_decreasing(a) && lp::is_non_decreasing(b));
auto i = a.begin();
auto j = b.begin();
while (true) {
if (i == a.end()) {
return true;
}
if (j == b.end()) {
return false;
}
if (*i == *j) {
i++;j++;
continue;
}
j++;
}
}
// b / a, where a is a factor of b and both vectors are sorted
template <typename T>
svector<unsigned> vector_div(const T & b, const T & a) {
SASSERT(lp::is_non_decreasing(a));
SASSERT(lp::is_non_decreasing(b));
SASSERT(is_proper_factor(a, b));
svector<unsigned> r;
auto i = a.begin();
auto j = b.begin();
while (true) {
if (j == b.end()) {
break;
}
if (i == a.end()) {
r.push_back(*j);
j++;
continue;
}
if (*i == *j) {
i++;j++;
continue;
}
r.push_back(*j);
j++;
}
return r;
}
} // namespace lp
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<lp::numeric_pair<lp::mpq>> {
inline size_t operator()(const lp::numeric_pair<lp::mpq> & v) const {
size_t seed = 0;
hash_combine(seed, v.x);
hash_combine(seed, v.y);
return seed;
}
};
inline
void intersect_set(std::unordered_set<unsigned>& p, const std::unordered_set<unsigned>& w) {
for (auto it = p.begin(); it != p.end(); ) {
auto iit = it;
iit ++;
if (w.find(*it) == w.end())
p.erase(it);
it = iit;
}
}
}

View file

@ -1,84 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <utility>
#include <memory>
#include <string>
#include "util/vector.h"
#include "util/debug.h"
#include "util/lp/lu_def.h"
namespace lp {
template double dot_product<double, double>(vector<double> const&, vector<double> const&);
template lu<static_matrix<double, double>>::lu(static_matrix<double, double> const&, vector<unsigned int>&, lp_settings&);
template void lu<static_matrix<double, double>>::push_matrix_to_tail(tail_matrix<double, double>*);
template void lu<static_matrix<double, double>>::replace_column(double, indexed_vector<double>&, unsigned);
template void lu<static_matrix<double, double>>::solve_Bd(unsigned int, indexed_vector<double>&, indexed_vector<double>&);
template lu<static_matrix<double, double>>::~lu();
template void lu<static_matrix<mpq, mpq>>::push_matrix_to_tail(tail_matrix<mpq, mpq>*);
template void lu<static_matrix<mpq, mpq>>::solve_Bd(unsigned int, indexed_vector<mpq>&, indexed_vector<mpq>&);
template lu<static_matrix<mpq, mpq>>::~lu();
template void lu<static_matrix<mpq, impq>>::push_matrix_to_tail(tail_matrix<mpq, impq >*);
template void lu<static_matrix<mpq, impq>>::solve_Bd(unsigned int, indexed_vector<mpq>&, indexed_vector<mpq>&);
template lu<static_matrix<mpq, impq>>::~lu();
template mpq dot_product<mpq, mpq>(vector<mpq > const&, vector<mpq > const&);
template void init_factorization<static_matrix<double, double>>
(lu<static_matrix<double, double>>*&, static_matrix<double, double>&, vector<unsigned int>&, lp_settings&);
template void init_factorization<static_matrix<mpq, mpq>>
(lu<static_matrix<mpq,mpq>>*&, static_matrix<mpq, mpq>&, vector<unsigned int>&, lp_settings&);
template void init_factorization<static_matrix<mpq, impq>>(lu<static_matrix<mpq, impq> >*&, static_matrix<mpq, impq >&, vector<unsigned int>&, lp_settings&);
template void print_matrix<square_sparse_matrix<double, double>>(square_sparse_matrix<double, double>&, std::ostream & out);
template void print_matrix<static_matrix<mpq,mpq>>(static_matrix<mpq, mpq>&, std::ostream&);
template void print_matrix<static_matrix<mpq, impq> >(static_matrix<mpq, impq >&, std::ostream&);
template void print_matrix<static_matrix<double, double>>(static_matrix<double, double>&, std::ostream & out);
#ifdef Z3DEBUG
template bool lu<static_matrix<double, double>>::is_correct(const vector<unsigned>& basis);
template bool lu<static_matrix<mpq, impq>>::is_correct( vector<unsigned int> const &);
template dense_matrix<double, double> get_B<static_matrix<double, double>>(lu<static_matrix<double, double>>&, const vector<unsigned>& basis);
template dense_matrix<mpq, mpq> get_B<static_matrix<mpq, mpq>>(lu<static_matrix<mpq, mpq>>&, vector<unsigned int> const&);
#endif
template bool lu<static_matrix<double, double>>::pivot_the_row(int); // NOLINT
template void lu<static_matrix<double, double>>::init_vector_w(unsigned int, indexed_vector<double>&);
template void lu<static_matrix<double, double>>::solve_By(vector<double>&);
template void lu<static_matrix<double, double>>::solve_By_when_y_is_ready_for_X(vector<double>&);
template void lu<static_matrix<double, double>>::solve_yB_with_error_check(vector<double>&, const vector<unsigned>& basis);
template void lu<static_matrix<double, double>>::solve_yB_with_error_check_indexed(indexed_vector<double>&, vector<int> const&, const vector<unsigned> & basis, const lp_settings&);
template void lu<static_matrix<mpq, mpq>>::replace_column(mpq, indexed_vector<mpq>&, unsigned);
template void lu<static_matrix<mpq, mpq>>::solve_By(vector<mpq >&);
template void lu<static_matrix<mpq, mpq>>::solve_By_when_y_is_ready_for_X(vector<mpq >&);
template void lu<static_matrix<mpq, mpq>>::solve_yB_with_error_check(vector<mpq >&, const vector<unsigned>& basis);
template void lu<static_matrix<mpq, mpq>>::solve_yB_with_error_check_indexed(indexed_vector<mpq>&, vector< int > const&, const vector<unsigned> & basis, const lp_settings&);
template void lu<static_matrix<mpq, impq> >::solve_yB_with_error_check_indexed(indexed_vector<mpq>&, vector< int > const&, const vector<unsigned> & basis, const lp_settings&);
template void lu<static_matrix<mpq, impq> >::init_vector_w(unsigned int, indexed_vector<mpq>&);
template void lu<static_matrix<mpq, impq> >::replace_column(mpq, indexed_vector<mpq>&, unsigned);
template void lu<static_matrix<mpq, impq> >::solve_Bd_faster(unsigned int, indexed_vector<mpq>&);
template void lu<static_matrix<mpq, impq> >::solve_By(vector<impq >&);
template void lu<static_matrix<mpq, impq> >::solve_By_when_y_is_ready_for_X(vector<impq >&);
template void lu<static_matrix<mpq, impq> >::solve_yB_with_error_check(vector<mpq >&, const vector<unsigned>& basis);
template void lu<static_matrix<mpq, mpq>>::solve_By(indexed_vector<mpq>&);
template void lu<static_matrix<double, double>>::solve_By(indexed_vector<double>&);
template void lu<static_matrix<double, double>>::solve_yB_indexed(indexed_vector<double>&);
template void lu<static_matrix<mpq, impq> >::solve_yB_indexed(indexed_vector<mpq>&);
template void lu<static_matrix<mpq, mpq>>::solve_By_for_T_indexed_only(indexed_vector<mpq>&, lp_settings const&);
template void lu<static_matrix<double, double>>::solve_By_for_T_indexed_only(indexed_vector<double>&, lp_settings const&);
#ifdef Z3DEBUG
template void print_matrix<tail_matrix<double, double>>(tail_matrix<double, double>&, std::ostream&);
#endif
}

View file

@ -1,383 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
for matrix B we have
t0*...*tn-1*B = Q*U*R
here ti are matrices corresponding to pivot operations,
including columns and rows swaps,
or a multiplication matrix row by a number
Q, R - permutations and U is an upper triangular matrix
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/vector.h"
#include "util/debug.h"
#include <algorithm>
#include <set>
#include "util/lp/square_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 lp {
template <typename T, typename X> // print the nr x nc submatrix at the top left corner
void print_submatrix(square_sparse_matrix<T, X> & m, unsigned mr, unsigned nc);
template <typename M>
void print_matrix(M &m, std::ostream & out);
template <typename T, typename X>
X dot_product(const vector<T> & a, const vector<X> & b) {
lp_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 Z3DEBUG
m_one_over_val = numeric_traits<T>::one() / m_val;
#endif
}
bool is_dense() const override { return false; }
one_elem_on_diag(const one_elem_on_diag & o);
#ifdef Z3DEBUG
unsigned m_m;
unsigned m_n;
void set_number_of_rows(unsigned m) override { m_m = m; m_n = m; }
void set_number_of_columns(unsigned n) override { m_m = n; m_n = n; }
T m_one_over_val;
T get_elem (unsigned i, unsigned j) const override;
unsigned row_count() const override { return m_m; } // not defined }
unsigned column_count() const override { return m_m; } // not defined }
#endif
void apply_from_left(vector<X> & w, lp_settings &) override {
w[m_i] /= m_val;
}
void apply_from_right(vector<T> & w) override {
w[m_i] /= m_val;
}
void apply_from_right(indexed_vector<T> & w) override {
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) override;
void conjugate_by_permutation(permutation_matrix<T, X> & p) {
// this = p * this * p(-1)
#ifdef Z3DEBUG
// auto rev = p.get_reverse();
// auto deb = ((*this) * rev);
// deb = p * deb;
#endif
m_i = p.apply_reverse(m_i);
#ifdef Z3DEBUG
// lp_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 M>
class lu {
LU_status m_status;
public:
typedef typename M::coefftype T;
typedef typename M::argtype X;
// the fields
unsigned m_dim;
const M & m_A;
permutation_matrix<T, X> m_Q;
permutation_matrix<T, X> m_R;
permutation_matrix<T, X> m_r_wave;
square_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;
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;
// 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(const M & A,
vector<unsigned>& basis,
lp_settings & settings);
lu(const M & A, lp_settings&);
void debug_test_of_basis(const M & 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, square_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 Z3DEBUG
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[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);
bool is_correct();
#ifdef Z3DEBUG
dense_matrix<T, X> tail_product();
dense_matrix<T, X> get_left_side(const vector<unsigned>& basis);
dense_matrix<T, X> get_left_side();
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() {
lp_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();
lp_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.var()];
if (h < 0) {
continue;
}
columns_to_replace.insert(c.var());
}
}
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();
lp_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) {
lp_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 M>
void init_factorization(lu<M>* & factorization, M & m_A, vector<unsigned> & m_basis, lp_settings &m_settings);
#ifdef Z3DEBUG
template <typename T, typename X, typename M>
dense_matrix<T, X> get_B(lu<M>& f, const vector<unsigned>& basis);
template <typename T, typename X, typename M>
dense_matrix<T, X> get_B(lu<M>& f);
#endif
}

View file

@ -1,990 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <string>
#include <algorithm>
#include <set>
#include "util/vector.h"
#include <utility>
#include "util/debug.h"
#include "util/lp/lu.h"
namespace lp {
template <typename T, typename X, typename M> // print the nr x nc submatrix at the top left corner
void print_submatrix(square_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 M>
void print_matrix(M &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(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>
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 Z3DEBUG
m_m = m_n = o.m_m;
m_one_over_val = numeric_traits<T>::one() / o.m_val;
#endif
}
#ifdef Z3DEBUG
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 M>
lu<M>::lu(const M& A,
vector<unsigned>& basis,
lp_settings & settings):
m_status(LU_status::OK),
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_failure(false),
m_row_eta_work_vector(A.row_count()),
m_refactor_counter(0) {
lp_assert(!(numeric_traits<T>::precise() && settings.use_tableau()));
#ifdef Z3DEBUG
debug_test_of_basis(A, basis);
#endif
++m_settings.st().m_num_factorizations;
create_initial_factorization();
#ifdef Z3DEBUG
// lp_assert(check_correctness());
#endif
}
template <typename M>
lu<M>::lu(const M& A,
lp_settings & settings):
m_status(LU_status::OK),
m_dim(A.row_count()),
m_A(A),
m_Q(m_dim),
m_R(m_dim),
m_r_wave(m_dim),
m_U(A), // create the square matrix that eventually will be factorized
m_settings(settings),
m_failure(false),
m_row_eta_work_vector(A.row_count()),
m_refactor_counter(0) {
lp_assert(A.row_count() == A.column_count());
create_initial_factorization();
#ifdef Z3DEBUG
lp_assert(is_correct());
#endif
}
template <typename M>
void lu<M>::debug_test_of_basis( M const & A, vector<unsigned> & basis) {
std::set<unsigned> set;
for (unsigned i = 0; i < A.row_count(); i++) {
lp_assert(basis[i]< A.column_count());
set.insert(basis[i]);
}
lp_assert(set.size() == A.row_count());
}
template <typename M>
void lu<M>::solve_By(indexed_vector<X> & y) {
lp_assert(false); // not implemented
// init_vector_y(y);
// solve_By_when_y_is_ready(y);
}
template <typename M>
void lu<M>::solve_By(vector<X> & y) {
init_vector_y(y);
solve_By_when_y_is_ready_for_X(y);
}
template <typename M>
void lu<M>::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 M>
void lu<M>::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 M>
void lu<M>::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 M>
void lu<M>::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 M>
void lu< M>::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 M>
void lu< M>::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 M>
void lu< M>::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 M>
void lu< M>::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 Z3DEBUG
(*e)->set_number_of_columns(m_dim);
#endif
(*e)->apply_from_right(y);
}
}
template <typename M>
void lu< M>::solve_yB_indexed(indexed_vector<T>& y) {
lp_assert(y.is_OK());
// first solve yU = cb*R(-1)
m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1)
lp_assert(y.is_OK());
m_U.solve_y_U_indexed(y, m_settings); // got y*U=cb*R(-1)
lp_assert(y.is_OK());
m_Q.apply_reverse_from_right_to_T(y);
lp_assert(y.is_OK());
for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) {
#ifdef Z3DEBUG
(*e)->set_number_of_columns(m_dim);
#endif
(*e)->apply_from_right(y);
lp_assert(y.is_OK());
}
}
template <typename M>
void lu< M>::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 M>
void lu< M>::add_delta_to_solution_indexed(indexed_vector<T>& y) {
// the delta sits in m_y_copy, put result into y
lp_assert(y.is_OK());
lp_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);
}
lp_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>();
}
lp_assert(y.is_OK());
}
template <typename M>
void lu< M>::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 M>
void lu< M>::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();
lp_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
lp_assert(m_ii.is_OK());
m_ii.clear();
m_ii.resize(y.data_size());
lp_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.var();
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);
}
}
lp_assert(m_y_copy.is_OK());
}
// solves y*B = y
// y is the input
template <typename M>
void lu< M>::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;
}
lp_assert(m_y_copy.is_OK());
lp_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);
lp_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);
}
lp_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 M>
void lu< M>::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 M>
void lu< M>::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 M>
lu< M>::~lu(){
for (auto t : m_tail) {
delete t;
}
}
template <typename M>
void lu< M>::init_vector_y(vector<X> & y) {
apply_lp_list_to_y(y);
m_Q.apply_reverse_from_left_to_X(y);
}
template <typename M>
void lu< M>::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: lp_assert(numeric_traits<T>::precise() || check_vector_for_small_values(w, m_settings));
}
// see Chvatal 24.3
template <typename M>
void lu< M>::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 M>
void lu< M>::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: lp_assert(check_vector_for_small_values(w, m_settings));
}
}
template <typename M>
void lu< M>::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 M>
void lu< M>::swap_rows(int j, int k) {
if (j != k) {
m_Q.transpose_from_left(j, k);
m_U.swap_rows(j, k);
}
}
template <typename M>
void lu< M>::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 M>
bool lu< M>::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 M>
eta_matrix<typename M::coefftype, typename M::argtype> * lu< M>::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 M>
eta_matrix<typename M::coefftype, typename M::argtype> * lu<M>::get_eta_matrix_for_pivot(unsigned j, square_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 M>
unsigned lu<M>::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 Z3DEBUG
template <typename M>
void lu<M>::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 M>
void lu<M>::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 M>
void lu<M>::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--) {
lp_assert(abs(w[i] - w[i]) < 0.0000001);
}
}
#endif
template <typename M>
void lu<M>::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 M>
bool lu<M>::is_correct(const vector<unsigned>& basis) {
#ifdef Z3DEBUG
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
}
template <typename M>
bool lu<M>::is_correct() {
#ifdef Z3DEBUG
if (get_status() != LU_status::OK) {
return false;
}
dense_matrix<T, X> left_side = get_left_side();
dense_matrix<T, X> right_side = get_right_side();
return left_side == right_side;
#else
return true;
#endif
}
#ifdef Z3DEBUG
template <typename M>
dense_matrix<typename M::coefftype, typename M::argtype> lu<M>::tail_product() {
lp_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 M>
dense_matrix<typename M::coefftype, typename M::argtype> lu<M>::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 M>
dense_matrix<typename M::coefftype, typename M::argtype> lu<M>::get_left_side() {
dense_matrix<T, X> left_side = get_B(*this);
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 M>
dense_matrix<typename M::coefftype, typename M::argtype> lu<M>::get_right_side() {
auto ret = U() * R();
ret = Q() * ret;
return ret;
}
#endif
// needed for debugging purposes
template <typename M>
void lu<M>::copy_w(T *buffer, indexed_vector<T> & w) {
unsigned i = m_dim;
while (i--) {
buffer[i] = w[i];
}
}
// needed for debugging purposes
template <typename M>
void lu<M>::restore_w(T *buffer, indexed_vector<T> & w) {
unsigned i = m_dim;
while (i--) {
w[i] = buffer[i];
}
}
template <typename M>
bool lu<M>::all_columns_and_rows_are_active() {
unsigned i = m_dim;
while (i--) {
lp_assert(m_U.col_is_active(i));
lp_assert(m_U.row_is_active(i));
}
return true;
}
template <typename M>
bool lu<M>::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 M>
void lu<M>::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 M>
void lu<M>::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: lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
// lp_assert(is_correct());
// lp_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);
lp_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;
// lp_assert(is_correct());
// lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
}
template <typename M>
void lu<M>::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 M>
void lu<M>::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;
lp_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 M>
void lu<M>::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);
lp_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;
lp_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 M>
row_eta_matrix<typename M::coefftype, typename M::argtype> *lu<M>::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 Z3DEBUG
auto ret = new row_eta_matrix<typename M::coefftype, typename M::argtype>(replaced_column, lowest_row_of_the_bump, m_dim);
#else
auto ret = new row_eta_matrix<typename M::coefftype, typename M::argtype>(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 M>
void lu<M>::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);
// lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
// lp_assert(w.is_OK() && m_row_eta_work_vector.is_OK());
}
template <typename M>
void lu<M>::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];
// lp_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);
// lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
}
template <typename M>
void lu<M>::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 Z3DEBUG
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 M>
void init_factorization(lu<M>* & factorization, M & m_A, vector<unsigned> & m_basis, lp_settings &m_settings) {
if (factorization != nullptr)
delete factorization;
factorization = new lu<M>(m_A, m_basis, m_settings);
// if (factorization->get_status() != LU_status::OK)
// LP_OUT(m_settings, "failing in init_factorization" << std::endl);
}
#ifdef Z3DEBUG
template <typename M>
dense_matrix<typename M::coefftype, typename M::argtype> get_B(lu<M>& f, const vector<unsigned>& basis) {
lp_assert(basis.size() == f.dimension());
lp_assert(basis.size() == f.m_U.dimension());
dense_matrix<typename M::coefftype, typename M::argtype> 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;
}
template <typename M>
dense_matrix<typename M::coefftype, typename M::argtype> get_B(lu<M>& f) {
dense_matrix<typename M::coefftype, typename M::argtype> 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.m_A[i][j]);
return B;
}
#endif
}

View file

@ -1,31 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/lp_settings.h"
#include "util/lp/matrix_def.h"
#include "util/lp/static_matrix.h"
#include <string>
#ifdef Z3DEBUG
template bool lp::matrix<double, double>::is_equal(lp::matrix<double, double> const&);
template bool lp::matrix<lp::mpq, lp::numeric_pair<lp::mpq> >::is_equal(lp::matrix<lp::mpq, lp::numeric_pair<lp::mpq> > const&);
template bool lp::matrix<lp::mpq, lp::mpq>::is_equal(lp::matrix<lp::mpq, lp::mpq> const&);
#endif
template void lp::print_matrix<double, double>(lp::matrix<double, double> const*, std::ostream & out);
template void lp::print_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >(lp::matrix<lp::mpq, lp::numeric_pair<lp::mpq> > const *, std::basic_ostream<char, std::char_traits<char> > &);
template void lp::print_matrix<lp::mpq, lp::mpq>(lp::matrix<lp::mpq, lp::mpq> const*, std::ostream&);

View file

@ -1,71 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/numeric_pair.h"
#include "util/vector.h"
#include <string>
#include "util/lp/lp_settings.h"
namespace lp {
// 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, unsigned blanks = 0);
void print_string_matrix(vector<vector<std::string>> & A, std::ostream &, unsigned blanks_in_front = 0);
template <typename T, typename X>
void print_matrix(matrix<T, X> const * m, std::ostream & out);
template <typename T>
void print_matrix(const vector<vector<T>> & A, std::ostream & out, unsigned blanks_in_front = 0) {
vector<vector<std::string>> s(A.size());
for (unsigned i = 0; i < A.size(); i++) {
for (const auto & v : A[i]) {
s[i].push_back(T_to_string(v));
}
}
print_string_matrix(s, out, blanks_in_front);
}
}

View file

@ -1,134 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include <cmath>
#include <string>
#include "util/lp/matrix.h"
namespace lp {
#ifdef Z3DEBUG
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;
}
#endif
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 = static_cast<unsigned>(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, unsigned blanks_in_front) {
for (unsigned i = 0; i < A.size(); i++) {
for (unsigned j = 0; j < static_cast<unsigned>(A[i].size()); j++) {
if (i != 0 && j == 0)
print_blanks(blanks_in_front, out);
print_blanks(ws[j] - static_cast<unsigned>(A[i][j].size()), out);
out << A[i][j] << " ";
}
out << std::endl;
}
}
void print_string_matrix(vector<vector<std::string>> & A, std::ostream & out, unsigned blanks_in_front) {
vector<unsigned> widths;
if (!A.empty())
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, blanks_in_front);
out << std::endl;
}
template <typename T>
void print_matrix(vector<vector<T>> & A, std::ostream & out, unsigned blanks_in_front = 0) {
vector<vector<std::string>> s(A.size());
for (unsigned i = 0; i < A.size(); i++) {
for (const auto & v : A[i]) {
s[i].push_back(T_to_string(v));
}
}
print_string_matrix(s, out, blanks_in_front);
}
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);
}
}

View file

@ -1,40 +0,0 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Nikolaj Bjorner
*/
#include "util/lp/lar_solver.h"
#include "util/lp/monomial.h"
namespace nla {
template <typename T>
bool check_assignment(T const& m, variable_map_type & vars) {
rational r1 = vars[m.var()];
if (r1.is_zero()) {
for (auto w : m.vars()) {
if (vars[w].is_zero())
return true;
}
return false;
}
rational r2(1);
for (auto w : m.vars()) {
r2 *= vars[w];
}
return r1 == r2;
}
template <typename K>
bool check_assignments(const K & monomials,
const lp::lar_solver& s,
variable_map_type & vars) {
s.get_model(vars);
for (auto const& m : monomials) {
if (!check_assignment(m, vars)) return false;
}
return true;
}
template bool check_assignments<vector<mon_eq>>(const vector<mon_eq>&,
const lp::lar_solver& s,
variable_map_type & vars);
}

View file

@ -1,79 +0,0 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Nikolaj Bjorner
Lev Nachmanson
*/
#pragma once
#include "util/lp/lp_settings.h"
#include "util/vector.h"
#include "util/lp/lar_solver.h"
#include "util/lp/nla_defs.h"
namespace nla {
/*
* represents definition m_v = v1*v2*...*vn,
* where m_vs = [v1, v2, .., vn]
*/
class mon_eq {
// fields
lp::var_index m_v;
svector<lp::var_index> m_vs;
public:
// constructors
mon_eq(lp::var_index v, unsigned sz, lp::var_index const* vs):
m_v(v), m_vs(sz, vs) {
std::sort(m_vs.begin(), m_vs.end());
}
mon_eq(lp::var_index v, const svector<lp::var_index> &vs):
m_v(v), m_vs(vs) {
std::sort(m_vs.begin(), m_vs.end());
}
mon_eq() {}
unsigned var() const { return m_v; }
unsigned size() const { return m_vs.size(); }
const svector<lp::var_index>& vars() const { return m_vs; }
svector<lp::var_index>& vars() { return m_vs; }
bool empty() const { return m_vs.empty(); }
};
// support the congruence
class monomial: public mon_eq {
// fields
svector<lpvar> m_rvars;
bool m_rsign;
mutable unsigned m_visited;
public:
// constructors
monomial(lpvar v, unsigned sz, lpvar const* vs, unsigned idx): monomial(v, svector<lpvar>(sz, vs), idx) {
}
monomial(lpvar v, const svector<lpvar> &vs, unsigned idx) : mon_eq(v, vs), m_rsign(false), m_visited(0) {
std::sort(vars().begin(), vars().end());
}
unsigned visited() const { return m_visited; }
unsigned& visited() { return m_visited; }
svector<lpvar> const& rvars() const { return m_rvars; }
bool rsign() const { return m_rsign; }
void reset_rfields() { m_rsign = false; m_rvars.reset(); SASSERT(m_rvars.size() == 0); }
void push_rvar(signed_var sv) { m_rsign ^= sv.sign(); m_rvars.push_back(sv.var()); }
void sort_rvars() {
std::sort(m_rvars.begin(), m_rvars.end());
}
};
inline std::ostream& operator<<(std::ostream& out, monomial const& m) {
return out << m.var() << " := " << m.vars() << " r ( " << sign_to_rat(m.rsign()) << " * " << m.rvars() << ")";
}
typedef std::unordered_map<lpvar, rational> variable_map_type;
template <typename T>
bool check_assignment(T const& m, variable_map_type & vars);
template <typename K>
bool check_assignments(const K & monomimials,
const lp::lar_solver& s,
variable_map_type & vars);
} // end of namespace nla

View file

@ -1,894 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#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 lp {
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(const 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 {
T m_low;
T m_upper;
bool m_low_is_set;
bool m_upper_is_set;
bool m_value_is_fixed;
T m_fixed_value;
bool m_free;
// constructor
bound() : m_low(numeric_traits<T>::zero()),
m_low_is_set(true),
m_upper_is_set(false),
m_value_is_fixed(false),
m_free(false) {} // 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;
unsigned m_index;
column(const std::string &name, unsigned index): m_name(name),
m_bound(nullptr),
m_index(index) {
}
};
struct row {
row_type m_type;
std::string m_name;
std::unordered_map<std::string, T> m_row_columns;
unsigned m_index;
T m_right_side;
T m_range;
row(row_type type, const std::string &name, unsigned index) :
m_type(type),
m_name(name),
m_index(index),
m_right_side(zero_of_type<T>()),
m_range(zero_of_type<T>())
{
}
};
bool m_is_OK;
std::string m_file_name;
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;
unsigned m_line_number;
std::ostream * m_message_stream;
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;
}
lp_assert(m_line.size() >= offset);
lp_assert(m_line.size() >> i);
lp_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_lower_bound(col, numeric_traits<T>::zero());
return;
}
if (b->m_free) {
return;
}
if (b->m_low_is_set) {
solver->set_lower_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.empty() && 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(const 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(const std::string & column_name, const 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) {
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(const 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;
lp_assert(m_line.size() >= 14);
vector<std::string> bound_string = split_and_trim(m_line.substr(name_offset, m_line.size()));
if (bound_string.empty()) {
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) {
lp_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) {
lp_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;
lp_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) {
lp_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;
delete col->m_bound;
delete col;
}
}
mps_reader(const std::string & file_name):
m_is_OK(true),
m_file_name(file_name),
m_file_stream(file_name),
m_cost_line_count(0),
m_line_number(0),
m_message_stream(& std::cout) {}
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 = static_cast<unsigned>(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), false);
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, false);
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, false);
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, false);
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, false);
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;
}
};
}

View file

@ -1,796 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/nla_basics_lemmas.h"
#include "util/lp/nla_core.h"
#include "util/lp/factorization_factory_imp.h"
namespace nla {
basics::basics(core * c) : common(c) {}
// Monomials m and n vars have the same values, up to "sign"
// Generate a lemma if values of m.var() and n.var() are not the same up to sign
bool basics::basic_sign_lemma_on_two_monomials(const monomial& m, const monomial& n) {
const rational sign = sign_to_rat(m.rsign() ^ n.rsign());
if (val(m) == val(n) * sign)
return false;
TRACE("nla_solver", tout << "sign contradiction:\nm = " << pp_mon(c(), m) << "n= " << pp_mon(c(), n) << "sign: " << sign << "\n";);
generate_sign_lemma(m, n, sign);
return true;
}
void basics::generate_zero_lemmas(const monomial& m) {
SASSERT(!val(m).is_zero() && c().product_value(m.vars()).is_zero());
int sign = nla::rat_sign(val(m));
unsigned_vector fixed_zeros;
lpvar zero_j = find_best_zero(m, fixed_zeros);
SASSERT(is_set(zero_j));
unsigned zero_power = 0;
for (lpvar j : m.vars()){
if (j == zero_j) {
zero_power++;
continue;
}
get_non_strict_sign(j, sign);
if (sign == 0)
break;
}
if (sign && is_even(zero_power)) {
sign = 0;
}
TRACE("nla_solver_details", tout << "zero_j = " << zero_j << ", sign = " << sign << "\n";);
if (sign == 0) { // have to generate a non-convex lemma
add_trival_zero_lemma(zero_j, m);
} else { // here we know the sign of zero_j
generate_strict_case_zero_lemma(m, zero_j, sign);
}
for (lpvar j : fixed_zeros)
add_fixed_zero_lemma(m, j);
}
bool basics::try_get_non_strict_sign_from_bounds(lpvar j, int& sign) const {
SASSERT(sign);
if (c().has_lower_bound(j) && c().get_lower_bound(j) >= rational(0))
return true;
if (c().has_upper_bound(j) && c().get_upper_bound(j) <= rational(0)) {
sign = -sign;
return true;
}
sign = 0;
return false;
}
void basics::get_non_strict_sign(lpvar j, int& sign) const {
const rational v = val(j);
if (v.is_zero()) {
try_get_non_strict_sign_from_bounds(j, sign);
} else {
sign *= nla::rat_sign(v);
}
}
void basics::basic_sign_lemma_model_based_one_mon(const monomial& m, int product_sign) {
if (product_sign == 0) {
TRACE("nla_solver_bl", tout << "zero product sign: " << pp_mon(_(), m)<< "\n"; );
generate_zero_lemmas(m);
} else {
add_empty_lemma();
for(lpvar j: m.vars()) {
negate_strict_sign(j);
}
c().mk_ineq(m.var(), product_sign == 1? llc::GT : llc::LT);
TRACE("nla_solver", c().print_lemma(tout); tout << "\n";);
}
}
bool basics::basic_sign_lemma_model_based() {
unsigned start = c().random();
unsigned sz = c().m_to_refine.size();
for (unsigned i = sz; i-- > 0; ) {
monomial const& m = c().m_emons[c().m_to_refine[(start + i) % sz]];
int mon_sign = nla::rat_sign(val(m));
int product_sign = c().rat_sign(m);
if (mon_sign != product_sign) {
basic_sign_lemma_model_based_one_mon(m, product_sign);
if (c().done())
return true;
}
}
return c().m_lemma_vec->size() > 0;
}
bool basics::basic_sign_lemma_on_mon(lpvar v, std::unordered_set<unsigned> & explored){
if (!try_insert(v, explored)) {
return false;
}
const monomial& m_v = c().m_emons[v];
TRACE("nla_solver", tout << "m_v = " << pp_rmon(c(), m_v););
CTRACE("nla_solver", !c().m_emons.is_canonized(m_v),
c().m_emons.display(c(), tout);
c().m_evars.display(tout);
);
SASSERT(c().m_emons.is_canonized(m_v));
for (auto const& m : c().m_emons.enum_sign_equiv_monomials(v)) {
TRACE("nla_solver_details", tout << "m = " << pp_rmon(c(), m););
SASSERT(m.rvars() == m_v.rvars());
if (m_v.var() != m.var() && basic_sign_lemma_on_two_monomials(m_v, m) && done())
return true;
}
TRACE("nla_solver_details", tout << "return false\n";);
return false;
}
/**
* \brief <generate lemma by using the fact that -ab = (-a)b) and
-ab = a(-b)
*/
bool basics::basic_sign_lemma(bool derived) {
if (!derived)
return basic_sign_lemma_model_based();
std::unordered_set<unsigned> explored;
for (lpvar i : c().m_to_refine){
if (basic_sign_lemma_on_mon(i, explored))
return true;
}
return false;
}
// the value of the i-th monomial has to be equal to the value of the k-th monomial modulo sign
// but it is not the case in the model
void basics::generate_sign_lemma(const monomial& m, const monomial& n, const rational& sign) {
add_empty_lemma();
TRACE("nla_solver",
tout << "m = " << pp_rmon(_(), m);
tout << "n = " << pp_rmon(_(), n);
);
c().mk_ineq(m.var(), -sign, n.var(), llc::EQ);
explain(m);
TRACE("nla_solver", tout << "m exp = "; _().print_explanation(_().current_expl(), tout););
explain(n);
TRACE("nla_solver", tout << "n exp = "; _().print_explanation(_().current_expl(), tout););
TRACE("nla_solver", c().print_lemma(tout););
}
// try to find a variable j such that val(j) = 0
// and the bounds on j contain 0 as an inner point
lpvar basics::find_best_zero(const monomial& m, unsigned_vector & fixed_zeros) const {
lpvar zero_j = -1;
for (unsigned j : m.vars()){
if (val(j).is_zero()){
if (c().var_is_fixed_to_zero(j))
fixed_zeros.push_back(j);
if (!is_set(zero_j) || c().zero_is_an_inner_point_of_bounds(j))
zero_j = j;
}
}
return zero_j;
}
void basics::add_trival_zero_lemma(lpvar zero_j, const monomial& m) {
add_empty_lemma();
c().mk_ineq(zero_j, llc::NE);
c().mk_ineq(m.var(), llc::EQ);
TRACE("nla_solver", c().print_lemma(tout););
}
void basics::generate_strict_case_zero_lemma(const monomial& m, unsigned zero_j, int sign_of_zj) {
TRACE("nla_solver_bl", tout << "sign_of_zj = " << sign_of_zj << "\n";);
// we know all the signs
add_empty_lemma();
c().mk_ineq(zero_j, (sign_of_zj == 1? llc::GT : llc::LT));
for (unsigned j : m.vars()){
if (j != zero_j) {
negate_strict_sign(j);
}
}
negate_strict_sign(m.var());
TRACE("nla_solver", c().print_lemma(tout););
}
void basics::add_fixed_zero_lemma(const monomial& m, lpvar j) {
add_empty_lemma();
c().explain_fixed_var(j);
c().mk_ineq(m.var(), llc::EQ);
TRACE("nla_solver", c().print_lemma(tout););
}
void basics::negate_strict_sign(lpvar j) {
TRACE("nla_solver_details", tout << pp_var(c(), j) << "\n";);
if (!val(j).is_zero()) {
int sign = nla::rat_sign(val(j));
c().mk_ineq(j, (sign == 1? llc::LE : llc::GE));
} else { // val(j).is_zero()
if (c().has_lower_bound(j) && c().get_lower_bound(j) >= rational(0)) {
c().explain_existing_lower_bound(j);
c().mk_ineq(j, llc::GT);
} else {
SASSERT(c().has_upper_bound(j) && c().get_upper_bound(j) <= rational(0));
c().explain_existing_upper_bound(j);
c().mk_ineq(j, llc::LT);
}
}
}
// here we use the fact
// xy = 0 -> x = 0 or y = 0
bool basics::basic_lemma_for_mon_zero(const monomial& rm, const factorization& f) {
NOT_IMPLEMENTED_YET();
return true;
#if 0
TRACE("nla_solver", c().trace_print_monomial_and_factorization(rm, f, tout););
add_empty_lemma();
c().explain_fixed_var(var(rm));
std::unordered_set<lpvar> processed;
for (auto j : f) {
if (try_insert(var(j), processed))
c().mk_ineq(var(j), llc::EQ);
}
explain(rm);
TRACE("nla_solver", c().print_lemma(tout););
return true;
#endif
}
// use basic multiplication properties to create a lemma
bool basics::basic_lemma(bool derived) {
if (basic_sign_lemma(derived))
return true;
if (derived)
return false;
const auto& rm_ref = c().m_to_refine;
TRACE("nla_solver", tout << "rm_ref = "; print_vector(rm_ref, tout););
unsigned start = c().random();
unsigned sz = rm_ref.size();
for (unsigned j = 0; j < sz; ++j) {
lpvar v = rm_ref[(j + start) % rm_ref.size()];
const monomial& r = c().m_emons[v];
SASSERT (!c().check_monomial(c().m_emons[v]));
basic_lemma_for_mon(r, derived);
}
return false;
}
// Use basic multiplication properties to create a lemma
// for the given monomial.
// "derived" means derived from constraints - the alternative is model based
void basics::basic_lemma_for_mon(const monomial& rm, bool derived) {
if (derived)
basic_lemma_for_mon_derived(rm);
else
basic_lemma_for_mon_model_based(rm);
}
bool basics::basic_lemma_for_mon_derived(const monomial& rm) {
if (c().var_is_fixed_to_zero(var(rm))) {
for (auto factorization : factorization_factory_imp(rm, c())) {
if (factorization.is_empty())
continue;
if (basic_lemma_for_mon_zero(rm, factorization) ||
basic_lemma_for_mon_neutral_derived(rm, factorization)) {
explain(factorization);
return true;
}
}
} else {
for (auto factorization : factorization_factory_imp(rm, c())) {
if (factorization.is_empty())
continue;
if (basic_lemma_for_mon_non_zero_derived(rm, factorization) ||
basic_lemma_for_mon_neutral_derived(rm, factorization) ||
proportion_lemma_derived(rm, factorization)) {
explain(factorization);
return true;
}
}
}
return false;
}
// x = 0 or y = 0 -> xy = 0
bool basics::basic_lemma_for_mon_non_zero_derived(const monomial& rm, const factorization& f) {
TRACE("nla_solver", c().trace_print_monomial_and_factorization(rm, f, tout););
if (! c().var_is_separated_from_zero(var(rm)))
return false;
int zero_j = -1;
for (auto j : f) {
if ( c().var_is_fixed_to_zero(var(j))) {
zero_j = var(j);
break;
}
}
if (zero_j == -1) {
return false;
}
add_empty_lemma();
c().explain_fixed_var(zero_j);
c().explain_var_separated_from_zero(var(rm));
explain(rm);
TRACE("nla_solver", c().print_lemma(tout););
return true;
}
// use the fact that
// |xabc| = |x| and x != 0 -> |a| = |b| = |c| = 1
bool basics::basic_lemma_for_mon_neutral_monomial_to_factor_derived(const monomial& rm, const factorization& f) {
TRACE("nla_solver", c().trace_print_monomial_and_factorization(rm, f, tout););
lpvar mon_var = c().m_emons[rm.var()].var();
TRACE("nla_solver", c().trace_print_monomial_and_factorization(rm, f, tout); tout << "\nmon_var = " << mon_var << "\n";);
const auto mv = val(mon_var);
const auto abs_mv = abs(mv);
if (abs_mv == rational::zero()) {
return false;
}
bool mon_var_is_sep_from_zero = c().var_is_separated_from_zero(mon_var);
lpvar jl = -1;
for (auto fc : f ) {
lpvar j = var(fc);
if (abs(val(j)) == abs_mv && c().vars_are_equiv(j, mon_var) &&
(mon_var_is_sep_from_zero || c().var_is_separated_from_zero(j))) {
jl = j;
break;
}
}
if (jl == static_cast<lpvar>(-1))
return false;
lpvar not_one_j = -1;
for (auto j : f ) {
if (var(j) == jl) {
continue;
}
if (abs(val(j)) != rational(1)) {
not_one_j = var(j);
break;
}
}
if (not_one_j == static_cast<lpvar>(-1)) {
return false;
}
add_empty_lemma();
// mon_var = 0
if (mon_var_is_sep_from_zero)
c().explain_var_separated_from_zero(mon_var);
else
c().explain_var_separated_from_zero(jl);
c().explain_equiv_vars(mon_var, jl);
// not_one_j = 1
c().mk_ineq(not_one_j, llc::EQ, rational(1));
// not_one_j = -1
c().mk_ineq(not_one_j, llc::EQ, -rational(1));
explain(rm);
TRACE("nla_solver", c().print_lemma(tout); );
return true;
}
bool basics::basic_lemma_for_mon_neutral_derived(const monomial& rm, const factorization& factorization) {
return
basic_lemma_for_mon_neutral_monomial_to_factor_derived(rm, factorization);
}
// x != 0 or y = 0 => |xy| >= |y|
void basics::proportion_lemma_model_based(const monomial& rm, const factorization& factorization) {
rational rmv = abs(val(rm));
if (rmv.is_zero()) {
SASSERT(c().has_zero_factor(factorization));
return;
}
int factor_index = 0;
for (factor f : factorization) {
if (abs(val(f)) > rmv) {
generate_pl(rm, factorization, factor_index);
return;
}
factor_index++;
}
}
// x != 0 or y = 0 => |xy| >= |y|
bool basics::proportion_lemma_derived(const monomial& rm, const factorization& factorization) {
return false;
rational rmv = abs(val(rm));
if (rmv.is_zero()) {
SASSERT(c().has_zero_factor(factorization));
return false;
}
int factor_index = 0;
for (factor f : factorization) {
if (abs(val(f)) > rmv) {
generate_pl(rm, factorization, factor_index);
return true;
}
factor_index++;
}
return false;
}
// if there are no zero factors then |m| >= |m[factor_index]|
void basics::generate_pl_on_mon(const monomial& m, unsigned factor_index) {
add_empty_lemma();
unsigned mon_var = m.var();
rational mv = val(mon_var);
rational sm = rational(nla::rat_sign(mv));
c().mk_ineq(sm, mon_var, llc::LT);
for (unsigned fi = 0; fi < m.size(); fi ++) {
lpvar j = m.vars()[fi];
if (fi != factor_index) {
c().mk_ineq(j, llc::EQ);
} else {
rational jv = val(j);
rational sj = rational(nla::rat_sign(jv));
SASSERT(sm*mv < sj*jv);
c().mk_ineq(sj, j, llc::LT);
c().mk_ineq(sm, mon_var, -sj, j, llc::GE );
}
}
TRACE("nla_solver", c().print_lemma(tout); );
}
// none of the factors is zero and the product is not zero
// -> |fc[factor_index]| <= |rm|
void basics::generate_pl(const monomial& m, const factorization& fc, int factor_index) {
TRACE("nla_solver", tout << "factor_index = " << factor_index << ", m = "
<< pp_mon(c(), m);
tout << ", fc = "; c().print_factorization(fc, tout);
tout << "orig mon = "; c().print_monomial(c().emons()[m.var()], tout););
if (fc.is_mon()) {
generate_pl_on_mon(m, factor_index);
return;
}
add_empty_lemma();
int fi = 0;
rational mv = val(m);
rational sm = rational(nla::rat_sign(mv));
unsigned mon_var = var(m);
c().mk_ineq(sm, mon_var, llc::LT);
for (factor f : fc) {
if (fi++ != factor_index) {
c().mk_ineq(var(f), llc::EQ);
} else {
lpvar j = var(f);
rational jv = val(j);
rational sj = rational(nla::rat_sign(jv));
SASSERT(sm*mv < sj*jv);
c().mk_ineq(sj, j, llc::LT);
c().mk_ineq(sm, mon_var, -sj, j, llc::GE );
}
}
if (!fc.is_mon()) {
explain(fc);
explain(m);
}
TRACE("nla_solver", c().print_lemma(tout); );
}
bool basics::is_separated_from_zero(const factorization& f) const {
for (const factor& fc: f) {
lpvar j = var(fc);
if (!(c().var_has_positive_lower_bound(j) || c().var_has_negative_upper_bound(j))) {
return false;
}
}
return true;
}
// here we use the fact xy = 0 -> x = 0 or y = 0
void basics::basic_lemma_for_mon_zero_model_based(const monomial& rm, const factorization& f) {
TRACE("nla_solver", c().trace_print_monomial_and_factorization(rm, f, tout););
SASSERT(val(rm).is_zero()&& ! c().rm_check(rm));
add_empty_lemma();
if (!is_separated_from_zero(f)) {
c().mk_ineq(var(rm), llc::NE);
for (auto j : f) {
c().mk_ineq(var(j), llc::EQ);
}
} else {
c().mk_ineq(var(rm), llc::NE);
for (auto j : f) {
c().explain_separation_from_zero(var(j));
}
}
explain(f);
TRACE("nla_solver", c().print_lemma(tout););
}
void basics::basic_lemma_for_mon_model_based(const monomial& rm) {
TRACE("nla_solver_bl", tout << "rm = " << pp_mon(_(), rm) << "\n";);
if (val(rm).is_zero()) {
for (auto factorization : factorization_factory_imp(rm, c())) {
if (factorization.is_empty())
continue;
basic_lemma_for_mon_zero_model_based(rm, factorization);
basic_lemma_for_mon_neutral_model_based(rm, factorization); // todo - the same call is made in the else branch
}
} else {
for (auto factorization : factorization_factory_imp(rm, c())) {
if (factorization.is_empty())
continue;
basic_lemma_for_mon_non_zero_model_based(rm, factorization);
basic_lemma_for_mon_neutral_model_based(rm, factorization);
proportion_lemma_model_based(rm, factorization) ;
}
}
}
// use the fact that
// |xabc| = |x| and x != 0 -> |a| = |b| = |c| = 1
bool basics::basic_lemma_for_mon_neutral_monomial_to_factor_model_based_fm(const monomial& m) {
TRACE("nla_solver_bl", c().print_monomial(m, tout););
lpvar mon_var = m.var();
const auto mv = val(mon_var);
const auto abs_mv = abs(mv);
if (abs_mv == rational::zero()) {
return false;
}
lpvar jl = -1;
for (auto j : m.vars() ) {
if (abs(val(j)) == abs_mv) {
jl = j;
break;
}
}
if (jl == static_cast<lpvar>(-1))
return false;
lpvar not_one_j = -1;
for (auto j : m.vars() ) {
if (j == jl) {
continue;
}
if (abs(val(j)) != rational(1)) {
not_one_j = j;
break;
}
}
if (not_one_j == static_cast<lpvar>(-1)) {
return false;
}
add_empty_lemma();
// mon_var = 0
c().mk_ineq(mon_var, llc::EQ);
// negate abs(jl) == abs()
if (val(jl) == - val(mon_var))
c().mk_ineq(jl, mon_var, llc::NE, c().current_lemma());
else // jl == mon_var
c().mk_ineq(jl, -rational(1), mon_var, llc::NE);
// not_one_j = 1
c().mk_ineq(not_one_j, llc::EQ, rational(1));
// not_one_j = -1
c().mk_ineq(not_one_j, llc::EQ, -rational(1));
TRACE("nla_solver", c().print_lemma(tout); );
return true;
}
// use the fact
// 1 * 1 ... * 1 * x * 1 ... * 1 = x
bool basics::basic_lemma_for_mon_neutral_from_factors_to_monomial_model_based_fm(const monomial& m) {
lpvar not_one = -1;
rational sign(1);
TRACE("nla_solver_bl", tout << "m = "; c().print_monomial(m, tout););
for (auto j : m.vars()){
auto v = val(j);
if (v == rational(1)) {
continue;
}
if (v == -rational(1)) {
sign = - sign;
continue;
}
if (not_one == static_cast<lpvar>(-1)) {
not_one = j;
continue;
}
// if we are here then there are at least two factors with values different from one and minus one: cannot create the lemma
return false;
}
if (not_one + 1) { // we found the only not_one
if (val(m) == val(not_one) * sign) {
TRACE("nla_solver", tout << "the whole equal to the factor" << std::endl;);
return false;
}
}
add_empty_lemma();
for (auto j : m.vars()){
if (not_one == j) continue;
c().mk_ineq(j, llc::NE, val(j));
}
if (not_one == static_cast<lpvar>(-1)) {
c().mk_ineq(m.var(), llc::EQ, sign);
} else {
c().mk_ineq(m.var(), -sign, not_one, llc::EQ);
}
TRACE("nla_solver", c().print_lemma(tout););
return true;
}
// use the fact that
// |xabc| = |x| and x != 0 -> |a| = |b| = |c| = 1
bool basics::basic_lemma_for_mon_neutral_monomial_to_factor_model_based(const monomial& rm, const factorization& f) {
TRACE("nla_solver_bl", c().trace_print_monomial_and_factorization(rm, f, tout););
lpvar mon_var = c().m_emons[rm.var()].var();
TRACE("nla_solver_bl", c().trace_print_monomial_and_factorization(rm, f, tout); tout << "\nmon_var = " << mon_var << "\n";);
const auto mv = val(mon_var);
const auto abs_mv = abs(mv);
if (abs_mv == rational::zero()) {
return false;
}
lpvar jl = -1;
for (auto j : f ) {
if (abs(val(j)) == abs_mv) {
jl = var(j);
break;
}
}
if (jl == static_cast<lpvar>(-1))
return false;
lpvar not_one_j = -1;
for (auto j : f ) {
if (var(j) == jl) {
continue;
}
if (abs(val(j)) != rational(1)) {
not_one_j = var(j);
break;
}
}
if (not_one_j == static_cast<lpvar>(-1)) {
return false;
}
add_empty_lemma();
// mon_var = 0
c().mk_ineq(mon_var, llc::EQ);
// negate abs(jl) == abs()
if (val(jl) == - val(mon_var))
c().mk_ineq(jl, mon_var, llc::NE, c().current_lemma());
else // jl == mon_var
c().mk_ineq(jl, -rational(1), mon_var, llc::NE);
// not_one_j = 1
c().mk_ineq(not_one_j, llc::EQ, rational(1));
// not_one_j = -1
c().mk_ineq(not_one_j, llc::EQ, -rational(1));
explain(rm);
explain(f);
TRACE("nla_solver", c().print_lemma(tout); );
return true;
}
void basics::basic_lemma_for_mon_neutral_model_based(const monomial& rm, const factorization& f) {
if (f.is_mon()) {
basic_lemma_for_mon_neutral_monomial_to_factor_model_based_fm(f.mon());
basic_lemma_for_mon_neutral_from_factors_to_monomial_model_based_fm(f.mon());
}
else {
basic_lemma_for_mon_neutral_monomial_to_factor_model_based(rm, f);
basic_lemma_for_mon_neutral_from_factors_to_monomial_model_based(rm, f);
}
}
// use the fact
// 1 * 1 ... * 1 * x * 1 ... * 1 = x
bool basics::basic_lemma_for_mon_neutral_from_factors_to_monomial_model_based(const monomial& m, const factorization& f) {
rational sign = sign_to_rat(m.rsign());
SASSERT(m.rsign() == canonize_sign(f));
TRACE("nla_solver_bl", tout << pp_rmon(_(), m) <<"\nf = "; c().print_factorization(f, tout); tout << "sign = " << sign << '\n'; );
lpvar not_one = -1;
for (auto j : f){
TRACE("nla_solver_bl", tout << "j = "; c().print_factor_with_vars(j, tout););
auto v = val(j);
if (v == rational(1)) {
continue;
}
if (v == -rational(1)) {
sign = - sign;
continue;
}
if (not_one == static_cast<lpvar>(-1)) {
not_one = var(j);
continue;
}
// if we are here then there are at least two factors with absolute values different from one : cannot create the lemma
return false;
}
if (not_one + 1) {
// we found the only not_one
if (val(m) == val(not_one) * sign) {
TRACE("nla_solver", tout << "the whole is equal to the factor" << std::endl;);
return false;
}
} else {
// we have +-ones only in the factorization
if (val(m) == sign) {
return false;
}
}
TRACE("nla_solver_bl", tout << "not_one = " << not_one << "\n";);
add_empty_lemma();
for (auto j : f){
lpvar var_j = var(j);
if (not_one == var_j) continue;
TRACE("nla_solver_bl", tout << "j = "; c().print_factor_with_vars(j, tout););
c().mk_ineq(var_j, llc::NE, val(var_j));
}
if (not_one == static_cast<lpvar>(-1)) {
c().mk_ineq(m.var(), llc::EQ, sign);
} else {
c().mk_ineq(m.var(), -sign, not_one, llc::EQ);
}
explain(m);
explain(f);
TRACE("nla_solver",
c().print_lemma(tout);
tout << "m = " << pp_rmon(c(), m);
);
return true;
}
void basics::basic_lemma_for_mon_non_zero_model_based_mf(const factorization& f) {
TRACE("nla_solver_bl", c().print_factorization(f, tout););
int zero_j = -1;
for (auto j : f) {
if (val(j).is_zero()) {
zero_j = var(j);
break;
}
}
if (zero_j == -1) { return; }
add_empty_lemma();
c().mk_ineq(zero_j, llc::NE);
c().mk_ineq(f.mon().var(), llc::EQ);
TRACE("nla_solver", c().print_lemma(tout););
}
// x = 0 or y = 0 -> xy = 0
void basics::basic_lemma_for_mon_non_zero_model_based(const monomial& rm, const factorization& f) {
TRACE("nla_solver_bl", c().trace_print_monomial_and_factorization(rm, f, tout););
if (f.is_mon())
basic_lemma_for_mon_non_zero_model_based_mf(f);
else
basic_lemma_for_mon_non_zero_model_based_mf(f);
}
}

View file

@ -1,107 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/monomial.h"
#include "util/lp/factorization.h"
#include "util/lp/nla_common.h"
namespace nla {
class core;
struct basics: common {
basics(core *core);
bool basic_sign_lemma_on_two_monomials(const monomial& m, const monomial& n);
void basic_sign_lemma_model_based_one_mon(const monomial& m, int product_sign);
bool basic_sign_lemma_model_based();
bool basic_sign_lemma_on_mon(unsigned i, std::unordered_set<unsigned> & explore);
/**
* \brief <generate lemma by using the fact that -ab = (-a)b) and
-ab = a(-b)
*/
bool basic_sign_lemma(bool derived);
bool basic_lemma_for_mon_zero(const monomial& rm, const factorization& f);
void basic_lemma_for_mon_zero_model_based(const monomial& rm, const factorization& f);
void basic_lemma_for_mon_non_zero_model_based(const monomial& rm, const factorization& f);
// x = 0 or y = 0 -> xy = 0
void basic_lemma_for_mon_non_zero_model_based_rm(const monomial& rm, const factorization& f);
void basic_lemma_for_mon_non_zero_model_based_mf(const factorization& f);
// x = 0 or y = 0 -> xy = 0
bool basic_lemma_for_mon_non_zero_derived(const monomial& rm, const factorization& f);
// use the fact that
// |xabc| = |x| and x != 0 -> |a| = |b| = |c| = 1
bool basic_lemma_for_mon_neutral_monomial_to_factor_model_based(const monomial& rm, const factorization& f);
// use the fact that
// |xabc| = |x| and x != 0 -> |a| = |b| = |c| = 1
bool basic_lemma_for_mon_neutral_monomial_to_factor_model_based_fm(const monomial& m);
bool basic_lemma_for_mon_neutral_monomial_to_factor_derived(const monomial& rm, const factorization& f);
// use the fact
// 1 * 1 ... * 1 * x * 1 ... * 1 = x
bool basic_lemma_for_mon_neutral_from_factors_to_monomial_model_based(const monomial& rm, const factorization& f);
// use the fact
// 1 * 1 ... * 1 * x * 1 ... * 1 = x
bool basic_lemma_for_mon_neutral_from_factors_to_monomial_model_based_fm(const monomial& m);
// use the fact
// 1 * 1 ... * 1 * x * 1 ... * 1 = x
bool basic_lemma_for_mon_neutral_from_factors_to_monomial_derived(const monomial& rm, const factorization& f);
void basic_lemma_for_mon_neutral_model_based(const monomial& rm, const factorization& f);
bool basic_lemma_for_mon_neutral_derived(const monomial& rm, const factorization& factorization);
void basic_lemma_for_mon_model_based(const monomial& rm);
bool basic_lemma_for_mon_derived(const monomial& rm);
// Use basic multiplication properties to create a lemma
// for the given monomial.
// "derived" means derived from constraints - the alternative is model based
void basic_lemma_for_mon(const monomial& rm, bool derived);
// use basic multiplication properties to create a lemma
bool basic_lemma(bool derived);
void generate_sign_lemma(const monomial& m, const monomial& n, const rational& sign);
void generate_zero_lemmas(const monomial& m);
lpvar find_best_zero(const monomial& m, unsigned_vector & fixed_zeros) const;
bool try_get_non_strict_sign_from_bounds(lpvar j, int& sign) const;
void get_non_strict_sign(lpvar j, int& sign) const;
void add_trival_zero_lemma(lpvar zero_j, const monomial& m);
void generate_strict_case_zero_lemma(const monomial& m, unsigned zero_j, int sign_of_zj);
void add_fixed_zero_lemma(const monomial& m, lpvar j);
void negate_strict_sign(lpvar j);
// x != 0 or y = 0 => |xy| >= |y|
void proportion_lemma_model_based(const monomial& rm, const factorization& factorization);
// x != 0 or y = 0 => |xy| >= |y|
bool proportion_lemma_derived(const monomial& rm, const factorization& factorization);
// if there are no zero factors then |m| >= |m[factor_index]|
void generate_pl_on_mon(const monomial& m, unsigned factor_index);
// none of the factors is zero and the product is not zero
// -> |fc[factor_index]| <= |rm|
void generate_pl(const monomial& rm, const factorization& fc, int factor_index);
bool is_separated_from_zero(const factorization&) const;
};
}

View file

@ -1,126 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/nla_common.h"
#include "util/lp/nla_core.h"
namespace nla {
bool common::done() const { return c().done(); }
template <typename T> void common::explain(const T& t) {
c().explain(t, c().current_expl());
}
template void common::explain<monomial>(const monomial& t);
template void common::explain<factor>(const factor& t);
template void common::explain<factorization>(const factorization& t);
void common::explain(lpvar j) { c().explain(j, c().current_expl()); }
template <typename T> rational common::val(T const& t) const { return c().val(t); }
template rational common::val<monomial>(monomial const& t) const;
template rational common::val<factor>(factor const& t) const;
rational common::val(lpvar t) const { return c().val(t); }
template <typename T> lpvar common::var(T const& t) const { return c().var(t); }
template lpvar common::var<factor>(factor const& t) const;
template lpvar common::var<monomial>(monomial const& t) const;
void common::add_empty_lemma() { c().add_empty_lemma(); }
template <typename T> bool common::canonize_sign(const T& t) const {
return c().canonize_sign(t);
}
template bool common::canonize_sign<monomial>(const monomial&) const;
template bool common::canonize_sign<factor>(const factor&) const;
template bool common::canonize_sign<lpvar>(const lpvar&) const;
template bool common::canonize_sign<factorization>(const factorization&) const;
void common::mk_ineq(lp::lar_term& t, llc cmp, const rational& rs){
c().mk_ineq(t, cmp, rs);
}
void common::mk_ineq(const rational& a, lpvar j, const rational& b, lpvar k, llc cmp, const rational& rs){
c().mk_ineq(a, j, b, j, cmp, rs);
}
void common::mk_ineq(lpvar j, const rational& b, lpvar k, llc cmp, const rational& rs){
c().mk_ineq(j, b, k, cmp, rs);
}
void common::mk_ineq(lpvar j, const rational& b, lpvar k, llc cmp){
c().mk_ineq(j, b, k, cmp);
}
void common::mk_ineq(const rational& a, lpvar j, const rational& b, lpvar k, llc cmp) {
c().mk_ineq(a, j, b, k, cmp);
}
void common::mk_ineq(bool a, lpvar j, bool b, lpvar k, llc cmp) {
c().mk_ineq(sign_to_rat(a), j, sign_to_rat(b), k, cmp);
}
void common::mk_ineq(const rational& a ,lpvar j, lpvar k, llc cmp, const rational& rs) {
c().mk_ineq(a, j, k, cmp, rs);
}
void common::mk_ineq(lpvar j, lpvar k, llc cmp, const rational& rs) {
c().mk_ineq(j, k, cmp, rs);}
void common::mk_ineq(lpvar j, llc cmp, const rational& rs){
c().mk_ineq(j, cmp, rs);}
void common::mk_ineq(const rational& a, lpvar j, llc cmp, const rational& rs) {
c().mk_ineq(a, j, cmp, rs);
}
void common::mk_ineq(const rational& a, lpvar j, llc cmp){
c().mk_ineq(a, j, cmp);
}
void common::mk_ineq(lpvar j, llc cmp){
c().mk_ineq(j, cmp);
}
std::ostream& common::print_lemma(std::ostream& out) const {
return c().print_lemma(out);
}
template <typename T>
std::ostream& common::print_product(const T & m, std::ostream& out) const {
return c().print_product(m, out);
}
template
std::ostream& common::print_product<unsigned_vector>(const unsigned_vector & m, std::ostream& out) const;
std::ostream& common::print_monomial(const monomial & m, std::ostream& out) const {
return c().print_monomial(m, out);
}
std::ostream& common::print_factor(const factor & f, std::ostream& out) const {
return c().print_factor(f, out);
}
std::ostream& common::print_var(lpvar j, std::ostream& out) const {
return c().print_var(j, out);
}
bool common::check_monomial(const monomial& m) const {
return c().check_monomial(m);
}
unsigned common::random() {
return c().random();
}
}

View file

@ -1,96 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/rational.h"
#include "util/lp/nla_defs.h"
#include "util/lp/lar_term.h"
#include "util/lp/monomial.h"
#include "util/lp/emonomials.h"
#include "util/lp/factorization.h"
namespace nla {
inline llc negate(llc cmp) {
switch(cmp) {
case llc::LE: return llc::GT;
case llc::LT: return llc::GE;
case llc::GE: return llc::LT;
case llc::GT: return llc::LE;
case llc::EQ: return llc::NE;
case llc::NE: return llc::EQ;
default: SASSERT(false);
};
return cmp; // not reachable
}
class core;
struct common {
core* m_core;
common(core* c): m_core(c) {}
core& c() { return *m_core; }
const core& c() const { return *m_core; }
core& _() { return *m_core; }
const core& _() const { return *m_core; }
template <typename T> rational val(T const& t) const;
rational val(lpvar) const;
rational rval(const monomial&) const;
template <typename T> lpvar var(T const& t) const;
bool done() const;
template <typename T> void explain(const T&);
void explain(lpvar);
void add_empty_lemma();
template <typename T> bool canonize_sign(const T&) const;
void mk_ineq(lp::lar_term& t, llc cmp, const rational& rs);
void mk_ineq(const rational& a, lpvar j, const rational& b, lpvar k, llc cmp, const rational& rs);
void mk_ineq(lpvar j, const rational& b, lpvar k, llc cmp, const rational& rs);
void mk_ineq(lpvar j, const rational& b, lpvar k, llc cmp);
void mk_ineq(const rational& a, lpvar j, const rational& b, lpvar k, llc cmp);
void mk_ineq(bool a, lpvar j, bool b, lpvar k, llc cmp);
void mk_ineq(const rational& a ,lpvar j, lpvar k, llc cmp, const rational& rs);
void mk_ineq(lpvar j, lpvar k, llc cmp, const rational& rs);
void mk_ineq(lpvar j, llc cmp, const rational& rs);
void mk_ineq(const rational& a, lpvar j, llc cmp, const rational& rs);
void mk_ineq(const rational& a, lpvar j, llc cmp);
void mk_ineq(lpvar j, llc cmp);
std::ostream& print_lemma(std::ostream&) const;
template <typename T>
std::ostream& print_product(const T & m, std::ostream& out) const;
std::ostream& print_factor(const factor &, std::ostream& out) const;
std::ostream& print_var(lpvar, std::ostream& out) const;
std::ostream& print_monomial(const monomial & m, std::ostream& out) const;
std::ostream& print_rooted_monomial(const monomial &, std::ostream& out) const;
std::ostream& print_rooted_monomial_with_vars(const monomial&, std::ostream& out) const;
bool check_monomial(const monomial&) const;
unsigned random();
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,374 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/factorization.h"
#include "util/lp/lp_types.h"
#include "util/lp/var_eqs.h"
#include "util/lp/nla_tangent_lemmas.h"
#include "util/lp/nla_basics_lemmas.h"
#include "util/lp/nla_order_lemmas.h"
#include "util/lp/nla_monotone_lemmas.h"
#include "util/lp/emonomials.h"
namespace nla {
template <typename A, typename B>
bool try_insert(const A& elem, B& collection) {
auto it = collection.find(elem);
if (it != collection.end())
return false;
collection.insert(elem);
return true;
}
typedef lp::constraint_index lpci;
typedef lp::lconstraint_kind llc;
typedef lp::constraint_index lpci;
typedef lp::explanation expl_set;
typedef lp::var_index lpvar;
inline int rat_sign(const rational& r) { return r.is_pos()? 1 : ( r.is_neg()? -1 : 0); }
inline rational rrat_sign(const rational& r) { return rational(rat_sign(r)); }
inline bool is_set(unsigned j) { return static_cast<int>(j) != -1; }
inline bool is_even(unsigned k) { return (k >> 1) << 1 == k; }
struct ineq {
lp::lconstraint_kind m_cmp;
lp::lar_term m_term;
rational m_rs;
ineq(lp::lconstraint_kind cmp, const lp::lar_term& term, const rational& rs) : m_cmp(cmp), m_term(term), m_rs(rs) {}
bool operator==(const ineq& a) const {
return m_cmp == a.m_cmp && m_term == a.m_term && m_rs == a.m_rs;
}
const lp::lar_term& term() const { return m_term; };
lp::lconstraint_kind cmp() const { return m_cmp; };
const rational& rs() const { return m_rs; };
};
class lemma {
vector<ineq> m_ineqs;
lp::explanation m_expl;
public:
void push_back(const ineq& i) { m_ineqs.push_back(i);}
size_t size() const { return m_ineqs.size() + m_expl.size(); }
const vector<ineq>& ineqs() const { return m_ineqs; }
vector<ineq>& ineqs() { return m_ineqs; }
lp::explanation& expl() { return m_expl; }
const lp::explanation& expl() const { return m_expl; }
bool is_conflict() const { return m_ineqs.empty() && !m_expl.empty(); }
};
class core {
public:
var_eqs<emonomials> m_evars;
lp::lar_solver& m_lar_solver;
vector<lemma> * m_lemma_vec;
svector<lpvar> m_to_refine;
tangents m_tangents;
basics m_basics;
order m_order;
monotone m_monotone;
emonomials m_emons;
core(lp::lar_solver& s);
bool compare_holds(const rational& ls, llc cmp, const rational& rs) const;
rational value(const lp::lar_term& r) const;
lp::lar_term subs_terms_to_columns(const lp::lar_term& t) const;
bool ineq_holds(const ineq& n) const;
bool lemma_holds(const lemma& l) const;
rational val(lpvar j) const { return m_lar_solver.get_column_value_rational(j); }
rational val(const monomial& m) const { return m_lar_solver.get_column_value_rational(m.var()); }
bool canonize_sign_is_correct(const monomial& m) const;
lpvar var(monomial const& sv) const { return sv.var(); }
rational val_rooted(const monomial& m) const { return m.rsign()*val(m.var()); }
rational val(const factor& f) const { return f.rat_sign() * (f.is_var()? val(f.var()) : val(m_emons[f.var()])); }
rational val(const factorization&) const;
lpvar var(const factor& f) const { return f.var(); }
svector<lpvar> sorted_rvars(const factor& f) const;
bool done() const;
void add_empty_lemma();
// the value of the factor is equal to the value of the variable multiplied
// by the canonize_sign
bool canonize_sign(const factor& f) const;
bool canonize_sign(const factorization& f) const;
bool canonize_sign(lpvar j) const;
// the value of the rooted monomias is equal to the value of the m.var() variable multiplied
// by the canonize_sign
bool canonize_sign(const monomial& m) const;
void deregister_monomial_from_monomialomials (const monomial & m, unsigned i);
void deregister_monomial_from_tables(const monomial & m, unsigned i);
// returns the monomial index
void add(lpvar v, unsigned sz, lpvar const* vs);
void push();
void pop(unsigned n);
rational mon_value_by_vars(unsigned i) const;
rational product_value(const unsigned_vector & m) const;
// return true iff the monomial value is equal to the product of the values of the factors
bool check_monomial(const monomial& m) const;
void explain(const monomial& m, lp::explanation& exp) const;
void explain(const factor& f, lp::explanation& exp) const;
void explain(lpvar j, lp::explanation& exp) const;
void explain_existing_lower_bound(lpvar j);
void explain_existing_upper_bound(lpvar j);
void explain_separation_from_zero(lpvar j);
void explain_var_separated_from_zero(lpvar j);
void explain_fixed_var(lpvar j);
std::ostream & print_ineq(const ineq & in, std::ostream & out) const;
std::ostream & print_var(lpvar j, std::ostream & out) const;
std::ostream & print_monomials(std::ostream & out) const;
std::ostream & print_ineqs(const lemma& l, std::ostream & out) const;
std::ostream & print_factorization(const factorization& f, std::ostream& out) const;
template <typename T>
std::ostream& print_product(const T & m, std::ostream& out) const;
std::ostream & print_factor(const factor& f, std::ostream& out) const;
std::ostream & print_factor_with_vars(const factor& f, std::ostream& out) const;
std::ostream& print_monomial(const monomial& m, std::ostream& out) const;
std::ostream& print_bfc(const factorization& m, std::ostream& out) const;
std::ostream& print_monomial_with_vars(unsigned i, std::ostream& out) const;
template <typename T>
std::ostream& print_product_with_vars(const T& m, std::ostream& out) const;
std::ostream& print_monomial_with_vars(const monomial& m, std::ostream& out) const;
std::ostream& print_explanation(const lp::explanation& exp, std::ostream& out) const;
template <typename T>
void trace_print_rms(const T& p, std::ostream& out);
void trace_print_monomial_and_factorization(const monomial& rm, const factorization& f, std::ostream& out) const;
void print_monomial_stats(const monomial& m, std::ostream& out);
void print_stats(std::ostream& out);
std::ostream& print_lemma(std::ostream& out) const;
void print_specific_lemma(const lemma& l, std::ostream& out) const;
void trace_print_ol(const monomial& ac,
const factor& a,
const factor& c,
const monomial& bc,
const factor& b,
std::ostream& out);
void mk_ineq(lp::lar_term& t, llc cmp, const rational& rs);
void mk_ineq(const rational& a, lpvar j, const rational& b, lpvar k, llc cmp, const rational& rs);
void mk_ineq(bool a, lpvar j, bool b, lpvar k, llc cmp, const rational& rs);
void mk_ineq(bool a, lpvar j, bool b, lpvar k, llc cmp);
void mk_ineq(lpvar j, const rational& b, lpvar k, llc cmp, const rational& rs);
void mk_ineq(lpvar j, const rational& b, lpvar k, llc cmp);
void mk_ineq(const rational& a, lpvar j, const rational& b, lpvar k, llc cmp);
void mk_ineq(const rational& a ,lpvar j, lpvar k, llc cmp, const rational& rs);
void mk_ineq(lpvar j, lpvar k, llc cmp, const rational& rs);
void mk_ineq(lpvar j, llc cmp, const rational& rs);
void mk_ineq(const rational& a, lpvar j, llc cmp, const rational& rs);
void mk_ineq(const rational& a, lpvar j, llc cmp);
void mk_ineq(lpvar j, lpvar k, llc cmp, lemma& l);
void mk_ineq(lpvar j, llc cmp);
void maybe_add_a_factor(lpvar i,
const factor& c,
std::unordered_set<lpvar>& found_vars,
std::unordered_set<unsigned>& found_rm,
vector<factor> & r) const;
llc apply_minus(llc cmp);
void fill_explanation_and_lemma_sign(const monomial& a, const monomial & b, rational const& sign);
svector<lpvar> reduce_monomial_to_rooted(const svector<lpvar> & vars, rational & sign) const;
monomial_coeff canonize_monomial(monomial const& m) const;
lemma& current_lemma();
const lemma& current_lemma() const;
vector<ineq>& current_ineqs();
lp::explanation& current_expl();
const lp::explanation& current_expl() const;
int vars_sign(const svector<lpvar>& v);
bool has_upper_bound(lpvar j) const;
bool has_lower_bound(lpvar j) const;
const rational& get_upper_bound(unsigned j) const;
const rational& get_lower_bound(unsigned j) const;
bool zero_is_an_inner_point_of_bounds(lpvar j) const;
int rat_sign(const monomial& m) const;
inline int rat_sign(lpvar j) const { return nla::rat_sign(val(j)); }
bool sign_contradiction(const monomial& m) const;
bool var_is_fixed_to_zero(lpvar j) const;
bool var_is_fixed_to_val(lpvar j, const rational& v) const;
bool var_is_fixed(lpvar j) const;
bool find_canonical_monomial_of_vars(const svector<lpvar>& vars, lpvar & i) const;
bool is_canonical_monomial(lpvar) const;
bool elists_are_consistent(bool check_in_model) const;
bool elist_is_consistent(const std::unordered_set<lpvar>&) const;
bool var_has_positive_lower_bound(lpvar j) const;
bool var_has_negative_upper_bound(lpvar j) const;
bool var_is_separated_from_zero(lpvar j) const;
bool vars_are_equiv(lpvar a, lpvar b) const;
void explain_equiv_vars(lpvar a, lpvar b);
void explain(const factorization& f, lp::explanation& exp);
bool explain_upper_bound(const lp::lar_term& t, const rational& rs, lp::explanation& e) const;
bool explain_lower_bound(const lp::lar_term& t, const rational& rs, lp::explanation& e) const;
bool explain_coeff_lower_bound(const lp::lar_term::ival& p, rational& bound, lp::explanation& e) const;
bool explain_coeff_upper_bound(const lp::lar_term::ival& p, rational& bound, lp::explanation& e) const;
bool explain_ineq(const lp::lar_term& t, llc cmp, const rational& rs);
bool explain_by_equiv(const lp::lar_term& t, lp::explanation& e);
bool has_zero_factor(const factorization& factorization) const;
template <typename T>
bool mon_has_zero(const T& product) const;
lp::lp_settings& settings();
const lp::lp_settings& settings() const;
unsigned random();
void map_monomial_vars_to_monomial_indices(unsigned i);
void map_vars_to_monomials();
// we look for octagon constraints here, with a left part +-x +- y
void collect_equivs();
void collect_equivs_of_fixed_vars();
bool is_octagon_term(const lp::lar_term& t, bool & sign, lpvar& i, lpvar &j) const;
void add_equivalence_maybe(const lp::lar_term *t, lpci c0, lpci c1);
void init_vars_equivalence();
bool vars_table_is_ok() const;
bool rm_table_is_ok() const;
bool tables_are_ok() const;
bool var_is_a_root(lpvar j) const;
template <typename T>
bool vars_are_roots(const T& v) const;
void register_monomial_in_tables(unsigned i_mon);
void register_monomials_in_tables();
void clear();
void init_search();
void init_to_refine();
bool divide(const monomial& bc, const factor& c, factor & b) const;
void negate_factor_equality(const factor& c, const factor& d);
void negate_factor_relation(const rational& a_sign, const factor& a, const rational& b_sign, const factor& b);
std::unordered_set<lpvar> collect_vars(const lemma& l) const;
bool rm_check(const monomial&) const;
std::unordered_map<unsigned, unsigned_vector> get_rm_by_arity();
void add_abs_bound(lpvar v, llc cmp);
void add_abs_bound(lpvar v, llc cmp, rational const& bound);
bool find_bfc_to_refine_on_monomial(const monomial&, factorization & bf);
bool find_bfc_to_refine(const monomial* & m, factorization& bf);
void negate_relation(unsigned j, const rational& a);
bool conflict_found() const;
lbool inner_check(bool derived);
lbool check(vector<lemma>& l_vec);
bool no_lemmas_hold() const;
lbool test_check(vector<lemma>& l);
lpvar map_to_root(lpvar) const;
}; // end of core
struct pp_mon {
core const& c;
monomial const& m;
pp_mon(core const& c, monomial const& m): c(c), m(m) {}
pp_mon(core const& c, lpvar v): c(c), m(c.m_emons[v]) {}
};
struct pp_rmon {
core const& c;
monomial const& m;
pp_rmon(core const& c, monomial const& m): c(c), m(m) {}
pp_rmon(core const& c, lpvar v): c(c), m(c.m_emons[v]) {}
};
inline std::ostream& operator<<(std::ostream& out, pp_mon const& p) { return p.c.print_monomial(p.m, out); }
inline std::ostream& operator<<(std::ostream& out, pp_rmon const& p) { return p.c.print_monomial_with_vars(p.m, out); }
struct pp_fac {
core const& c;
factor const& f;
pp_fac(core const& c, factor const& f): c(c), f(f) {}
};
inline std::ostream& operator<<(std::ostream& out, pp_fac const& f) { return f.c.print_factor(f.f, out); }
struct pp_var {
core const& c;
lpvar v;
pp_var(core const& c, lpvar v): c(c), v(v) {}
};
inline std::ostream& operator<<(std::ostream& out, pp_var const& v) { return v.c.print_var(v.v, out); }
} // end of namespace nla

View file

@ -1,103 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
#include "util/lp/lp_types.h"
#include "util/lp/column_info.h"
#include "util/lp/explanation.h"
namespace nla {
typedef lp::constraint_index lpci;
typedef lp::lconstraint_kind llc;
typedef lp::constraint_index lpci;
typedef lp::explanation expl_set;
typedef lp::var_index lpvar;
struct from_index_dummy{};
class signed_var {
unsigned m_sv;
public:
// constructor, sign = true means minus
signed_var(lpvar v, bool sign): m_sv((v << 1) + (sign ? 1 : 0)) {}
explicit signed_var(unsigned sv) : m_sv(sv) {}
// constructor
bool sign() const { return 0 != (m_sv & 0x1); }
lpvar var() const { return m_sv >> 1; }
unsigned index() const { return m_sv; }
void neg() { m_sv = m_sv ^ 1; }
friend signed_var operator~(signed_var const& sv) {
return signed_var(sv.var(), !sv.sign());
}
bool operator==(signed_var const& other) const {
return m_sv == other.m_sv;
}
bool operator!=(signed_var const& other) const {
return m_sv != other.m_sv;
}
rational rsign() const { return sign() ? rational::minus_one() : rational::one(); }
std::ostream& display(std::ostream& out) const {
return out << (sign()?"-":"") << var();
}
};
inline std::ostream& operator<<(std::ostream& out, signed_var const& sv) { return sv.display(out); }
/*
* represents definition m_v = coeff* v1*v2*...*vn,
* where m_vs = [v1, v2, .., vn]
*/
class monomial_coeff {
svector<lp::var_index> m_vs;
rational m_coeff;
public:
monomial_coeff(const svector<lp::var_index>& vs, rational const& coeff): m_vs(vs), m_coeff(coeff) {}
rational const& coeff() const { return m_coeff; }
const svector<lp::var_index> & vars() const { return m_vs; }
};
template <typename T> bool has_zero(const T& product) {
for (const rational & t : product) {
if (t.is_zero())
return true;
}
return false;
}
template <typename T>
bool uniform_le(const T& a, const T& b, unsigned & strict_i) {
SASSERT(a.size() == b.size());
strict_i = -1;
bool z_b = false;
for (unsigned i = 0; i < a.size(); i++) {
if (a[i] > b[i]){
return false;
}
SASSERT(!a[i].is_neg());
if (a[i] < b[i]){
strict_i = i;
} else if (b[i].is_zero()) {
z_b = true;
}
}
if (z_b) {strict_i = -1;}
return true;
}
inline rational sign_to_rat(bool s) { return rational(s? -1 : 1); }
}

View file

@ -1,121 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/nla_basics_lemmas.h"
#include "util/lp/nla_core.h"
// #include "util/lp/factorization_factory_imp.h"
namespace nla {
monotone::monotone(core * c) : common(c) {}
void monotone::monotonicity_lemma() {
unsigned shift = random();
unsigned size = c().m_to_refine.size();
for(unsigned i = 0; i < size && !done(); i++) {
lpvar v = c().m_to_refine[(i + shift) % size];
monotonicity_lemma(c().m_emons[v]);
}
}
void monotone::negate_abs_a_le_abs_b(lpvar a, lpvar b, bool strict) {
rational av = val(a);
rational as = rational(nla::rat_sign(av));
rational bv = val(b);
rational bs = rational(nla::rat_sign(bv));
TRACE("nla_solver", tout << "av = " << av << ", bv = " << bv << "\n";);
SASSERT(as*av <= bs*bv);
llc mod_s = strict? (llc::LE): (llc::LT);
mk_ineq(as, a, mod_s); // |a| <= 0 || |a| < 0
if (a != b) {
mk_ineq(bs, b, mod_s); // |b| <= 0 || |b| < 0
mk_ineq(as, a, -bs, b, llc::GT); // negate |aj| <= |bj|
}
}
void monotone::assert_abs_val_a_le_abs_var_b(
const monomial& a,
const monomial& b,
bool strict) {
lpvar aj = var(a);
lpvar bj = var(b);
rational av = val(aj);
rational as = rational(nla::rat_sign(av));
rational bv = val(bj);
rational bs = rational(nla::rat_sign(bv));
// TRACE("nla_solver", tout << "rmv = " << rmv << ", jv = " << jv << "\n";);
mk_ineq(as, aj, llc::LT); // |aj| < 0
mk_ineq(bs, bj, llc::LT); // |bj| < 0
mk_ineq(as, aj, -bs, bj, strict? llc::LT : llc::LE); // |aj| < |bj|
}
void monotone::negate_abs_a_lt_abs_b(lpvar a, lpvar b) {
rational av = val(a);
rational as = rational(nla::rat_sign(av));
rational bv = val(b);
rational bs = rational(nla::rat_sign(bv));
TRACE("nla_solver", tout << "av = " << av << ", bv = " << bv << "\n";);
SASSERT(as*av < bs*bv);
mk_ineq(as, a, llc::LT); // |aj| < 0
mk_ineq(bs, b, llc::LT); // |bj| < 0
mk_ineq(as, a, -bs, b, llc::GE); // negate |aj| < |bj|
}
void monotone::monotonicity_lemma(monomial const& m) {
SASSERT(!check_monomial(m));
if (c().mon_has_zero(m.vars()))
return;
const rational prod_val = abs(c().product_value(m.vars()));
const rational m_val = abs(val(m));
if (m_val < prod_val)
monotonicity_lemma_lt(m, prod_val);
else if (m_val > prod_val)
monotonicity_lemma_gt(m, prod_val);
}
void monotone::monotonicity_lemma_gt(const monomial& m, const rational& prod_val) {
TRACE("nla_solver", tout << "prod_val = " << prod_val << "\n";);
add_empty_lemma();
for (lpvar j : m.vars()) {
c().add_abs_bound(j, llc::GT);
}
lpvar m_j = m.var();
c().add_abs_bound(m_j, llc::LE, prod_val);
TRACE("nla_solver", print_lemma(tout););
}
/** \brief enforce the inequality |m| >= product |m[i]| .
/\_i |m[i]| >= |val(m[i])| => |m| >= |product_i val(m[i])|
<=>
\/_i |m[i]| < |val(m[i])} or |m| >= |product_i val(m[i])|
*/
void monotone::monotonicity_lemma_lt(const monomial& m, const rational& prod_val) {
add_empty_lemma();
for (lpvar j : m.vars()) {
c().add_abs_bound(j, llc::LT);
}
lpvar m_j = m.var();
c().add_abs_bound(m_j, llc::GE, prod_val);
TRACE("nla_solver", print_lemma(tout););
}
}

View file

@ -1,37 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace nla {
class core;
class monotone : common {
public:
monotone(core *core);
void monotonicity_lemma();
private:
void monotonicity_lemma(monomial const& m);
void monotonicity_lemma_gt(const monomial& m, const rational& prod_val);
void monotonicity_lemma_lt(const monomial& m, const rational& prod_val);
std::vector<rational> get_sorted_key(const monomial& rm) const;
vector<std::pair<rational, lpvar>> get_sorted_key_with_rvars(const monomial& a) const;
void negate_abs_a_le_abs_b(lpvar a, lpvar b, bool strict);
void negate_abs_a_lt_abs_b(lpvar a, lpvar b);
void assert_abs_val_a_le_abs_var_b(const monomial& a, const monomial& b, bool strict);
};
}

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