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:
parent
b6513b8e2d
commit
33cbd29ed0
150 changed files with 524 additions and 479 deletions
|
@ -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})
|
|
@ -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());
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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&);
|
||||
}
|
|
@ -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); }
|
||||
};
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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) {}
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
|
@ -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); }
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
|
@ -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);
|
||||
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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); }
|
||||
|
||||
}
|
|
@ -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>&);
|
|
@ -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);
|
||||
};
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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(); }
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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); }
|
||||
|
||||
}
|
|
@ -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();
|
||||
};
|
||||
}
|
|
@ -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; }
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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(); }
|
||||
};
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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; }
|
||||
};
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
};
|
||||
}
|
|
@ -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; }
|
||||
};
|
||||
}
|
|
@ -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"
|
|
@ -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; }
|
||||
};
|
||||
}
|
|
@ -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";);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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
|
@ -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();
|
||||
};
|
||||
}
|
|
@ -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(); }
|
||||
};
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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););
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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
|
@ -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();
|
|
@ -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; }
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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')
|
||||
))
|
||||
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
|
@ -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;
|
||||
|
||||
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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&);
|
||||
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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 };
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
383
src/util/lp/lu.h
383
src/util/lp/lu.h
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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&);
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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 <rim(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;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
@ -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
|
||||
|
|
@ -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); }
|
||||
}
|
|
@ -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););
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue