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

Merge pull request #1715 from levnach/master

merge lar_solver/int_solver
This commit is contained in:
Nikolaj Bjorner 2018-07-01 12:20:02 -07:00 committed by GitHub
commit 5a2a8d7d5c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
159 changed files with 14144 additions and 9250 deletions

View file

@ -1,33 +1,37 @@
z3_add_component(lp
SOURCES
lp_utils.cpp
binary_heap_priority_queue_instances.cpp
binary_heap_upair_queue_instances.cpp
lp_bound_propagator.cpp
core_solver_pretty_printer_instances.cpp
dense_matrix_instances.cpp
eta_matrix_instances.cpp
indexed_vector_instances.cpp
lar_core_solver_instances.cpp
lp_core_solver_base_instances.cpp
lp_dual_core_solver_instances.cpp
lp_dual_simplex_instances.cpp
lp_primal_core_solver_instances.cpp
lp_primal_simplex_instances.cpp
lp_settings_instances.cpp
lp_solver_instances.cpp
lu_instances.cpp
matrix_instances.cpp
permutation_matrix_instances.cpp
quick_xplain.cpp
row_eta_matrix_instances.cpp
scaler_instances.cpp
sparse_matrix_instances.cpp
square_dense_submatrix_instances.cpp
static_matrix_instances.cpp
random_updater_instances.cpp
binary_heap_priority_queue.cpp
binary_heap_upair_queue.cpp
bound_propagator.cpp
core_solver_pretty_printer.cpp
dense_matrix.cpp
eta_matrix.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
matrix.cpp
nra_solver.cpp
permutation_matrix.cpp
row_eta_matrix.cpp
scaler.cpp
square_sparse_matrix.cpp
square_dense_submatrix.cpp
static_matrix.cpp
random_updater.cpp
COMPONENT_DEPENDENCIES
util
polynomial
nlsat
PYG_FILES
lp_params.pyg
)

76
src/util/lp/active_set.h Normal file
View file

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

View file

@ -18,7 +18,7 @@ Revision History:
--*/
#include "util/lp/numeric_pair.h"
#include "util/lp/binary_heap_priority_queue.hpp"
#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();

View file

@ -45,7 +45,7 @@ public:
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 two times automatically if needed
// The capacity will be enlarged each time twice if needed
binary_heap_priority_queue(unsigned n);
void clear() {
@ -75,7 +75,7 @@ public:
/// return the first element of the queue and removes it from the queue
unsigned dequeue();
unsigned peek() const {
SASSERT(m_heap_size > 0);
lp_assert(m_heap_size > 0);
return m_heap[1];
}
#ifdef Z3DEBUG

View file

@ -20,7 +20,7 @@ Revision History:
#include "util/vector.h"
#include "util/lp/binary_heap_priority_queue.h"
namespace lp {
// this is the child place in the heap
// "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]);
@ -48,8 +48,8 @@ template <typename T> void binary_heap_priority_queue<T>::decrease_priority(unsi
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];
SASSERT(i_index <= static_cast<int>(m_heap_size));
SASSERT(i_index == -1 || m_heap[i_index] == 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;
@ -71,7 +71,7 @@ template <typename T> void binary_heap_priority_queue<T>::remove(unsigned o) {
if (o_in_heap == -1) {
return; // nothing to do
}
SASSERT(static_cast<unsigned>(o_in_heap) <= m_heap_size);
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) {
@ -88,11 +88,11 @@ template <typename T> void binary_heap_priority_queue<T>::remove(unsigned o) {
}
}
} else {
SASSERT(static_cast<unsigned>(o_in_heap) == m_heap_size);
lp_assert(static_cast<unsigned>(o_in_heap) == m_heap_size);
m_heap_size--;
}
m_heap_inverse[o] = -1;
// SASSERT(is_consistent());
// lp_assert(is_consistent());
}
// n is the initial queue capacity.
// The capacity will be enlarged two times automatically if needed
@ -118,7 +118,7 @@ template <typename T> void binary_heap_priority_queue<T>::put_to_heap(unsigned 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;
SASSERT(o < m_priorities.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) {
@ -130,8 +130,12 @@ template <typename T> void binary_heap_priority_queue<T>::enqueue_new(unsigned o
// In this case the priority will be changed and the queue adjusted.
template <typename T> void binary_heap_priority_queue<T>::enqueue(unsigned o, const T & priority) {
if (o >= m_priorities.size()) {
resize(o << 1); // make the size twice larger
if (o == 0)
resize(2);
else
resize(o << 1); // make the size twice larger
}
if (m_heap_inverse[o] == -1)
enqueue_new(o, priority);
else
@ -150,7 +154,7 @@ template <typename T> void binary_heap_priority_queue<T>::change_priority_for_ex
/// 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) {
SASSERT(m_heap_size != 0);
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();
@ -184,7 +188,7 @@ template <typename T> void binary_heap_priority_queue<T>::put_the_last_at_the_to
}
/// return the first element of the queue and removes it from the queue
template <typename T> unsigned binary_heap_priority_queue<T>::dequeue() {
SASSERT(m_heap_size > 0);
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;

View file

@ -17,7 +17,7 @@ Revision History:
--*/
#include "util/lp/binary_heap_upair_queue.hpp"
#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);

View file

@ -29,7 +29,7 @@ template <typename T> binary_heap_upair_queue<T>::binary_heap_upair_queue(unsign
template <typename T> unsigned
binary_heap_upair_queue<T>::dequeue_available_spot() {
SASSERT(m_available_spots.empty() == false);
lp_assert(m_available_spots.empty() == false);
unsigned ret = m_available_spots.back();
m_available_spots.pop_back();
return ret;
@ -69,7 +69,7 @@ template <typename T> void binary_heap_upair_queue<T>::enqueue(unsigned i, unsig
m_pairs.resize(new_size);
}
ij_index = dequeue_available_spot();
// SASSERT(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
// 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 {
@ -79,7 +79,7 @@ template <typename T> void binary_heap_upair_queue<T>::enqueue(unsigned i, unsig
}
template <typename T> void binary_heap_upair_queue<T>::dequeue(unsigned & i, unsigned &j) {
SASSERT(!m_q.is_empty());
lp_assert(!m_q.is_empty());
unsigned ij_index = m_q.dequeue();
upair & p = m_pairs[ij_index];
i = p.first;

View file

@ -19,37 +19,93 @@ Revision History:
--*/
#pragma once
#include "util/vector.h"
#include "util/lp/linear_combination_iterator.h"
#include "util/lp/implied_bound.h"
#include "util/lp/test_bound_analyzer.h"
#include <functional>
#include "util/lp/lp_bound_propagator.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 {
struct term_with_basis_col {
const C & m_row;
unsigned m_bj;
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; }
};
term_with_basis_col(const C& row, unsigned bj) : m_row(row), m_bj(bj) {}
struct const_iterator {
// fields
typename C::const_iterator m_it;
unsigned m_bj;
linear_combination_iterator<mpq> & m_it;
lp_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;
//typedefs
typedef const_iterator self_type;
typedef ival value_type;
typedef ival reference;
typedef int difference_type;
typedef std::forward_iterator_tag iterator_category;
reference operator*() const {
if (m_bj == static_cast<unsigned>(-1))
return ival((*m_it).var(), (*m_it).coeff());
return ival(m_bj, - 1);
}
self_type operator++() { self_type i = *this; operator++(1); return i; }
self_type operator++(int) {
if (m_bj == static_cast<unsigned>(-1))
m_it++;
else
m_bj = static_cast<unsigned>(-1);
return *this;
}
// constructor
const_iterator(const typename C::const_iterator& it, unsigned bj) :
m_it(it),
m_bj(bj)
{}
bool operator==(const self_type &other) const {
return m_it == other.m_it && m_bj == other.m_bj ;
}
bool operator!=(const self_type &other) const { return !(*this == other); }
};
const_iterator begin() const {
return const_iterator( m_row.begin(), m_bj);
}
const_iterator end() const { return const_iterator(m_row.end(), m_bj); }
};
term_with_basis_col 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(
linear_combination_iterator<mpq> &it,
const numeric_pair<mpq>& rs,
unsigned row_or_term_index,
lp_bound_propagator & bp
const C & it,
unsigned bj, // basis column for the row
const numeric_pair<mpq>& rs,
unsigned row_or_term_index,
bound_propagator & bp
)
:
m_it(it),
m_row(it, bj),
m_bp(bp),
m_row_or_term_index(row_or_term_index),
m_column_of_u(-1),
@ -60,11 +116,11 @@ public :
unsigned j;
void analyze() {
mpq a; unsigned j;
while (((m_column_of_l != -2) || (m_column_of_u != -2)) && m_it.next(a, j))
analyze_bound_on_var_on_coeff(j, a);
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)
@ -76,42 +132,42 @@ public :
limit_all_monoids_from_above();
}
bool bound_is_available(unsigned j, bool low_bound) {
return (low_bound && low_bound_is_available(j)) ||
(!low_bound && upper_bound_is_available(j));
bool 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;
}
{
case column_type::fixed:
case column_type::boxed:
case column_type::upper_bound:
return true;
default:
return false;
}
}
bool low_bound_is_available(unsigned j) const {
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::low_bound:
return true;
default:
return false;
}
{
case column_type::fixed:
case column_type::boxed:
case column_type::lower_bound:
return true;
default:
return false;
}
}
const impq & ub(unsigned j) const {
SASSERT(upper_bound_is_available(j));
lp_assert(upper_bound_is_available(j));
return m_bp.get_upper_bound(j);
}
const impq & lb(unsigned j) const {
SASSERT(low_bound_is_available(j));
return m_bp.get_low_bound(j);
lp_assert(lower_bound_is_available(j));
return m_bp.get_lower_bound(j);
}
@ -151,7 +207,7 @@ public :
strict = !is_zero(ub(j).y);
return a * ub(j).x;
}
strict = !is_zero(lb(j).y);
return a * lb(j).x;
}
@ -160,34 +216,32 @@ public :
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;
SASSERT(is_zero(total));
m_it.reset();
mpq a; unsigned j;
while (m_it.next(a, j)) {
lp_assert(is_zero(total));
for (const auto& p : m_row) {
bool str;
total -= monoid_min(a, j, str);
total -= monoid_min(p.coeff(), p.var(), str);
if (str)
strict++;
}
m_it.reset();
while (m_it.next(a, j)) {
for (const auto &p : m_row) {
bool str;
bool a_is_pos = is_pos(a);
mpq bound = total / a + monoid_min_no_mult(a_is_pos, j, str);
bool a_is_pos = is_pos(p.coeff());
mpq bound = total / p.coeff() + monoid_min_no_mult(a_is_pos, p.var(), str);
if (a_is_pos) {
limit_j(j, bound, true, false, strict - static_cast<int>(str) > 0);
limit_j(p.var(), bound, true, false, strict - static_cast<int>(str) > 0);
}
else {
limit_j(j, bound, false, true, strict - static_cast<int>(str) > 0);
limit_j(p.var(), bound, false, true, strict - static_cast<int>(str) > 0);
}
}
}
@ -195,52 +249,50 @@ public :
void limit_all_monoids_from_below() {
int strict = 0;
mpq total;
SASSERT(is_zero(total));
m_it.reset();
mpq a; unsigned j;
while (m_it.next(a, j)) {
lp_assert(is_zero(total));
for (const auto &p : m_row) {
bool str;
total -= monoid_max(a, j, str);
total -= monoid_max(p.coeff(), p.var(), str);
if (str)
strict++;
}
m_it.reset();
while (m_it.next(a, j)) {
for (const auto& p : m_row) {
bool str;
bool a_is_pos = is_pos(a);
mpq bound = total / a + monoid_max_no_mult(a_is_pos, j, str);
bool astrict = strict - static_cast<int>(str) > 0;
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(j, bound, true, true, astrict);
limit_j(p.var(), bound, true, true, astrict);
}
else {
limit_j(j, bound, false, false, astrict);
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, a;
mpq u_coeff;
unsigned j;
mpq bound = -m_rs.x;
m_it.reset();
bool strict = false;
while (m_it.next(a, j)) {
for (const auto& p : m_row) {
j = p.var();
if (j == static_cast<unsigned>(m_column_of_u)) {
u_coeff = a;
u_coeff = p.coeff();
continue;
}
bool str;
bound -= monoid_max(a, j, str);
bound -= monoid_max(p.coeff(), j, str);
if (str)
strict = true;
}
bound /= u_coeff;
if (numeric_traits<impq>::is_pos(u_coeff)) {
limit_j(m_column_of_u, bound, true, true, strict);
} else {
@ -252,19 +304,19 @@ public :
void limit_monoid_l_from_above() {
// we are going to limit from above the monoid m_column_of_l,
// every other monoid is impossible to limit from above
mpq l_coeff, a;
mpq l_coeff;
unsigned j;
mpq bound = -m_rs.x;
bool strict = false;
m_it.reset();
while (m_it.next(a, j)) {
for (const auto &p : m_row) {
j = p.var();
if (j == static_cast<unsigned>(m_column_of_l)) {
l_coeff = a;
l_coeff = p.coeff();
continue;
}
bool str;
bound -= monoid_min(a, j, str);
bound -= monoid_min(p.coeff(), j, str);
if (str)
strict = true;
}
@ -275,51 +327,51 @@ public :
limit_j(m_column_of_l, bound, false, true, strict);
}
}
// // it is the coefficent before the bounded column
// void provide_evidence(bool coeff_is_pos) {
// /*
// auto & be = m_ibounds.back();
// bool low_bound = be.m_low_bound;
// bool lower_bound = be.m_lower_bound;
// if (!coeff_is_pos)
// low_bound = !low_bound;
// auto it = m_it.clone();
// lower_bound = !lower_bound;
// auto it = m_row.clone();
// mpq a; unsigned j;
// while (it->next(a, j)) {
// if (be.m_j == j) continue;
// SASSERT(bound_is_available(j, is_neg(a) ? low_bound : !low_bound));
// 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)? low_bound: !low_bound);
// 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_low_bound, bool strict){
m_bp.try_add_bound(u, j, is_low_bound, coeff_before_j_is_pos, m_row_or_term_index, strict);
void 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::low_bound:
case column_type::lower_bound:
if (numeric_traits<mpq>::is_pos(a))
advance_u(j);
else
else
advance_l(j);
break;
case column_type::upper_bound:
@ -337,14 +389,16 @@ public :
}
}
static void analyze_row(linear_combination_iterator<mpq> &it,
static void analyze_row(const C & row,
unsigned bj, // basis column for the row
const numeric_pair<mpq>& rs,
unsigned row_or_term_index,
lp_bound_propagator & bp
bound_propagator & bp
) {
bound_analyzer_on_row a(it, rs, row_or_term_index, bp);
bound_analyzer_on_row a(row, bj, rs, row_or_term_index, bp);
a.analyze();
}
};
}

View file

@ -0,0 +1,58 @@
/*
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);
if (m_lar_solver.is_term(j)) {
// lp treats terms as not having a free coefficient, restoring it below for the outside consumption
v += m_lar_solver.get_term(j).m_v;
}
lconstraint_kind kind = is_low? GE : LE;
if (strict)
kind = static_cast<lconstraint_kind>(kind / 2);
if (!bound_is_interesting(j, kind, v))
return;
unsigned k; // index to ibounds
if (is_low) {
if (try_get_value(m_improved_lower_bounds, j, k)) {
auto & found_bound = m_ibounds[k];
if (v > found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict)) {
found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
TRACE("try_add_bound", m_lar_solver.print_implied_bound(found_bound, tout););
}
} else {
m_improved_lower_bounds[j] = m_ibounds.size();
m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
TRACE("try_add_bound", m_lar_solver.print_implied_bound(m_ibounds.back(), tout););
}
} else { // the upper bound case
if (try_get_value(m_improved_upper_bounds, j, k)) {
auto & found_bound = m_ibounds[k];
if (v < found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict)) {
found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
TRACE("try_add_bound", m_lar_solver.print_implied_bound(found_bound, tout););
}
} else {
m_improved_upper_bounds[j] = m_ibounds.size();
m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
TRACE("try_add_bound", m_lar_solver.print_implied_bound(m_ibounds.back(), tout););
}
}
}
}

View file

@ -0,0 +1,27 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/lp_settings.h"
namespace lp {
class lar_solver;
class bound_propagator {
std::unordered_map<unsigned, unsigned> m_improved_lower_bounds; // these maps map a column index to the corresponding index in ibounds
std::unordered_map<unsigned, unsigned> m_improved_upper_bounds;
lar_solver & m_lar_solver;
public:
vector<implied_bound> m_ibounds;
public:
bound_propagator(lar_solver & ls);
column_type get_column_type(unsigned) const;
const impq & get_lower_bound(unsigned) const;
const impq & get_upper_bound(unsigned) const;
void try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict);
virtual bool bound_is_interesting(unsigned vi,
lp::lconstraint_kind kind,
const rational & bval) {return true;}
unsigned number_of_found_bounds() const { return m_ibounds.size(); }
virtual void consume(mpq const& v, lp::constraint_index j) = 0;
};
}

View file

@ -30,11 +30,11 @@ inline bool is_valid(unsigned j) { return static_cast<int>(j) >= 0;}
template <typename T>
class column_info {
std::string m_name;
bool m_low_bound_is_set;
bool m_low_bound_is_strict;
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_low_bound;
T m_lower_bound;
T m_upper_bound;
T m_fixed_value;
bool m_is_fixed;
@ -43,11 +43,11 @@ class column_info {
public:
bool operator==(const column_info & c) const {
return m_name == c.m_name &&
m_low_bound_is_set == c.m_low_bound_is_set &&
m_low_bound_is_strict == c.m_low_bound_is_strict &&
m_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_low_bound_is_set || m_low_bound == c.m_low_bound) &&
(!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 &&
@ -60,8 +60,8 @@ public:
}
// the default constructor
column_info():
m_low_bound_is_set(false),
m_low_bound_is_strict(false),
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),
@ -70,8 +70,8 @@ public:
{}
column_info(unsigned column_index) :
m_low_bound_is_set(false),
m_low_bound_is_strict(false),
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),
@ -81,11 +81,11 @@ public:
column_info(const column_info & ci) {
m_name = ci.m_name;
m_low_bound_is_set = ci.m_low_bound_is_set;
m_low_bound_is_strict = ci.m_low_bound_is_strict;
m_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_low_bound = ci.m_low_bound;
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;
@ -98,7 +98,7 @@ public:
}
column_type get_column_type() const {
return m_is_fixed? column_type::fixed : (m_low_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::low_bound) : (m_upper_bound_is_set? column_type::upper_bound: column_type::free_column));
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 {
@ -106,25 +106,25 @@ public:
return column_type::fixed;
}
if (m_low_bound_is_set) {
return m_upper_bound_is_set? column_type::boxed: column_type::low_bound;
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_low_bound() const {
SASSERT(m_low_bound_is_set);
return m_low_bound;
T get_lower_bound() const {
lp_assert(m_lower_bound_is_set);
return m_lower_bound;
}
T get_upper_bound() const {
SASSERT(m_upper_bound_is_set);
lp_assert(m_upper_bound_is_set);
return m_upper_bound;
}
bool low_bound_is_set() const {
return m_low_bound_is_set;
bool lower_bound_is_set() const {
return m_lower_bound_is_set;
}
bool upper_bound_is_set() const {
@ -138,23 +138,23 @@ public:
if (is_flipped()){
return m_upper_bound;
}
return m_low_bound_is_set? m_low_bound : numeric_traits<T>::zero();
return m_lower_bound_is_set? m_lower_bound : numeric_traits<T>::zero();
}
bool is_flipped() {
return m_upper_bound_is_set && !m_low_bound_is_set;
return m_upper_bound_is_set && !m_lower_bound_is_set;
}
bool adjusted_low_bound_is_set() {
return !is_flipped()? low_bound_is_set(): upper_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(): low_bound_is_set();
return !is_flipped()? upper_bound_is_set(): lower_bound_is_set();
}
T get_adjusted_upper_bound() {
return get_upper_bound() - get_low_bound();
return get_upper_bound() - get_lower_bound();
}
bool is_fixed() const {
@ -162,7 +162,7 @@ public:
}
bool is_free() {
return !m_low_bound_is_set && !m_upper_bound_is_set;
return !m_lower_bound_is_set && !m_upper_bound_is_set;
}
void set_fixed_value(T v) {
@ -171,7 +171,7 @@ public:
}
T get_fixed_value() const {
SASSERT(m_is_fixed);
lp_assert(m_is_fixed);
return m_fixed_value;
}
@ -191,9 +191,9 @@ public:
return m_name;
}
void set_low_bound(T const & l) {
m_low_bound = l;
m_low_bound_is_set = true;
void set_lower_bound(T const & l) {
m_lower_bound = l;
m_lower_bound_is_set = true;
}
void set_upper_bound(T const & l) {
@ -201,8 +201,8 @@ public:
m_upper_bound_is_set = true;
}
void unset_low_bound() {
m_low_bound_is_set = false;
void unset_lower_bound() {
m_lower_bound_is_set = false;
}
void unset_upper_bound() {
@ -213,8 +213,8 @@ public:
m_is_fixed = false;
}
bool low_bound_holds(T v) {
return !low_bound_is_set() || v >= m_low_bound -T(0.0000001);
bool lower_bound_holds(T v) {
return !lower_bound_is_set() || v >= m_lower_bound -T(0.0000001);
}
bool upper_bound_holds(T v) {
@ -222,36 +222,36 @@ public:
}
bool bounds_hold(T v) {
return low_bound_holds(v) && upper_bound_holds(v);
return lower_bound_holds(v) && upper_bound_holds(v);
}
bool adjusted_bounds_hold(T v) {
return adjusted_low_bound_holds(v) && adjusted_upper_bound_holds(v);
return adjusted_lower_bound_holds(v) && adjusted_upper_bound_holds(v);
}
bool adjusted_low_bound_holds(T v) {
return !adjusted_low_bound_is_set() || v >= -T(0.0000001);
bool adjusted_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()) || (!low_bound_is_set()))
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() || low_bound_is_strict();
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_low_bound();
return get_upper_bound() < get_lower_bound();
// at least on bound is strict
return get_upper_bound() <= get_low_bound(); // the equality is impossible
return get_upper_bound() <= get_lower_bound(); // the equality is impossible
}
bool low_bound_is_strict() const {
return m_low_bound_is_strict;
bool lower_bound_is_strict() const {
return m_lower_bound_is_strict;
}
void set_low_bound_strict(bool val) {
m_low_bound_is_strict = val;
void set_lower_bound_strict(bool val) {
m_lower_bound_is_strict = val;
}
bool upper_bound_is_strict() const {

View file

@ -19,31 +19,19 @@ Revision History:
--*/
#include <string>
#include "util/lp/linear_combination_iterator.h"
#include "util/lp/static_matrix.h"
namespace lp {
class column_namer {
public:
virtual std::string get_column_name(unsigned j) const = 0;
template <typename T>
void print_linear_iterator(linear_combination_iterator<T>* it, std::ostream & out) const {
void print_row(const row_strip<T> & row, std::ostream & out) const {
vector<std::pair<T, unsigned>> coeff;
T a;
unsigned i;
while (it->next(a, i)) {
coeff.push_back(std::make_pair(a, i));
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_iterator_indices_only(linear_combination_iterator<T>* it, std::ostream & out) const {
vector<std::pair<T, unsigned>> coeff;
T a;
unsigned i;
while (it->next(a, i)) {
coeff.emplace_back(a, i);
}
print_linear_combination_of_column_indices_only(coeff, out);
}
template <typename T>
void print_linear_combination_of_column_indices_only(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const {
@ -65,10 +53,34 @@ public:
else if (val != numeric_traits<T>::one())
out << T_to_string(val);
out << "_" << it.second;
out << "v" << it.second;
}
}
template <typename T>
void print_linear_combination_of_column_indices_std(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const {
bool first = true;
for (const auto & it : coeffs) {
auto val = it.first;
if (first) {
first = false;
} else {
if (numeric_traits<T>::is_pos(val)) {
out << " + ";
} else {
out << " - ";
val = -val;
}
}
if (val == -numeric_traits<T>::one())
out << " - ";
else if (val != numeric_traits<T>::one())
out << val;
out << get_column_name(it.second);
}
}
template <typename T>
void print_linear_combination_of_column_indices(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const {
bool first = true;

99
src/util/lp/constraint.h Normal file
View file

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

View file

@ -22,8 +22,8 @@ Revision History:
namespace lp {
template <typename V>
struct conversion_helper {
static V get_low_bound(const column_info<mpq> & ci) {
return V(ci.get_low_bound(), ci.low_bound_is_strict()? 1 : 0);
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) {
@ -37,20 +37,20 @@ struct conversion_helper <double> {
if (!ci.upper_bound_is_strict())
return ci.get_upper_bound().get_double();
double eps = 0.00001;
if (!ci.low_bound_is_set())
if (!ci.lower_bound_is_set())
return ci.get_upper_bound().get_double() - eps;
eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double() / 1000, 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_low_bound(const column_info<mpq> & ci) {
if (!ci.low_bound_is_strict())
return ci.get_low_bound().get_double();
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_low_bound().get_double() + eps;
eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double() / 1000, eps);
return ci.get_low_bound().get_double() + eps;
return ci.get_lower_bound().get_double() + eps;
eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps);
return ci.get_lower_bound().get_double() + eps;
}
};

View file

@ -18,7 +18,7 @@ Revision History:
--*/
#include "util/lp/numeric_pair.h"
#include "util/lp/core_solver_pretty_printer.hpp"
#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();

View file

@ -48,7 +48,7 @@ class core_solver_pretty_printer {
std::string m_cost_title;
std::string m_basis_heading_title;
std::string m_x_title;
std::string m_low_bounds_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;
@ -75,7 +75,7 @@ public:
void init_column_widths();
void adjust_width_with_low_bound(unsigned column, unsigned & w);
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);
@ -97,7 +97,7 @@ public:
void print_x();
std::string get_low_bound_string(unsigned j);
std::string get_lower_bound_string(unsigned j);
std::string get_upp_bound_string(unsigned j);

View file

@ -38,7 +38,7 @@ core_solver_pretty_printer<T, X>::core_solver_pretty_printer(lp_core_solver_base
m_rs(ncols(), zero_of_type<X>()),
m_w_buff(core_solver.m_w),
m_ed_buff(core_solver.m_ed) {
m_low_bounds_title = "low";
m_lower_bounds_title = "low";
m_upp_bounds_title = "upp";
m_exact_norm_title = "exact cn";
m_approx_norm_title = "approx cn";
@ -105,6 +105,8 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_m_
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],
@ -139,9 +141,9 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_co
}
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_low_bound(unsigned column, unsigned & w) {
if (!m_core_solver.low_bounds_are_set()) return;
w = std::max(w, (unsigned)T_to_string(m_core_solver.low_bound_value(column)).size());
template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_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());
@ -151,11 +153,11 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_
switch (m_core_solver.get_column_type(column)) {
case column_type::fixed:
case column_type::boxed:
adjust_width_with_low_bound(column, w);
adjust_width_with_lower_bound(column, w);
adjust_width_with_upper_bound(column, w);
break;
case column_type::low_bound:
adjust_width_with_low_bound(column, w);
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);
@ -163,7 +165,7 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_
case column_type::free_column:
break;
default:
SASSERT(false);
lp_assert(false);
break;
}
}
@ -236,13 +238,13 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_x
m_out << std::endl;
}
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_low_bound_string(unsigned j) {
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::low_bound:
case column_type::lower_bound:
case column_type::fixed:
if (m_core_solver.low_bounds_are_set())
return T_to_string(m_core_solver.low_bound_value(j));
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;
@ -268,12 +270,12 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_l
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - static_cast<unsigned>(m_low_bounds_title.size());
m_out << m_low_bounds_title;
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_low_bound_string(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
@ -372,7 +374,7 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_g
unsigned width = m_column_widths[col];
string s = row[col];
int number_of_blanks = width - static_cast<unsigned>(s.size());
SASSERT(number_of_blanks >= 0);
lp_assert(number_of_blanks >= 0);
print_blanks(number_of_blanks, m_out);
m_out << s << ' ';
if (col < row.size() - 1) {
@ -383,7 +385,7 @@ template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_g
string rs = T_to_string(rst);
int nb = m_rs_width - static_cast<int>(rs.size());
SASSERT(nb >= 0);
lp_assert(nb >= 0);
print_blanks(nb + 1, m_out);
m_out << rs << std::endl;
}

View file

@ -18,7 +18,7 @@ Revision History:
--*/
#include "util/lp/lp_settings.h"
#include "util/lp/dense_matrix.hpp"
#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>&);

View file

@ -46,7 +46,7 @@ public:
dense_matrix(unsigned m, unsigned n);
dense_matrix operator*=(matrix<T, X> const & a) {
SASSERT(column_count() == a.row_count());
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++) {
@ -100,7 +100,7 @@ public:
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);

View file

@ -23,7 +23,6 @@ Revision History:
#include "util/lp/numeric_pair.h"
#include "util/lp/dense_matrix.h"
namespace lp {
template <typename T> void print_vector(const vector<T> & t, std::ostream & out);
template <typename T, typename X> dense_matrix<T, X>::dense_matrix(unsigned m, unsigned n) : m_m(m), m_n(n), m_values(m * n, numeric_traits<T>::zero()) {
}
@ -185,7 +184,7 @@ template <typename T, typename X> void dense_matrix<T, X>::multiply_row_by_const
template <typename T, typename X>
dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b){
SASSERT(a.column_count() == b.row_count());
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++) {

View file

@ -1,334 +0,0 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#pragma once
#include <map>
namespace lp {
// represents the set of disjoint intervals of integer number
struct disjoint_intervals {
std::map<int, short> m_endpoints; // 0 means start, 1 means end, 2 means both - for a point interval
bool m_empty;
// constructors create an interval containing all integer numbers or an empty interval
disjoint_intervals() : m_empty(false) {}
disjoint_intervals(bool is_empty) : m_empty(is_empty) {}
bool is_start(short x) const { return x == 0 || x == 2; }
bool is_start(const std::map<int, short>::iterator & it) const {
return is_start(it->second);
}
bool is_start(const std::map<int, short>::reverse_iterator & it) const {
return is_start(it->second);
}
bool is_end(short x) const { return x == 1 || x == 2; }
bool is_end(const std::map<int, short>::iterator & it) const {
return is_end(it->second);
}
bool is_end(const std::map<int, short>::reverse_iterator & it) const {
return is_end(it->second);
}
int pos(const std::map<int, short>::iterator & it) const {
return it->first;
}
int pos(const std::map<int, short>::reverse_iterator & it) const {
return it->first;
}
int bound_kind(const std::map<int, short>::iterator & it) const {
return it->second;
}
int bound_kind(const std::map<int, short>::reverse_iterator & it) const {
return it->second;
}
bool is_proper_start(short x) const { return x == 0; }
bool is_proper_end(short x) const { return x == 1; }
bool is_proper_end(const std::map<int, short>::iterator & it) const {
return is_proper_end(it->second);
}
bool is_proper_end(const std::map<int, short>::reverse_iterator & it) const {
return is_proper_end(it->second);
}
bool is_one_point_interval(short x) const { return x == 2; }
bool is_one_point_interval(const std::map<int, short>::iterator & it) const {
return is_one_point_interval(it->second);
}
bool is_one_point_interval(const std::map<int, short>::reverse_iterator & it) const {
return is_one_point_interval(it->second);
}
void erase(int x) {
m_endpoints.erase(x);
}
void set_one_point_segment(int x) {
m_endpoints[x] = 2;
}
void set_start(int x) {
m_endpoints[x] = 0;
}
void set_end(int x) {
m_endpoints[x] = 1;
}
void remove_all_endpoints_below(int x) {
while (m_endpoints.begin() != m_endpoints.end() && m_endpoints.begin()->first < x)
m_endpoints.erase(m_endpoints.begin());
}
// we intersect the existing set with the half open to the right interval
void intersect_with_lower_bound(int x) {
if (m_empty)
return;
if (m_endpoints.empty()) {
set_start(x);
return;
}
bool pos_inf = has_pos_inf();
auto it = m_endpoints.begin();
while (it != m_endpoints.end() && pos(it) < x) {
m_endpoints.erase(it);
it = m_endpoints.begin();
}
if (m_endpoints.empty()) {
if (!pos_inf) {
m_empty = true;
return;
}
set_start(x);
return;
}
lp_assert(pos(it) >= x);
if (pos(it) == x) {
if (is_proper_end(it))
set_one_point_segment(x);
}
else { // x(it) > x
if (is_proper_end(it)) {
set_start(x);
}
}
lp_assert(is_correct());
}
// we intersect the existing set with the half open interval
void intersect_with_upper_bound(int x) {
if (m_empty)
return;
if (m_endpoints.empty()) {
set_end(x);
return;
}
bool neg_inf = has_neg_inf();
auto it = m_endpoints.rbegin();
while (!m_endpoints.empty() && pos(it) > x) {
m_endpoints.erase(std::prev(m_endpoints.end()));
it = m_endpoints.rbegin();
}
if (m_endpoints.empty()) {
if (!neg_inf) {
m_empty = true;
return;
}
set_end(x);
}
lp_assert(pos(it) <= x);
if (pos(it) == x) {
if (is_one_point_interval(it)) {}
else if (is_proper_end(it)) {}
else {// is_proper_start(it->second)
set_one_point_segment(x);
}
}
else { // pos(it) < x}
if (is_start(it))
set_end(x);
}
lp_assert(is_correct());
}
bool has_pos_inf() const {
if (m_empty)
return false;
if (m_endpoints.empty())
return true;
lp_assert(m_endpoints.rbegin() != m_endpoints.rend());
return m_endpoints.rbegin()->second == 0;
}
bool has_neg_inf() const {
if (m_empty)
return false;
if (m_endpoints.empty())
return true;
auto it = m_endpoints.begin();
return is_proper_end(it->second);//m_endpoints.begin());
}
// we are intersecting
void intersect_with_interval(int x, int y) {
if (m_empty)
return;
lp_assert(x <= y);
intersect_with_lower_bound(x);
intersect_with_upper_bound(y);
}
// add an intervar [x, inf]
void unite_with_interval_x_pos_inf(int x) {
if (m_empty) {
set_start(x);
m_empty = false;
return;
}
while (!m_endpoints.empty() && pos(m_endpoints.rbegin()) > x) {
m_endpoints.erase(std::prev(m_endpoints.end()));
}
if (m_endpoints.empty()) {
set_start(x);
return;
}
auto it = m_endpoints.rbegin();
lp_assert(pos(it) <= x);
if (pos(it) == x) {
if (is_end(it)) {
m_endpoints.erase(x);
} else {
set_start(x);
}
} else if (pos(it) == x - 1 && is_end(it)) {
m_endpoints.erase(x - 1); // closing the gap
} else {
if (!has_pos_inf())
set_start(x);
}
}
// add an interval [-inf, x]
void unite_with_interval_neg_inf_x(int x) {
if (m_empty) {
set_end(x);
m_empty = false;
return;
}
auto it = m_endpoints.upper_bound(x);
if (it == m_endpoints.end()) {
bool pos_inf = has_pos_inf();
m_endpoints.clear();
// it could be the case where x is inside of the last infinite interval with pos inf
if (!pos_inf)
set_end(x);
return;
}
lp_assert(pos(it) > x);
if (is_one_point_interval(pos(it))) {
set_end(it->second);
} else {
if (is_start(it->second)) {
set_end(x);
}
}
while (!m_endpoints.empty() && m_endpoints.begin()->first < x) {
m_endpoints.erase(m_endpoints.begin());
}
lp_assert(is_correct());
}
void unite_with_interval(int x, int y) {
lp_assert(false); // not implemented
}
bool is_correct() const {
if (m_empty) {
if (m_endpoints.size() > 0) {
std::cout << "is empty is true but m_endpoints.size() = " << m_endpoints.size() << std::endl;
return false;
}
return true;
}
bool expect_end;
bool prev = false;
int prev_x;
for (auto t : m_endpoints) {
if (prev && (expect_end != t.second > 0)) {
std::cout << "x = " << t.first << "\n";
if (expect_end) {
std::cout << "expecting an interval end\n";
} else {
std::cout << "expecting an interval start\n";
}
return false;
}
if (t.second == 2) {
expect_end = false; // swallow a point interval
} else {
if (prev)
expect_end = !expect_end;
else
expect_end = is_start(t.second);
}
if (prev) {
if (t.first - prev_x <= 1) {
std::cout << "the sequence is not increasing or the gap is too small: " << prev_x << ", " << t.first << std::endl;
return false;
}
}
prev = true;
prev_x = t.first;
}
return true;
}
void print(std::ostream & out) const {
if (m_empty) {
out << "empty\n";
return;
}
if (m_endpoints.empty()){
out << "[-oo,oo]\n";
return;
}
bool first = true;
for (auto t : m_endpoints) {
if (first) {
if (t.second == 1) {
out << "[-oo," << t.first << "]";
}
else if (t.second == 0)
out << "[" << t.first << ",";
else if (t.second == 2)
out << "[" << t.first << "]";
first = false;
} else {
if (t.second==0)
out << "[" << t.first << ",";
else if (t.second == 1)
out << t.first << "]";
else if (t.second == 2)
out << "[" << t.first << "]";
}
}
if (has_pos_inf())
out << "oo]";
out << "\n";
}
};
}

View file

@ -20,7 +20,7 @@ Revision History:
#include <memory>
#include "util/vector.h"
#include "util/lp/numeric_pair.h"
#include "util/lp/eta_matrix.hpp"
#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;

View file

@ -76,7 +76,7 @@ public:
void push_back(unsigned row_index, T val ) {
SASSERT(row_index != m_column_index);
lp_assert(row_index != m_column_index);
m_column_vector.push_back(row_index, val);
}

View file

@ -75,7 +75,7 @@ void eta_matrix<T, X>::apply_from_right(vector<T> & w) {
}
w[m_column_index] = t;
#ifdef Z3DEBUG
// SASSERT(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
// lp_assert(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
// delete clone_w;
#endif
}
@ -115,8 +115,8 @@ void eta_matrix<T, X>::apply_from_right(indexed_vector<T> & w) {
}
#ifdef Z3DEBUG
// SASSERT(w.is_OK());
// SASSERT(vectors_are_equal<T>(wcopy, w.m_data));
// lp_assert(w.is_OK());
// lp_assert(vectors_are_equal<T>(wcopy, w.m_data));
#endif
}
#ifdef Z3DEBUG
@ -145,7 +145,7 @@ void eta_matrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & p) {
pair.first = p.get_rev(pair.first);
}
#ifdef Z3DEBUG
// SASSERT(deb == *this);
// lp_assert(deb == *this);
#endif
}
}

32
src/util/lp/explanation.h Normal file
View file

@ -0,0 +1,32 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
struct explanation {
void clear() { m_explanation.clear(); }
vector<std::pair<mpq, constraint_index>> m_explanation;
void push_justification(constraint_index j, const mpq& v) {
m_explanation.push_back(std::make_pair(v, j));
}
void push_justification(constraint_index j) {
m_explanation.push_back(std::make_pair(one_of_type<mpq>(), j));
}
};
}

View file

@ -0,0 +1,259 @@
/*++
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.size() > 0? 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)]); }
#ifdef Z3DEBUG
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);
}
#endif
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) {
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] = p.coeff();
}
}
void copy_column_to_indexed_vector(unsigned entering, indexed_vector<mpq> &w ) const {
lp_assert(false); // not implemented
}
general_matrix operator*(const general_matrix & m) const {
lp_assert(m.row_count() == column_count());
general_matrix ret(row_count(), m.column_count());
for (unsigned i = 0; i < row_count(); i ++) {
for (unsigned j = 0; j < m.column_count(); j++) {
mpq a(0);
for (unsigned k = 0; k < column_count(); k++)
a += ((*this)[i][k])*m[k][j];
ret[i][j] = a;
}
}
return ret;
}
bool elements_are_equal(const general_matrix& m) const {
for (unsigned i = 0; i < row_count(); i++)
for (unsigned j = 0; j < column_count(); j++)
if ( (*this)[i][j] != m[i][j])
return false;
return true;
}
bool elements_are_equal_modulo(const general_matrix& m, const mpq & d) const {
for (unsigned i = 0; i < row_count(); i++)
for (unsigned j = 0; j < column_count(); j++)
if (!is_zero(((*this)[i][j] - m[i][j]) % d))
return false;
return true;
}
bool operator==(const general_matrix& m) const {
return row_count() == m.row_count() && column_count() == m.column_count() && elements_are_equal(m);
}
bool operator!=(const general_matrix& m) const {
return !(*this == m);
}
bool equal_modulo(const general_matrix& m, const mpq & d) const {
return row_count() == m.row_count() && column_count() == m.column_count() && elements_are_equal_modulo(m, d);
}
vector<mpq> operator*(const vector<mpq> & x) const {
vector<mpq> r;
lp_assert(x.size() == column_count());
for (unsigned i = 0; i < row_count(); i++) {
mpq v(0);
for (unsigned j = 0; j < column_count(); j++) {
v += (*this)[i][j] * x[j];
}
r.push_back(v);
}
return r;
}
// bool create_upper_triangle(general_matrix& m, vector<mpq>& x) {
// for (unsigned i = 1; i < m.row_count(); i++) {
// lp_assert(false); // to be continued
// }
// }
// bool solve_A_x_equal_b(const general_matrix& m, vector<mpq>& x, const vector<mpq>& b) const {
// auto m_copy = m;
// // for square matrices
// lp_assert(row_count() == b.size());
// lp_assert(x.size() == column_count());
// lp_assert(row_count() == column_count());
// x = b;
// create_upper_triangle(copy_of_m, x);
// solve_on_triangle(copy_of_m, x);
// }
//
void transpose_rows(unsigned i, unsigned l) {
lp_assert(i != l);
m_row_permutation.transpose_from_right(i, l);
}
void transpose_columns(unsigned j, unsigned k) {
lp_assert(j != k);
m_column_permutation.transpose_from_left(j, k);
}
general_matrix(){}
general_matrix(unsigned n) :
m_row_permutation(n),
m_column_permutation(n),
m_data(n)
{
for (auto& v : m_data){
v.resize(n);
}
}
general_matrix(unsigned m, unsigned n) :
m_row_permutation(m),
m_column_permutation(n),
m_data(m) {
for (auto& v : m_data){
v.resize(n);
}
}
void shrink_to_rank(const svector<unsigned>& basis_rows) {
if (basis_rows.size() == row_count()) return;
vector<vector<mpq>> data; // todo : not efficient code
for (unsigned i : basis_rows)
data.push_back(m_data[i]);
m_data = data;
}
// used for debug only
general_matrix take_first_n_columns(unsigned n) const {
lp_assert(n <= column_count());
if (n == column_count())
return *this;
general_matrix ret(row_count(), n);
for (unsigned i = 0; i < row_count(); i++)
for (unsigned j = 0; j < n; j++)
ret[i][j] = (*this)[i][j];
return ret;
}
inline
friend vector<mpq> operator*(const vector<mpq> & f, const general_matrix& a) {
vector<mpq> r(a.column_count());
for (unsigned j = 0; j < a.column_count(); j ++) {
mpq t = zero_of_type<mpq>();
for (unsigned i = 0; i < a.row_count(); i++) {
t += f[i] * a[i][j];
}
r[j] = t;
}
return r;
}
};
}

View file

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

623
src/util/lp/hnf.h Normal file
View file

@ -0,0 +1,623 @@
/*++
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_int(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) {
lp_assert(is_correct_modulo());
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);
lp_assert(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) {
lp_assert(is_correct_modulo());
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);
lp_assert(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_int(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();
lp_assert(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; }
};
}

232
src/util/lp/hnf_cutter.h Normal file
View file

@ -0,0 +1,232 @@
/*++
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 {
var_register m_var_register;
general_matrix m_A;
vector<const lar_term*> m_terms;
svector<constraint_index> m_constraints_for_explanation;
vector<mpq> m_right_sides;
lp_settings & m_settings;
mpq m_abs_max;
bool m_overflow;
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>()) {}
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_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) {
m_terms.push_back(t);
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) {
m_A.init_row_from_container(i, * m_terms[i], [this](unsigned j) { return m_var_register.add_var(j);});
}
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_int(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_monomial(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++) {
lp_assert(is_zero(x[m_var_register.local_to_external(j)].y));
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
#ifdef Z3DEBUG
,
const vector<mpq> & x0
#endif
) {
// we suppose that x0 has at least one non integer element
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);
// std::cout << "max = " << m_abs_max << ", d = " << d << ", d/max = " << ceil (d /m_abs_max) << std::endl;
//std::cout << "max cube " << m_abs_max * m_abs_max * m_abs_max << std::endl;
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);
// general_matrix A_orig = m_A;
vector<mpq> b = create_b(basis_rows);
lp_assert(m_A * x0 == b);
// vector<mpq> bcopy = b;
find_h_minus_1_b(h.W(), b);
// lp_assert(bcopy == h.W().take_first_n_columns(b.size()) * b);
int cut_row = find_cut_row_index(b);
if (cut_row == -1)
return lia_move::undef;
// the matrix is not square - we can get
// all integers in b's projection
vector<mpq> row(m_A.column_count());
get_ei_H_minus_1(cut_row, h.W(), row);
vector<mpq> f = row * m_A;
fill_term(f, t);
k = floor(b[cut_row]);
upper = true;
return lia_move::cut;
}
svector<unsigned> vars() const { return m_var_register.vars(); }
};
}

View file

@ -24,34 +24,34 @@ namespace lp {
struct implied_bound {
mpq m_bound;
unsigned m_j; // the column for which the bound has been found
bool m_is_low_bound;
bool m_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_low_bound? GE : LE;
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_low_bound == o.m_is_low_bound && m_bound == o.m_bound &&
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 low_bound,
bool lower_bound,
bool coeff_before_j_is_pos,
unsigned row_or_term_index,
bool strict):
m_bound(a),
m_j(j),
m_is_low_bound(low_bound),
m_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) {}
m_strict(strict) {
}
};
}

View file

@ -18,7 +18,7 @@ Revision History:
--*/
#include "util/vector.h"
#include "util/lp/indexed_vector.hpp"
#include "util/lp/indexed_vector_def.h"
namespace lp {
template void indexed_vector<double>::clear();
template void indexed_vector<double>::clear_all();
@ -42,11 +42,12 @@ template void lp::indexed_vector<double>::print(std::basic_ostream<char,struct s
template void lp::indexed_vector<lp::numeric_pair<lp::mpq> >::print(std::ostream&);
#endif
}
template void lp::print_vector<double>(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::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::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream<char, std::char_traits<char> > &);
// template void lp::print_vector<std::pair<lp::mpq, unsigned int> >(vector<std::pair<lp::mpq, unsigned int>> const&, std::ostream&);
template void lp::indexed_vector<lp::numeric_pair<lp::mpq> >::erase_from_index(unsigned int);

View file

@ -28,11 +28,9 @@ Revision History:
#include <unordered_set>
namespace lp {
template <typename T> void print_vector(const vector<T> & t, std::ostream & out);
template <typename T> void print_vector(const buffer<T> & t, std::ostream & out);
template <typename T> void print_sparse_vector(const vector<T> & t, std::ostream & out);
void print_vector(const vector<mpq> & t, std::ostream & out);
void print_vector_as_doubles(const vector<mpq> & t, std::ostream & out);
template <typename T>
class indexed_vector {
public:
@ -90,16 +88,7 @@ public:
}
void set_value(const T& value, unsigned index);
void set_value_as_in_dictionary(unsigned index) {
SASSERT(index < m_data.size());
T & loc = m_data[index];
if (is_zero(loc)) {
m_index.push_back(index);
loc = one_of_type<T>(); // use as a characteristic function
}
}
void clear();
void clear_all();
const T& operator[] (unsigned i) const {
@ -175,6 +164,55 @@ public:
}
}
}
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; }
};
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;

View file

@ -22,21 +22,6 @@ Revision History:
#include "util/lp/lp_settings.h"
namespace lp {
template <typename T>
void print_vector(const vector<T> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++)
out << t[i] << " ";
out << std::endl;
}
template <typename T>
void print_vector(const buffer<T> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++)
out << t[i] << " ";
out << std::endl;
}
template <typename T>
void print_sparse_vector(const vector<T> & t, std::ostream & out) {
for (unsigned i = 0; i < t.size(); i++) {
@ -46,7 +31,7 @@ void print_sparse_vector(const vector<T> & t, std::ostream & out) {
out << std::endl;
}
void print_vector(const vector<mpq> & t, std::ostream & out) {
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;
@ -56,13 +41,13 @@ template <typename T>
void indexed_vector<T>::resize(unsigned data_size) {
clear();
m_data.resize(data_size, numeric_traits<T>::zero());
SASSERT(is_OK());
lp_assert(is_OK());
}
template <typename T>
void indexed_vector<T>::set_value(const T& value, unsigned index) {
m_data[index] = value;
SASSERT(std::find(m_index.begin(), m_index.end(), index) == m_index.end());
lp_assert(std::find(m_index.begin(), m_index.end(), index) == m_index.end());
m_index.push_back(index);
}

View file

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

View file

@ -1,591 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
// here we are inside lp::lar_solver class
bool strategy_is_undecided() const {
return m_settings.simplex_strategy() == simplex_strategy_enum::undecided;
}
var_index add_var(unsigned ext_j) {
var_index i;
SASSERT (ext_j < m_terms_start_index);
if (ext_j >= m_terms_start_index)
throw 0; // todo : what is the right way to exit?
if (try_get_val(m_ext_vars_to_columns, ext_j, i)) {
return i;
}
SASSERT(m_vars_to_ul_pairs.size() == A_r().column_count());
i = A_r().column_count();
m_vars_to_ul_pairs.push_back (ul_pair(static_cast<unsigned>(-1)));
add_non_basic_var_to_core_fields(ext_j);
SASSERT(sizes_are_correct());
return i;
}
void register_new_ext_var_index(unsigned ext_v) {
SASSERT(!contains(m_ext_vars_to_columns, ext_v));
unsigned j = static_cast<unsigned>(m_ext_vars_to_columns.size());
m_ext_vars_to_columns[ext_v] = j;
SASSERT(m_columns_to_ext_vars_or_term_indices.size() == j);
m_columns_to_ext_vars_or_term_indices.push_back(ext_v);
}
void add_non_basic_var_to_core_fields(unsigned ext_j) {
register_new_ext_var_index(ext_j);
m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column);
m_columns_with_changed_bound.increase_size_by_one();
add_new_var_to_core_fields_for_mpq(false);
if (use_lu())
add_new_var_to_core_fields_for_doubles(false);
}
void add_new_var_to_core_fields_for_doubles(bool register_in_basis) {
unsigned j = A_d().column_count();
A_d().add_column();
SASSERT(m_mpq_lar_core_solver.m_d_x.size() == j);
// SASSERT(m_mpq_lar_core_solver.m_d_low_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later
m_mpq_lar_core_solver.m_d_x.resize(j + 1 );
m_mpq_lar_core_solver.m_d_low_bounds.resize(j + 1);
m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1);
SASSERT(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method
if (register_in_basis) {
A_d().add_row();
m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size());
m_mpq_lar_core_solver.m_d_basis.push_back(j);
}else {
m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast<int>(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1);
m_mpq_lar_core_solver.m_d_nbasis.push_back(j);
}
}
void add_new_var_to_core_fields_for_mpq(bool register_in_basis) {
unsigned j = A_r().column_count();
A_r().add_column();
SASSERT(m_mpq_lar_core_solver.m_r_x.size() == j);
// SASSERT(m_mpq_lar_core_solver.m_r_low_bounds.size() == j && m_mpq_lar_core_solver.m_r_upper_bounds.size() == j); // restore later
m_mpq_lar_core_solver.m_r_x.resize(j + 1);
m_mpq_lar_core_solver.m_r_low_bounds.increase_size_by_one();
m_mpq_lar_core_solver.m_r_upper_bounds.increase_size_by_one();
m_mpq_lar_core_solver.m_r_solver.m_inf_set.increase_size_by_one();
m_mpq_lar_core_solver.m_r_solver.m_costs.resize(j + 1);
m_mpq_lar_core_solver.m_r_solver.m_d.resize(j + 1);
SASSERT(m_mpq_lar_core_solver.m_r_heading.size() == j); // as A().column_count() on the entry to the method
if (register_in_basis) {
A_r().add_row();
m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size());
m_mpq_lar_core_solver.m_r_basis.push_back(j);
if (m_settings.bound_propagation())
m_rows_with_changed_bounds.insert(A_r().row_count() - 1);
} else {
m_mpq_lar_core_solver.m_r_heading.push_back(- static_cast<int>(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1);
m_mpq_lar_core_solver.m_r_nbasis.push_back(j);
}
}
var_index add_term_undecided(const vector<std::pair<mpq, var_index>> & coeffs,
const mpq &m_v) {
m_terms.push_back(new lar_term(coeffs, m_v));
m_orig_terms.push_back(new lar_term(coeffs, m_v));
return m_terms_start_index + m_terms.size() - 1;
}
// terms
var_index add_term(const vector<std::pair<mpq, var_index>> & coeffs,
const mpq &m_v) {
if (strategy_is_undecided())
return add_term_undecided(coeffs, m_v);
m_terms.push_back(new lar_term(coeffs, m_v));
m_orig_terms.push_back(new lar_term(coeffs, m_v));
unsigned adjusted_term_index = m_terms.size() - 1;
var_index ret = m_terms_start_index + adjusted_term_index;
if (use_tableau() && !coeffs.empty()) {
add_row_for_term(m_orig_terms.back(), ret);
if (m_settings.bound_propagation())
m_rows_with_changed_bounds.insert(A_r().row_count() - 1);
}
SASSERT(m_ext_vars_to_columns.size() == A_r().column_count());
return ret;
}
void add_row_for_term(const lar_term * term, unsigned term_ext_index) {
SASSERT(sizes_are_correct());
add_row_from_term_no_constraint(term, term_ext_index);
SASSERT(sizes_are_correct());
}
void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) {
register_new_ext_var_index(term_ext_index);
// j will be a new variable
unsigned j = A_r().column_count();
ul_pair ul(j);
m_vars_to_ul_pairs.push_back(ul);
add_basic_var_to_core_fields();
if (use_tableau()) {
auto it = iterator_on_term_with_basis_var(*term, j);
A_r().fill_last_row_with_pivoting(it,
m_mpq_lar_core_solver.m_r_solver.m_basis_heading);
m_mpq_lar_core_solver.m_r_solver.m_b.resize(A_r().column_count(), zero_of_type<mpq>());
} else {
fill_last_row_of_A_r(A_r(), term);
}
m_mpq_lar_core_solver.m_r_x[j] = get_basic_var_value_from_row_directly(A_r().row_count() - 1);
if (use_lu())
fill_last_row_of_A_d(A_d(), term);
}
void add_basic_var_to_core_fields() {
bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver();
SASSERT(!use_lu || A_r().column_count() == A_d().column_count());
m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column);
m_columns_with_changed_bound.increase_size_by_one();
m_rows_with_changed_bounds.increase_size_by_one();
add_new_var_to_core_fields_for_mpq(true);
if (use_lu)
add_new_var_to_core_fields_for_doubles(true);
}
constraint_index add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) {
constraint_index ci = m_constraints.size();
if (!is_term(j)) { // j is a var
auto vc = new lar_var_constraint(j, kind, right_side);
m_constraints.push_back(vc);
update_column_type_and_bound(j, kind, right_side, ci);
} else {
add_var_bound_on_constraint_for_term(j, kind, right_side, ci);
}
SASSERT(sizes_are_correct());
return ci;
}
void update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index) {
switch(m_mpq_lar_core_solver.m_column_types[j]) {
case column_type::free_column:
update_free_column_type_and_bound(j, kind, right_side, constr_index);
break;
case column_type::boxed:
update_boxed_column_type_and_bound(j, kind, right_side, constr_index);
break;
case column_type::low_bound:
update_low_bound_column_type_and_bound(j, kind, right_side, constr_index);
break;
case column_type::upper_bound:
update_upper_bound_column_type_and_bound(j, kind, right_side, constr_index);
break;
case column_type::fixed:
update_fixed_column_type_and_bound(j, kind, right_side, constr_index);
break;
default:
SASSERT(false); // cannot be here
}
}
void add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) {
SASSERT(is_term(j));
unsigned adjusted_term_index = adjust_term_index(j);
unsigned term_j;
if (try_get_val(m_ext_vars_to_columns, j, term_j)) {
mpq rs = right_side - m_orig_terms[adjusted_term_index]->m_v;
m_constraints.push_back(new lar_term_constraint(m_orig_terms[adjusted_term_index], kind, right_side));
update_column_type_and_bound(term_j, kind, rs, ci);
}
else {
add_constraint_from_term_and_create_new_column_row(j, m_orig_terms[adjusted_term_index], kind, right_side);
}
}
void add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term,
lconstraint_kind kind, const mpq & right_side) {
add_row_from_term_no_constraint(term, term_j);
unsigned j = A_r().column_count() - 1;
update_column_type_and_bound(j, kind, right_side - term->m_v, m_constraints.size());
m_constraints.push_back(new lar_term_constraint(term, kind, right_side));
SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size());
}
void decide_on_strategy_and_adjust_initial_state() {
SASSERT(strategy_is_undecided());
if (m_vars_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) {
m_settings.simplex_strategy() = simplex_strategy_enum::lu;
} else {
m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows; // todo: when to switch to tableau_costs?
}
adjust_initial_state();
}
void adjust_initial_state() {
switch (m_settings.simplex_strategy()) {
case simplex_strategy_enum::lu:
adjust_initial_state_for_lu();
break;
case simplex_strategy_enum::tableau_rows:
adjust_initial_state_for_tableau_rows();
break;
case simplex_strategy_enum::tableau_costs:
SASSERT(false); // not implemented
case simplex_strategy_enum::undecided:
adjust_initial_state_for_tableau_rows();
break;
}
}
void adjust_initial_state_for_lu() {
copy_from_mpq_matrix(A_d());
unsigned n = A_d().column_count();
m_mpq_lar_core_solver.m_d_x.resize(n);
m_mpq_lar_core_solver.m_d_low_bounds.resize(n);
m_mpq_lar_core_solver.m_d_upper_bounds.resize(n);
m_mpq_lar_core_solver.m_d_heading = m_mpq_lar_core_solver.m_r_heading;
m_mpq_lar_core_solver.m_d_basis = m_mpq_lar_core_solver.m_r_basis;
/*
unsigned j = A_d().column_count();
A_d().add_column();
SASSERT(m_mpq_lar_core_solver.m_d_x.size() == j);
// SASSERT(m_mpq_lar_core_solver.m_d_low_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later
m_mpq_lar_core_solver.m_d_x.resize(j + 1 );
m_mpq_lar_core_solver.m_d_low_bounds.resize(j + 1);
m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1);
SASSERT(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method
if (register_in_basis) {
A_d().add_row();
m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size());
m_mpq_lar_core_solver.m_d_basis.push_back(j);
}else {
m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast<int>(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1);
m_mpq_lar_core_solver.m_d_nbasis.push_back(j);
}*/
}
void adjust_initial_state_for_tableau_rows() {
for (unsigned j = 0; j < m_terms.size(); j++) {
if (contains(m_ext_vars_to_columns, j + m_terms_start_index))
continue;
add_row_from_term_no_constraint(m_terms[j], j + m_terms_start_index);
}
}
// this fills the last row of A_d and sets the basis column: -1 in the last column of the row
void fill_last_row_of_A_d(static_matrix<double, double> & A, const lar_term* ls) {
SASSERT(A.row_count() > 0);
SASSERT(A.column_count() > 0);
unsigned last_row = A.row_count() - 1;
SASSERT(A.m_rows[last_row].empty());
for (auto & t : ls->m_coeffs) {
SASSERT(!is_zero(t.second));
var_index j = t.first;
A.set(last_row, j, - t.second.get_double());
}
unsigned basis_j = A.column_count() - 1;
A.set(last_row, basis_j, - 1 );
}
void update_free_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_ind) {
mpq y_of_bound(0);
switch (kind) {
case LT:
y_of_bound = -1;
case LE:
m_mpq_lar_core_solver.m_column_types[j] = column_type::upper_bound;
SASSERT(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound);
SASSERT(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j);
{
auto up = numeric_pair<mpq>(right_side, y_of_bound);
m_mpq_lar_core_solver.m_r_upper_bounds[j] = up;
}
set_upper_bound_witness(j, constr_ind);
break;
case GT:
y_of_bound = 1;
case GE:
m_mpq_lar_core_solver.m_column_types[j] = column_type::low_bound;
SASSERT(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j);
{
auto low = numeric_pair<mpq>(right_side, y_of_bound);
m_mpq_lar_core_solver.m_r_low_bounds[j] = low;
}
set_low_bound_witness(j, constr_ind);
break;
case EQ:
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = numeric_pair<mpq>(right_side, zero_of_type<mpq>());
set_upper_bound_witness(j, constr_ind);
set_low_bound_witness(j, constr_ind);
break;
default:
SASSERT(false);
}
m_columns_with_changed_bound.insert(j);
}
void update_upper_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) {
SASSERT(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound);
mpq y_of_bound(0);
switch (kind) {
case LT:
y_of_bound = -1;
case LE:
{
auto up = numeric_pair<mpq>(right_side, y_of_bound);
if (up < m_mpq_lar_core_solver.m_r_upper_bounds()[j]) {
m_mpq_lar_core_solver.m_r_upper_bounds[j] = up;
set_upper_bound_witness(j, ci);
m_columns_with_changed_bound.insert(j);
}
}
break;
case GT:
y_of_bound = 1;
case GE:
m_mpq_lar_core_solver.m_column_types[j] = column_type::boxed;
{
auto low = numeric_pair<mpq>(right_side, y_of_bound);
m_mpq_lar_core_solver.m_r_low_bounds[j] = low;
set_low_bound_witness(j, ci);
m_columns_with_changed_bound.insert(j);
if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
} else {
m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j]? column_type::boxed : column_type::fixed;
}
}
break;
case EQ:
{
auto v = numeric_pair<mpq>(right_side, zero_of_type<mpq>());
if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
m_status = INFEASIBLE;
set_low_bound_witness(j, ci);
m_infeasible_column_index = j;
} else {
m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v;
m_columns_with_changed_bound.insert(j);
set_low_bound_witness(j, ci);
set_upper_bound_witness(j, ci);
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
}
break;
}
break;
default:
SASSERT(false);
}
}
void update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) {
SASSERT(m_status == INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::boxed && m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j]));
mpq y_of_bound(0);
switch (kind) {
case LT:
y_of_bound = -1;
case LE:
{
auto up = numeric_pair<mpq>(right_side, y_of_bound);
if (up < m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
m_mpq_lar_core_solver.m_r_upper_bounds[j] = up;
set_upper_bound_witness(j, ci);
m_columns_with_changed_bound.insert(j);
}
if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
m_status = INFEASIBLE;
SASSERT(false);
m_infeasible_column_index = j;
} else {
if (m_mpq_lar_core_solver.m_r_low_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j])
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
}
}
break;
case GT:
y_of_bound = 1;
case GE:
{
auto low = numeric_pair<mpq>(right_side, y_of_bound);
if (low > m_mpq_lar_core_solver.m_r_low_bounds[j]) {
m_mpq_lar_core_solver.m_r_low_bounds[j] = low;
m_columns_with_changed_bound.insert(j);
set_low_bound_witness(j, ci);
}
if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
} else if ( low == m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
}
}
break;
case EQ:
{
auto v = numeric_pair<mpq>(right_side, zero_of_type<mpq>());
if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
set_upper_bound_witness(j, ci);
} else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
set_low_bound_witness(j, ci);
} else {
m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v;
set_low_bound_witness(j, ci);
set_upper_bound_witness(j, ci);
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
m_columns_with_changed_bound.insert(j);
}
break;
}
default:
SASSERT(false);
}
}
void update_low_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) {
SASSERT(m_mpq_lar_core_solver.m_column_types()[j] == column_type::low_bound);
mpq y_of_bound(0);
switch (kind) {
case LT:
y_of_bound = -1;
case LE:
{
auto up = numeric_pair<mpq>(right_side, y_of_bound);
m_mpq_lar_core_solver.m_r_upper_bounds[j] = up;
set_upper_bound_witness(j, ci);
m_columns_with_changed_bound.insert(j);
if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
} else {
m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j]? column_type::boxed : column_type::fixed;
}
}
break;
case GT:
y_of_bound = 1;
case GE:
{
auto low = numeric_pair<mpq>(right_side, y_of_bound);
if (low > m_mpq_lar_core_solver.m_r_low_bounds[j]) {
m_mpq_lar_core_solver.m_r_low_bounds[j] = low;
m_columns_with_changed_bound.insert(j);
set_low_bound_witness(j, ci);
}
}
break;
case EQ:
{
auto v = numeric_pair<mpq>(right_side, zero_of_type<mpq>());
if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
set_upper_bound_witness(j, ci);
} else {
m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v;
set_low_bound_witness(j, ci);
set_upper_bound_witness(j, ci);
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
}
m_columns_with_changed_bound.insert(j);
break;
}
default:
SASSERT(false);
}
}
void update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) {
SASSERT(m_status == INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::fixed && m_mpq_lar_core_solver.m_r_low_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j]));
SASSERT(m_status == INFEASIBLE || (m_mpq_lar_core_solver.m_r_low_bounds()[j].y.is_zero() && m_mpq_lar_core_solver.m_r_upper_bounds()[j].y.is_zero()));
auto v = numeric_pair<mpq>(right_side, mpq(0));
mpq y_of_bound(0);
switch (kind) {
case LT:
if (v <= m_mpq_lar_core_solver.m_r_low_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
set_upper_bound_witness(j, ci);
}
break;
case LE:
{
if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
set_upper_bound_witness(j, ci);
}
}
break;
case GT:
{
if (v >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index =j;
set_low_bound_witness(j, ci);
}
}
break;
case GE:
{
if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
set_low_bound_witness(j, ci);
}
}
break;
case EQ:
{
if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
set_upper_bound_witness(j, ci);
} else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
m_status = INFEASIBLE;
m_infeasible_column_index = j;
set_low_bound_witness(j, ci);
}
break;
}
default:
SASSERT(false);
}
}

View file

@ -35,7 +35,7 @@ public:
return m_data[j] >= 0;
}
void insert(unsigned j) {
SASSERT(j < m_data.size());
lp_assert(j < m_data.size());
if (contains(j)) return;
m_data[j] = m_index.size();
m_index.push_back(j);

1294
src/util/lp/int_solver.cpp Normal file

File diff suppressed because it is too large Load diff

166
src/util/lp/int_solver.h Normal file
View file

@ -0,0 +1,166 @@
/*++
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(lar_term& t, mpq& k, explanation& ex, bool & upper);
lia_move check_(lar_term& t, mpq& k, explanation& ex, bool & upper);
bool move_non_basic_column_to_bounds(unsigned j);
lia_move check_wrapper(lar_term& t, mpq& k, explanation& ex);
bool is_base(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);
const impq & lower_bound(unsigned j) const;
const impq & upper_bound(unsigned j) const;
bool is_int(unsigned j) const;
bool is_real(unsigned j) const;
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(unsigned j, const impq & new_val);
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;
const impq & get_value(unsigned j) 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;
bool move_non_basic_columns_to_bounds();
void branch_infeasible_int_var(unsigned);
lia_move mk_gomory_cut(unsigned inf_col, const row_strip<mpq>& row);
lia_move report_conflict_from_gomory_cut();
void adjust_term_and_k_for_some_ints_case_gomory(mpq& lcm_den);
lia_move proceed_with_gomory_cut(unsigned j);
int find_free_var_in_gomory_row(const row_strip<mpq>& );
bool is_gomory_cut_target(const row_strip<mpq>&);
bool at_bound(unsigned j) const;
bool at_low(unsigned j) const;
bool at_upper(unsigned j) const;
bool has_low(unsigned j) const;
bool has_upper(unsigned j) const;
unsigned row_of_basic_column(unsigned j) const;
inline static bool is_rational(const impq & n) {
return is_zero(n.y);
}
public:
void display_column(std::ostream & out, unsigned j) const;
inline static
mpq fractional_part(const impq & n) {
lp_assert(is_rational(n));
return n.x - floor(n.x);
}
private:
void real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0);
void int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0);
constraint_index column_upper_bound_constraint(unsigned j) const;
constraint_index column_lower_bound_constraint(unsigned j) const;
void display_row_info(std::ostream & out, unsigned row_index) const;
void gomory_cut_adjust_t_and_k(vector<std::pair<mpq, unsigned>> & pol, lar_term & t, mpq &k, bool num_ints, mpq &lcm_den);
bool current_solution_is_inf_on_cut() const;
public:
bool shift_var(unsigned j, unsigned range);
private:
unsigned random();
bool has_inf_int() const;
lia_move create_branch_on_column(int j);
public:
bool is_term(unsigned j) const;
bool left_branch_is_more_narrow_than_right(unsigned);
lia_move find_cube();
bool tighten_terms_for_cube();
bool tighten_term_for_cube(unsigned);
unsigned column_count() const;
bool all_columns_are_bounded() const;
impq get_cube_delta_for_term(const lar_term&) const;
void find_feasible_solution();
int find_inf_int_nbasis_column() const;
lia_move run_gcd_test();
lia_move gomory_cut();
lia_move hnf_cut();
lia_move make_hnf_cut();
bool init_terms_for_hnf_cut();
bool hnf_matrix_is_empty() const;
void try_add_term_to_A_for_hnf(unsigned term_index);
bool hnf_has_var_with_non_integral_value() const;
bool hnf_cutter_is_full() const;
void patch_nbasic_column(unsigned j, bool patch_only_int_vals);
};
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -40,12 +40,11 @@ inline std::string lconstraint_kind_string(lconstraint_kind t) {
case GT: return std::string(">");
case EQ: return std::string("=");
}
SASSERT(false);
lp_unreachable();
return std::string(); // it is unreachable
}
class lar_base_constraint {
public:
struct lar_base_constraint {
lconstraint_kind m_kind;
mpq m_right_side;
virtual vector<std::pair<mpq, var_index>> get_left_side_coefficients() const = 0;
@ -88,7 +87,7 @@ public:
: lar_base_constraint(kind, right_side), m_coeffs(left_side) {}
lar_constraint(const lar_base_constraint & c) {
SASSERT(false); // should not be called : todo!
lp_assert(false); // should not be called : todo!
}
unsigned size() const override {

View file

@ -22,4 +22,4 @@ Revision History:
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lar_core_solver.hpp"
#include "util/lp/lar_core_solver_def.h"

View file

@ -26,12 +26,9 @@ Revision History:
#include "util/lp/indexed_vector.h"
#include "util/lp/binary_heap_priority_queue.h"
#include "util/lp/breakpoint.h"
#include "util/lp/stacked_unordered_set.h"
#include "util/lp/lp_primal_core_solver.h"
#include "util/lp/stacked_vector.h"
#include "util/lp/lar_solution_signature.h"
#include "util/lp/iterator_on_column.h"
#include "util/lp/iterator_on_indexed_vector.h"
#include "util/lp/stacked_value.h"
namespace lp {
@ -50,7 +47,7 @@ public:
stacked_vector<column_type> m_column_types;
// r - solver fields, for rational numbers
vector<numeric_pair<mpq>> m_r_x; // the solution
stacked_vector<numeric_pair<mpq>> m_r_low_bounds;
stacked_vector<numeric_pair<mpq>> m_r_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;
@ -62,7 +59,7 @@ public:
// d - solver fields, for doubles
vector<double> m_d_x; // the solution in doubles
vector<double> m_d_low_bounds;
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;
@ -155,11 +152,11 @@ public:
void fill_evidence(unsigned row);
unsigned get_number_of_non_ints() const;
void solve();
bool low_bounds_are_set() const { return true; }
bool lower_bounds_are_set() const { return true; }
const indexed_vector<mpq> & get_pivot_row() const {
return m_r_solver.m_pivot_row;
@ -183,16 +180,16 @@ public:
}
void push() {
SASSERT(m_r_solver.basis_heading_is_correct());
SASSERT(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct());
SASSERT(m_column_types.size() == m_r_A.column_count());
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_low_bounds.push();
m_r_lower_bounds.push();
m_r_upper_bounds.push();
if (!settings().use_tableau()) {
push_vector(m_r_pushed_basis, m_r_basis);
@ -207,7 +204,7 @@ public:
template <typename K>
void push_vector(stacked_vector<K> & pushed_vector, const vector<K> & vector) {
SASSERT(pushed_vector.size() <= vector.size());
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]);
@ -234,7 +231,7 @@ public:
// rationals
if (!settings().use_tableau())
m_r_A.pop(k);
m_r_low_bounds.pop(k);
m_r_lower_bounds.pop(k);
m_r_upper_bounds.pop(k);
m_column_types.pop(k);
@ -257,8 +254,8 @@ public:
pop_basis(k);
m_stacked_simplex_strategy.pop(k);
settings().simplex_strategy() = m_stacked_simplex_strategy;
SASSERT(m_r_solver.basis_heading_is_correct());
SASSERT(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct());
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 {
@ -276,11 +273,11 @@ public:
bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair<mpq> & delta) {
auto & x = m_r_x[j];
switch (pos_type) {
case at_low_bound:
if (x == m_r_solver.m_low_bounds[j])
case at_lower_bound:
if (x == m_r_solver.m_lower_bounds[j])
return false;
delta = m_r_solver.m_low_bounds[j] - x;
m_r_solver.m_x[j] = m_r_solver.m_low_bounds[j];
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:
@ -300,30 +297,30 @@ public:
delta = m_r_solver.m_upper_bounds[j] - x;
x = m_r_solver.m_upper_bounds[j];
break;
case column_type::low_bound:
delta = m_r_solver.m_low_bounds[j] - x;
x = m_r_solver.m_low_bounds[j];
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_low_bounds[j] - x;
x = m_r_solver.m_low_bounds[j];
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_low_bounds[j] - x;
x = m_r_solver.m_low_bounds[j];
delta = m_r_solver.m_lower_bounds[j] - x;
x = m_r_solver.m_lower_bounds[j];
break;
default:
SASSERT(false);
lp_assert(false);
}
break;
default:
SASSERT(false);
lp_unreachable();
}
m_r_solver.remove_column_from_inf_set(j);
return true;
@ -332,7 +329,7 @@ public:
void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) {
SASSERT(m_r_solver.inf_set_is_correct());
lp_assert(m_r_solver.inf_set_is_correct());
for (auto &t : signature) {
unsigned j = t.first;
if (m_r_heading[j] >= 0)
@ -344,12 +341,11 @@ public:
for (const auto & cc : m_r_solver.m_A.m_columns[j]){
unsigned i = cc.m_i;
unsigned jb = m_r_solver.m_basis[i];
m_r_solver.m_x[jb] -= delta * m_r_solver.m_A.get_val(cc);
m_r_solver.update_column_in_inf_set(jb);
m_r_solver.update_x_with_delta_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc));
}
SASSERT(m_r_solver.A_mult_x_is_off() == false);
CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false);
}
SASSERT(m_r_solver.inf_set_is_correct());
lp_assert(m_r_solver.inf_set_is_correct());
}
@ -357,11 +353,11 @@ public:
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;
SASSERT(m_r_heading[j] < 0);
lp_assert(m_r_heading[j] < 0);
auto pos_type = t.second;
switch (pos_type) {
case at_low_bound:
s.m_x[j] = s.m_low_bounds[j];
case at_lower_bound:
s.m_x[j] = s.m_lower_bounds[j];
break;
case at_fixed:
case at_upper_bound:
@ -374,33 +370,33 @@ public:
case not_at_bound:
switch (m_column_types[j]) {
case column_type::free_column:
SASSERT(false); // unreachable
lp_assert(false); // unreachable
case column_type::upper_bound:
s.m_x[j] = s.m_upper_bounds[j];
break;
case column_type::low_bound:
s.m_x[j] = s.m_low_bounds[j];
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_low_bounds[j];
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_low_bounds[j];
s.m_x[j] = s.m_lower_bounds[j];
break;
default:
SASSERT(false);
lp_assert(false);
}
break;
default:
SASSERT(false);
lp_unreachable();
}
}
SASSERT(is_zero_vector(s.m_b));
lp_assert(is_zero_vector(s.m_b));
s.solve_Ax_eq_b();
}
@ -433,7 +429,7 @@ public:
// the queues of delayed indices
std::queue<unsigned> entr_q, leav_q;
auto * l = cs.m_factorization;
SASSERT(l->get_status() == LU_status::OK);
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];
@ -461,8 +457,8 @@ public:
continue;
}
}
SASSERT(cs.m_basis_heading[entering] < 0);
SASSERT(cs.m_basis_heading[leaving] >= 0);
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]);
@ -486,7 +482,7 @@ public:
void solve_on_signature_tableau(const lar_solution_signature & signature, const vector<unsigned> & changes_of_basis) {
r_basis_is_OK();
SASSERT(settings().use_tableau());
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
@ -505,10 +501,10 @@ public:
return;
m_r_solver.stop_tracing_basis_changes();
// and now catch up in the double solver
SASSERT(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2);
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);
}
SASSERT(r_basis_is_OK());
lp_assert(r_basis_is_OK());
}
bool adjust_x_of_column(unsigned j) {
@ -522,16 +518,16 @@ public:
}
m_r_solver.snap_column_to_bound_tableau(j);
SASSERT(m_r_solver.column_is_feasible(j));
lp_assert(m_r_solver.column_is_feasible(j));
m_r_solver.m_inf_set.erase(j);
*/
SASSERT(false);
lp_assert(false);
return true;
}
bool catch_up_in_lu_tableau(const vector<unsigned> & trace_of_basis_change, const vector<int> & basis_heading) {
SASSERT(r_basis_is_OK());
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) {
@ -561,8 +557,8 @@ public:
continue;
}
}
SASSERT(m_r_solver.m_basis_heading[entering] < 0);
SASSERT(m_r_solver.m_basis_heading[leaving] >= 0);
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
@ -572,12 +568,12 @@ public:
#endif
m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]);
#ifdef Z3DEBUG
SASSERT(t);
lp_assert(t);
#endif
return false;
}
}
SASSERT(r_basis_is_OK());
lp_assert(r_basis_is_OK());
return true;
}
@ -587,14 +583,14 @@ public:
if (!m_r_solver.m_settings.use_tableau())
return true;
for (unsigned j : m_r_solver.m_basis) {
SASSERT(m_r_solver.m_A.m_columns[j].size() == 1);
SASSERT(m_r_solver.m_A.get_val(m_r_solver.m_A.m_columns[j][0]) == one_of_type<mpq>());
lp_assert(m_r_solver.m_A.m_columns[j].size() == 1);
lp_assert(m_r_solver.m_A.get_val(m_r_solver.m_A.m_columns[j][0]) == one_of_type<mpq>());
}
for (unsigned j =0; j < m_r_solver.m_basis_heading.size(); j++) {
if (m_r_solver.m_basis_heading[j] >= 0) continue;
if (m_r_solver.m_column_types[j] == column_type::fixed) continue;
SASSERT(static_cast<unsigned>(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size());
SASSERT( m_r_solver.m_basis_heading[j] <= -1);
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;
@ -614,7 +610,6 @@ public:
}
if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back
// std::cout << "no_r_lu" << std::endl;
catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver);
m_r_solver.find_feasible_solution();
m_d_basis = m_r_basis;
@ -630,7 +625,7 @@ public:
return;
m_r_solver.stop_tracing_basis_changes();
// and now catch up in the double solver
SASSERT(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2);
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);
}
}
@ -656,7 +651,7 @@ public:
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();
SASSERT(signature.size() == 0);
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);
@ -666,27 +661,27 @@ public:
void get_bounds_for_double_solver() {
unsigned n = m_n();
m_d_low_bounds.resize(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 (low_bound_is_set(j)) {
const auto & lb = m_r_solver.m_low_bounds[j];
m_d_low_bounds[j] = lb.x.get_double() + delta * lb.y.get_double();
if (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();
SASSERT(!low_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_low_bounds[j]));
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> & low_bounds,
vector<double> & lower_bounds,
vector<double> & upper_bounds) {
vector<double> column_scale_vector;
vector<double> right_side_vector(A.column_count());
@ -706,8 +701,8 @@ public:
if (m_r_solver.column_has_upper_bound(j)) {
upper_bounds[j] /= column_scale_vector[j];
}
if (m_r_solver.column_has_low_bound(j)) {
low_bounds[j] /= column_scale_vector[j];
if (m_r_solver.column_has_lower_bound(j)) {
lower_bounds[j] /= column_scale_vector[j];
}
}
}
@ -734,17 +729,17 @@ public:
}
bool low_bound_is_set(unsigned j) const {
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::low_bound:
case column_type::lower_bound:
case column_type::boxed:
case column_type::fixed:
return true;
default:
SASSERT(false);
lp_assert(false);
}
return false;
}
@ -752,27 +747,27 @@ public:
bool upper_bound_is_set(unsigned j) const {
switch (m_column_types[j]) {
case column_type::free_column:
case column_type::low_bound:
case column_type::lower_bound:
return false;
case column_type::upper_bound:
case column_type::boxed:
case column_type::fixed:
return true;
default:
SASSERT(false);
lp_assert(false);
}
return false;
}
void update_delta(mpq& delta, numeric_pair<mpq> const& l, numeric_pair<mpq> const& u) const {
SASSERT(l <= u);
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;
}
}
SASSERT(l.x + delta * l.y <= u.x + delta * u.y);
lp_assert(l.x + delta * l.y <= u.x + delta * u.y);
}
@ -781,8 +776,7 @@ public:
for (unsigned j = 0; j < m_r_A.column_count(); j++ ) {
if (m_column_types()[j] != column_type::boxed)
continue;
update_delta(delta, m_r_low_bounds[j], m_r_upper_bounds[j]);
update_delta(delta, m_r_lower_bounds[j], m_r_upper_bounds[j]);
}
return delta;
}
@ -791,8 +785,8 @@ public:
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 (low_bound_is_set(j))
update_delta(delta, m_r_low_bounds[j], m_r_x[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]);
}
@ -803,14 +797,38 @@ public:
m_r_solver.init_column_row_non_zeroes();
}
linear_combination_iterator<mpq> * get_column_iterator(unsigned j) {
if (settings().use_tableau()) {
return new iterator_on_column<mpq, numeric_pair<mpq>>(m_r_solver.m_A.m_columns[j], m_r_solver.m_A);
} else {
m_r_solver.solve_Bd(j);
return new iterator_on_indexed_vector<mpq>(m_r_solver.m_ed);
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];
}
const bool column_is_bounded(unsigned j) const {
switch(m_column_types()[j]) {
case column_type::fixed:
case column_type::boxed:
return true;
default:
return false;
}
}
const vector<unsigned>& r_basis() const { return m_r_basis; }
const vector<unsigned>& r_nbasis() const { return m_r_nbasis; }
};
}

View file

@ -54,7 +54,7 @@ lar_core_solver::lar_core_solver(
m_r_heading,
m_costs_dummy,
m_column_types(),
m_r_low_bounds(),
m_r_lower_bounds(),
m_r_upper_bounds(),
settings,
column_names),
@ -66,15 +66,15 @@ lar_core_solver::lar_core_solver(
m_d_heading,
m_d_costs_dummy,
m_column_types(),
m_d_low_bounds,
m_d_lower_bounds,
m_d_upper_bounds,
settings,
column_names){}
void lar_core_solver::init_costs(bool first_time) {
SASSERT(false); // should not be called
// SASSERT(this->m_x.size() >= this->m_n());
// SASSERT(this->m_column_types.size() >= this->m_n());
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;
@ -84,7 +84,7 @@ void lar_core_solver::init_costs(bool first_time) {
// 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);
// SASSERT(false);
// lp_assert(false);
// }
// if (inf == this->m_infeasibility)
// this->m_iters_with_no_cost_growing++;
@ -108,17 +108,17 @@ void lar_core_solver::init_cost_for_column(unsigned j) {
if (x > this->m_upper_bounds[j]) {
this->m_costs[j] = 1;
this->m_infeasibility += x - this->m_upper_bounds[j];
} else if (x < this->m_low_bounds[j]) {
this->m_infeasibility += this->m_low_bounds[j] - x;
} 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 low_bound:
if (x < this->m_low_bounds[j]) {
case lower_bound:
if (x < this->m_lower_bounds[j]) {
this->m_costs[j] = -1;
this->m_infeasibility += this->m_low_bounds[j] - x;
this->m_infeasibility += this->m_lower_bounds[j] - x;
} else {
this->m_costs[j] = numeric_traits<T>::zero();
}
@ -135,7 +135,7 @@ void lar_core_solver::init_cost_for_column(unsigned j) {
this->m_costs[j] = numeric_traits<T>::zero();
break;
default:
SASSERT(false);
lp_assert(false);
break;
}*/
}
@ -154,7 +154,7 @@ int lar_core_solver::column_is_out_of_bounds(unsigned j) {
return 1;
}
return 0;
case low_bound:
case lower_bound:
if (this->x_below_low_bound(j)) {
return -1;
}
@ -168,30 +168,14 @@ int lar_core_solver::column_is_out_of_bounds(unsigned j) {
return 0;
break;
}*/
SASSERT(false);
lp_assert(false);
return true;
}
void lar_core_solver::calculate_pivot_row(unsigned i) {
SASSERT(!m_r_solver.use_tableau());
SASSERT(m_r_solver.m_pivot_row.is_OK());
m_r_solver.m_pivot_row_of_B_1.clear();
m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m());
m_r_solver.m_pivot_row.clear();
m_r_solver.m_pivot_row.resize(m_r_solver.m_n());
if (m_r_solver.m_settings.use_tableau()) {
unsigned basis_j = m_r_solver.m_basis[i];
for (auto & c : m_r_solver.m_A.m_rows[i]) {
if (c.m_j != basis_j)
m_r_solver.m_pivot_row.set_value(c.get_val(), c.m_j);
}
return;
}
m_r_solver.calculate_pivot_row_of_B_1(i);
m_r_solver.calculate_pivot_row_when_pivot_row_of_B1_is_ready(i);
m_r_solver.calculate_pivot_row(i);
}
@ -238,7 +222,7 @@ void lar_core_solver::calculate_pivot_row(unsigned i) {
}
void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() {
SASSERT(m_r_solver.A_mult_x_is_off() == false);
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();
@ -271,34 +255,44 @@ void lar_core_solver::fill_not_improvable_zero_sum() {
}
}
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() {
SASSERT(m_r_solver.non_basic_columns_are_set_correctly());
SASSERT(m_r_solver.inf_set_is_correct());
if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) {
m_r_solver.set_status(OPTIMAL);
return;
}
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);
return;
}
++settings().st().m_need_to_solve_inf;
SASSERT(!m_r_solver.A_mult_x_is_off());
SASSERT((!settings().use_tableau()) || r_basis_is_OK());
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()) {
prefix_d();
lar_solution_signature solution_signature;
vector<unsigned> changes_of_basis = find_solution_signature_with_doubles(solution_signature);
if (m_d_solver.get_status() == TIME_EXHAUSTED) {
m_r_solver.set_status(TIME_EXHAUSTED);
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);
SASSERT(!settings().use_tableau() || r_basis_is_OK());
lp_assert(!settings().use_tableau() || r_basis_is_OK());
} else {
if (!settings().use_tableau()) {
bool snapped = m_r_solver.snap_non_basic_x_to_bound();
SASSERT(m_r_solver.non_basic_columns_are_set_correctly());
lp_assert(m_r_solver.non_basic_columns_are_set_correctly());
if (snapped)
m_r_solver.solve_Ax_eq_b();
}
@ -306,16 +300,16 @@ void lar_core_solver::solve() {
m_r_solver.find_feasible_solution();
else
m_r_solver.solve();
SASSERT(!settings().use_tableau() || r_basis_is_OK());
lp_assert(!settings().use_tableau() || r_basis_is_OK());
}
if (m_r_solver.get_status() == INFEASIBLE) {
if (m_r_solver.get_status() == lp_status::INFEASIBLE) {
fill_not_improvable_zero_sum();
} else if (m_r_solver.get_status() != UNBOUNDED) {
m_r_solver.set_status(OPTIMAL);
} else if (m_r_solver.get_status() != lp_status::UNBOUNDED) {
m_r_solver.set_status(lp_status::OPTIMAL);
}
SASSERT(r_basis_is_OK());
SASSERT(m_r_solver.non_basic_columns_are_set_correctly());
SASSERT(m_r_solver.inf_set_is_correct());
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());
}

2264
src/util/lp/lar_solver.cpp Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Lev Nachmanson
*/
#include "util/lp/lar_solver.cpp"
template void lp::lar_solver::copy_from_mpq_matrix<double,double>(class lp::static_matrix<double,double> &);

View file

@ -1,22 +1,22 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Copyright (c) 2017 Microsoft Corporation
Module Name:
Module Name:
<name>
<name>
Abstract:
Abstract:
<abstract>
<abstract>
Author:
Author:
Lev Nachmanson (levnach)
Lev Nachmanson (levnach)
Revision History:
Revision History:
--*/
--*/
#pragma once
#include "util/lp/indexed_vector.h"
namespace lp {
@ -25,7 +25,7 @@ struct lar_term {
std::unordered_map<unsigned, mpq> m_coeffs;
mpq m_v;
lar_term() {}
void add_to_map(unsigned j, const mpq& c) {
void add_monomial(const mpq& c, unsigned j) {
auto it = m_coeffs.find(j);
if (it == m_coeffs.end()) {
m_coeffs.emplace(j, c);
@ -36,6 +36,10 @@ struct lar_term {
}
}
bool is_empty() const {
return m_coeffs.size() == 0 && is_zero(m_v);
}
unsigned size() const { return static_cast<unsigned>(m_coeffs.size()); }
const std::unordered_map<unsigned, mpq> & coeffs() const {
@ -45,7 +49,7 @@ struct lar_term {
lar_term(const vector<std::pair<mpq, unsigned>>& coeffs,
const mpq & v) : m_v(v) {
for (const auto & p : coeffs) {
add_to_map(p.second, p.first);
add_monomial(p.first, p.second);
}
}
bool operator==(const lar_term & a) const { return false; } // take care not to create identical terms
@ -67,7 +71,7 @@ struct lar_term {
if (it == m_coeffs.end()) return;
const mpq & b = it->second;
for (unsigned it_j :li.m_index) {
add_to_map(it_j, - b * li.m_data[it_j]);
add_monomial(- b * li.m_data[it_j], it_j);
}
m_coeffs.erase(it);
}
@ -75,5 +79,61 @@ struct lar_term {
bool contains(unsigned j) const {
return m_coeffs.find(j) != m_coeffs.end();
}
void negate() {
for (auto & t : m_coeffs)
t.second.neg();
}
template <typename T>
T apply(const vector<T>& x) const {
T ret = T(m_v);
for (const auto & t : m_coeffs) {
ret += t.second * x[t.first];
}
return ret;
}
void clear() {
m_coeffs.clear();
m_v = zero_of_type<mpq>();
}
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
std::unordered_map<unsigned, mpq>::const_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->first, m_it->second);
}
self_type operator++() { self_type i = *this; m_it++; return i; }
self_type operator++(int) { m_it++; return *this; }
const_iterator(std::unordered_map<unsigned, mpq>::const_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(); }
};
}

31
src/util/lp/lia_move.h Normal file
View file

@ -0,0 +1,31 @@
/*++
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
};
}

View file

@ -1,65 +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>
struct linear_combination_iterator {
virtual bool next(T & a, unsigned & i) = 0;
virtual bool next(unsigned & i) = 0;
virtual void reset() = 0;
virtual linear_combination_iterator * clone() = 0;
virtual ~linear_combination_iterator(){}
virtual unsigned size() const = 0;
};
template <typename T>
struct linear_combination_iterator_on_vector : linear_combination_iterator<T> {
vector<std::pair<T, unsigned>> & m_vector;
int m_offset;
bool next(T & a, unsigned & i) {
if(m_offset >= m_vector.size())
return false;
auto & p = m_vector[m_offset];
a = p.first;
i = p.second;
m_offset++;
return true;
}
bool next(unsigned & i) {
if(m_offset >= m_vector.size())
return false;
auto & p = m_vector[m_offset];
i = p.second;
m_offset++;
return true;
}
void reset() {m_offset = 0;}
linear_combination_iterator<T> * clone() {
return new linear_combination_iterator_on_vector(m_vector);
}
linear_combination_iterator_on_vector(vector<std::pair<T, unsigned>> & vec):
m_vector(vec),
m_offset(0)
{}
unsigned size() const { return m_vector.size(); }
};
}

View file

@ -1,67 +0,0 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/lar_solver.h"
namespace lp {
lp_bound_propagator::lp_bound_propagator(lar_solver & ls):
m_lar_solver(ls) {}
column_type lp_bound_propagator::get_column_type(unsigned j) const {
return m_lar_solver.m_mpq_lar_core_solver.m_column_types()[j];
}
const impq & lp_bound_propagator::get_low_bound(unsigned j) const {
return m_lar_solver.m_mpq_lar_core_solver.m_r_low_bounds()[j];
}
const impq & lp_bound_propagator::get_upper_bound(unsigned j) const {
return m_lar_solver.m_mpq_lar_core_solver.m_r_upper_bounds()[j];
}
void lp_bound_propagator::try_add_bound(const mpq & v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) {
unsigned term_j = m_lar_solver.adjust_column_index_to_term_index(j);
mpq w = v;
if (term_j != j) {
j = term_j;
w += m_lar_solver.get_term(term_j).m_v; // when terms are turned into the columns they "lose" the right side, at this moment they aquire it back
}
lconstraint_kind kind = is_low? GE : LE;
if (strict)
kind = static_cast<lconstraint_kind>(kind / 2);
if (!bound_is_interesting(j, kind, w))
return;
unsigned k; // index to ibounds
if (is_low) {
if (try_get_val(m_improved_low_bounds, j, k)) {
auto & found_bound = m_ibounds[k];
if (w > found_bound.m_bound || (w == found_bound.m_bound && found_bound.m_strict == false && strict))
found_bound = implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
} else {
m_improved_low_bounds[j] = m_ibounds.size();
m_ibounds.push_back(implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
}
} else { // the upper bound case
if (try_get_val(m_improved_upper_bounds, j, k)) {
auto & found_bound = m_ibounds[k];
if (w < found_bound.m_bound || (w == found_bound.m_bound && found_bound.m_strict == false && strict))
found_bound = implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
} else {
m_improved_upper_bounds[j] = m_ibounds.size();
m_ibounds.push_back(implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
}
}
}
}

View file

@ -1,42 +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"
namespace lp {
class lar_solver;
class lp_bound_propagator {
std::unordered_map<unsigned, unsigned> m_improved_low_bounds; // these maps map a column index to the corresponding index in ibounds
std::unordered_map<unsigned, unsigned> m_improved_upper_bounds;
lar_solver & m_lar_solver;
public:
vector<implied_bound> m_ibounds;
public:
lp_bound_propagator(lar_solver & ls);
column_type get_column_type(unsigned) const;
const impq & get_low_bound(unsigned) const;
const impq & get_upper_bound(unsigned) const;
void try_add_bound(const mpq & v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict);
virtual bool bound_is_interesting(unsigned vi,
lp::lconstraint_kind kind,
const rational & bval) {return true;}
unsigned number_of_found_bounds() const { return m_ibounds.size(); }
virtual void consume(mpq const& v, unsigned j) { std::cout << "doh\n"; }
};
}

View file

@ -22,7 +22,7 @@ Revision History:
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lp_core_solver_base.hpp"
#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;
@ -144,3 +144,5 @@ template bool lp::lp_core_solver_base<lp::mpq, lp::mpq>::inf_set_is_correct() co
template bool lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::infeasibility_costs_are_correct() const;
template bool lp::lp_core_solver_base<lp::mpq, lp::mpq >::infeasibility_costs_are_correct() const;
template bool lp::lp_core_solver_base<double, double >::infeasibility_costs_are_correct() const;
template void lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::calculate_pivot_row(unsigned int);
template bool lp::lp_core_solver_base<lp::mpq, lp::numeric_pair<lp::mpq> >::remove_from_basis(unsigned int);

View file

@ -28,6 +28,7 @@ Revision History:
#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
@ -38,7 +39,17 @@ class lp_core_solver_base {
private:
lp_status m_status;
public:
bool current_x_is_feasible() const { return m_inf_set.size() == 0; }
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;
@ -58,13 +69,13 @@ public:
lp_settings & m_settings;
vector<T> m_y; // the buffer for yB = cb
// a device that is able to solve Bx=c, xB=d, and change the basis
lu<T, X> * m_factorization;
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_low_bounds;
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;
@ -74,6 +85,7 @@ public:
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;
@ -108,7 +120,7 @@ public:
lp_settings & settings,
const column_namer& column_names,
const vector<column_type> & column_types,
const vector<X> & low_bound_values,
const vector<X> & lower_bound_values,
const vector<X> & upper_bound_values);
void allocate_basis_heading();
@ -197,11 +209,11 @@ public:
bool need_to_pivot_to_basis_tableau() const {
SASSERT(m_A.is_correct());
lp_assert(m_A.is_correct());
unsigned m = m_A.row_count();
for (unsigned i = 0; i < m; i++) {
unsigned bj = m_basis[i];
SASSERT(m_A.m_columns[bj].size() > 0);
lp_assert(m_A.m_columns[bj].size() > 0);
if (m_A.m_columns[bj].size() > 1 || m_A.get_val(m_A.m_columns[bj][0]) != one_of_type<mpq>()) return true;
}
return false;
@ -210,10 +222,9 @@ public:
bool reduced_costs_are_correct_tableau() const {
if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
return true;
SASSERT(m_A.is_correct());
lp_assert(m_A.is_correct());
if (m_using_infeas_costs) {
if (infeasibility_costs_are_correct() == false) {
std::cout << "infeasibility_costs_are_correct() does not hold" << std::endl;
return false;
}
}
@ -222,9 +233,6 @@ public:
for (unsigned j = 0; j < n; j++) {
if (m_basis_heading[j] >= 0) {
if (!is_zero(m_d[j])) {
std::cout << "case a\n";
print_column_info(j, std::cout);
return false;
}
} else {
@ -233,8 +241,6 @@ public:
d -= this->m_costs[this->m_basis[cc.m_i]] * this->m_A.get_val(cc);
}
if (m_d[j] != d) {
std::cout << "case b\n";
print_column_info(j, std::cout);
return false;
}
}
@ -253,14 +259,14 @@ public:
}
bool x_below_low_bound(unsigned p) const {
return below_bound(m_x[p], m_low_bounds[p]);
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_low_bound(unsigned p) const {
return above_bound(m_x[p], m_low_bounds[p]);
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 {
@ -271,15 +277,15 @@ public:
bool x_above_upper_bound(unsigned p) const {
return above_bound(m_x[p], m_upper_bounds[p]);
}
bool x_is_at_low_bound(unsigned j) const {
return at_bound(m_x[j], m_low_bounds[j]);
bool x_is_at_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_low_bound(j) || x_is_at_upper_bound(j);
return x_is_at_lower_bound(j) || x_is_at_upper_bound(j);
}
bool column_is_feasible(unsigned j) const;
@ -318,8 +324,8 @@ public:
void fill_reduced_costs_from_m_y_by_rows();
void copy_rs_to_xB(vector<X> & rs);
virtual bool low_bounds_are_set() const { return false; }
X low_bound_value(unsigned j) const { return m_low_bounds[j]; }
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]; }
@ -329,7 +335,7 @@ public:
}
X bound_span(unsigned j) const {
return m_upper_bounds[j] - m_low_bounds[j];
return m_upper_bounds[j] - m_lower_bounds[j];
}
std::string column_name(unsigned column) const;
@ -357,21 +363,21 @@ public:
case column_type::fixed:
if (x_is_at_bound(j))
break;
m_x[j] = m_low_bounds[j];
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_low_bounds[j];
m_x[j] = m_lower_bounds[j];
else
m_x[j] = m_upper_bounds[j];
return true;
case column_type::low_bound:
if (x_is_at_low_bound(j))
case column_type::lower_bound:
if (x_is_at_lower_bound(j))
break;
m_x[j] = m_low_bounds[j];
m_x[j] = m_lower_bounds[j];
return true;
case column_type::upper_bound:
if (x_is_at_upper_bound(j))
@ -385,50 +391,47 @@ public:
}
bool make_column_feasible(unsigned j, numeric_pair<mpq> & delta) {
SASSERT(m_basis_heading[j] < 0);
bool ret = false;
lp_assert(m_basis_heading[j] < 0);
auto & x = m_x[j];
switch (m_column_types[j]) {
case column_type::fixed:
SASSERT(m_low_bounds[j] == m_upper_bounds[j]);
if (x != m_low_bounds[j]) {
delta = m_low_bounds[j] - x;
x = m_low_bounds[j];
return true;
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_low_bounds[j]) {
delta = m_low_bounds[j] - x;
x = m_low_bounds[j];
return true;
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;
x = m_upper_bounds[j];
return true;
ret = true;
}
break;
case column_type::low_bound:
if (x < m_low_bounds[j]) {
delta = m_low_bounds[j] - x;
x = m_low_bounds[j];
return true;
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;
x = m_upper_bounds[j];
return true;
ret = true;
}
break;
case column_type::free_column:
break;
default:
SASSERT(false);
break;
}
return false;
if (ret)
add_delta_to_x_and_call_tracker(j, delta);
return ret;
}
@ -444,6 +447,8 @@ public:
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() {
@ -473,7 +478,7 @@ public:
}
void change_basis_unconditionally(unsigned entering, unsigned leaving) {
SASSERT(m_basis_heading[entering] < 0);
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;
@ -492,7 +497,8 @@ public:
}
void change_basis(unsigned entering, unsigned leaving) {
SASSERT(m_basis_heading[entering] < 0);
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;
@ -522,8 +528,8 @@ public:
if (!this->x_is_at_bound(j))
return false;
break;
case column_type::low_bound:
if (!this->x_is_at_low_bound(j))
case column_type::lower_bound:
if (!this->x_is_at_lower_bound(j))
return false;
break;
case column_type::upper_bound:
@ -533,7 +539,7 @@ public:
case column_type::free_column:
break;
default:
SASSERT(false);
lp_assert(false);
break;
}
return true;
@ -541,7 +547,6 @@ public:
bool non_basic_columns_are_set_correctly() const {
for (unsigned j : this->m_nbasis)
if (!column_is_feasible(j)) {
print_column_info(j, std::cout);
return false;
}
return true;
@ -552,10 +557,10 @@ public:
switch (m_column_types[j]) {
case column_type::fixed:
case column_type::boxed:
out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl;
out << "(" << m_lower_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl;
break;
case column_type::low_bound:
out << m_low_bounds[j] << std::endl;
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;
@ -566,36 +571,38 @@ public:
}
void print_column_info(unsigned j, std::ostream & out) const {
out << "column_index = " << j << ", name = "<< column_name(j) << " type = " << column_type_to_string(m_column_types[j]) << std::endl;
out << "j = " << j << ", name = "<< column_name(j);
switch (m_column_types[j]) {
case column_type::fixed:
case column_type::boxed:
out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl;
out << " [" << m_lower_bounds[j] << ", " << m_upper_bounds[j] << "]";
break;
case column_type::low_bound:
out << m_low_bounds[j] << std::endl;
case column_type::lower_bound:
out << " [" << m_lower_bounds[j] << "," << "oo" << "]";
break;
case column_type::upper_bound:
out << m_upper_bounds[j] << std::endl;
out << " [-oo, " << m_upper_bounds[j] << ']';
break;
case column_type::free_column:
out << " [-oo, oo]";
break;
default:
SASSERT(false);
lp_assert(false);
}
std::cout << "basis heading = " << m_basis_heading[j] << std::endl;
std::cout << "x = " << m_x[j] << std::endl;
/*
std::cout << "cost = " << m_costs[j] << std::endl;
std:: cout << "m_d = " << m_d[j] << std::endl;*/
// out << "basis heading = " << m_basis_heading[j] << std::endl;
out << " x = " << m_x[j];
if (m_basis_heading[j] >= 0)
out << " base\n";
else
out << " nbas\n";
}
bool column_is_free(unsigned j) { return this->m_column_type[j] == free; }
bool column_is_free(unsigned j) const { return this->m_column_type[j] == free; }
bool column_has_upper_bound(unsigned j) {
bool column_has_upper_bound(unsigned j) const {
switch(m_column_types[j]) {
case column_type::free_column:
case column_type::low_bound:
case column_type::lower_bound:
return false;
default:
return true;
@ -605,13 +612,13 @@ public:
bool bounds_for_boxed_are_set_correctly() const {
for (unsigned j = 0; j < m_column_types.size(); j++) {
if (m_column_types[j] != column_type::boxed) continue;
if (m_low_bounds[j] > m_upper_bounds[j])
if (m_lower_bounds[j] > m_upper_bounds[j])
return false;
}
return true;
}
bool column_has_low_bound(unsigned j) {
bool column_has_lower_bound(unsigned j) const {
switch(m_column_types[j]) {
case column_type::free_column:
case column_type::upper_bound:
@ -671,26 +678,43 @@ public:
return m_inf_set.contains(j);
}
void update_column_in_inf_set(unsigned j) {
if (column_is_feasible(j)) {
m_inf_set.erase(j);
} else {
m_inf_set.insert(j);
}
void 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) {
m_x[j] = v;
}
void add_delta_to_x_and_call_tracker(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);
SASSERT(!column_is_feasible(j));
lp_assert(!column_is_feasible(j));
}
void remove_column_from_inf_set(unsigned j) {
m_inf_set.erase(j);
SASSERT(column_is_feasible(j));
lp_assert(column_is_feasible(j));
}
bool costs_on_nbasis_are_zeros() const {
SASSERT(this->basis_heading_is_correct());
lp_assert(this->basis_heading_is_correct());
for (unsigned j = 0; j < this->m_n(); j++) {
if (this->m_basis_heading[j] < 0)
SASSERT(is_zero(this->m_costs[j]));
lp_assert(is_zero(this->m_costs[j]));
}
return true;
}
@ -701,5 +725,10 @@ public:
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];
}
};
}

View file

@ -35,11 +35,11 @@ lp_core_solver_base(static_matrix<T, X> & A,
lp_settings & settings,
const column_namer& column_names,
const vector<column_type> & column_types,
const vector<X> & low_bound_values,
const vector<X> & lower_bound_values,
const vector<X> & upper_bound_values):
m_total_iterations(0),
m_iters_with_no_cost_growing(0),
m_status(FEASIBLE),
m_status(lp_status::FEASIBLE),
m_inf_set(A.column_count()),
m_using_infeas_costs(false),
m_pivot_row_of_B_1(A.row_count()),
@ -59,7 +59,7 @@ lp_core_solver_base(static_matrix<T, X> & A,
m_d(m_n()),
m_ed(m_m()),
m_column_types(column_types),
m_low_bounds(low_bound_values),
m_lower_bounds(lower_bound_values),
m_upper_bounds(upper_bound_values),
m_column_norms(m_n()),
m_copy_of_xB(m_m()),
@ -68,7 +68,7 @@ lp_core_solver_base(static_matrix<T, X> & A,
m_tracing_basis_changes(false),
m_pivoted_rows(nullptr),
m_look_for_feasible_solution_only(false) {
SASSERT(bounds_for_boxed_are_set_correctly());
lp_assert(bounds_for_boxed_are_set_correctly());
init();
init_basis_heading_and_non_basic_columns_vector();
}
@ -76,7 +76,7 @@ lp_core_solver_base(static_matrix<T, X> & A,
template <typename T, typename X> void lp_core_solver_base<T, X>::
allocate_basis_heading() { // the rest of initilization will be handled by the factorization class
init_basis_heading_and_non_basic_columns_vector();
SASSERT(basis_heading_is_correct());
lp_assert(basis_heading_is_correct());
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
init() {
@ -142,7 +142,7 @@ solve_yB(vector<T> & y) {
// }
// }
template <typename T, typename X> void lp_core_solver_base<T, X>::solve_Bd(unsigned entering, indexed_vector<T> & column) {
SASSERT(!m_settings.use_tableau());
lp_assert(!m_settings.use_tableau());
if (m_factorization == nullptr) {
init_factorization(m_factorization, m_A, m_basis, m_settings);
}
@ -152,19 +152,19 @@ template <typename T, typename X> void lp_core_solver_base<T, X>::solve_Bd(unsig
template <typename T, typename X> void lp_core_solver_base<T, X>::
solve_Bd(unsigned entering) {
SASSERT(m_ed.is_OK());
lp_assert(m_ed.is_OK());
m_factorization->solve_Bd(entering, m_ed, m_w);
if (this->precise())
m_columns_nz[entering] = m_ed.m_index.size();
SASSERT(m_ed.is_OK());
SASSERT(m_w.is_OK());
lp_assert(m_ed.is_OK());
lp_assert(m_w.is_OK());
#ifdef Z3DEBUG
// auto B = get_B(*m_factorization, m_basis);
// vector<T> a(m_m());
// m_A.copy_column_to_vector(entering, a);
// vector<T> cd(m_ed.m_data);
// B.apply_from_left(cd, m_settings);
// SASSERT(vectors_are_equal(cd , a));
// lp_assert(vectors_are_equal(cd , a));
#endif
}
@ -223,16 +223,11 @@ restore_m_ed(T * buffer) {
template <typename T, typename X> bool lp_core_solver_base<T, X>::
A_mult_x_is_off() const {
SASSERT(m_x.size() == m_A.column_count());
lp_assert(m_x.size() == m_A.column_count());
if (numeric_traits<T>::precise()) {
for (unsigned i = 0; i < m_m(); i++) {
for (unsigned i = 0; i < m_m(); i++) {
X delta = m_b[i] - m_A.dot_product_with_row(i, m_x);
if (delta != numeric_traits<X>::zero()) {
std::cout << "precise x is off (";
std::cout << "m_b[" << i << "] = " << m_b[i] << " ";
std::cout << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ';
std::cout << "delta = " << delta << ' ';
std::cout << "iters = " << total_iterations() << ")" << std::endl;
return true;
}
}
@ -259,17 +254,12 @@ A_mult_x_is_off() const {
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
A_mult_x_is_off_on_index(const vector<unsigned> & index) const {
SASSERT(m_x.size() == m_A.column_count());
lp_assert(m_x.size() == m_A.column_count());
if (numeric_traits<T>::precise()) return false;
#if RUN_A_MULT_X_IS_OFF_FOR_PRECESE
for (unsigned i : index) {
X delta = m_b[i] - m_A.dot_product_with_row(i, m_x);
if (delta != numeric_traits<X>::zero()) {
// std::cout << "x is off (";
// std::cout << "m_b[" << i << "] = " << m_b[i] << " ";
// std::cout << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ';
// std::cout << "delta = " << delta << ' ';
// std::cout << "iters = " << total_iterations() << ")" << std::endl;
return true;
}
}
@ -299,13 +289,13 @@ A_mult_x_is_off_on_index(const vector<unsigned> & index) const {
// from page 182 of Istvan Maros's book
template <typename T, typename X> void lp_core_solver_base<T, X>::
calculate_pivot_row_of_B_1(unsigned pivot_row) {
SASSERT(! use_tableau());
SASSERT(m_pivot_row_of_B_1.is_OK());
lp_assert(! use_tableau());
lp_assert(m_pivot_row_of_B_1.is_OK());
m_pivot_row_of_B_1.clear();
m_pivot_row_of_B_1.set_value(numeric_traits<T>::one(), pivot_row);
SASSERT(m_pivot_row_of_B_1.is_OK());
lp_assert(m_pivot_row_of_B_1.is_OK());
m_factorization->solve_yB_with_error_check_indexed(m_pivot_row_of_B_1, m_basis_heading, m_basis, m_settings);
SASSERT(m_pivot_row_of_B_1.is_OK());
lp_assert(m_pivot_row_of_B_1.is_OK());
}
@ -391,15 +381,15 @@ set_non_basic_x_to_correct_bounds() {
for (unsigned j : non_basis()) {
switch (m_column_types[j]) {
case column_type::boxed:
m_x[j] = m_d[j] < 0? m_upper_bounds[j]: m_low_bounds[j];
m_x[j] = m_d[j] < 0? m_upper_bounds[j]: m_lower_bounds[j];
break;
case column_type::low_bound:
m_x[j] = m_low_bounds[j];
SASSERT(column_is_dual_feasible(j));
case column_type::lower_bound:
m_x[j] = m_lower_bounds[j];
lp_assert(column_is_dual_feasible(j));
break;
case column_type::upper_bound:
m_x[j] = m_upper_bounds[j];
SASSERT(column_is_dual_feasible(j));
lp_assert(column_is_dual_feasible(j));
break;
default:
break;
@ -411,21 +401,18 @@ column_is_dual_feasible(unsigned j) const {
switch (m_column_types[j]) {
case column_type::fixed:
case column_type::boxed:
return (x_is_at_low_bound(j) && d_is_not_negative(j)) ||
return (x_is_at_lower_bound(j) && d_is_not_negative(j)) ||
(x_is_at_upper_bound(j) && d_is_not_positive(j));
case column_type::low_bound:
return x_is_at_low_bound(j) && d_is_not_negative(j);
case column_type::lower_bound:
return x_is_at_lower_bound(j) && d_is_not_negative(j);
case column_type::upper_bound:
LP_OUT(m_settings, "upper_bound type should be switched to low_bound" << std::endl);
SASSERT(false); // impossible case
lp_assert(false); // impossible case
case column_type::free_column:
return numeric_traits<X>::is_zero(m_d[j]);
default:
LP_OUT(m_settings, "column = " << j << std::endl);
LP_OUT(m_settings, "unexpected column type = " << column_type_to_string(m_column_types[j]) << std::endl);
SASSERT(false);
lp_unreachable();
}
SASSERT(false);
lp_unreachable();
return false;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
@ -484,14 +471,14 @@ template <typename T, typename X> bool lp_core_solver_base<T, X>::column_is_feas
case column_type::boxed:
if (this->above_bound(x, this->m_upper_bounds[j])) {
return false;
} else if (this->below_bound(x, this->m_low_bounds[j])) {
} else if (this->below_bound(x, this->m_lower_bounds[j])) {
return false;
} else {
return true;
}
break;
case column_type::low_bound:
if (this->below_bound(x, this->m_low_bounds[j])) {
case column_type::lower_bound:
if (this->below_bound(x, this->m_lower_bounds[j])) {
return false;
} else {
return true;
@ -508,7 +495,7 @@ template <typename T, typename X> bool lp_core_solver_base<T, X>::column_is_feas
return true;
break;
default:
SASSERT(false);
lp_unreachable();
}
return false; // it is unreachable
}
@ -530,9 +517,6 @@ template <typename T, typename X> bool lp_core_solver_base<T, X>::inf_set_is_cor
bool is_feas = column_is_feasible(j);
if (is_feas == belongs_to_set) {
print_column_info(j, std::cout);
std::cout << "belongs_to_set = " << belongs_to_set << std::endl;
std::cout <<( is_feas? "feas":"inf") << std::endl;
return false;
}
}
@ -549,7 +533,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) {
if (!find_x_by_solving()) {
restore_x(entering, tt);
if(A_mult_x_is_off()) {
m_status = FLOATING_POINT_ERROR;
m_status = lp_status::FLOATING_POINT_ERROR;
m_iters_with_no_cost_growing++;
return false;
}
@ -559,7 +543,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) {
if (m_factorization->get_status() != LU_status::OK) {
std::stringstream s;
// s << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations();
m_status = FLOATING_POINT_ERROR;
m_status = lp_status::FLOATING_POINT_ERROR;
return false;
}
return false;
@ -581,19 +565,19 @@ update_basis_and_x(int entering, int leaving, X const & tt) {
init_lu();
if (m_factorization->get_status() != LU_status::OK) {
if (m_look_for_feasible_solution_only && !precise()) {
m_status = UNSTABLE;
m_status = lp_status::UNSTABLE;
delete m_factorization;
m_factorization = nullptr;
return false;
}
// LP_OUT(m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations() << std::endl);
restore_x_and_refactor(entering, leaving, tt);
if (m_status == FLOATING_POINT_ERROR)
if (m_status == lp_status::FLOATING_POINT_ERROR)
return false;
SASSERT(!A_mult_x_is_off());
CASSERT("A_off", !A_mult_x_is_off());
m_iters_with_no_cost_growing++;
// LP_OUT(m_settings, "rolled back after failing of init_factorization()" << std::endl);
m_status = UNSTABLE;
m_status = lp_status::UNSTABLE;
return false;
}
return true;
@ -602,7 +586,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) {
template <typename T, typename X> bool lp_core_solver_base<T, X>::
divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) {
SASSERT(numeric_traits<T>::precise());
lp_assert(numeric_traits<T>::precise());
int pivot_index = -1;
auto & row = m_A.m_rows[pivot_row];
unsigned size = row.size();
@ -629,7 +613,7 @@ divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) {
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
pivot_column_tableau(unsigned j, unsigned piv_row_index) {
if (!divide_row_by_pivot(piv_row_index, j))
if (!divide_row_by_pivot(piv_row_index, j))
return false;
auto &column = m_A.m_columns[j];
int pivot_col_cell_index = -1;
@ -643,7 +627,7 @@ pivot_column_tableau(unsigned j, unsigned piv_row_index) {
return false;
if (pivot_col_cell_index != 0) {
SASSERT(column.size() > 1);
lp_assert(column.size() > 1);
// swap the pivot column cell with the head cell
auto c = column[0];
column[0] = column[pivot_col_cell_index];
@ -654,7 +638,7 @@ pivot_column_tableau(unsigned j, unsigned piv_row_index) {
}
while (column.size() > 1) {
auto & c = column.back();
SASSERT(c.m_i != piv_row_index);
lp_assert(c.m_i != piv_row_index);
if(! m_A.pivot_row_to_row_given_cell(piv_row_index, c, j)) {
return false;
}
@ -702,7 +686,7 @@ non_basis_is_correctly_represented_in_heading() const {
}
for (unsigned j = 0; j < m_A.column_count(); j++) {
if (m_basis_heading[j] >= 0) {
SASSERT(static_cast<unsigned>(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j);
lp_assert(static_cast<unsigned>(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j);
}
}
return true;
@ -710,26 +694,22 @@ non_basis_is_correctly_represented_in_heading() const {
template <typename T, typename X> bool lp_core_solver_base<T, X>::
basis_heading_is_correct() const {
SASSERT(m_basis_heading.size() == m_A.column_count());
SASSERT(m_basis.size() == m_A.row_count());
SASSERT(m_nbasis.size() <= m_A.column_count() - m_A.row_count()); // for the dual the size of non basis can be smaller
lp_assert(m_basis_heading.size() == m_A.column_count());
lp_assert(m_basis.size() == m_A.row_count());
lp_assert(m_nbasis.size() <= m_A.column_count() - m_A.row_count()); // for the dual the size of non basis can be smaller
if (!basis_has_no_doubles()) {
// std::cout << "basis_has_no_doubles" << std::endl;
return false;
}
if (!non_basis_has_no_doubles()) {
// std::cout << "non_basis_has_no_doubles" << std::endl;
return false;
}
if (!basis_is_correctly_represented_in_heading()) {
// std::cout << "basis_is_correctly_represented_in_heading" << std::endl;
return false;
}
if (!non_basis_is_correctly_represented_in_heading()) {
// std::cout << "non_basis_is_correctly_represented_in_heading" << std::endl;
return false;
}
@ -856,12 +836,12 @@ solve_Ax_eq_b() {
template <typename T, typename X> void lp_core_solver_base<T, X>::
snap_non_basic_x_to_bound_and_free_to_zeroes() {
for (unsigned j : non_basis()) {
SASSERT(j < m_x.size());
lp_assert(j < m_x.size());
switch (m_column_types[j]) {
case column_type::fixed:
case column_type::boxed:
case column_type::low_bound:
m_x[j] = m_low_bounds[j];
case column_type::lower_bound:
m_x[j] = m_lower_bounds[j];
break;
case column_type::upper_bound:
m_x[j] = m_upper_bounds[j];
@ -894,23 +874,23 @@ template <typename T, typename X> non_basic_column_value_position lp_core_solver
get_non_basic_column_value_position(unsigned j) const {
switch (m_column_types[j]) {
case column_type::fixed:
return x_is_at_low_bound(j)? at_fixed : not_at_bound;
return x_is_at_lower_bound(j)? at_fixed : not_at_bound;
case column_type::free_column:
return free_of_bounds;
case column_type::boxed:
return x_is_at_low_bound(j)? at_low_bound :(
return x_is_at_lower_bound(j)? at_lower_bound :(
x_is_at_upper_bound(j)? at_upper_bound:
not_at_bound
);
case column_type::low_bound:
return x_is_at_low_bound(j)? at_low_bound : not_at_bound;
case column_type::lower_bound:
return x_is_at_lower_bound(j)? at_lower_bound : not_at_bound;
case column_type::upper_bound:
return x_is_at_upper_bound(j)? at_upper_bound : not_at_bound;
default:
SASSERT(false);
lp_unreachable();
}
SASSERT(false);
return at_low_bound;
lp_unreachable();
return at_lower_bound;
}
template <typename T, typename X> void lp_core_solver_base<T, X>::init_lu() {
@ -938,59 +918,80 @@ template <typename T, typename X> void lp_core_solver_base<T, X>::transpose_row
transpose_basis(i, j);
m_A.transpose_rows(i, j);
}
// j is the new basic column, j_basic - the leaving column
template <typename T, typename X> bool lp_core_solver_base<T, X>::pivot_column_general(unsigned j, unsigned j_basic, indexed_vector<T> & w) {
lp_assert(m_basis_heading[j] < 0);
lp_assert(m_basis_heading[j_basic] >= 0);
unsigned row_index = m_basis_heading[j_basic];
if (m_settings.m_simplex_strategy == simplex_strategy_enum::lu) {
if (m_factorization->need_to_refactor()) {
init_lu();
}
else {
m_factorization->prepare_entering(j, w); // to init vector w
m_factorization->replace_column(zero_of_type<T>(), w, row_index);
}
if (m_factorization->get_status() != LU_status::OK) {
init_lu();
return false;
}
else {
change_basis(j, j_basic);
}
}
else { // the tableau case
if (pivot_column_tableau(j, row_index))
change_basis(j, j_basic);
else return false;
}
return true;
}
template <typename T, typename X> void lp_core_solver_base<T, X>::pivot_fixed_vars_from_basis() {
// run over basis and non-basis at the same time
indexed_vector<T> w(m_basis.size()); // the buffer
unsigned i = 0; // points to basis
unsigned j = 0; // points to nonbasis
for (; i < m_basis.size() && j < m_nbasis.size(); i++) {
unsigned ii = m_basis[i];
unsigned jj;
for (; i < m_basis.size(); i++) {
unsigned basic_j = m_basis[i];
if (get_column_type(ii) != column_type::fixed) continue;
while (j < m_nbasis.size()) {
for (; j < m_nbasis.size(); j++) {
jj = m_nbasis[j];
if (get_column_type(jj) != column_type::fixed)
if (get_column_type(basic_j) != column_type::fixed) continue;
T a;
unsigned j;
for (auto &c : m_A.m_rows[i]) {
j = c.var();
if (j == basic_j)
continue;
if (get_column_type(j) != column_type::fixed) {
if (pivot_column_general(j, basic_j, w))
break;
}
if (j >= m_nbasis.size())
break;
j++;
if (m_factorization->need_to_refactor()) {
change_basis(jj, ii);
init_lu();
} else {
m_factorization->prepare_entering(jj, w); // to init vector w
m_factorization->replace_column(zero_of_type<T>(), w, m_basis_heading[ii]);
change_basis(jj, ii);
}
if (m_factorization->get_status() != LU_status::OK) {
change_basis(ii, jj);
init_lu();
} else {
break;
}
}
SASSERT(m_factorization->get_status()== LU_status::OK);
}
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::remove_from_basis(unsigned basic_j) {
indexed_vector<T> w(m_basis.size()); // the buffer
unsigned i = m_basis_heading[basic_j];
for (auto &c : m_A.m_rows[i]) {
if (c.var() == basic_j)
continue;
if (pivot_column_general(c.var(), basic_j, w))
return true;
}
return false;
}
template <typename T, typename X> bool
lp_core_solver_base<T, X>::infeasibility_costs_are_correct() const {
if (! this->m_using_infeas_costs)
return true;
SASSERT(costs_on_nbasis_are_zeros());
lp_assert(costs_on_nbasis_are_zeros());
for (unsigned j :this->m_basis) {
if (!infeasibility_cost_is_correct_for_column(j)) {
std::cout << "infeasibility_cost_is_correct_for_column does not hold\n";
print_column_info(j, std::cout);
return false;
}
if (!is_zero(m_d[j])) {
std::cout << "m_d is not zero\n";
print_column_info(j, std::cout);
return false;
}
}
@ -1012,7 +1013,7 @@ lp_core_solver_base<T, X>::infeasibility_cost_is_correct_for_column(unsigned j)
}
return is_zero(this->m_costs[j]);
case column_type::low_bound:
case column_type::lower_bound:
if (this->x_below_low_bound(j)) {
return this->m_costs[j] == -r;
}
@ -1026,9 +1027,31 @@ lp_core_solver_base<T, X>::infeasibility_cost_is_correct_for_column(unsigned j)
case column_type::free_column:
return is_zero(this->m_costs[j]);
default:
SASSERT(false);
lp_assert(false);
return true;
}
}
template <typename T, typename X>
void lp_core_solver_base<T, X>::calculate_pivot_row(unsigned i) {
lp_assert(!use_tableau());
lp_assert(m_pivot_row.is_OK());
m_pivot_row_of_B_1.clear();
m_pivot_row_of_B_1.resize(m_m());
m_pivot_row.clear();
m_pivot_row.resize(m_n());
if (m_settings.use_tableau()) {
unsigned basic_j = m_basis[i];
for (auto & c : m_A.m_rows[i]) {
if (c.m_j != basic_j)
m_pivot_row.set_value(c.get_val(), c.m_j);
}
return;
}
calculate_pivot_row_of_B_1(i);
calculate_pivot_row_when_pivot_row_of_B1_is_ready(i);
}
}

View file

@ -22,7 +22,7 @@ Revision History:
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lp_dual_core_solver.hpp"
#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>&,

View file

@ -56,7 +56,7 @@ public:
vector<int> & heading,
vector<T> & costs,
vector<column_type> & column_type_array,
vector<X> & low_bound_values,
vector<X> & lower_bound_values,
vector<X> & upper_bound_values,
lp_settings & settings,
const column_namer & column_names):
@ -70,7 +70,7 @@ public:
settings,
column_names,
column_type_array,
low_bound_values,
lower_bound_values,
upper_bound_values),
m_can_enter_basis(can_enter_basis),
m_a_wave(this->m_m()),
@ -110,7 +110,7 @@ public:
bool done();
T get_edge_steepness_for_low_bound(unsigned p);
T get_edge_steepness_for_lower_bound(unsigned p);
T get_edge_steepness_for_upper_bound(unsigned p);
@ -174,7 +174,7 @@ public:
// it is positive if going from low bound to upper bound and negative if going from upper bound to low bound
T signed_span_of_boxed(unsigned j) {
return this->x_is_at_low_bound(j)? this->bound_span(j): - this->bound_span(j);
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();
@ -207,6 +207,6 @@ public:
void solve();
bool low_bounds_are_set() const override { return true; }
bool lower_bounds_are_set() const override { return true; }
};
}

View file

@ -38,7 +38,7 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_non_ba
while (j--) {
if (this->m_basis_heading[j] >= 0 ) continue;
if (m_can_enter_basis[j]) {
SASSERT(std::find(nb.begin(), nb.end(), j) == nb.end());
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());
}
@ -97,25 +97,25 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::start_with_ini
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>::done() {
if (this->get_status() == OPTIMAL) {
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(ITERATIONS_EXHAUSTED);
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_low_bound(unsigned p) {
SASSERT(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m());
T del = this->m_x[p] - this->m_low_bounds[p];
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) {
SASSERT(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m());
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]];
@ -127,7 +127,7 @@ template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(u
case column_type::fixed:
case column_type::boxed:
if (this->x_below_low_bound(p)) {
T del = get_edge_steepness_for_low_bound(p);
T del = get_edge_steepness_for_lower_bound(p);
return del;
}
if (this->x_above_upper_bound(p)) {
@ -135,9 +135,9 @@ template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(u
return del;
}
return numeric_traits<T>::zero();
case column_type::low_bound:
case column_type::lower_bound:
if (this->x_below_low_bound(p)) {
T del = get_edge_steepness_for_low_bound(p);
T del = get_edge_steepness_for_lower_bound(p);
return del;
}
return numeric_traits<T>::zero();
@ -150,12 +150,12 @@ template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(u
return numeric_traits<T>::zero();
break;
case column_type::free_column:
SASSERT(numeric_traits<T>::is_zero(this->m_d[p]));
lp_assert(numeric_traits<T>::is_zero(this->m_d[p]));
return numeric_traits<T>::zero();
default:
SASSERT(false);
lp_unreachable();
}
SASSERT(false);
lp_unreachable();
return numeric_traits<T>::zero();
}
@ -185,8 +185,8 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::pricing_loop(u
}
} while (i != initial_offset_in_rows && rows_left);
if (m_r == -1) {
if (this->get_status() != UNSTABLE) {
this->set_status(OPTIMAL);
if (this->get_status() != lp_status::UNSTABLE) {
this->set_status(lp_status::OPTIMAL);
}
} else {
m_p = this->m_basis[m_r];
@ -196,10 +196,10 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::pricing_loop(u
return;
}
// failure in advance_on_known_p
if (this->get_status() == FLOATING_POINT_ERROR) {
if (this->get_status() == lp_status::FLOATING_POINT_ERROR) {
return;
}
this->set_status(UNSTABLE);
this->set_status(lp_status::UNSTABLE);
m_forbidden_rows.insert(m_r);
}
}
@ -224,9 +224,9 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::advance_on_kno
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
SASSERT(false); // not implemented yet
lp_unreachable(); // not implemented yet
} else {
SASSERT(pivot_compare_result == 1);
lp_assert(pivot_compare_result == 1);
this->init_lu();
}
DSE_FTran();
@ -243,38 +243,38 @@ template <typename T, typename X> int lp_dual_core_solver<T, X>::define_sign_of_
if (this->x_above_upper_bound(m_p)) {
return 1;
}
SASSERT(false);
case column_type::low_bound:
lp_unreachable();
case column_type::lower_bound:
if (this->x_below_low_bound(m_p)) {
return -1;
}
SASSERT(false);
lp_unreachable();
case column_type::upper_bound:
if (this->x_above_upper_bound(m_p)) {
return 1;
}
SASSERT(false);
lp_unreachable();
default:
SASSERT(false);
lp_unreachable();
}
SASSERT(false);
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::low_bound:
SASSERT(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bounds[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:
SASSERT(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j]));
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 low_bound = this->x_is_at_low_bound(j);
bool lower_bound = this->x_is_at_lower_bound(j);
bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
return low_bound == grawing;
return lower_bound == grawing;
}
case column_type::fixed: // is always dual feasible so we ingore it
return false;
@ -302,28 +302,28 @@ template <typename T, typename X> T lp_dual_core_solver<T, X>::get_delta() {
switch (this->m_column_types[m_p]) {
case column_type::boxed:
if (this->x_below_low_bound(m_p)) {
return this->m_x[m_p] - this->m_low_bounds[m_p];
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];
}
SASSERT(false);
case column_type::low_bound:
lp_unreachable();
case column_type::lower_bound:
if (this->x_below_low_bound(m_p)) {
return this->m_x[m_p] - this->m_low_bounds[m_p];
return this->m_x[m_p] - this->m_lower_bounds[m_p];
}
SASSERT(false);
lp_unreachable();
case column_type::upper_bound:
if (this->x_above_upper_bound(m_p)) {
return get_edge_steepness_for_upper_bound(m_p);
}
SASSERT(false);
lp_unreachable();
case column_type::fixed:
return this->m_x[m_p] - this->m_upper_bounds[m_p];
default:
SASSERT(false);
lp_unreachable();
}
SASSERT(false);
lp_unreachable();
return zero_of_type<T>();
}
@ -370,11 +370,11 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::update_betas()
template <typename T, typename X> void lp_dual_core_solver<T, X>::apply_flips() {
for (unsigned j : m_flipped_boxed) {
SASSERT(this->x_is_at_bound(j));
if (this->x_is_at_low_bound(j)) {
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_low_bounds[j];
this->m_x[j] = this->m_lower_bounds[j];
}
}
}
@ -382,17 +382,17 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::apply_flips()
template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_column_to_bounds(unsigned j) {
switch (this->m_column_type[j]) {
case column_type::fixed:
this->m_x[j] = this->m_low_bounds[j];
this->m_x[j] = this->m_lower_bounds[j];
break;
case column_type::boxed:
if (this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_low_bounds[j];
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::low_bound:
this->m_x[j] = this->m_low_bounds[j];
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];
@ -400,7 +400,7 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_column
case column_type::free_column:
break;
default:
SASSERT(false);
lp_unreachable();
}
}
@ -456,15 +456,15 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::basis_change_a
return false;
}
SASSERT(d_is_correct());
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_low_bound:
case at_lower_bound:
case at_fixed:
this->m_x[m_q] = this->m_low_bounds[m_q];
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];
@ -472,7 +472,7 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::recover_leavin
case free_of_bounds:
this->m_x[m_q] = zero_of_type<X>();
default:
SASSERT(false);
lp_unreachable();
}
}
@ -481,12 +481,12 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::revert_to_prev
this->change_basis_unconditionally(m_p, m_q);
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings);
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR); // complete failure
this->set_status(lp_status::FLOATING_POINT_ERROR); // complete failure
return;
}
recover_leaving();
if (!this->find_x_by_solving()) {
this->set_status(FLOATING_POINT_ERROR);
this->set_status(lp_status::FLOATING_POINT_ERROR);
return;
}
recalculate_xB_and_d();
@ -497,23 +497,23 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::revert_to_prev
template <typename T, typename X> bool lp_dual_core_solver<T, X>::snap_runaway_nonbasic_column(unsigned j) {
switch (this->m_column_types[j]) {
case column_type::fixed:
case column_type::low_bound:
if (!this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_low_bounds[j];
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_low_bound = abs(this->m_low_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]);
if (closer_to_low_bound) {
if (!this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_low_bounds[j];
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_low_bounds[j];
this->m_x[j] = this->m_lower_bounds[j];
return true;
}
}
@ -535,12 +535,6 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::snap_runaway_n
template <typename T, typename X> bool lp_dual_core_solver<T, X>::problem_is_dual_feasible() const {
for (unsigned j : this->non_basis()){
if (!this->column_is_dual_feasible(j)) {
// std::cout << "column " << j << " is not dual feasible" << std::endl;
// std::cout << "m_d[" << j << "] = " << this->m_d[j] << std::endl;
// std::cout << "x[" << j << "] = " << this->m_x[j] << std::endl;
// std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl;
// std::cout << "bounds = " << this->m_low_bounds[j] << "," << this->m_upper_bounds[j] << std::endl;
// std::cout << "total_iterations = " << this->total_iterations() << std::endl;
return false;
}
}
@ -566,10 +560,10 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::delta_keeps_th
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::set_status_to_tentative_dual_unbounded_or_dual_unbounded() {
if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) {
this->set_status(DUAL_UNBOUNDED);
if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) {
this->set_status(lp_status::DUAL_UNBOUNDED);
} else {
this->set_status(TENTATIVE_DUAL_UNBOUNDED);
this->set_status(lp_status::TENTATIVE_DUAL_UNBOUNDED);
}
}
@ -599,10 +593,10 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::tight_breakpoi
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>();
SASSERT(m_breakpoint_set.size() > 0);
lp_assert(m_breakpoint_set.size() > 0);
for (auto j : m_breakpoint_set) {
T t;
if (this->x_is_at_low_bound(j)) {
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]);
@ -620,7 +614,7 @@ template <typename T, typename X> T lp_dual_core_solver<T, X>::calculate_harris_
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_tight_set_on_harris_delta(const T & harris_delta ){
m_tight_set.clear();
for (auto j : m_breakpoint_set) {
if (this->x_is_at_low_bound(j)) {
if (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);
}
@ -648,7 +642,7 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_on_tigh
}
}
m_tight_set.erase(m_q);
SASSERT(m_q != -1);
lp_assert(m_q != -1);
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_and_tight_set() {
@ -675,7 +669,7 @@ template <typename T, typename X> bool lp_dual_core_solver<T, X>::ratio_test() {
set_status_to_tentative_dual_unbounded_or_dual_unbounded();
return false;
}
this->set_status(FEASIBLE);
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;
@ -731,19 +725,19 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::update_xb_afte
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() == TENTATIVE_DUAL_UNBOUNDED) {
if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) {
number_of_rows_to_try = this->m_m();
} else {
this->set_status(FEASIBLE);
this->set_status(lp_status::FEASIBLE);
}
pricing_loop(number_of_rows_to_try, offset_in_rows);
SASSERT(problem_is_dual_feasible());
lp_assert(problem_is_dual_feasible());
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // see the page 35
SASSERT(d_is_correct());
SASSERT(problem_is_dual_feasible());
SASSERT(this->basis_heading_is_correct());
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 {
@ -751,7 +745,7 @@ template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // s
return;
}
one_iteration();
} while (this->get_status() != FLOATING_POINT_ERROR && this->get_status() != DUAL_UNBOUNDED && this->get_status() != OPTIMAL &&
} while (this->get_status() != lp_status::FLOATING_POINT_ERROR && this->get_status() != lp_status::DUAL_UNBOUNDED && this->get_status() != lp_status::OPTIMAL &&
this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements
&& this->total_iterations() <= this->m_settings.max_total_number_of_iterations);
}

View file

@ -17,7 +17,7 @@ Revision History:
--*/
#include "util/lp/lp_dual_simplex.hpp"
#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;

View file

@ -28,7 +28,7 @@ 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_low_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver
vector<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;

View file

@ -22,61 +22,61 @@ 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 OPTIMAL:
case lp_status::OPTIMAL:
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
this->m_status = FEASIBLE;
this->m_status = lp_status::FEASIBLE;
} else {
this->m_status = UNBOUNDED;
this->m_status = lp_status::UNBOUNDED;
}
break;
case DUAL_UNBOUNDED:
SASSERT(false);
case ITERATIONS_EXHAUSTED:
this->m_status = ITERATIONS_EXHAUSTED;
case lp_status::DUAL_UNBOUNDED:
lp_unreachable();
case lp_status::ITERATIONS_EXHAUSTED:
this->m_status = lp_status::ITERATIONS_EXHAUSTED;
break;
case TIME_EXHAUSTED:
this->m_status = TIME_EXHAUSTED;
case lp_status::TIME_EXHAUSTED:
this->m_status = lp_status::TIME_EXHAUSTED;
break;
case FLOATING_POINT_ERROR:
this->m_status = FLOATING_POINT_ERROR;
case lp_status::FLOATING_POINT_ERROR:
this->m_status = lp_status::FLOATING_POINT_ERROR;
break;
default:
SASSERT(false);
lp_unreachable();
}
}
template <typename T, typename X> void lp_dual_simplex<T, X>::fix_logical_for_stage2(unsigned j) {
SASSERT(j >= this->number_of_core_structurals());
lp_assert(j >= this->number_of_core_structurals());
switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) {
case column_type::low_bound:
m_low_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::low_bound;
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_low_bounds[j] = numeric_traits<T>::zero();
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:
SASSERT(false);
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::low_bound:
m_low_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::low_bound;
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:
SASSERT(false);
lp_unreachable();
case column_type::boxed:
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
m_low_bounds[j] = numeric_traits<T>::zero();
m_lower_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = column_type::boxed;
m_can_enter_basis[j] = true;
break;
@ -85,7 +85,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fix_structural_for
m_column_types_of_core_solver[j] = column_type::free_column;
break;
default:
SASSERT(false);
lp_unreachable();
}
// T cost_was = this->m_costs[j];
this->set_scaled_cost(j);
@ -114,23 +114,23 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::solve_for_stage2()
m_core_solver->solve_yB(m_core_solver->m_y);
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
m_core_solver->set_status(FEASIBLE);
m_core_solver->set_status(lp_status::FEASIBLE);
m_core_solver->solve();
switch (m_core_solver->get_status()) {
case OPTIMAL:
this->m_status = OPTIMAL;
case lp_status::OPTIMAL:
this->m_status = lp_status::OPTIMAL;
break;
case DUAL_UNBOUNDED:
this->m_status = INFEASIBLE;
case lp_status::DUAL_UNBOUNDED:
this->m_status = lp_status::INFEASIBLE;
break;
case TIME_EXHAUSTED:
this->m_status = TIME_EXHAUSTED;
case lp_status::TIME_EXHAUSTED:
this->m_status = lp_status::TIME_EXHAUSTED;
break;
case FLOATING_POINT_ERROR:
this->m_status = FLOATING_POINT_ERROR;
case lp_status::FLOATING_POINT_ERROR:
this->m_status = lp_status::FLOATING_POINT_ERROR;
break;
default:
SASSERT(false);
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);
@ -144,7 +144,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_x_with_zeros(
}
template <typename T, typename X> void lp_dual_simplex<T, X>::stage1() {
SASSERT(m_core_solver == nullptr);
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());
@ -158,7 +158,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::stage1() {
this->m_heading,
this->m_costs,
this->m_column_types_of_core_solver,
this->m_low_bounds,
this->m_lower_bounds,
this->m_upper_bounds,
this->m_settings,
*this);
@ -166,7 +166,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::stage1() {
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
// skipping stage 1
m_core_solver->set_status(OPTIMAL);
m_core_solver->set_status(lp_status::OPTIMAL);
m_core_solver->set_total_iterations(0);
} else {
m_core_solver->solve();
@ -192,7 +192,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_s
}
template <typename T, typename X> column_type lp_dual_simplex<T, X>::get_column_type(unsigned j) {
SASSERT(j < this->m_A->column_count());
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()];
}
@ -201,12 +201,12 @@ template <typename T, typename X> column_type lp_dual_simplex<T, X>::get_column_
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
SASSERT(this->m_core_solver_columns_to_external_columns.find(j) !=
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];
SASSERT(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end());
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: {
@ -216,10 +216,10 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_
throw_exception(s.str());
break;
}
case column_type::low_bound: {
case column_type::lower_bound: {
m_can_enter_basis[j] = true;
this->set_scaled_cost(j);
this->m_low_bounds[j] = numeric_traits<T>::zero();
this->m_lower_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] =numeric_traits<T>::one();
break;
}
@ -227,30 +227,30 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_
m_can_enter_basis[j] = true;
this->set_scaled_cost(j);
this->m_upper_bounds[j] = free_bound;
this->m_low_bounds[j] = -free_bound;
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_low_bounds[j] = numeric_traits<T>::zero(); // is it needed?
this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits<T>::zero(); // is it needed?
break;
default:
SASSERT(false);
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;
SASSERT(get_column_type(j) != column_type::upper_bound);
if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::low_bound))) {
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_low_bounds[j] = numeric_traits<T>::zero();
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_low_bounds[j] = numeric_traits<T>::zero();
this->m_lower_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] = numeric_traits<T>::zero();
}
}
@ -269,7 +269,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_and_bou
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) {
SASSERT(row < this->row_count());
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];
@ -283,7 +283,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_s
break;
case Greater_or_equal:
set_type_for_logical(slack_var, column_type::low_bound);
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
@ -301,7 +301,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_s
break;
case Less_or_equal:
// introduce a non-negative slack variable
set_type_for_logical(slack_var, column_type::low_bound);
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
@ -328,7 +328,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::augment_matrix_A_a
m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials);
this->m_costs.resize(n);
this->m_upper_bounds.resize(n);
this->m_low_bounds.resize(n);
this->m_lower_bounds.resize(n);
m_can_enter_basis.resize(n);
this->m_basis.resize(this->m_A->row_count());
}
@ -351,7 +351,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::find_maximal_solut
this->flip_costs(); // do it for now, todo ( remove the flipping)
this->cleanup();
if (this->m_status == INFEASIBLE) {
if (this->m_status == lp_status::INFEASIBLE) {
return;
}
this->fill_matrix_A_and_init_right_side();
@ -361,7 +361,7 @@ template <typename T, typename X> void lp_dual_simplex<T, X>::find_maximal_solut
fill_first_stage_solver_fields();
copy_m_b_aside_and_set_it_to_zeros();
stage1();
if (this->m_status == FEASIBLE) {
if (this->m_status == lp_status::FEASIBLE) {
stage2();
}
}

View file

@ -23,8 +23,8 @@ Revision History:
#include "util/vector.h"
#include <functional>
#include "util/lp/lar_solver.h"
#include "util/lp/lp_primal_core_solver.hpp"
#include "util/lp/lp_primal_core_solver_tableau.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();

View file

@ -37,10 +37,9 @@ Revision History:
#include "util/lp/breakpoint.h"
#include "util/lp/binary_heap_priority_queue.h"
#include "util/lp/int_set.h"
#include "util/lp/iterator_on_row.h"
namespace lp {
// This core solver solves (Ax=b, low_bound_values \leq x \leq upper_bound_values, maximize costs*x )
// This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x )
// The right side b is given implicitly by x and the basis
template <typename T, typename X>
class lp_primal_core_solver:public lp_core_solver_base<T, X> {
@ -85,7 +84,7 @@ public:
// unsigned len = 100000000;
// for (unsigned j : this->m_inf_set.m_index) {
// int i = this->m_basis_heading[j];
// SASSERT(i >= 0);
// lp_assert(i >= 0);
// unsigned row_len = this->m_A.m_rows[i].size();
// if (row_len < len) {
// choices.clear();
@ -113,52 +112,52 @@ public:
bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const {
// sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos
// we have xbj = -aj * xj
SASSERT(this->m_basis_heading[j] < 0);
SASSERT(this->column_is_feasible(j));
lp_assert(this->m_basis_heading[j] < 0);
lp_assert(this->column_is_feasible(j));
switch (this->m_column_types[j]) {
case column_type::free_column: return true;
case column_type::fixed: return false;
case column_type::low_bound:
case column_type::lower_bound:
if (sign < 0)
return true;
return !this->x_is_at_low_bound(j);
return !this->x_is_at_lower_bound(j);
case column_type::upper_bound:
if (sign > 0)
return true;
return !this->x_is_at_upper_bound(j);
case column_type::boxed:
if (sign < 0)
return !this->x_is_at_low_bound(j);
return !this->x_is_at_lower_bound(j);
return !this->x_is_at_upper_bound(j);
}
SASSERT(false); // cannot be here
lp_assert(false); // cannot be here
return false;
}
bool needs_to_grow(unsigned bj) const {
SASSERT(!this->column_is_feasible(bj));
lp_assert(!this->column_is_feasible(bj));
switch(this->m_column_types[bj]) {
case column_type::free_column:
return false;
case column_type::fixed:
case column_type::low_bound:
case column_type::lower_bound:
case column_type::boxed:
return this-> x_below_low_bound(bj);
default:
return false;
}
SASSERT(false); // unreachable
lp_assert(false); // unreachable
return false;
}
int inf_sign_of_column(unsigned bj) const {
SASSERT(!this->column_is_feasible(bj));
lp_assert(!this->column_is_feasible(bj));
switch(this->m_column_types[bj]) {
case column_type::free_column:
return 0;
case column_type::low_bound:
case column_type::lower_bound:
return 1;
case column_type::fixed:
case column_type::boxed:
@ -166,7 +165,7 @@ public:
default:
return -1;
}
SASSERT(false); // unreachable
lp_assert(false); // unreachable
return 0;
}
@ -174,15 +173,15 @@ public:
bool monoid_can_decrease(const row_cell<T> & rc) const {
unsigned j = rc.m_j;
SASSERT(this->column_is_feasible(j));
lp_assert(this->column_is_feasible(j));
switch (this->m_column_types[j]) {
case column_type::free_column:
return true;
case column_type::fixed:
return false;
case column_type::low_bound:
case column_type::lower_bound:
if (is_pos(rc.get_val())) {
return this->x_above_low_bound(j);
return this->x_above_lower_bound(j);
}
return true;
@ -194,28 +193,28 @@ public:
return this->x_below_upper_bound(j);
case column_type::boxed:
if (is_pos(rc.get_val())) {
return this->x_above_low_bound(j);
return this->x_above_lower_bound(j);
}
return this->x_below_upper_bound(j);
default:
return false;
}
SASSERT(false); // unreachable
lp_assert(false); // unreachable
return false;
}
bool monoid_can_increase(const row_cell<T> & rc) const {
unsigned j = rc.m_j;
SASSERT(this->column_is_feasible(j));
lp_assert(this->column_is_feasible(j));
switch (this->m_column_types[j]) {
case column_type::free_column:
return true;
case column_type::fixed:
return false;
case column_type::low_bound:
case column_type::lower_bound:
if (is_neg(rc.get_val())) {
return this->x_above_low_bound(j);
return this->x_above_lower_bound(j);
}
return true;
@ -227,14 +226,14 @@ public:
return this->x_below_upper_bound(j);
case column_type::boxed:
if (is_neg(rc.get_val())) {
return this->x_above_low_bound(j);
return this->x_above_lower_bound(j);
}
return this->x_below_upper_bound(j);
default:
return false;
}
SASSERT(false); // unreachable
lp_assert(false); // unreachable
return false;
}
@ -344,24 +343,24 @@ public:
}
void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
SASSERT(m < 0 && this->m_column_types[j] == column_type::upper_bound);
lp_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound);
limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited);
}
void limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
SASSERT(m < 0 && this->m_column_types[j] == column_type::low_bound);
limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited);
void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
lp_assert(m < 0 && this->m_column_types[j] == column_type::lower_bound);
limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited);
}
void limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
SASSERT(m > 0 && this->m_column_types[j] == column_type::low_bound);
limit_inf_on_low_bound_m_pos(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited);
void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound);
limit_inf_on_lower_bound_m_pos(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited);
}
void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) {
SASSERT(m > 0 && this->m_column_types[j] == column_type::upper_bound);
lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound);
limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited);
};
@ -370,7 +369,7 @@ public:
void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector<unsigned> & leavings, T m, X & t, T & abs_of_d_of_leaving);
vector<T> m_low_bounds_dummy; // needed for the base class only
vector<T> m_lower_bounds_dummy; // needed for the base class only
X get_max_bound(vector<X> & b);
@ -403,7 +402,7 @@ public:
bool need_to_switch_costs() const {
if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
return false;
// SASSERT(calc_current_x_is_feasible() == current_x_is_feasible());
// lp_assert(calc_current_x_is_feasible() == current_x_is_feasible());
return this->current_x_is_feasible() == this->m_using_infeas_costs;
}
@ -420,7 +419,7 @@ public:
// returns the number of iterations
unsigned solve();
lu<T, X> * factorization() {return this->m_factorization;}
lu<static_matrix<T, X>> * factorization() {return this->m_factorization;}
void delete_factorization();
@ -445,7 +444,7 @@ public:
void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, const X &theta ) {
this->update_basis_and_x_tableau(entering, leaving, theta);
this->update_column_in_inf_set(entering);
this->track_column_feasibility(entering);
}
@ -458,23 +457,23 @@ public:
if (j == -1)
return -1;
SASSERT(!this->column_is_feasible(j));
lp_assert(!this->column_is_feasible(j));
switch (this->m_column_types[j]) {
case column_type::fixed:
case column_type::upper_bound:
new_val_for_leaving = this->m_upper_bounds[j];
break;
case column_type::low_bound:
new_val_for_leaving = this->m_low_bounds[j];
case column_type::lower_bound:
new_val_for_leaving = this->m_lower_bounds[j];
break;
case column_type::boxed:
if (this->x_above_upper_bound(j))
new_val_for_leaving = this->m_upper_bounds[j];
else
new_val_for_leaving = this->m_low_bounds[j];
new_val_for_leaving = this->m_lower_bounds[j];
break;
default:
SASSERT(false);
lp_assert(false);
new_val_for_leaving = numeric_traits<T>::zero(); // does not matter
}
return j;
@ -484,7 +483,7 @@ public:
X new_val_for_leaving;
int leaving = find_leaving_tableau_rows(new_val_for_leaving);
if (leaving == -1) {
this->set_status(OPTIMAL);
this->set_status(lp_status::OPTIMAL);
return;
}
@ -500,14 +499,14 @@ public:
T a_ent;
int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent);
if (entering == -1) {
this->set_status(INFEASIBLE);
this->set_status(lp_status::INFEASIBLE);
return;
}
X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent;
advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta );
SASSERT(this->m_x[leaving] == new_val_for_leaving);
lp_assert(this->m_x[leaving] == new_val_for_leaving);
if (this->current_x_is_feasible())
this->set_status(OPTIMAL);
this->set_status(lp_status::OPTIMAL);
}
void fill_breakpoints_array(unsigned entering);
@ -522,30 +521,30 @@ public:
void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta);
void decide_on_status_when_cannot_find_entering() {
SASSERT(!need_to_switch_costs());
this->set_status(this->current_x_is_feasible()? OPTIMAL: INFEASIBLE);
lp_assert(!need_to_switch_costs());
this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE);
}
// void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) {
// SASSERT(m < 0);
// SASSERT(this->m_column_type[j] == low_bound || this->m_column_type[j] == boxed);
// const X & eps = harris_eps_for_bound(this->m_low_bounds[j]);
// if (this->above_bound(this->m_x[j], this->m_low_bounds[j])) {
// theta = std::min((this->m_low_bounds[j] -this->m_x[j] - eps) / m, theta);
// lp_assert(m < 0);
// lp_assert(this->m_column_type[j] == lower_bound || this->m_column_type[j] == boxed);
// const X & eps = harris_eps_for_bound(this->m_lower_bounds[j]);
// if (this->above_bound(this->m_x[j], this->m_lower_bounds[j])) {
// theta = std::min((this->m_lower_bounds[j] -this->m_x[j] - eps) / m, theta);
// if (theta < zero_of_type<X>()) theta = zero_of_type<X>();
// }
// }
void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) {
SASSERT(m < 0);
const X& eps = harris_eps_for_bound(this->m_low_bounds[j]);
limit_theta((this->m_low_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited);
lp_assert(m < 0);
const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]);
limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited);
if (theta < zero_of_type<X>()) theta = zero_of_type<X>();
}
bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
// x gets smaller
SASSERT(m < 0);
lp_assert(m < 0);
if (numeric_traits<T>::precise()) {
if (this->below_bound(x, bound)) return false;
if (this->above_bound(x, bound)) {
@ -569,7 +568,7 @@ public:
bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
// x gets larger
SASSERT(m > 0);
lp_assert(m > 0);
if (numeric_traits<T>::precise()) {
if (this->above_bound(x, bound)) return false;
if (this->below_bound(x, bound)) {
@ -591,17 +590,17 @@ public:
return true;
}
void limit_inf_on_low_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
void limit_inf_on_lower_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
if (numeric_traits<T>::precise()) {
// x gets larger
SASSERT(m > 0);
lp_assert(m > 0);
if (this->below_bound(x, bound)) {
limit_theta((bound - x) / m, theta, unlimited);
}
}
else {
// x gets larger
SASSERT(m > 0);
lp_assert(m > 0);
const X& eps = harris_eps_for_bound(bound);
if (this->below_bound(x, bound)) {
limit_theta((bound - x + eps) / m, theta, unlimited);
@ -611,7 +610,7 @@ public:
void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) {
// x gets smaller
SASSERT(m < 0);
lp_assert(m < 0);
const X& eps = harris_eps_for_bound(bound);
if (this->above_bound(x, bound)) {
limit_theta((bound - x - eps) / m, theta, unlimited);
@ -619,9 +618,9 @@ public:
}
void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) {
// SASSERT(m > 0 && this->m_column_type[j] == column_type::boxed);
// lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed);
const X & x = this->m_x[j];
const X & lbound = this->m_low_bounds[j];
const X & lbound = this->m_lower_bounds[j];
if (this->below_bound(x, lbound)) {
const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]);
@ -639,14 +638,14 @@ public:
}
void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) {
// SASSERT(m < 0 && this->m_column_type[j] == column_type::boxed);
// lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed);
const X & x = this->m_x[j];
const X & ubound = this->m_upper_bounds[j];
if (this->above_bound(x, ubound)) {
const X& eps = harris_eps_for_bound(ubound);
limit_theta((ubound - x - eps) / m, theta, unlimited);
} else {
const X & lbound = this->m_low_bounds[j];
const X & lbound = this->m_lower_bounds[j];
if (this->above_bound(x, lbound)){
const X& eps = harris_eps_for_bound(lbound);
limit_theta((lbound - x - eps) / m, theta, unlimited);
@ -657,7 +656,7 @@ public:
}
}
void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) {
SASSERT(m > 0);
lp_assert(m > 0);
const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]);
if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) {
limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited);
@ -669,7 +668,7 @@ public:
}
void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) {
SASSERT(m > 0);
lp_assert(m > 0);
const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]);
limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited);
if (theta < zero_of_type<X>()) {
@ -679,7 +678,7 @@ public:
// j is a basic column or the entering, in any case x[j] has to stay feasible.
// m is the multiplier. updating t in a way that holds the following
// x[j] + t * m >= this->m_low_bounds[j]- harris_feasibility_tolerance ( if m < 0 )
// x[j] + t * m >= this->m_lower_bounds[j]- harris_feasibility_tolerance ( if m < 0 )
// or
// x[j] + t * m <= this->m_upper_bounds[j] + harris_feasibility_tolerance ( if m > 0)
void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) {
@ -696,15 +695,15 @@ public:
limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(j, m, theta, unlimited);
}
break;
case column_type::low_bound:
case column_type::lower_bound:
if (this->current_x_is_feasible()) {
if (m < 0)
limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited);
} else {
if (m < 0)
limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(j, m, theta, unlimited);
limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(j, m, theta, unlimited);
else
limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(j, m, theta, unlimited);
limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(j, m, theta, unlimited);
}
break;
// case fixed:
@ -735,7 +734,7 @@ public:
break;
default:
SASSERT(false);
lp_unreachable();
}
if (!unlimited && theta < zero_of_type<X>()) {
theta = zero_of_type<X>();
@ -770,7 +769,7 @@ public:
void init_reduced_costs();
bool low_bounds_are_set() const override { return true; }
bool lower_bounds_are_set() const override { return true; }
int advance_on_sorted_breakpoints(unsigned entering, X & t);
@ -794,7 +793,7 @@ public:
if (this->m_basis_heading[j] < 0)
continue;
if (!this->column_is_feasible(j))
this->m_inf_set.insert(j);
this->insert_column_into_inf_set(j);
}
}
@ -807,7 +806,7 @@ public:
if (this->x_above_upper_bound(j))
return 1;
break;
case column_type::low_bound:
case column_type::lower_bound:
if (this->x_below_low_bound(j))
return -1;
break;
@ -818,7 +817,7 @@ public:
case column_type::free_column:
return 0;
default:
SASSERT(false);
lp_assert(false);
}
return 0;
}
@ -842,18 +841,18 @@ public:
case column_type::fixed:
return 0;
case column_type::boxed:
if (this->x_is_at_low_bound(j))
if (this->x_is_at_lower_bound(j))
return 1;
return -1;
break;
case column_type::low_bound:
case column_type::lower_bound:
return 1;
break;
case column_type::upper_bound:
return -1;
break;
default:
SASSERT(false);
lp_assert(false);
}
return 0;
@ -879,7 +878,7 @@ public:
// the delta is between the old and the new cost (old - new)
void update_reduced_cost_for_basic_column_cost_change(const T & delta, unsigned j) {
SASSERT(this->m_basis_heading[j] >= 0);
lp_assert(this->m_basis_heading[j] >= 0);
unsigned i = static_cast<unsigned>(this->m_basis_heading[j]);
for (const row_cell<T> & rc : this->m_A.m_rows[i]) {
unsigned k = rc.m_j;
@ -906,7 +905,7 @@ public:
vector<int> & heading,
vector<T> & costs,
const vector<column_type> & column_type_array,
const vector<X> & low_bound_values,
const vector<X> & lower_bound_values,
const vector<X> & upper_bound_values,
lp_settings & settings,
const column_namer& column_names):
@ -919,7 +918,7 @@ public:
settings,
column_names,
column_type_array,
low_bound_values,
lower_bound_values,
upper_bound_values),
m_beta(A.row_count()),
m_epsilon_of_reduced_cost(T(1)/T(10000000)),
@ -930,7 +929,7 @@ public:
} else {
m_converted_harris_eps = zero_of_type<T>();
}
this->set_status(UNKNOWN);
this->set_status(lp_status::UNKNOWN);
}
// constructor
@ -954,12 +953,12 @@ public:
settings,
column_names,
column_type_array,
m_low_bounds_dummy,
m_lower_bounds_dummy,
upper_bound_values),
m_beta(A.row_count()),
m_converted_harris_eps(convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance)) {
SASSERT(initial_x_is_correct());
m_low_bounds_dummy.resize(A.column_count(), zero_of_type<T>());
lp_assert(initial_x_is_correct());
m_lower_bounds_dummy.resize(A.column_count(), zero_of_type<T>());
m_enter_price_eps = numeric_traits<T>::precise() ? numeric_traits<T>::zero() : T(1e-5);
#ifdef Z3DEBUG
// check_correctness();
@ -972,7 +971,7 @@ public:
basis_set.insert(this->m_basis[i]);
}
for (unsigned j = 0; j < this->m_n(); j++) {
if (this->column_has_low_bound(j) && this->m_x[j] < numeric_traits<T>::zero()) {
if (this->column_has_lower_bound(j) && this->m_x[j] < numeric_traits<T>::zero()) {
LP_OUT(this->m_settings, "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl);
return false;
}
@ -983,7 +982,7 @@ public:
}
if (basis_set.find(j) != basis_set.end()) continue;
if (this->m_column_types[j] == column_type::low_bound) {
if (this->m_column_types[j] == column_type::lower_bound) {
if (numeric_traits<T>::zero() != this->m_x[j]) {
LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits<T>::zero() << " is not equal to " << this->m_x[j] << std::endl);
return false;

View file

@ -25,12 +25,12 @@ Revision History:
#include <string>
#include "util/lp/lp_primal_core_solver.h"
namespace lp {
// This core solver solves (Ax=b, low_bound_values \leq x \leq upper_bound_values, maximize costs*x )
// This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x )
// The right side b is given implicitly by x and the basis
template <typename T, typename X>
void lp_primal_core_solver<T, X>::sort_non_basis_rational() {
SASSERT(numeric_traits<T>::precise());
lp_assert(numeric_traits<T>::precise());
if (this->m_settings.use_tableau()) {
std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) {
unsigned ca = this->m_A.number_of_non_zeroes_in_column(a);
@ -84,12 +84,12 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_on_breakpoin
bool ret;
const T & d = this->m_d[j];
switch (this->m_column_types[j]) {
case column_type::low_bound:
SASSERT(this->x_is_at_low_bound(j));
case column_type::lower_bound:
lp_assert(this->x_is_at_lower_bound(j));
ret = d < -m_epsilon_of_reduced_cost;
break;
case column_type::upper_bound:
SASSERT(this->x_is_at_upper_bound(j));
lp_assert(this->x_is_at_upper_bound(j));
ret = d > m_epsilon_of_reduced_cost;
break;
case column_type::fixed:
@ -97,16 +97,16 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_on_breakpoin
break;
case column_type::boxed:
{
bool low_bound = this->x_is_at_low_bound(j);
SASSERT(low_bound || this->x_is_at_upper_bound(j));
ret = (low_bound && d < -m_epsilon_of_reduced_cost) || ((!low_bound) && d > m_epsilon_of_reduced_cost);
bool lower_bound = this->x_is_at_lower_bound(j);
lp_assert(lower_bound || this->x_is_at_upper_bound(j));
ret = (lower_bound && d < -m_epsilon_of_reduced_cost) || ((!lower_bound) && d > m_epsilon_of_reduced_cost);
}
break;
case column_type::free_column:
ret = d > m_epsilon_of_reduced_cost || d < - m_epsilon_of_reduced_cost;
break;
default:
SASSERT(false);
lp_unreachable();
ret = false;
break;
}
@ -125,7 +125,7 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis(unsign
if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost)
return true;
break;
case column_type::low_bound:
case column_type::lower_bound:
if (dj > m_epsilon_of_reduced_cost) return true;;
break;
case column_type::upper_bound:
@ -137,19 +137,19 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis(unsign
return true;
break;
} else if (dj < - m_epsilon_of_reduced_cost) {
if (this->m_x[j] > this->m_low_bounds[j] + this->bound_span(j)/2)
if (this->m_x[j] > this->m_lower_bounds[j] + this->bound_span(j)/2)
return true;
}
break;
default:
SASSERT(false);
lp_unreachable();
break;
}
return false;
}
template <typename T, typename X>
bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis_precise(unsigned j) const {
SASSERT (numeric_traits<T>::precise());
lp_assert (numeric_traits<T>::precise());
if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search)
return column_is_benefitial_for_entering_on_breakpoints(j);
const T& dj = this->m_d[j];
@ -159,9 +159,9 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis_precis
if (!is_zero(dj))
return true;
break;
case column_type::low_bound:
case column_type::lower_bound:
if (dj > zero_of_type<T>()) return true;
if (dj < 0 && this->m_x[j] > this->m_low_bounds[j]){
if (dj < 0 && this->m_x[j] > this->m_lower_bounds[j]){
return true;
}
break;
@ -177,12 +177,12 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis_precis
return true;
break;
} else if (dj < zero_of_type<T>()) {
if (this->m_x[j] > this->m_low_bounds[j])
if (this->m_x[j] > this->m_lower_bounds[j])
return true;
}
break;
default:
SASSERT(false);
lp_unreachable();
break;
}
return false;
@ -190,7 +190,7 @@ bool lp_primal_core_solver<T, X>::column_is_benefitial_for_entering_basis_precis
template <typename T, typename X>
int lp_primal_core_solver<T, X>::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1)
SASSERT(numeric_traits<T>::precise());
lp_assert(numeric_traits<T>::precise());
if (number_of_benefitial_columns_to_go_over == 0)
return -1;
if (this->m_basis_sort_counter == 0) {
@ -274,7 +274,7 @@ int lp_primal_core_solver<T, X>::choose_entering_column(unsigned number_of_benef
template <typename T, typename X> int lp_primal_core_solver<T, X>::advance_on_sorted_breakpoints(unsigned entering, X &t) {
T slope_at_entering = this->m_d[entering];
breakpoint<X> * last_bp = nullptr;
SASSERT(m_breakpoint_indices_queue.is_empty()==false);
lp_assert(m_breakpoint_indices_queue.is_empty()==false);
while (m_breakpoint_indices_queue.is_empty() == false) {
unsigned bi = m_breakpoint_indices_queue.dequeue();
breakpoint<X> *b = &m_breakpoints[bi];
@ -289,7 +289,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::advance_on_so
}
}
}
SASSERT (last_bp != nullptr);
lp_assert (last_bp != nullptr);
t = last_bp->m_delta;
return last_bp->m_j;
}
@ -297,13 +297,13 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::advance_on_so
template <typename T, typename X> int
lp_primal_core_solver<T, X>::find_leaving_and_t_with_breakpoints(unsigned entering, X & t){
SASSERT(this->precise() == false);
lp_assert(this->precise() == false);
fill_breakpoints_array(entering);
return advance_on_sorted_breakpoints(entering, t);
}
template <typename T, typename X> bool lp_primal_core_solver<T, X>::get_harris_theta(X & theta) {
SASSERT(this->m_ed.is_OK());
lp_assert(this->m_ed.is_OK());
bool unlimited = true;
for (unsigned i : this->m_ed.m_index) {
if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue;
@ -360,13 +360,13 @@ template <typename T, typename X> bool lp_primal_core_solver<T, X>::try_jump_to_
if (m_sign_of_entering_delta > 0) {
t = this->m_upper_bounds[entering] - this->m_x[entering];
if (unlimited || t <= theta){
SASSERT(t >= zero_of_type<X>());
lp_assert(t >= zero_of_type<X>());
return true;
}
} else { // m_sign_of_entering_delta == -1
t = this->m_x[entering] - this->m_low_bounds[entering];
t = this->m_x[entering] - this->m_lower_bounds[entering];
if (unlimited || t <= theta) {
SASSERT(t >= zero_of_type<X>());
lp_assert(t >= zero_of_type<X>());
return true;
}
}
@ -375,16 +375,16 @@ template <typename T, typename X> bool lp_primal_core_solver<T, X>::try_jump_to_
if (m_sign_of_entering_delta > 0) {
t = this->m_upper_bounds[entering] - this->m_x[entering];
if (unlimited || t <= theta){
SASSERT(t >= zero_of_type<X>());
lp_assert(t >= zero_of_type<X>());
return true;
}
}
return false;
case column_type::low_bound:
case column_type::lower_bound:
if (m_sign_of_entering_delta < 0) {
t = this->m_x[entering] - this->m_low_bounds[entering];
t = this->m_x[entering] - this->m_lower_bounds[entering];
if (unlimited || t <= theta) {
SASSERT(t >= zero_of_type<X>());
lp_assert(t >= zero_of_type<X>());
return true;
}
}
@ -404,7 +404,7 @@ try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t ) {
return true;
}
// m_sign_of_entering_delta == -1
t = this->m_x[entering] - this->m_low_bounds[entering];
t = this->m_x[entering] - this->m_lower_bounds[entering];
return true;
}
@ -420,7 +420,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
do {
unsigned i = this->m_ed.m_index[k];
const T & ed = this->m_ed[i];
SASSERT(!numeric_traits<T>::is_zero(ed));
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) {
@ -439,7 +439,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
while (k != initial_k) {
unsigned i = this->m_ed.m_index[k];
const T & ed = this->m_ed[i];
SASSERT(!numeric_traits<T>::is_zero(ed));
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);
@ -479,7 +479,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leavi
return find_leaving_and_t_with_breakpoints(entering, t);
X theta;
bool unlimited = get_harris_theta(theta);
SASSERT(unlimited || theta >= zero_of_type<X>());
lp_assert(unlimited || theta >= zero_of_type<X>());
if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering;
if (unlimited)
return -1;
@ -489,7 +489,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leavi
// m is the multiplier. updating t in a way that holds the following
// x[j] + t * m >= m_low_bounds[j] ( if m < 0 )
// x[j] + t * m >= m_lower_bounds[j] ( if m < 0 )
// or
// x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0)
template <typename T, typename X> void
@ -501,7 +501,7 @@ lp_primal_core_solver<T, X>::get_bound_on_variable_and_update_leaving_precisely(
return;
default:break;
}
X tt = - (this->m_low_bounds[j] - this->m_x[j]) / m;
X tt = - (this->m_lower_bounds[j] - this->m_x[j]) / m;
if (numeric_traits<X>::is_neg(tt))
tt = zero_of_type<X>();
if (leavings.size() == 0 || tt < t || (tt == t && m > abs_of_d_of_leaving)) {
@ -516,7 +516,7 @@ lp_primal_core_solver<T, X>::get_bound_on_variable_and_update_leaving_precisely(
} else if (m < 0){
switch (this->m_column_types[j]) { // check that j has an upper bound
case column_type::free_column:
case column_type::low_bound:
case column_type::lower_bound:
return;
default:break;
}
@ -548,7 +548,7 @@ template <typename T, typename X> X lp_primal_core_solver<T, X>::get_max_boun
template <typename T, typename X> void lp_primal_core_solver<T, X>::check_Ax_equal_b() {
dense_matrix<T, X> d(this->m_A);
T * ls = d.apply_from_left_with_different_dims(this->m_x);
SASSERT(vectors_are_equal<T>(ls, this->m_b, this->m_m()));
lp_assert(vectors_are_equal<T>(ls, this->m_b, this->m_m()));
delete [] ls;
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::check_the_bounds() {
@ -558,8 +558,8 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::check_the
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::check_bound(unsigned i) {
SASSERT (!(this->column_has_low_bound(i) && (numeric_traits<T>::zero() > this->m_x[i])));
SASSERT (!(this->column_has_upper_bound(i) && (this->m_upper_bounds[i] < this->m_x[i])));
lp_assert (!(this->column_has_lower_bound(i) && (numeric_traits<T>::zero() > this->m_x[i])));
lp_assert (!(this->column_has_upper_bound(i) && (this->m_upper_bounds[i] < this->m_x[i])));
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::check_correctness() {
@ -575,8 +575,8 @@ void lp_primal_core_solver<T, X>::update_reduced_costs_from_pivot_row(unsigned e
// the basis heading has changed already
#ifdef Z3DEBUG
auto & basis_heading = this->m_basis_heading;
SASSERT(basis_heading[entering] >= 0 && static_cast<unsigned>(basis_heading[entering]) < this->m_m());
SASSERT(basis_heading[leaving] < 0);
lp_assert(basis_heading[entering] >= 0 && static_cast<unsigned>(basis_heading[entering]) < this->m_m());
lp_assert(basis_heading[leaving] < 0);
#endif
T pivot = this->m_pivot_row[entering];
T dq = this->m_d[entering]/pivot;
@ -599,7 +599,7 @@ void lp_primal_core_solver<T, X>::update_reduced_costs_from_pivot_row(unsigned e
template <typename T, typename X> int lp_primal_core_solver<T, X>::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) {
if (numeric_traits<T>::precise()) return 0;
T reduced_at_entering_was = this->m_d[entering]; // can benefit from going over non-zeros of m_ed
SASSERT(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost);
lp_assert(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost);
T refreshed_cost = this->m_costs[entering];
unsigned i = this->m_m();
while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i];
@ -634,7 +634,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::backup_an
m_costs_backup = this->m_costs;
} else {
T cost_max = std::max(max_abs_in_vector(this->m_costs), T(1));
SASSERT(m_costs_backup.size() == 0);
lp_assert(m_costs_backup.size() == 0);
for (unsigned j = 0; j < this->m_costs.size(); j++)
m_costs_backup.push_back(this->m_costs[j] /= cost_max);
}
@ -664,16 +664,16 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run(
template <typename T, typename X> void lp_primal_core_solver<T, X>::calc_working_vector_beta_for_column_norms(){
SASSERT(numeric_traits<T>::precise() == false);
SASSERT(this->m_ed.is_OK());
SASSERT(m_beta.is_OK());
lp_assert(numeric_traits<T>::precise() == false);
lp_assert(this->m_ed.is_OK());
lp_assert(m_beta.is_OK());
m_beta = this->m_ed;
this->m_factorization->solve_yB_with_error_check_indexed(m_beta, this->m_basis_heading, this->m_basis, this->m_settings);
}
template <typename T, typename X>
void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving(int entering, X & t) {
SASSERT(!this->A_mult_x_is_off() );
CASSERT("A_off", !this->A_mult_x_is_off() );
this->update_x(entering, t * m_sign_of_entering_delta);
if (this->A_mult_x_is_off_on_index(this->m_ed.m_index) && !this->find_x_by_solving()) {
this->init_lu();
@ -685,7 +685,7 @@ void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving(int entering
}
}
if (this->m_using_infeas_costs) {
SASSERT(is_zero(this->m_costs[entering]));
lp_assert(is_zero(this->m_costs[entering]));
init_infeasibility_costs_for_changed_basis_only();
}
if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
@ -698,10 +698,10 @@ void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving(int entering
}
template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_entering_and_leaving(int entering, int leaving, X & t) {
SASSERT(entering >= 0 && m_non_basis_list.back() == static_cast<unsigned>(entering));
SASSERT(this->m_using_infeas_costs || t >= zero_of_type<X>());
SASSERT(leaving >= 0 && entering >= 0);
SASSERT(entering != leaving || !is_zero(t)); // otherwise nothing changes
lp_assert(entering >= 0 && m_non_basis_list.back() == static_cast<unsigned>(entering));
lp_assert(this->m_using_infeas_costs || t >= zero_of_type<X>());
lp_assert(leaving >= 0 && entering >= 0);
lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes
if (entering == leaving) {
advance_on_entering_equal_leaving(entering, t);
return;
@ -713,14 +713,14 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
int pivot_compare_result = this->pivots_in_column_and_row_are_different(entering, leaving);
if (!pivot_compare_result){;}
else if (pivot_compare_result == 2) { // the sign is changed, cannot continue
this->set_status(UNSTABLE);
this->set_status(lp_status::UNSTABLE);
this->iters_with_no_cost_growing()++;
return;
} else {
SASSERT(pivot_compare_result == 1);
lp_assert(pivot_compare_result == 1);
this->init_lu();
if (this->m_factorization == nullptr || this->m_factorization->get_status() != LU_status::OK) {
this->set_status(UNSTABLE);
this->set_status(lp_status::UNSTABLE);
this->iters_with_no_cost_growing()++;
return;
}
@ -732,10 +732,10 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
t = -t;
}
if (!this->update_basis_and_x(entering, leaving, t)) {
if (this->get_status() == FLOATING_POINT_ERROR)
if (this->get_status() == lp_status::FLOATING_POINT_ERROR)
return;
if (this->m_look_for_feasible_solution_only) {
this->set_status(FLOATING_POINT_ERROR);
this->set_status(lp_status::FLOATING_POINT_ERROR);
return;
}
init_reduced_costs();
@ -748,7 +748,7 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
}
if (this->current_x_is_feasible()) {
this->set_status(FEASIBLE);
this->set_status(lp_status::FEASIBLE);
if (this->m_look_for_feasible_solution_only)
return;
}
@ -761,7 +761,7 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
} else {
update_reduced_costs_from_pivot_row(entering, leaving);
}
SASSERT(!need_to_switch_costs());
lp_assert(!need_to_switch_costs());
std::list<unsigned>::iterator it = m_non_basis_list.end();
it--;
* it = static_cast<unsigned>(leaving);
@ -769,13 +769,13 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_entering_precise(int entering) {
SASSERT(numeric_traits<T>::precise());
SASSERT(entering > -1);
lp_assert(numeric_traits<T>::precise());
lp_assert(entering > -1);
this->solve_Bd(entering);
X t;
int leaving = find_leaving_and_t_precise(entering, t);
if (leaving == -1) {
this->set_status(UNBOUNDED);
this->set_status(lp_status::UNBOUNDED);
return;
}
advance_on_entering_and_leaving(entering, leaving, t);
@ -786,12 +786,12 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_e
advance_on_entering_precise(entering);
return;
}
SASSERT(entering > -1);
lp_assert(entering > -1);
this->solve_Bd(entering);
int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering);
if (refresh_result) {
if (this->m_look_for_feasible_solution_only) {
this->set_status(FLOATING_POINT_ERROR);
this->set_status(lp_status::FLOATING_POINT_ERROR);
return;
}
@ -806,7 +806,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_e
int leaving = find_leaving_and_t(entering, t);
if (leaving == -1){
if (!this->current_x_is_feasible()) {
SASSERT(!numeric_traits<T>::precise()); // we cannot have unbounded with inf costs
lp_assert(!numeric_traits<T>::precise()); // we cannot have unbounded with inf costs
// if (m_look_for_feasible_solution_only) {
// this->m_status = INFEASIBLE;
@ -814,19 +814,19 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_e
// }
if (this->get_status() == UNSTABLE) {
this->set_status(FLOATING_POINT_ERROR);
if (this->get_status() == lp_status::UNSTABLE) {
this->set_status(lp_status::FLOATING_POINT_ERROR);
return;
}
init_infeasibility_costs();
this->set_status(UNSTABLE);
this->set_status(lp_status::UNSTABLE);
return;
}
if (this->get_status() == TENTATIVE_UNBOUNDED) {
this->set_status(UNBOUNDED);
if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) {
this->set_status(lp_status::UNBOUNDED);
} else {
this->set_status(TENTATIVE_UNBOUNDED);
this->set_status(lp_status::TENTATIVE_UNBOUNDED);
}
return;
}
@ -840,7 +840,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::push_forw
template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::get_number_of_non_basic_column_to_try_for_enter() {
unsigned ret = static_cast<unsigned>(this->m_nbasis.size());
if (this->get_status() == TENTATIVE_UNBOUNDED)
if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED)
return ret; // we really need to find entering with a large reduced cost
if (ret > 300) {
ret = (unsigned)(ret * this->m_settings.percent_of_entering_to_check / 100);
@ -864,15 +864,15 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_column
template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve() {
if (numeric_traits<T>::precise() && this->m_settings.use_tableau())
return solve_with_tableau();
init_run();
if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) {
this->set_status(FEASIBLE);
this->set_status(lp_status::FEASIBLE);
return 0;
}
if ((!numeric_traits<T>::precise()) && this->A_mult_x_is_off()) {
this->set_status(FLOATING_POINT_ERROR);
this->set_status(lp_status::FLOATING_POINT_ERROR);
return 0;
}
do {
@ -880,10 +880,11 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
return this->total_iterations();
}
one_iteration();
SASSERT(!this->m_using_infeas_costs || this->costs_on_nbasis_are_zeros());
lp_assert(!this->m_using_infeas_costs || this->costs_on_nbasis_are_zeros());
switch (this->get_status()) {
case OPTIMAL: // double check that we are at optimum
case INFEASIBLE:
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()) {
@ -892,7 +893,7 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status (FLOATING_POINT_ERROR);
this->set_status (lp_status::FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
@ -900,7 +901,7 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
decide_on_status_when_cannot_find_entering();
break;
}
this->set_status(UNKNOWN);
this->set_status(lp_status::UNKNOWN);
} else { // precise case
if (this->m_look_for_feasible_solution_only) { // todo: keep the reduced costs correct all the time!
init_reduced_costs();
@ -908,31 +909,31 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
decide_on_status_when_cannot_find_entering();
break;
}
this->set_status(UNKNOWN);
this->set_status(lp_status::UNKNOWN);
}
}
break;
case TENTATIVE_UNBOUNDED:
case lp_status::TENTATIVE_UNBOUNDED:
this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR);
this->set_status(lp_status::FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
break;
case UNBOUNDED:
case lp_status::UNBOUNDED:
if (this->current_x_is_infeasible()) {
init_reduced_costs();
this->set_status(UNKNOWN);
this->set_status(lp_status::UNKNOWN);
}
break;
case UNSTABLE:
SASSERT(! (numeric_traits<T>::precise()));
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(FLOATING_POINT_ERROR);
this->set_status(lp_status::FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
@ -941,13 +942,13 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
default:
break; // do nothing
}
} while (this->get_status() != FLOATING_POINT_ERROR
} while (this->get_status() != lp_status::FLOATING_POINT_ERROR
&&
this->get_status() != UNBOUNDED
this->get_status() != lp_status::UNBOUNDED
&&
this->get_status() != OPTIMAL
this->get_status() != lp_status::OPTIMAL
&&
this->get_status() != INFEASIBLE
this->get_status() != lp_status::INFEASIBLE
&&
this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements
&&
@ -955,7 +956,7 @@ template <typename T, typename X> unsigned lp_primal_core_solver<T, X>::solve()
&&
!(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only));
SASSERT(this->get_status() == FLOATING_POINT_ERROR
lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR
||
this->current_x_is_feasible() == false
||
@ -972,7 +973,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::delete_fa
// according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming"
template <typename T, typename X> void lp_primal_core_solver<T, X>::init_column_norms() {
SASSERT(numeric_traits<T>::precise() == false);
lp_assert(numeric_traits<T>::precise() == false);
for (unsigned j = 0; j < this->m_n(); j++) {
this->m_column_norms[j] = T(static_cast<int>(this->m_A.m_columns[j].size() + 1))
@ -982,7 +983,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::init_column_
// debug only
template <typename T, typename X> T lp_primal_core_solver<T, X>::calculate_column_norm_exactly(unsigned j) {
SASSERT(numeric_traits<T>::precise() == false);
lp_assert(numeric_traits<T>::precise() == false);
indexed_vector<T> w(this->m_m());
this->m_A.copy_column_to_vector(j, w);
vector<T> d(this->m_m());
@ -994,8 +995,8 @@ template <typename T, typename X> T lp_primal_core_solver<T, X>::calculate_colum
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::update_or_init_column_norms(unsigned entering, unsigned leaving) {
SASSERT(numeric_traits<T>::precise() == false);
SASSERT(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency);
lp_assert(numeric_traits<T>::precise() == false);
lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency);
if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) {
m_column_norm_update_counter = 0;
init_column_norms();
@ -1007,7 +1008,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::update_or
// following Swietanowski - A new steepest ...
template <typename T, typename X> void lp_primal_core_solver<T, X>::update_column_norms(unsigned entering, unsigned leaving) {
SASSERT(numeric_traits<T>::precise() == false);
lp_assert(numeric_traits<T>::precise() == false);
T pivot = this->m_pivot_row[entering];
T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot;
if (!numeric_traits<T>::precise()) {
@ -1042,8 +1043,8 @@ template <typename T, typename X> T lp_primal_core_solver<T, X>::calculate_no
// calling it stage1 is too cryptic
template <typename T, typename X> void lp_primal_core_solver<T, X>::find_feasible_solution() {
this->m_look_for_feasible_solution_only = true;
SASSERT(this->non_basic_columns_are_set_correctly());
this->set_status(UNKNOWN);
lp_assert(this->non_basic_columns_are_set_correctly());
this->set_status(lp_status::UNKNOWN);
solve();
}
@ -1087,15 +1088,15 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::fill_breakpo
template <typename T, typename X> bool lp_primal_core_solver<T, X>::done() {
if (this->get_status() == OPTIMAL || this->get_status() == FLOATING_POINT_ERROR) return true;
if (this->get_status() == INFEASIBLE) {
if (this->get_status() == lp_status::OPTIMAL || this->get_status() == lp_status::FLOATING_POINT_ERROR) return true;
if (this->get_status() == lp_status::INFEASIBLE) {
return true;
}
if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) {
this->get_status() = ITERATIONS_EXHAUSTED; return true;
this->get_status() = lp_status::ITERATIONS_EXHAUSTED; return true;
}
if (this->total_iterations() >= this->m_settings.max_total_number_of_iterations) {
this->get_status() = ITERATIONS_EXHAUSTED; return true;
this->get_status() = lp_status::ITERATIONS_EXHAUSTED; return true;
}
return false;
}
@ -1110,8 +1111,8 @@ void lp_primal_core_solver<T, X>::init_infeasibility_costs_for_changed_basis_onl
template <typename T, typename X>
void lp_primal_core_solver<T, X>::init_infeasibility_costs() {
SASSERT(this->m_x.size() >= this->m_n());
SASSERT(this->m_column_types.size() >= this->m_n());
lp_assert(this->m_x.size() >= this->m_n());
lp_assert(this->m_column_types.size() >= this->m_n());
for (unsigned j = this->m_n(); j--;)
init_infeasibility_cost_for_column(j);
this->m_using_infeas_costs = true;
@ -1135,7 +1136,7 @@ lp_primal_core_solver<T, X>::get_infeasibility_cost_for_column(unsigned j) const
ret = numeric_traits<T>::zero();
}
break;
case column_type::low_bound:
case column_type::lower_bound:
if (this->x_below_low_bound(j)) {
ret = -1;
} else {
@ -1153,7 +1154,7 @@ lp_primal_core_solver<T, X>::get_infeasibility_cost_for_column(unsigned j) const
ret = numeric_traits<T>::zero();
break;
default:
SASSERT(false);
lp_assert(false);
ret = numeric_traits<T>::zero(); // does not matter
break;
}
@ -1189,7 +1190,7 @@ lp_primal_core_solver<T, X>::init_infeasibility_cost_for_column(unsigned j) {
this->m_costs[j] = numeric_traits<T>::zero();
}
break;
case column_type::low_bound:
case column_type::lower_bound:
if (this->x_below_low_bound(j)) {
this->m_costs[j] = -1;
} else {
@ -1207,14 +1208,14 @@ lp_primal_core_solver<T, X>::init_infeasibility_cost_for_column(unsigned j) {
this->m_costs[j] = numeric_traits<T>::zero();
break;
default:
SASSERT(false);
lp_assert(false);
break;
}
if (numeric_traits<T>::is_zero(this->m_costs[j])) {
this->m_inf_set.erase(j);
this->remove_column_from_inf_set(j);
} else {
this->m_inf_set.insert(j);
this->insert_column_into_inf_set(j);
}
if (!this->m_settings.use_breakpoints_in_feasibility_search) {
this->m_costs[j] = - this->m_costs[j];
@ -1227,18 +1228,18 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_column
switch (this->m_column_type[j]) {
case column_type::fixed:
case column_type::boxed:
out << "( " << this->m_low_bounds[j] << " " << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl;
out << "( " << this->m_lower_bounds[j] << " " << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl;
break;
case column_type::upper_bound:
out << "( _" << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl;
break;
case column_type::low_bound:
out << "( " << this->m_low_bounds[j] << " " << this->m_x[j] << " " << "_ )" << std::endl;
case column_type::lower_bound:
out << "( " << this->m_lower_bounds[j] << " " << this->m_x[j] << " " << "_ )" << std::endl;
break;
case column_type::free_column:
out << "( _" << this->m_x[j] << "_)" << std::endl;
default:
SASSERT(false);
lp_unreachable();
}
}
@ -1277,7 +1278,7 @@ template <typename T, typename X> std::string lp_primal_core_solver<T, X>::break
case upper_break: return "upper_break";
case fixed_break: return "fixed_break";
default:
SASSERT(false);
lp_assert(false);
break;
}
return "type is not found";
@ -1290,7 +1291,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_breakp
template <typename T, typename X>
void lp_primal_core_solver<T, X>::init_reduced_costs() {
SASSERT(!this->use_tableau());
lp_assert(!this->use_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) {
@ -1305,12 +1306,12 @@ void lp_primal_core_solver<T, X>::init_reduced_costs() {
template <typename T, typename X> void lp_primal_core_solver<T, X>::change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering) {
if (b->m_j == entering) {
SASSERT(b->m_type != fixed_break && (!is_zero(b->m_delta)));
lp_assert(b->m_type != fixed_break && (!is_zero(b->m_delta)));
slope_at_entering += m_sign_of_entering_delta;
return;
}
SASSERT(this->m_basis_heading[b->m_j] >= 0);
lp_assert(this->m_basis_heading[b->m_j] >= 0);
unsigned i_row = this->m_basis_heading[b->m_j];
const T & d = - this->m_ed[i_row];
if (numeric_traits<T>::is_zero(d)) return;
@ -1329,27 +1330,27 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::change_sl
slope_at_entering += delta;
break;
default:
SASSERT(false);
lp_assert(false);
}
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::try_add_breakpoint_in_row(unsigned i) {
SASSERT(i < this->m_m());
lp_assert(i < this->m_m());
const T & d = this->m_ed[i]; // the coefficient before m_entering in the i-th row
if (d == 0) return; // the change of x[m_entering] will not change the corresponding basis x
unsigned j = this->m_basis[i];
const X & x = this->m_x[j];
switch (this->m_column_types[j]) {
case column_type::fixed:
try_add_breakpoint(j, x, d, fixed_break, this->m_low_bounds[j]);
try_add_breakpoint(j, x, d, fixed_break, this->m_lower_bounds[j]);
break;
case column_type::boxed:
try_add_breakpoint(j, x, d, low_break, this->m_low_bounds[j]);
try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]);
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]);
break;
case column_type::low_bound:
try_add_breakpoint(j, x, d, low_break, this->m_low_bounds[j]);
case column_type::lower_bound:
try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]);
break;
case column_type::upper_bound:
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]);
@ -1357,7 +1358,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::try_add_b
case column_type::free_column:
break;
default:
SASSERT(false);
lp_assert(false);
break;
}
}
@ -1369,10 +1370,10 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_bound_
switch (this->m_column_types[j]) {
case column_type::fixed:
case column_type::boxed:
out << "[" << this->m_low_bounds[j] << "," << this->m_upper_bounds[j] << "]" << std::endl;
out << "[" << this->m_lower_bounds[j] << "," << this->m_upper_bounds[j] << "]" << std::endl;
break;
case column_type::low_bound:
out << "[" << this->m_low_bounds[j] << ", inf" << std::endl;
case column_type::lower_bound:
out << "[" << this->m_lower_bounds[j] << ", inf" << std::endl;
break;
case column_type::upper_bound:
out << "inf ," << this->m_upper_bounds[j] << "]" << std::endl;
@ -1381,7 +1382,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_bound_
out << "inf, inf" << std::endl;
break;
default:
SASSERT(false);
lp_assert(false);
break;
}
}

View file

@ -28,14 +28,14 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::one_iteratio
else {
advance_on_entering_tableau(entering);
}
SASSERT(this->inf_set_is_correct());
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) {
this->set_status(UNBOUNDED);
this->set_status(lp_status::UNBOUNDED);
return;
}
advance_on_entering_and_leaving_tableau(entering, leaving, t);
@ -52,7 +52,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::choose_enteri
//this moment m_y = cB * B(-1)
unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter();
SASSERT(numeric_traits<T>::precise());
lp_assert(numeric_traits<T>::precise());
if (number_of_benefitial_columns_to_go_over == 0)
return -1;
if (this->m_basis_sort_counter == 0) {
@ -100,25 +100,26 @@ template <typename T, typename X>
unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
init_run_tableau();
if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) {
this->set_status(FEASIBLE);
this->set_status(lp_status::FEASIBLE);
return 0;
}
if ((!numeric_traits<T>::precise()) && this->A_mult_x_is_off()) {
this->set_status(FLOATING_POINT_ERROR);
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())
if (this->m_settings.use_tableau_rows()) {
one_iteration_tableau_rows();
}
else
one_iteration_tableau();
switch (this->get_status()) {
case OPTIMAL: // double check that we are at optimum
case INFEASIBLE:
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()) {
@ -127,7 +128,7 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR);
this->set_status(lp_status::FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
@ -135,7 +136,7 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
decide_on_status_when_cannot_find_entering();
break;
}
this->set_status(UNKNOWN);
this->set_status(lp_status::UNKNOWN);
} else { // precise case
if ((!this->infeasibility_costs_are_correct())) {
init_reduced_costs_tableau(); // forcing recalc
@ -143,31 +144,31 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
decide_on_status_when_cannot_find_entering();
break;
}
this->set_status(UNKNOWN);
this->set_status(lp_status::UNKNOWN);
}
}
break;
case TENTATIVE_UNBOUNDED:
case lp_status::TENTATIVE_UNBOUNDED:
this->init_lu();
if (this->m_factorization->get_status() != LU_status::OK) {
this->set_status(FLOATING_POINT_ERROR);
this->set_status(lp_status::FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
break;
case UNBOUNDED:
case lp_status::UNBOUNDED:
if (this->current_x_is_infeasible()) {
init_reduced_costs();
this->set_status(UNKNOWN);
this->set_status(lp_status::UNKNOWN);
}
break;
case UNSTABLE:
SASSERT(! (numeric_traits<T>::precise()));
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(FLOATING_POINT_ERROR);
this->set_status(lp_status::FLOATING_POINT_ERROR);
break;
}
init_reduced_costs();
@ -176,13 +177,13 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
default:
break; // do nothing
}
} while (this->get_status() != FLOATING_POINT_ERROR
} while (this->get_status() != lp_status::FLOATING_POINT_ERROR
&&
this->get_status() != UNBOUNDED
this->get_status() != lp_status::UNBOUNDED
&&
this->get_status() != OPTIMAL
this->get_status() != lp_status::OPTIMAL
&&
this->get_status() != INFEASIBLE
this->get_status() != lp_status::INFEASIBLE
&&
this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements
&&
@ -193,13 +194,13 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
this->m_settings.get_cancel_flag() == false);
if (this->m_settings.get_cancel_flag()) {
this->set_status(CANCELLED);
this->set_status(lp_status::CANCELLED);
}
SASSERT(
this->get_status() == FLOATING_POINT_ERROR
lp_assert(
this->get_status() == lp_status::FLOATING_POINT_ERROR
||
this->get_status() == CANCELLED
this->get_status() == lp_status::CANCELLED
||
this->current_x_is_feasible() == false
||
@ -208,13 +209,13 @@ unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
}
template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) {
SASSERT(this->A_mult_x_is_off() == false);
SASSERT(leaving >= 0 && entering >= 0);
SASSERT((this->m_settings.simplex_strategy() ==
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));
SASSERT(this->m_using_infeas_costs || !is_neg(t));
SASSERT(entering != leaving || !is_zero(t)); // otherwise nothing changes
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;
@ -225,7 +226,7 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
t = -t;
}
this->update_basis_and_x_tableau(entering, leaving, t);
SASSERT(this->A_mult_x_is_off() == false);
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]);
@ -240,7 +241,7 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
this->init_reduced_costs_tableau();
}
SASSERT(!need_to_switch_costs());
lp_assert(!need_to_switch_costs());
std::list<unsigned>::iterator it = m_non_basis_list.end();
it--;
* it = static_cast<unsigned>(leaving);
@ -249,7 +250,7 @@ template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_en
template <typename T, typename X>
void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving_tableau(int entering, X & t) {
SASSERT(!this->A_mult_x_is_off() );
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;
@ -270,7 +271,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
const column_cell & c = col[k];
unsigned i = c.m_i;
const T & ed = this->m_A.get_val(c);
SASSERT(!numeric_traits<T>::is_zero(ed));
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) {
@ -289,7 +290,7 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
const column_cell & c = col[k];
unsigned i = c.m_i;
const T & ed = this->m_A.get_val(c);
SASSERT(!numeric_traits<T>::is_zero(ed));
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);
@ -321,13 +322,12 @@ template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_
return m_leaving_candidates[k];
}
template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run_tableau() {
// print_matrix(&(this->m_A), std::cout);
SASSERT(this->A_mult_x_is_off() == false);
SASSERT(basis_columns_are_set_correctly());
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;
SASSERT(this->inf_set_is_correct());
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)
@ -341,13 +341,13 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run_tab
}
if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
init_tableau_rows();
SASSERT(this->reduced_costs_are_correct_tableau());
SASSERT(!this->need_to_pivot_to_basis_tableau());
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) {
SASSERT(this->use_tableau());
lp_assert(this->use_tableau());
update_x_tableau(entering, tt);
this->pivot_column_tableau(entering, this->m_basis_heading[leaving]);
this->change_basis(entering, leaving);
@ -355,36 +355,34 @@ update_basis_and_x_tableau(int entering, int leaving, X const & tt) {
}
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_call_tracker(entering, delta);
if (!this->m_using_infeas_costs) {
this->m_x[entering] += delta;
for (const auto & c : this->m_A.m_columns[entering]) {
unsigned i = c.m_i;
this->m_x[this->m_basis[i]] -= delta * this->m_A.get_val(c);
this->update_column_in_inf_set(this->m_basis[i]);
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
this->m_x[entering] += delta;
SASSERT(this->column_is_feasible(entering));
SASSERT(this->m_costs[entering] == zero_of_type<T>());
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.m_i;
unsigned j = this->m_basis[i];
this->m_x[j] -= delta * this->m_A.get_val(c);
this->add_delta_to_x_and_call_tracker(j, -delta * this->m_A.get_val(c));
update_inf_cost_for_column_tableau(j);
if (is_zero(this->m_costs[j]))
this->m_inf_set.erase(j);
this->remove_column_from_inf_set(j);
else
this->m_inf_set.insert(j);
this->insert_column_into_inf_set(j);
}
}
SASSERT(this->A_mult_x_is_off() == false);
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) {
SASSERT(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows);
SASSERT(this->m_using_infeas_costs);
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))

View file

@ -22,7 +22,7 @@ Revision History:
#include <string>
#include "util/vector.h"
#include <functional>
#include "util/lp/lp_primal_simplex.hpp"
#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;

View file

@ -26,12 +26,11 @@ Revision History:
#include "util/lp/column_info.h"
#include "util/lp/lp_primal_core_solver.h"
#include "util/lp/lp_solver.h"
#include "util/lp/iterator_on_row.h"
namespace 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_low_bounds;
vector<X> m_lower_bounds;
private:
unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); }

View file

@ -76,14 +76,14 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::fill_costs_and_x
int row,
unsigned & slack_var,
unsigned & artificial) {
SASSERT(row >= 0 && row < this->row_count());
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::low_bound;
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) {
@ -97,13 +97,13 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::fill_costs_and_x
break;
case Greater_or_equal:
this->m_column_types[slack_var] = column_type::low_bound;
this->m_column_types[slack_var] = column_type::lower_bound;
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
if (rs > 0) {
SASSERT(numeric_traits<T>::is_zero(this->m_x[slack_var]));
lp_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
// adding one artificial
this->m_column_types[artificial] = column_type::low_bound;
this->m_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;
@ -118,13 +118,13 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::fill_costs_and_x
break;
case Less_or_equal:
// introduce a non-negative slack variable
this->m_column_types[slack_var] = column_type::low_bound;
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
SASSERT(numeric_traits<T>::is_zero(this->m_x[slack_var]));
this->m_column_types[artificial] = column_type::low_bound;
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;
@ -192,12 +192,12 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::fill_A_x_and_bas
}
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) {
SASSERT(row < this->row_count());
lp_assert(row < this->row_count());
auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row);
SASSERT(ext_row_it != this->m_core_solver_rows_to_external_rows.end());
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);
SASSERT(constr_it != this->m_constraints.end());
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();
@ -208,34 +208,34 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::fill_A_x_and_bas
this->m_x[j] = this->m_b[row];
(*this->m_A)(row, j) = numeric_traits<T>::one();
this->m_column_types[j] = column_type::fixed;
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>();
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::low_bound;
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::low_bound;
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>();
this->m_column_types[j] = column_type::lower_bound;
this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type<X>();
break;
default:
SASSERT(false);
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 = OPTIMAL;
this->m_status = lp_status::OPTIMAL;
return;
}
m_low_bounds.clear();
m_low_bounds.resize(total_vars, zero_of_type<X>()); // low bounds are shifted ot zero
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();
@ -253,7 +253,7 @@ template <typename T, typename X> void lp_primal_simplex<T, X>::solve_with_total
this->m_heading,
this->m_costs,
this->m_column_types,
m_low_bounds,
m_lower_bounds,
this->m_upper_bounds,
this->m_settings, *this);
m_core_solver->solve();
@ -278,7 +278,6 @@ template <typename T, typename X> bool lp_primal_simplex<T, X>::bounds_hold(std:
}
if (!it.second->bounds_hold(sol_it->second)) {
// std::cout << "bounds do not hold for " << it.second->get_name() << std::endl;
it.second->bounds_hold(sol_it->second);
return false;
}
@ -296,10 +295,10 @@ template <typename T, typename X> T lp_primal_simplex<T, X>::get_row_value(unsig
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);
SASSERT(cit != this->m_map_from_var_index_to_column_info.end());
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());
SASSERT(sol_it != solution.end());
lp_assert(sol_it != solution.end());
T column_val = sol_it->second;
if (out != nullptr) {
(*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") ";
@ -344,7 +343,7 @@ template <typename T, typename X> bool lp_primal_simplex<T, X>::row_constraint_h
}
return true;;
}
SASSERT(false);
lp_unreachable();
return false; // it is unreachable
}

View file

@ -19,7 +19,7 @@ Revision History:
--*/
#include <memory>
#include "util/vector.h"
#include "util/lp/lp_settings.hpp"
#include "util/lp/lp_settings_def.h"
template bool lp::vectors_are_equal<double>(vector<double> const&, vector<double> const&);
template bool lp::vectors_are_equal<lp::mpq>(vector<lp::mpq > const&, vector<lp::mpq> const&);

View file

@ -31,13 +31,16 @@ namespace lp {
typedef unsigned var_index;
typedef unsigned constraint_index;
typedef unsigned row_index;
typedef vector<std::pair<mpq, constraint_index>> explanation_t;
enum class column_type {
free_column = 0,
low_bound = 1,
upper_bound = 2,
boxed = 3,
fixed = 4
};
lower_bound = 1,
upper_bound = 2,
boxed = 3,
fixed = 4
};
enum class simplex_strategy_enum {
undecided = 3,
@ -48,7 +51,7 @@ enum class simplex_strategy_enum {
std::string column_type_to_string(column_type t);
enum lp_status {
enum class lp_status {
UNKNOWN,
INFEASIBLE,
TENTATIVE_UNBOUNDED,
@ -81,7 +84,7 @@ inline std::ostream& operator<<(std::ostream& out, lp_status status) {
lp_status lp_status_from_string(std::string status);
enum non_basic_column_value_position { at_low_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound };
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
@ -91,11 +94,22 @@ public:
};
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;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
@ -115,53 +129,76 @@ private:
};
default_lp_resource_limit m_default_resource_limit;
lp_resource_limit* m_resource_limit;
lp_resource_limit* m_resource_limit;
// used for debug output
std::ostream* m_debug_out;
std::ostream* m_debug_out;
// used for messages, for example, the computation progress messages
std::ostream* m_message_out;
std::ostream* m_message_out;
stats m_stats;
random_gen m_rand;
stats m_stats;
random_gen m_rand;
public:
unsigned reps_in_scaler;
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;
double pivot_epsilon;
// see Chatal, page 115
double positive_price_epsilon;
double positive_price_epsilon;
// a quatation "if some choice of the entering vairable leads to an eta matrix
// whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ...
double entering_diag_epsilon;
int c_partial_pivoting; // this is the constant c from page 410
unsigned depth_of_rook_search;
bool using_partial_pivoting;
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;
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
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
bool m_bound_propagation;
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;
unsigned m_hnf_cut_period;
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;
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;
}
@ -172,15 +209,15 @@ public:
lp_settings() : m_default_resource_limit(*this),
m_resource_limit(&m_default_resource_limit),
m_debug_out( &std::cout),
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),
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),
@ -189,7 +226,6 @@ public:
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),
@ -214,7 +250,15 @@ public:
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)
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)
{}
void set_resource_limit(lp_resource_limit& lim) { m_resource_limit = &lim; }
@ -284,8 +328,6 @@ public:
return is_eps_small_general<T>(t, tolerance_for_artificials);
}
// the method of lar solver to use
bool presolve_with_double_solver_for_lar;
simplex_strategy_enum m_simplex_strategy;
simplex_strategy_enum simplex_strategy() const {
return m_simplex_strategy;
}
@ -307,20 +349,9 @@ public:
return m_simplex_strategy == simplex_strategy_enum::tableau_rows;
}
int report_frequency;
bool print_statistics;
unsigned column_norms_update_frequency;
bool scale_with_ratio;
double density_threshold; // need to tune it up, todo
#ifdef Z3DEBUG
static unsigned ddd; // used for debugging
#endif
bool use_breakpoints_in_feasibility_search;
unsigned random_next() { return m_rand(); }
void random_seed(unsigned s) { m_rand.set_seed(s); }
unsigned max_row_length_for_bound_propagation;
bool backup_costs;
unsigned column_number_threshold_for_using_lu_in_lar_solver;
}; // end of lp_settings class
@ -343,7 +374,7 @@ inline std::string T_to_string(const numeric_pair<mpq> & t) {
inline std::string T_to_string(const mpq & t) {
std::ostringstream strs;
strs << t.get_double();
strs << t;
return strs.str();
}
@ -382,7 +413,7 @@ inline void print_blanks(int n, std::ostream & 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) {
SASSERT(v.size() > 0);
lp_assert(v.size() > 0);
unsigned j = v.size() - 1;
for (; j > 0; j-- )
if (v[j] <= v[j - 1]) {

View file

@ -26,30 +26,30 @@ std::string column_type_to_string(column_type t) {
switch (t) {
case column_type::fixed: return "fixed";
case column_type::boxed: return "boxed";
case column_type::low_bound: return "low_bound";
case column_type::lower_bound: return "lower_bound";
case column_type::upper_bound: return "upper_bound";
case column_type::free_column: return "free_column";
default: SASSERT(false);
default: lp_unreachable();
}
return "unknown"; // it is unreachable
}
const char* lp_status_to_string(lp_status status) {
switch (status) {
case UNKNOWN: return "UNKNOWN";
case INFEASIBLE: return "INFEASIBLE";
case UNBOUNDED: return "UNBOUNDED";
case TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED";
case DUAL_UNBOUNDED: return "DUAL_UNBOUNDED";
case OPTIMAL: return "OPTIMAL";
case FEASIBLE: return "FEASIBLE";
case FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR";
case TIME_EXHAUSTED: return "TIME_EXHAUSTED";
case ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED";
case EMPTY: return "EMPTY";
case UNSTABLE: return "UNSTABLE";
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:
SASSERT(false);
lp_unreachable();
}
return "UNKNOWN"; // it is unreachable
}
@ -64,7 +64,7 @@ lp_status lp_status_from_string(std::string status) {
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;
SASSERT(false);
lp_unreachable();
return lp_status::UNKNOWN; // it is unreachable
}
@ -74,14 +74,12 @@ bool vectors_are_equal(T * a, vector<T> &b, unsigned n) {
if (numeric_traits<T>::precise()) {
for (unsigned i = 0; i < n; i ++){
if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
// std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false;
}
}
} else {
for (unsigned i = 0; i < n; i ++){
if (std::abs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) {
// std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false;
}
}
@ -97,7 +95,6 @@ bool vectors_are_equal(const vector<T> & a, const vector<T> &b) {
if (numeric_traits<T>::precise()) {
for (unsigned i = 0; i < n; i ++){
if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
// std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false;
}
}
@ -112,7 +109,6 @@ bool vectors_are_equal(const vector<T> & a, const vector<T> &b) {
}
if (fabs(da - db) > 0.000001) {
// std::cout << "a[" << i <<"] = " << a[i] << ", but " << "b[" << i <<"] = " << b[i] << std::endl;
return false;
}
}

View file

@ -18,7 +18,7 @@ Revision History:
--*/
#include <string>
#include "util/lp/lp_solver.hpp"
#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();
@ -34,7 +34,6 @@ template void lp::lp_solver<double, double>::print_statistics_on_A(std::ostream
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 std::string lp::lp_solver<double, double>::get_column_name(unsigned int) const;
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();
@ -54,4 +53,3 @@ 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;
template std::string lp::lp_solver<lp::mpq, lp::mpq>::get_column_name(unsigned int) const;

View file

@ -28,7 +28,6 @@ Revision History:
#include "util/lp/static_matrix.h"
#include "util/lp/lp_core_solver_base.h"
#include "util/lp/scaler.h"
#include "util/lp/linear_combination_iterator.h"
#include "util/lp/bound_analyzer_on_row.h"
namespace lp {
enum lp_relation {
@ -114,9 +113,9 @@ public:
// returns -1 if not found
virtual int get_column_index_by_name(std::string name) const;
void set_low_bound(unsigned i, T bound) {
void set_lower_bound(unsigned i, T bound) {
column_info<T> *ci = get_or_create_column_info(i);
ci->set_low_bound(bound);
ci->set_lower_bound(bound);
}
void set_upper_bound(unsigned i, T bound) {
@ -124,8 +123,8 @@ public:
ci->set_upper_bound(bound);
}
void unset_low_bound(unsigned i) {
get_or_create_column_info(i)->unset_low_bound();
void unset_lower_bound(unsigned i) {
get_or_create_column_info(i)->unset_lower_bound();
}
void unset_upper_bound(unsigned i) {
@ -194,9 +193,9 @@ protected:
void pin_vars_on_row_with_sign(std::unordered_map<unsigned, T> & row, T sign );
bool get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound);
bool get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & lower_bound);
bool get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_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);
@ -244,7 +243,7 @@ protected:
void count_slacks_and_artificials_for_row(unsigned i);
T low_bound_shift_for_row(unsigned i);
T lower_bound_shift_for_row(unsigned i);
void fill_m_b();

View file

@ -47,7 +47,7 @@ template <typename T, typename X> T lp_solver<T, X>::get_column_cost_value(unsig
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) {
SASSERT(m_constraints.find(row_index) == m_constraints.end());
lp_assert(m_constraints.find(row_index) == m_constraints.end());
lp_constraint<T, X> cs(right_side, relation);
m_constraints[row_index] = cs;
}
@ -173,29 +173,29 @@ template <typename T, typename X> void lp_solver<T, X>::pin_vars_on_row_with_sig
column_info<T> * ci = m_map_from_var_index_to_column_info[j];
T a = t.second;
if (a * sign > numeric_traits<T>::zero()) {
SASSERT(ci->upper_bound_is_set());
lp_assert(ci->upper_bound_is_set());
ci->set_fixed_value(ci->get_upper_bound());
} else {
SASSERT(ci->low_bound_is_set());
ci->set_fixed_value(ci->get_low_bound());
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 & low_bound) {
low_bound = numeric_traits<T>::zero();
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->low_bound_is_set()) {
low_bound += ci->get_low_bound() * a;
if (ci->lower_bound_is_set()) {
lower_bound += ci->get_lower_bound() * a;
} else {
return false;
}
} else {
if (ci->upper_bound_is_set()) {
low_bound += ci->get_upper_bound() * a;
lower_bound += ci->get_upper_bound() * a;
} else {
return false;
}
@ -204,20 +204,20 @@ template <typename T, typename X> bool lp_solver<T, X>::get_minimal_row_value
return true;
}
template <typename T, typename X> bool lp_solver<T, X>::get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound) {
low_bound = numeric_traits<T>::zero();
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->low_bound_is_set()) {
low_bound += ci->get_low_bound() * a;
if (ci->lower_bound_is_set()) {
lower_bound += ci->get_lower_bound() * a;
} else {
return false;
}
} else {
if (ci->upper_bound_is_set()) {
low_bound += ci->get_upper_bound() * a;
lower_bound += ci->get_upper_bound() * a;
} else {
return false;
}
@ -238,17 +238,17 @@ template <typename T, typename X> bool lp_solver<T, X>::row_e_is_obsolete(std
T rs = m_constraints[row_index].m_rs;
if (row_is_zero(row)) {
if (!is_zero(rs))
m_status = INFEASIBLE;
m_status = lp_status::INFEASIBLE;
return true;
}
T low_bound;
bool lb = get_minimal_row_value(row, low_bound);
T lower_bound;
bool lb = get_minimal_row_value(row, lower_bound);
if (lb) {
T diff = low_bound - rs;
T diff = lower_bound - rs;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
// low_bound > rs + m_settings.refactor_epsilon
m_status = INFEASIBLE;
// 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)){
@ -263,7 +263,7 @@ template <typename T, typename X> bool lp_solver<T, X>::row_e_is_obsolete(std
T diff = rs - upper_bound;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
// upper_bound < rs - m_settings.refactor_tolerance
m_status = INFEASIBLE;
m_status = lp_status::INFEASIBLE;
return true;
}
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
@ -279,7 +279,7 @@ template <typename T, typename X> bool lp_solver<T, X>::row_ge_is_obsolete(std:
T rs = m_constraints[row_index].m_rs;
if (row_is_zero(row)) {
if (rs > zero_of_type<X>())
m_status = INFEASIBLE;
m_status = lp_status::INFEASIBLE;
return true;
}
@ -288,7 +288,7 @@ template <typename T, typename X> bool lp_solver<T, X>::row_ge_is_obsolete(std:
T diff = rs - upper_bound;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
// upper_bound < rs - m_settings.refactor_tolerance
m_status = INFEASIBLE;
m_status = lp_status::INFEASIBLE;
return true;
}
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
@ -301,18 +301,18 @@ template <typename T, typename X> bool lp_solver<T, X>::row_ge_is_obsolete(std:
}
template <typename T, typename X> bool lp_solver<T, X>::row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
T low_bound;
T lower_bound;
T rs = m_constraints[row_index].m_rs;
if (row_is_zero(row)) {
if (rs < zero_of_type<X>())
m_status = INFEASIBLE;
m_status = lp_status::INFEASIBLE;
return true;
}
if (get_minimal_row_value(row, low_bound)) {
T diff = low_bound - rs;
if (get_minimal_row_value(row, lower_bound)) {
T diff = lower_bound - rs;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
// low_bound > rs + m_settings.refactor_tolerance
// lower_bound > rs + m_settings.refactor_tolerance
m_status = lp_status::INFEASIBLE;
return true;
}
@ -343,7 +343,7 @@ template <typename T, typename X> bool lp_solver<T, X>::row_is_obsolete(std::
case lp_relation::Less_or_equal:
return row_le_is_obsolete(row, row_index);
}
SASSERT(false);
lp_unreachable();
return false; // it is unreachable
}
@ -358,7 +358,7 @@ template <typename T, typename X> void lp_solver<T, X>::remove_fixed_or_zero_col
vector<unsigned> removed;
for (auto & col : row) {
unsigned j = col.first;
SASSERT(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end());
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);
@ -427,7 +427,7 @@ template <typename T, typename X> void lp_solver<T, X>::map_external_columns_to_
}
unsigned j = col.first;
auto column_info_it = m_map_from_var_index_to_column_info.find(j);
SASSERT(column_info_it != m_map_from_var_index_to_column_info.end());
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
@ -450,14 +450,14 @@ 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);
SASSERT(row_it != m_external_rows_to_core_solver_rows.end());
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);
SASSERT(column_info_it != m_map_from_var_index_to_column_info.end());
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();
SASSERT(is_valid(col));
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;
@ -471,7 +471,7 @@ template <typename T, typename X> void lp_solver<T, X>::fill_A_from_A_values() {
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();
SASSERT(m_A == nullptr);
lp_assert(m_A == nullptr);
fill_A_from_A_values();
m_b.resize(m_A->row_count());
}
@ -483,7 +483,7 @@ template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artific
}
template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artificials_for_row(unsigned i) {
SASSERT(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
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:
@ -504,7 +504,7 @@ template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artific
}
}
template <typename T, typename X> T lp_solver<T, X>::low_bound_shift_for_row(unsigned i) {
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);
@ -519,10 +519,10 @@ template <typename T, typename X> T lp_solver<T, X>::low_bound_shift_for_row(
template <typename T, typename X> void lp_solver<T, X>::fill_m_b() {
for (int i = this->row_count() - 1; i >= 0; i--) {
SASSERT(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
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 - low_bound_shift_for_row(external_i);
this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i);
}
}
@ -545,7 +545,7 @@ template <typename T, typename X> T lp_solver<T, X>::get_column_value_with_core_
return v;
}
if (!ci->is_flipped()) {
return v + ci->get_low_bound();
return v + ci->get_lower_bound();
}
// the flipped case when there is only upper bound
@ -557,13 +557,13 @@ template <typename T, typename X> T lp_solver<T, X>::get_column_value_with_core_
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
SASSERT(j < this->m_column_scale.size());
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 *= -1;
}
SASSERT(ci->is_fixed() == false);
lp_assert(ci->is_fixed() == false);
this->m_costs[j] = cost * this->m_column_scale[j];
}
}

View file

@ -18,9 +18,10 @@ Revision History:
--*/
#include "util/lp/lp_utils.h"
#ifdef lp_for_z3
namespace lp {
double numeric_traits<double>::g_zero = 0.0;
double numeric_traits<double>::g_one = 1.0;
}
#endif

View file

@ -22,8 +22,15 @@ Revision History:
#include "util/lp/numeric_pair.h"
#include "util/debug.h"
#include <unordered_map>
template <typename C>
void print_vector(const C & t, std::ostream & out) {
for (const auto & p : t)
out << p << " ";
out << std::endl;
}
template <typename A, typename B>
bool try_get_val(const std::unordered_map<A,B> & map, const A& key, B & val) {
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;
@ -35,19 +42,32 @@ bool contains(const std::unordered_map<A, B> & map, const A& key) {
return map.find(key) != map.end();
}
#ifdef lp_for_z3
#ifdef Z3DEBUG
#define Z3DEBUG 1
#endif
namespace lp {
inline void throw_exception(const std::string & str) {
throw default_exception(str);
}
typedef z3_exception exception;
inline void throw_exception(const std::string & str) {
throw default_exception(str);
}
typedef z3_exception exception;
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); }
#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_int(const X & v) { return numeric_traits<X>::is_int(v); }
template <typename X> inline bool precise() { return numeric_traits<X>::precise(); }
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(); }
}
namespace std {
template<>
@ -84,3 +104,70 @@ struct hash<lp::numeric_pair<lp::mpq>> {
};
}
#else // else of #if lp_for_z3
#include <utility>
#include <functional>
//include "util/numerics/mpq.h"
//include "util/numerics/numeric_traits.h"
//include "util/numerics/double.h"
#ifdef __CLANG__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif
namespace std {
template<>
struct hash<lp::mpq> {
inline size_t operator()(const lp::mpq & v) const {
return v.hash();
}
};
}
namespace lp {
template <typename X> inline bool precise() { return numeric_traits<X>::precise();}
template <typename X> inline X one_of_type() { return numeric_traits<X>::one(); }
template <typename X> inline bool is_zero(const X & v) { return numeric_traits<X>::is_zero(v); }
template <typename X> inline bool is_pos(const X & v) { return numeric_traits<X>::is_pos(v); }
template <typename X> inline bool is_int(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(v); }
template <typename X> inline double get_double(const X & v) { return numeric_traits<X>::get_double(v); }
template <typename T> inline T zero_of_type() {return numeric_traits<T>::zero();}
inline void throw_exception(std::string str) { throw exception(str); }
template <typename T> inline T from_string(std::string const & ) { lp_unreachable();}
template <> double inline from_string<double>(std::string const & str) { return atof(str.c_str());}
template <> mpq inline from_string<mpq>(std::string const & str) {
return mpq(atof(str.c_str()));
}
} // closing lp
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;
}
};
} // std
#ifdef __CLANG__
#pragma clang diagnostic pop
#endif
#endif

84
src/util/lp/lu.cpp Normal file
View file

@ -0,0 +1,84 @@
/*++
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&);
#ifdef Z3DEBUG
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);
template bool lu<static_matrix<double, double>>::is_correct(const vector<unsigned>& basis);
template bool lu<static_matrix<mpq, impq>>::is_correct( vector<unsigned int> const &);
template dense_matrix<double, double> get_B<static_matrix<double, double>>(lu<static_matrix<double, double>>&, const vector<unsigned>& basis);
template dense_matrix<mpq, mpq> get_B<static_matrix<mpq, mpq>>(lu<static_matrix<mpq, mpq>>&, vector<unsigned int> const&);
#endif
template bool lu<static_matrix<double, double>>::pivot_the_row(int); // NOLINT
template void lu<static_matrix<double, double>>::init_vector_w(unsigned int, indexed_vector<double>&);
template void lu<static_matrix<double, double>>::solve_By(vector<double>&);
template void lu<static_matrix<double, double>>::solve_By_when_y_is_ready_for_X(vector<double>&);
template void lu<static_matrix<double, double>>::solve_yB_with_error_check(vector<double>&, const vector<unsigned>& basis);
template void lu<static_matrix<double, double>>::solve_yB_with_error_check_indexed(indexed_vector<double>&, vector<int> const&, const vector<unsigned> & basis, const lp_settings&);
template void lu<static_matrix<mpq, mpq>>::replace_column(mpq, indexed_vector<mpq>&, unsigned);
template void lu<static_matrix<mpq, mpq>>::solve_By(vector<mpq >&);
template void lu<static_matrix<mpq, mpq>>::solve_By_when_y_is_ready_for_X(vector<mpq >&);
template void lu<static_matrix<mpq, mpq>>::solve_yB_with_error_check(vector<mpq >&, const vector<unsigned>& basis);
template void lu<static_matrix<mpq, mpq>>::solve_yB_with_error_check_indexed(indexed_vector<mpq>&, vector< int > const&, const vector<unsigned> & basis, const lp_settings&);
template void lu<static_matrix<mpq, impq> >::solve_yB_with_error_check_indexed(indexed_vector<mpq>&, vector< int > const&, const vector<unsigned> & basis, const lp_settings&);
template void lu<static_matrix<mpq, impq> >::init_vector_w(unsigned int, indexed_vector<mpq>&);
template void lu<static_matrix<mpq, impq> >::replace_column(mpq, indexed_vector<mpq>&, unsigned);
template void lu<static_matrix<mpq, impq> >::solve_Bd_faster(unsigned int, indexed_vector<mpq>&);
template void lu<static_matrix<mpq, impq> >::solve_By(vector<impq >&);
template void lu<static_matrix<mpq, impq> >::solve_By_when_y_is_ready_for_X(vector<impq >&);
template void lu<static_matrix<mpq, impq> >::solve_yB_with_error_check(vector<mpq >&, const vector<unsigned>& basis);
template void lu<static_matrix<mpq, mpq>>::solve_By(indexed_vector<mpq>&);
template void lu<static_matrix<double, double>>::solve_By(indexed_vector<double>&);
template void lu<static_matrix<double, double>>::solve_yB_indexed(indexed_vector<double>&);
template void lu<static_matrix<mpq, impq> >::solve_yB_indexed(indexed_vector<mpq>&);
template void lu<static_matrix<mpq, mpq>>::solve_By_for_T_indexed_only(indexed_vector<mpq>&, lp_settings const&);
template void lu<static_matrix<double, double>>::solve_By_for_T_indexed_only(indexed_vector<double>&, lp_settings const&);
#ifdef Z3DEBUG
template void print_matrix<tail_matrix<double, double>>(tail_matrix<double, double>&, std::ostream&);
#endif
}

View file

@ -8,7 +8,12 @@ Module 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)
@ -24,7 +29,7 @@ Revision History:
#include "util/debug.h"
#include <algorithm>
#include <set>
#include "util/lp/sparse_matrix.h"
#include "util/lp/square_sparse_matrix.h"
#include "util/lp/static_matrix.h"
#include <string>
#include "util/lp/numeric_pair.h"
@ -36,18 +41,16 @@ Revision History:
namespace lp {
#ifdef Z3DEBUG
template <typename T, typename X> // print the nr x nc submatrix at the top left corner
void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc);
void print_submatrix(square_sparse_matrix<T, X> & m, unsigned mr, unsigned nc);
template<typename T, typename X>
void print_matrix(static_matrix<T, X> &m, std::ostream & out);
template <typename M>
void print_matrix(M &m, std::ostream & out);
template <typename T, typename X>
void print_matrix(sparse_matrix<T, X>& m, std::ostream & out);
#endif
template <typename T, typename X>
X dot_product(const vector<T> & a, const vector<X> & b) {
SASSERT(a.size() == b.size());
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];
@ -114,7 +117,7 @@ public:
m_i = p.apply_reverse(m_i);
#ifdef Z3DEBUG
// SASSERT(*this == deb);
// lp_assert(*this == deb);
#endif
}
}; // end of one_elem_on_diag
@ -123,17 +126,20 @@ enum class LU_status { OK, Degenerated};
// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
template <typename T, typename X>
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;
static_matrix<T, X> const &m_A;
const M & m_A;
permutation_matrix<T, X> m_Q;
permutation_matrix<T, X> m_R;
permutation_matrix<T, X> m_r_wave;
sparse_matrix<T, X> m_U;
square_sparse_matrix<T, X> m_U;
square_dense_submatrix<T, X>* m_dense_LU;
vector<tail_matrix<T, X> *> m_tail;
@ -147,10 +153,11 @@ public:
// constructor
// if A is an m by n matrix then basis has length m and values in [0,n); the values are all different
// they represent the set of m columns
lu(static_matrix<T, X> const & A,
lu(const M & A,
vector<unsigned>& basis,
lp_settings & settings);
void debug_test_of_basis(static_matrix<T, X> const & A, vector<unsigned> & basis);
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);
@ -222,7 +229,7 @@ public:
eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j);
// we're processing the column j now
eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j, sparse_matrix<T, X>& copy_of_U);
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);
@ -245,7 +252,7 @@ public:
}
T B_(unsigned i, unsigned j, const vector<unsigned>& basis) {
return m_A.get_elem(i, basis[j]);
return m_A[i][basis[j]];
}
unsigned dimension() { return m_dim; }
@ -261,11 +268,13 @@ public:
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
@ -306,7 +315,7 @@ public:
bool need_to_refactor() { return m_refactor_counter >= 200; }
void adjust_dimension_with_matrix_A() {
SASSERT(m_A.row_count() >= m_dim);
lp_assert(m_A.row_count() >= m_dim);
m_dim = m_A.row_count();
m_U.resize(m_dim);
m_Q.resize(m_dim);
@ -320,7 +329,7 @@ public:
unsigned m = m_A.row_count();
unsigned m_prev = m_U.dimension();
SASSERT(m_A.column_count() == heading.size());
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]) {
@ -336,14 +345,14 @@ public:
void add_last_rows_to_B(const vector<int> & heading, const std::unordered_set<unsigned> & columns_to_replace) {
unsigned m = m_A.row_count();
SASSERT(m_A.column_count() == heading.size());
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) {
SASSERT(heading[j] >= 0);
lp_assert(heading[j] >= 0);
replace_column_with_only_change_at_last_rows(j, heading[j]);
if (get_status() == LU_status::Degenerated)
break;
@ -364,11 +373,14 @@ public:
}; // end of lu
template <typename T, typename X>
void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, vector<unsigned> & m_basis, lp_settings &m_settings);
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>
dense_matrix<T, X> get_B(lu<T, X>& f, const vector<unsigned>& basis);
template <typename T, typename X, typename M>
dense_matrix<T, X> get_B(lu<M>& f, const vector<unsigned>& basis);
template <typename T, typename X, typename M>
dense_matrix<T, X> get_B(lu<M>& f);
#endif
}

View file

@ -26,8 +26,8 @@ Revision History:
#include "util/lp/lu.h"
namespace lp {
#ifdef Z3DEBUG
template <typename T, typename X> // print the nr x nc submatrix at the top left corner
void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc, std::ostream & out) {
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++) {
@ -44,15 +44,14 @@ void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc, std::ost
print_matrix_with_widths(A, widths, out);
}
template<typename T, typename X>
void print_matrix(static_matrix<T, X> &m, std::ostream & out) {
template<typename M>
void print_matrix(M &m, std::ostream & out) {
vector<vector<std::string>> A;
vector<unsigned> widths;
std::set<std::pair<unsigned, unsigned>> domain = m.get_domain();
for (unsigned i = 0; i < m.row_count(); i++) {
A.push_back(vector<std::string>());
for (unsigned j = 0; j < m.column_count(); j++) {
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
A[i].push_back(T_to_string(m[i][j]));
}
}
@ -63,23 +62,6 @@ void print_matrix(static_matrix<T, X> &m, std::ostream & out) {
print_matrix_with_widths(A, widths, out);
}
template <typename T, typename X>
void print_matrix(sparse_matrix<T, X>& m, std::ostream & out) {
vector<vector<std::string>> A;
vector<unsigned> widths;
for (unsigned i = 0; i < m.row_count(); i++) {
A.push_back(vector<std::string>());
for (unsigned j = 0; j < m.column_count(); j++) {
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
}
}
for (unsigned j = 0; j < m.column_count(); j++) {
widths.push_back(get_width_of_column(j, A));
}
print_matrix_with_widths(A, widths, out);
}
#endif
@ -122,10 +104,10 @@ void one_elem_on_diag<T, X>::apply_from_left_to_T(indexed_vector<T> & w, lp_sett
// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
template <typename T, typename X>
lu<T, X>::lu(static_matrix<T, X> const & A,
vector<unsigned>& basis,
lp_settings & settings):
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),
@ -137,42 +119,62 @@ lu<T, X>::lu(static_matrix<T, X> const & A,
m_failure(false),
m_row_eta_work_vector(A.row_count()),
m_refactor_counter(0) {
SASSERT(!(numeric_traits<T>::precise() && settings.use_tableau()));
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
// SASSERT(check_correctness());
// lp_assert(check_correctness());
#endif
}
template <typename T, typename X>
void lu<T, X>::debug_test_of_basis(static_matrix<T, X> const & A, vector<unsigned> & basis) {
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++) {
SASSERT(basis[i]< A.column_count());
lp_assert(basis[i]< A.column_count());
set.insert(basis[i]);
}
SASSERT(set.size() == A.row_count());
lp_assert(set.size() == A.row_count());
}
template <typename T, typename X>
void lu<T, X>::solve_By(indexed_vector<X> & y) {
SASSERT(false); // not implemented
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 T, typename X>
void lu<T, X>::solve_By(vector<X> & 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 T, typename X>
void lu<T, X>::solve_By_when_y_is_ready_for_X(vector<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
@ -189,8 +191,8 @@ void lu<T, X>::solve_By_when_y_is_ready_for_X(vector<X> & y) {
}
}
template <typename T, typename X>
void lu<T, X>::solve_By_when_y_is_ready_for_T(vector<T> & y, vector<unsigned> & index) {
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
@ -214,8 +216,8 @@ void lu<T, X>::solve_By_when_y_is_ready_for_T(vector<T> & y, vector<unsigned> &
}
}
template <typename T, typename X>
void lu<T, X>::solve_By_for_T_indexed_only(indexed_vector<T> & y, const lp_settings & settings) {
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);
@ -226,8 +228,8 @@ void lu<T, X>::solve_By_for_T_indexed_only(indexed_vector<T> & y, const lp_setti
m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal
}
template <typename T, typename X>
void lu<T, X>::print_matrix_compact(std::ostream & f) {
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;
@ -241,8 +243,8 @@ void lu<T, X>::print_matrix_compact(std::ostream & f) {
}
f << "matrix_end" << std::endl;
}
template <typename T, typename X>
void lu<T, X>::print(indexed_vector<T> & w, const vector<unsigned>& basis) {
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);
@ -256,8 +258,8 @@ void lu<T, X>::print(indexed_vector<T> & w, const vector<unsigned>& basis) {
print_indexed_vector(w, f);
f.close();
}
template <typename T, typename X>
void lu<T, X>::solve_Bd(unsigned a_column, indexed_vector<T> & d, indexed_vector<T> & w) {
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
@ -270,14 +272,14 @@ void lu<T, X>::solve_Bd(unsigned a_column, indexed_vector<T> & d, indexed_vector
}
}
template <typename T, typename X>
void lu<T, X>::solve_Bd_faster(unsigned a_column, indexed_vector<T> & d) { // puts the a_column into d
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 T, typename X>
void lu<T, X>::solve_yB(vector<T>& y) {
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)
@ -290,37 +292,37 @@ void lu<T, X>::solve_yB(vector<T>& y) {
}
}
template <typename T, typename X>
void lu<T, X>::solve_yB_indexed(indexed_vector<T>& y) {
SASSERT(y.is_OK());
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)
SASSERT(y.is_OK());
lp_assert(y.is_OK());
m_U.solve_y_U_indexed(y, m_settings); // got y*U=cb*R(-1)
SASSERT(y.is_OK());
lp_assert(y.is_OK());
m_Q.apply_reverse_from_right_to_T(y);
SASSERT(y.is_OK());
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);
SASSERT(y.is_OK());
lp_assert(y.is_OK());
}
}
template <typename T, typename X>
void lu<T, X>::add_delta_to_solution(const vector<T>& yc, vector<T>& y){
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 T, typename X>
void lu<T, X>::add_delta_to_solution_indexed(indexed_vector<T>& y) {
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
SASSERT(y.is_OK());
SASSERT(m_y_copy.is_OK());
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)
@ -330,7 +332,7 @@ void lu<T, X>::add_delta_to_solution_indexed(indexed_vector<T>& y) {
if (m_ii[i] == 0)
m_ii.set_value(1, i);
}
SASSERT(m_ii.is_OK());
lp_assert(m_ii.is_OK());
y.m_index.clear();
for (unsigned i : m_ii.m_index) {
@ -341,24 +343,24 @@ void lu<T, X>::add_delta_to_solution_indexed(indexed_vector<T>& y) {
v = zero_of_type<T>();
}
SASSERT(y.is_OK());
lp_assert(y.is_OK());
}
template <typename T, typename X>
void lu<T, X>::find_error_of_yB(vector<T>& yc, const vector<T>& y, const vector<unsigned>& m_basis) {
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 T, typename X>
void lu<T, X>::find_error_of_yB_indexed(const indexed_vector<T>& y, const vector<int>& heading, const lp_settings& settings) {
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();
SASSERT(!numeric_traits<T>::precise());
lp_assert(!numeric_traits<T>::precise());
{
vector<unsigned> d_basis(y.m_data.size());
@ -379,10 +381,10 @@ void lu<T, X>::find_error_of_yB_indexed(const indexed_vector<T>& y, const vector
}
}
#endif
SASSERT(m_ii.is_OK());
lp_assert(m_ii.is_OK());
m_ii.clear();
m_ii.resize(y.data_size());
SASSERT(m_y_copy.is_OK());
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];
@ -414,7 +416,7 @@ void lu<T, X>::find_error_of_yB_indexed(const indexed_vector<T>& y, const vector
m_y_copy.set_value(v, k);
}
}
SASSERT(m_y_copy.is_OK());
lp_assert(m_y_copy.is_OK());
}
@ -423,8 +425,8 @@ void lu<T, X>::find_error_of_yB_indexed(const indexed_vector<T>& y, const vector
// solves y*B = y
// y is the input
template <typename T, typename X>
void lu<T, X>::solve_yB_with_error_check_indexed(indexed_vector<T> & y, const vector<int>& heading, const vector<unsigned> & basis, const lp_settings & settings) {
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);
@ -434,12 +436,12 @@ void lu<T, X>::solve_yB_with_error_check_indexed(indexed_vector<T> & y, const ve
}
return;
}
SASSERT(m_y_copy.is_OK());
SASSERT(y.is_OK());
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);
SASSERT(y.is_OK());
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);
@ -451,7 +453,7 @@ void lu<T, X>::solve_yB_with_error_check_indexed(indexed_vector<T> & y, const ve
solve_yB_indexed(m_y_copy);
add_delta_to_solution_indexed(y);
}
SASSERT(m_y_copy.is_OK());
lp_assert(m_y_copy.is_OK());
} else {
solve_yB_with_error_check(y.m_data, basis);
y.restore_index_and_clean_from_data();
@ -461,8 +463,8 @@ void lu<T, X>::solve_yB_with_error_check_indexed(indexed_vector<T> & y, const ve
// solves y*B = y
// y is the input
template <typename T, typename X>
void lu<T, X>::solve_yB_with_error_check(vector<T> & y, const vector<unsigned>& basis) {
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;
@ -475,8 +477,8 @@ void lu<T, X>::solve_yB_with_error_check(vector<T> & y, const vector<unsigned>&
add_delta_to_solution(yc, y);
m_y_copy.clear_all();
}
template <typename T, typename X>
void lu<T, X>::apply_Q_R_to_U(permutation_matrix<T, X> & r_wave) {
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);
}
@ -488,62 +490,62 @@ void lu<T, X>::apply_Q_R_to_U(permutation_matrix<T, X> & r_wave) {
// solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering
// variable
template <typename T, typename X>
lu<T, X>::~lu(){
template <typename M>
lu< M>::~lu(){
for (auto t : m_tail) {
delete t;
}
}
template <typename T, typename X>
void lu<T, X>::init_vector_y(vector<X> & y) {
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 T, typename X>
void lu<T, X>::perform_transformations_on_w(indexed_vector<T>& w) {
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: SASSERT(numeric_traits<T>::precise() || check_vector_for_small_values(w, m_settings));
// TBD does not compile: lp_assert(numeric_traits<T>::precise() || check_vector_for_small_values(w, m_settings));
}
// see Chvatal 24.3
template <typename T, typename X>
void lu<T, X>::init_vector_w(unsigned entering, indexed_vector<T> & w) {
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 T, typename X>
void lu<T, X>::apply_lp_list_to_w(indexed_vector<T> & 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: SASSERT(check_vector_for_small_values(w, m_settings));
// TBD does not compile: lp_assert(check_vector_for_small_values(w, m_settings));
}
}
template <typename T, typename X>
void lu<T, X>::apply_lp_list_to_y(vector<X>& y) {
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 T, typename X>
void lu<T, X>::swap_rows(int j, int k) {
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 T, typename X>
void lu<T, X>::swap_columns(int j, int pivot_column) {
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 T, typename X>
bool lu<T, X>::pivot_the_row(int row) {
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;
@ -560,8 +562,8 @@ bool lu<T, X>::pivot_the_row(int row) {
return true;
}
// we're processing the column j now
template <typename T, typename X>
eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j) {
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);
@ -569,16 +571,16 @@ eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j) {
return ret;
}
// we're processing the column j now
template <typename T, typename X>
eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j, sparse_matrix<T, X>& copy_of_U) {
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 T, typename X>
unsigned lu<T, X>::transform_U_to_V_by_replacing_column(indexed_vector<T> & w,
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);
@ -586,15 +588,15 @@ unsigned lu<T, X>::transform_U_to_V_by_replacing_column(indexed_vector<T> & w,
}
#ifdef Z3DEBUG
template <typename T, typename X>
void lu<T, X>::check_vector_w(unsigned entering) {
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 T, typename X>
void lu<T, X>::check_apply_matrix_to_vector(matrix<T, X> *lp, T *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);
@ -602,21 +604,21 @@ void lu<T, X>::check_apply_matrix_to_vector(matrix<T, X> *lp, T *w) {
}
}
template <typename T, typename X>
void lu<T, X>::check_apply_lp_lists_to_w(T * 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--) {
SASSERT(abs(w[i] - w[i]) < 0.0000001);
lp_assert(abs(w[i] - w[i]) < 0.0000001);
}
}
#endif
template <typename T, typename X>
void lu<T, X>::process_column(int j) {
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) {
@ -637,8 +639,8 @@ void lu<T, X>::process_column(int j) {
m_failure = true;
}
}
template <typename T, typename X>
bool lu<T, X>::is_correct(const vector<unsigned>& basis) {
template <typename M>
bool lu<M>::is_correct(const vector<unsigned>& basis) {
#ifdef Z3DEBUG
if (get_status() != LU_status::OK) {
return false;
@ -651,11 +653,25 @@ bool lu<T, X>::is_correct(const vector<unsigned>& basis) {
#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 T, typename X>
dense_matrix<T, X> lu<T, X>::tail_product() {
SASSERT(tail_size() > 0);
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);
@ -665,8 +681,8 @@ dense_matrix<T, X> lu<T, X>::tail_product() {
}
return left_side;
}
template <typename T, typename X>
dense_matrix<T, X> lu<T, X>::get_left_side(const vector<unsigned>& basis) {
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);
@ -676,8 +692,19 @@ dense_matrix<T, X> lu<T, X>::get_left_side(const vector<unsigned>& basis) {
}
return left_side;
}
template <typename T, typename X>
dense_matrix<T, X> lu<T, X>::get_right_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;
@ -685,8 +712,8 @@ dense_matrix<T, X> lu<T, X>::get_right_side() {
#endif
// needed for debugging purposes
template <typename T, typename X>
void lu<T, X>::copy_w(T *buffer, indexed_vector<T> & w) {
template <typename M>
void lu<M>::copy_w(T *buffer, indexed_vector<T> & w) {
unsigned i = m_dim;
while (i--) {
buffer[i] = w[i];
@ -694,24 +721,24 @@ void lu<T, X>::copy_w(T *buffer, indexed_vector<T> & w) {
}
// needed for debugging purposes
template <typename T, typename X>
void lu<T, X>::restore_w(T *buffer, indexed_vector<T> & w) {
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 T, typename X>
bool lu<T, X>::all_columns_and_rows_are_active() {
template <typename M>
bool lu<M>::all_columns_and_rows_are_active() {
unsigned i = m_dim;
while (i--) {
SASSERT(m_U.col_is_active(i));
SASSERT(m_U.row_is_active(i));
lp_assert(m_U.col_is_active(i));
lp_assert(m_U.row_is_active(i));
}
return true;
}
template <typename T, typename X>
bool lu<T, X>::too_dense(unsigned j) const {
template <typename M>
bool lu<M>::too_dense(unsigned j) const {
unsigned r = m_dim - j;
if (r < 5)
return false;
@ -720,8 +747,8 @@ bool lu<T, X>::too_dense(unsigned j) const {
// return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j);
return r * r * m_settings.density_threshold <= m_U.get_n_of_active_elems();
}
template <typename T, typename X>
void lu<T, X>::pivot_in_dense_mode(unsigned i) {
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;
@ -733,8 +760,8 @@ void lu<T, X>::pivot_in_dense_mode(unsigned i) {
}
m_dense_LU->pivot(i, m_settings);
}
template <typename T, typename X>
void lu<T, X>::create_initial_factorization(){
template <typename M>
void lu<M>::create_initial_factorization(){
m_U.prepare_for_factorization();
unsigned j;
for (j = 0; j < m_dim; j++) {
@ -748,9 +775,9 @@ void lu<T, X>::create_initial_factorization(){
}
}
if (j == m_dim) {
// TBD does not compile: SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
// SASSERT(is_correct());
// SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
// 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++;
@ -763,16 +790,16 @@ void lu<T, X>::create_initial_factorization(){
}
}
m_dense_LU->update_parent_matrix(m_settings);
SASSERT(m_dense_LU->is_L_matrix());
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;
// SASSERT(is_correct());
// SASSERT(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));
}
template <typename T, typename X>
void lu<T, X>::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave) {
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;
@ -790,12 +817,12 @@ void lu<T, X>::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_
m_U.multiply_from_right(r_wave);
m_U.multiply_from_left_with_reverse(r_wave);
}
template <typename T, typename X>
void lu<T, X>::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) {
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;
SASSERT(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value));
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);
@ -805,8 +832,8 @@ void lu<T, X>::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) {
}
}
template <typename T, typename X>
void lu<T, X>::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) {
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++) {
@ -816,14 +843,14 @@ void lu<T, X>::pivot_and_solve_the_system(unsigned replaced_column, unsigned low
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);
SASSERT(col >= j || numeric_traits<T>::is_zero(iv.m_value));
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;
SASSERT(numeric_traits<T>::is_zero(delta) == false);
lp_assert(numeric_traits<T>::is_zero(delta) == false);
@ -846,8 +873,8 @@ void lu<T, X>::pivot_and_solve_the_system(unsigned replaced_column, unsigned low
}
// see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last
// row at the same time
template <typename T, typename X>
row_eta_matrix<T, X> *lu<T, X>::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) {
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);
@ -861,9 +888,9 @@ row_eta_matrix<T, X> *lu<T, X>::get_row_eta_matrix_and_set_row_vector(unsigned r
}
}
#ifdef Z3DEBUG
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump, m_dim);
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<T, X>(replaced_column, lowest_row_of_the_bump);
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) {
@ -880,8 +907,8 @@ row_eta_matrix<T, X> *lu<T, X>::get_row_eta_matrix_and_set_row_vector(unsigned r
return ret;
}
template <typename T, typename X>
void lu<T, X>::replace_column(T pivot_elem_for_checking, indexed_vector<T> & w, unsigned leaving_column_of_U){
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);
@ -900,15 +927,15 @@ void lu<T, X>::replace_column(T pivot_elem_for_checking, indexed_vector<T> & w,
push_matrix_to_tail(row_eta);
}
calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump);
// SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
// SASSERT(w.is_OK() && m_row_eta_work_vector.is_OK());
// 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 T, typename X>
void lu<T, X>::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){
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];
// SASSERT(m_row_eta_work_vector.is_OK());
// 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
@ -919,11 +946,11 @@ void lu<T, X>::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned
}
calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem);
// SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
// lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
}
template <typename T, typename X>
void lu<T, X>::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) {
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);
@ -933,26 +960,35 @@ void lu<T, X>::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bum
l->conjugate_by_permutation(m_Q);
}
template <typename T, typename X>
void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, vector<unsigned> & m_basis, lp_settings &m_settings) {
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<T, X>(m_A, m_basis, m_settings);
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 T, typename X>
dense_matrix<T, X> get_B(lu<T, X>& f, const vector<unsigned>& basis) {
SASSERT(basis.size() == f.dimension());
SASSERT(basis.size() == f.m_U.dimension());
dense_matrix<T, X> B(f.dimension(), f.dimension());
template <typename M>
dense_matrix<typename M::coefftype, typename M::argtype> get_B(lu<M>& f, const vector<unsigned>& basis) {
lp_assert(basis.size() == f.dimension());
lp_assert(basis.size() == f.m_U.dimension());
dense_matrix<typename M::coefftype, typename M::argtype> B(f.dimension(), f.dimension());
for (unsigned i = 0; i < f.dimension(); i++)
for (unsigned j = 0; j < f.dimension(); j++)
B.set_elem(i, j, f.B_(i, j, basis));
return B;
}
template <typename M>
dense_matrix<typename M::coefftype, typename M::argtype> get_B(lu<M>& f) {
dense_matrix<typename M::coefftype, typename M::argtype> B(f.dimension(), f.dimension());
for (unsigned i = 0; i < f.dimension(); i++)
for (unsigned j = 0; j < f.dimension(); j++)
B.set_elem(i, j, f.m_A[i][j]);
return B;
}
#endif
}

View file

@ -1,78 +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.hpp"
template double lp::dot_product<double, double>(vector<double> const&, vector<double> const&);
template lp::lu<double, double>::lu(lp::static_matrix<double, double> const&, vector<unsigned int>&, lp::lp_settings&);
template void lp::lu<double, double>::push_matrix_to_tail(lp::tail_matrix<double, double>*);
template void lp::lu<double, double>::replace_column(double, lp::indexed_vector<double>&, unsigned);
template void lp::lu<double, double>::solve_Bd(unsigned int, lp::indexed_vector<double>&, lp::indexed_vector<double>&);
template lp::lu<double, double>::~lu();
template void lp::lu<lp::mpq, lp::mpq>::push_matrix_to_tail(lp::tail_matrix<lp::mpq, lp::mpq>*);
template void lp::lu<lp::mpq, lp::mpq>::solve_Bd(unsigned int, lp::indexed_vector<lp::mpq>&, lp::indexed_vector<lp::mpq>&);
template lp::lu<lp::mpq, lp::mpq>::~lu();
template void lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::push_matrix_to_tail(lp::tail_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >*);
template void lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::solve_Bd(unsigned int, lp::indexed_vector<lp::mpq>&, lp::indexed_vector<lp::mpq>&);
template lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::~lu();
template lp::mpq lp::dot_product<lp::mpq, lp::mpq>(vector<lp::mpq > const&, vector<lp::mpq > const&);
template void lp::init_factorization<double, double>(lp::lu<double, double>*&, lp::static_matrix<double, double>&, vector<unsigned int>&, lp::lp_settings&);
template void lp::init_factorization<lp::mpq, lp::mpq>(lp::lu<lp::mpq, lp::mpq>*&, lp::static_matrix<lp::mpq, lp::mpq>&, vector<unsigned int>&, lp::lp_settings&);
template void lp::init_factorization<lp::mpq, lp::numeric_pair<lp::mpq> >(lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >*&, lp::static_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >&, vector<unsigned int>&, lp::lp_settings&);
#ifdef Z3DEBUG
template void lp::print_matrix<double, double>(lp::sparse_matrix<double, double>&, std::ostream & out);
template void lp::print_matrix<lp::mpq, lp::mpq>(lp::static_matrix<lp::mpq, lp::mpq>&, std::ostream&);
template void lp::print_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >(lp::static_matrix<lp::mpq, lp::numeric_pair<lp::mpq> >&, std::ostream&);
template void lp::print_matrix<double, double>(lp::static_matrix<double, double>&, std::ostream & out);
template bool lp::lu<double, double>::is_correct(const vector<unsigned>& basis);
template bool lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::is_correct( vector<unsigned int> const &);
template lp::dense_matrix<double, double> lp::get_B<double, double>(lp::lu<double, double>&, const vector<unsigned>& basis);
template lp::dense_matrix<lp::mpq, lp::mpq> lp::get_B<lp::mpq, lp::mpq>(lp::lu<lp::mpq, lp::mpq>&, vector<unsigned int> const&);
#endif
template bool lp::lu<double, double>::pivot_the_row(int); // NOLINT
template void lp::lu<double, double>::init_vector_w(unsigned int, lp::indexed_vector<double>&);
template void lp::lu<double, double>::solve_By(vector<double>&);
template void lp::lu<double, double>::solve_By_when_y_is_ready_for_X(vector<double>&);
template void lp::lu<double, double>::solve_yB_with_error_check(vector<double>&, const vector<unsigned>& basis);
template void lp::lu<double, double>::solve_yB_with_error_check_indexed(lp::indexed_vector<double>&, vector<int> const&, const vector<unsigned> & basis, const lp_settings&);
template void lp::lu<lp::mpq, lp::mpq>::replace_column(lp::mpq, lp::indexed_vector<lp::mpq>&, unsigned);
template void lp::lu<lp::mpq, lp::mpq>::solve_By(vector<lp::mpq >&);
template void lp::lu<lp::mpq, lp::mpq>::solve_By_when_y_is_ready_for_X(vector<lp::mpq >&);
template void lp::lu<lp::mpq, lp::mpq>::solve_yB_with_error_check(vector<lp::mpq >&, const vector<unsigned>& basis);
template void lp::lu<lp::mpq, lp::mpq>::solve_yB_with_error_check_indexed(lp::indexed_vector<lp::mpq>&, vector< int > const&, const vector<unsigned> & basis, const lp_settings&);
template void lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::solve_yB_with_error_check_indexed(lp::indexed_vector<lp::mpq>&, vector< int > const&, const vector<unsigned> & basis, const lp_settings&);
template void lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::init_vector_w(unsigned int, lp::indexed_vector<lp::mpq>&);
template void lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::replace_column(lp::mpq, lp::indexed_vector<lp::mpq>&, unsigned);
template void lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::solve_Bd_faster(unsigned int, lp::indexed_vector<lp::mpq>&);
template void lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::solve_By(vector<lp::numeric_pair<lp::mpq> >&);
template void lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::solve_By_when_y_is_ready_for_X(vector<lp::numeric_pair<lp::mpq> >&);
template void lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::solve_yB_with_error_check(vector<lp::mpq >&, const vector<unsigned>& basis);
template void lp::lu<lp::mpq, lp::mpq>::solve_By(lp::indexed_vector<lp::mpq>&);
template void lp::lu<double, double>::solve_By(lp::indexed_vector<double>&);
template void lp::lu<double, double>::solve_yB_indexed(lp::indexed_vector<double>&);
template void lp::lu<lp::mpq, lp::mpq>::solve_yB_indexed(lp::indexed_vector<lp::mpq>&);
template void lp::lu<lp::mpq, lp::numeric_pair<lp::mpq> >::solve_yB_indexed(lp::indexed_vector<lp::mpq>&);
template void lp::lu<lp::mpq, lp::mpq>::solve_By_for_T_indexed_only(lp::indexed_vector<lp::mpq>&, lp::lp_settings const&);
template void lp::lu<double, double>::solve_By_for_T_indexed_only(lp::indexed_vector<double>&, lp::lp_settings const&);

View file

@ -19,7 +19,7 @@ Revision History:
--*/
#include "util/lp/lp_settings.h"
#ifdef Z3DEBUG
#include "util/lp/matrix.hpp"
#include "util/lp/matrix_def.h"
#include "util/lp/static_matrix.h"
#include <string>
template void lp::print_matrix<double, double>(lp::matrix<double, double> const*, std::ostream & out);

View file

@ -49,11 +49,25 @@ void apply_to_vector(matrix<T, X> & m, T * w);
unsigned get_width_of_column(unsigned j, vector<vector<std::string>> & A);
void print_matrix_with_widths(vector<vector<std::string>> & A, vector<unsigned> & ws, std::ostream & out);
void print_string_matrix(vector<vector<std::string>> & A);
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);
}
}
#endif

View file

@ -82,9 +82,11 @@ unsigned get_width_of_column(unsigned j, vector<vector<std::string>> & A) {
return r;
}
void print_matrix_with_widths(vector<vector<std::string>> & A, vector<unsigned> & ws, std::ostream & out) {
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] << " ";
}
@ -92,7 +94,7 @@ void print_matrix_with_widths(vector<vector<std::string>> & A, vector<unsigned>
}
}
void print_string_matrix(vector<vector<std::string>> & A, std::ostream & out) {
void print_string_matrix(vector<vector<std::string>> & A, std::ostream & out, unsigned blanks_in_front) {
vector<unsigned> widths;
if (A.size() > 0)
@ -100,10 +102,23 @@ void print_string_matrix(vector<vector<std::string>> & A, std::ostream & out) {
widths.push_back(get_width_of_column(j, A));
}
print_matrix_with_widths(A, widths, out);
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());

33
src/util/lp/monomial.h Normal file
View file

@ -0,0 +1,33 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
struct monomial {
mpq m_coeff; // the coefficient of the monomial
var_index m_var; // the variable index
public:
monomial(const mpq& coeff, var_index var) : m_coeff(coeff), m_var(var) {}
monomial(var_index var) : monomial(one_of_type<mpq>(), var) {}
const mpq & coeff() const { return m_coeff; }
mpq & coeff() { return m_coeff; }
var_index var() const { return m_var; }
std::pair<mpq, var_index> to_pair() const { return std::make_pair(coeff(), var());}
};
}

View file

@ -175,15 +175,15 @@ class mps_reader {
if (m_line[i] == ' ')
break;
}
SASSERT(m_line.size() >= offset);
SASSERT(m_line.size() >> i);
SASSERT(i >= offset);
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_low_bound(col, numeric_traits<T>::zero());
solver->set_lower_bound(col, numeric_traits<T>::zero());
return;
}
@ -191,7 +191,7 @@ class mps_reader {
return;
}
if (b->m_low_is_set) {
solver->set_low_bound(col, b->m_low);
solver->set_lower_bound(col, b->m_low);
}
if (b->m_upper_is_set) {
solver->set_upper_bound(col, b->m_upper);
@ -303,7 +303,6 @@ class mps_reader {
do {
read_line();
if (m_line.find("RHS") == 0) {
// cout << "found RHS" << std::endl;
break;
}
if (m_line.size() < 22) {
@ -512,7 +511,7 @@ class mps_reader {
void create_or_update_bound() {
const unsigned name_offset = 14;
SASSERT(m_line.size() >= 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.size() == 0) {
@ -618,7 +617,7 @@ class mps_reader {
}
for (auto s : row_with_range->m_row_columns) {
SASSERT(m_columns.find(s.first) != m_columns.end());
lp_assert(m_columns.find(s.first) != m_columns.end());
other_bound_range_row->m_row_columns[s.first] = s.second;
}
}
@ -694,7 +693,7 @@ class mps_reader {
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) {
SASSERT(m_columns.find(s.first) != m_columns.end());
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 {
@ -729,7 +728,7 @@ class mps_reader {
void set_solver_cost(row * row, lp_solver<T, X> *solver) {
for (auto s : row->m_row_columns) {
std::string name = s.first;
SASSERT(m_columns.find(name) != m_columns.end());
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);
}
@ -738,7 +737,7 @@ class mps_reader {
public:
void set_message_stream(std::ostream * o) {
SASSERT(o != nullptr);
lp_assert(o != nullptr);
m_message_stream = o;
}
vector<std::string> column_names() {
@ -825,7 +824,7 @@ public:
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));
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);
@ -843,20 +842,20 @@ public:
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);
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);
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);
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);
@ -865,7 +864,7 @@ public:
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);
solver->add_var(col->m_index, false);
auto b = col->m_bound;
if (b == nullptr) return;

264
src/util/lp/nra_solver.cpp Normal file
View file

@ -0,0 +1,264 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Nikolaj Bjorner
*/
#include "util/lp/lar_solver.h"
#include "util/lp/nra_solver.h"
#include "nlsat/nlsat_solver.h"
#include "math/polynomial/polynomial.h"
#include "math/polynomial/algebraic_numbers.h"
#include "util/map.h"
namespace nra {
struct mon_eq {
mon_eq(lp::var_index v, unsigned sz, lp::var_index const* vs):
m_v(v), m_vs(sz, vs) {}
lp::var_index m_v;
svector<lp::var_index> m_vs;
};
struct solver::imp {
lp::lar_solver& s;
reslimit& m_limit;
params_ref m_params;
u_map<polynomial::var> m_lp2nl; // map from lar_solver variables to nlsat::solver variables
scoped_ptr<nlsat::solver> m_nlsat;
vector<mon_eq> m_monomials;
unsigned_vector m_monomials_lim;
mutable std::unordered_map<lp::var_index, rational> m_variable_values; // current model
imp(lp::lar_solver& s, reslimit& lim, params_ref const& p):
s(s),
m_limit(lim),
m_params(p) {
}
bool need_check() {
return !m_monomials.empty() && !check_assignments();
}
void add(lp::var_index v, unsigned sz, lp::var_index const* vs) {
m_monomials.push_back(mon_eq(v, sz, vs));
}
void push() {
m_monomials_lim.push_back(m_monomials.size());
}
void pop(unsigned n) {
if (n == 0) return;
m_monomials.shrink(m_monomials_lim[m_monomials_lim.size() - n]);
m_monomials_lim.shrink(m_monomials_lim.size() - n);
}
/*
\brief Check if polynomials are well defined.
multiply values for vs and check if they are equal to value for v.
epsilon has been computed.
*/
bool check_assignment(mon_eq const& m) const {
rational r1 = m_variable_values[m.m_v];
rational r2(1);
for (auto w : m.m_vs) {
r2 *= m_variable_values[w];
}
return r1 == r2;
}
bool check_assignments() const {
s.get_model(m_variable_values);
for (auto const& m : m_monomials) {
if (!check_assignment(m)) return false;
}
return true;
}
/**
\brief one-shot nlsat check.
A one shot checker is the least functionality that can
enable non-linear reasoning.
In addition to checking satisfiability we would also need
to identify equalities in the model that should be assumed
with the remaining solver.
TBD: use partial model from lra_solver to prime the state of nlsat_solver.
TBD: explore more incremental ways of applying nlsat (using assumptions)
*/
lbool check(lp::explanation_t& ex) {
SASSERT(need_check());
m_nlsat = alloc(nlsat::solver, m_limit, m_params, false);
m_lp2nl.reset();
vector<nlsat::assumption, false> core;
// add linear inequalities from lra_solver
for (unsigned i = 0; i < s.constraint_count(); ++i) {
add_constraint(i);
}
// add polynomial definitions.
for (auto const& m : m_monomials) {
add_monomial_eq(m);
}
// TBD: add variable bounds?
lbool r = m_nlsat->check();
TRACE("arith", display(tout); m_nlsat->display(tout << r << "\n"););
switch (r) {
case l_true:
break;
case l_false:
ex.reset();
m_nlsat->get_core(core);
for (auto c : core) {
unsigned idx = static_cast<unsigned>(static_cast<imp*>(c) - this);
ex.push_back(std::pair<rational, unsigned>(rational(1), idx));
TRACE("arith", tout << "ex: " << idx << "\n";);
}
break;
case l_undef:
break;
}
return r;
}
void add_monomial_eq(mon_eq const& m) {
polynomial::manager& pm = m_nlsat->pm();
svector<polynomial::var> vars;
for (auto v : m.m_vs) {
vars.push_back(lp2nl(v));
}
polynomial::monomial_ref m1(pm.mk_monomial(vars.size(), vars.c_ptr()), pm);
polynomial::monomial_ref m2(pm.mk_monomial(lp2nl(m.m_v), 1), pm);
polynomial::monomial* mls[2] = { m1, m2 };
polynomial::scoped_numeral_vector coeffs(pm.m());
coeffs.push_back(mpz(1));
coeffs.push_back(mpz(-1));
polynomial::polynomial_ref p(pm.mk_polynomial(2, coeffs.c_ptr(), mls), pm);
polynomial::polynomial* ps[1] = { p };
bool even[1] = { false };
nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, even);
m_nlsat->mk_clause(1, &lit, 0);
}
void add_constraint(unsigned idx) {
auto& c = s.get_constraint(idx);
auto& pm = m_nlsat->pm();
auto k = c.m_kind;
auto rhs = c.m_right_side;
auto lhs = c.get_left_side_coefficients();
auto sz = lhs.size();
svector<polynomial::var> vars;
rational den = denominator(rhs);
for (auto kv : lhs) {
vars.push_back(lp2nl(kv.second));
den = lcm(den, denominator(kv.first));
}
vector<rational> coeffs;
for (auto kv : lhs) {
coeffs.push_back(den * kv.first);
}
rhs *= den;
polynomial::polynomial_ref p(pm.mk_linear(sz, coeffs.c_ptr(), vars.c_ptr(), -rhs), pm);
polynomial::polynomial* ps[1] = { p };
bool is_even[1] = { false };
nlsat::literal lit;
nlsat::assumption a = this + idx;
switch (k) {
case lp::lconstraint_kind::LE:
lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even);
break;
case lp::lconstraint_kind::GE:
lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even);
break;
case lp::lconstraint_kind::LT:
lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even);
break;
case lp::lconstraint_kind::GT:
lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even);
break;
case lp::lconstraint_kind::EQ:
lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even);
break;
}
m_nlsat->mk_clause(1, &lit, a);
}
bool is_int(lp::var_index v) {
return s.var_is_int(v);
}
polynomial::var lp2nl(lp::var_index v) {
polynomial::var r;
if (!m_lp2nl.find(v, r)) {
r = m_nlsat->mk_var(is_int(v));
m_lp2nl.insert(v, r);
}
return r;
}
nlsat::anum const& value(lp::var_index v) const {
return m_nlsat->value(m_lp2nl.find(v));
}
nlsat::anum_manager& am() {
return m_nlsat->am();
}
std::ostream& display(std::ostream& out) const {
for (auto m : m_monomials) {
out << "v" << m.m_v << " = ";
for (auto v : m.m_vs) {
out << "v" << v << " ";
}
out << "\n";
}
return out;
}
};
solver::solver(lp::lar_solver& s, reslimit& lim, params_ref const& p) {
m_imp = alloc(imp, s, lim, p);
}
solver::~solver() {
dealloc(m_imp);
}
void solver::add_monomial(lp::var_index v, unsigned sz, lp::var_index const* vs) {
m_imp->add(v, sz, vs);
}
lbool solver::check(lp::explanation_t& ex) {
return m_imp->check(ex);
}
bool solver::need_check() {
return m_imp->need_check();
}
void solver::push() {
m_imp->push();
}
void solver::pop(unsigned n) {
m_imp->pop(n);
}
std::ostream& solver::display(std::ostream& out) const {
return m_imp->display(out);
}
nlsat::anum const& solver::value(lp::var_index v) const {
return m_imp->value(v);
}
nlsat::anum_manager& solver::am() {
return m_imp->am();
}
}

70
src/util/lp/nra_solver.h Normal file
View file

@ -0,0 +1,70 @@
/*
Copyright (c) 2017 Microsoft Corporation
Author: Nikolaj Bjorner
*/
#pragma once
#include "util/vector.h"
#include "util/lp/lp_settings.h"
#include "util/rlimit.h"
#include "util/params.h"
#include "nlsat/nlsat_solver.h"
namespace lp {
class lar_solver;
}
namespace nra {
class solver {
struct imp;
imp* m_imp;
public:
solver(lp::lar_solver& s, reslimit& lim, params_ref const& p = params_ref());
~solver();
/*
\brief Add a definition v = vs[0]*vs[1]*...*vs[sz-1]
The variable v is equal to the product of variables vs.
*/
void add_monomial(lp::var_index v, unsigned sz, lp::var_index const* vs);
/*
\brief Check feasiblity of linear constraints augmented by polynomial definitions
that are added.
*/
lbool check(lp::explanation_t& ex);
/*
\brief determine whether nra check is needed.
*/
bool need_check();
/*
\brief Access model.
*/
nlsat::anum const& value(lp::var_index v) const;
nlsat::anum_manager& am();
/*
\brief push and pop scope.
Monomial definitions are retraced when popping scope.
*/
void push();
void pop(unsigned n);
/*
\brief display state
*/
std::ostream& display(std::ostream& out) const;
};
}

View file

@ -18,21 +18,29 @@ Revision History:
--*/
#pragma once
#define lp_for_z3
#include <string>
#include <cmath>
#include <algorithm>
#ifdef lp_for_z3
#include "../rational.h"
#include "../sstream.h"
#include "../z3_exception.h"
#else
// include "util/numerics/mpq.h"
// include "util/numerics/numeric_traits.h"
#endif
namespace lp {
typedef rational mpq; // rename rationals
#ifdef lp_for_z3 // rename rationals
typedef rational mpq;
#else
typedef lp::mpq mpq;
#endif
template <typename T>
std::string T_to_string(const T & t); // forward definition
#ifdef lp_for_z3
template <typename T> class numeric_traits {};
template <> class numeric_traits<unsigned> {
@ -42,8 +50,25 @@ public:
static unsigned one() { return 1; }
static bool is_zero(unsigned v) { return v == 0; }
static double get_double(unsigned const & d) { return d; }
static bool is_int(unsigned) {return true;}
static bool is_pos(unsigned) {return true;}
};
template <> class numeric_traits<int> {
public:
static bool precise() { return true; }
static int const zero() { return 0; }
static int const one() { return 1; }
static bool is_zero(int v) { return v == 0; }
static double const get_double(int const & d) { return d; }
static bool is_int(int) {return true;}
static bool is_pos(int j) {return j > 0;}
static bool is_neg(int j) {return j < 0;}
static int ceil_ratio(int a, int b) { return static_cast<int>(ceil(mpq(a, b)).get_int32());}
static int floor_ratio(int a, int b) { return static_cast<int>(floor(mpq(a, b)).get_int32());}
};
template <> class numeric_traits<double> {
public:
static bool precise() { return false; }
@ -71,14 +96,23 @@ template <> class numeric_traits<double> {
static rational from_string(std::string const & str) { return rational(str.c_str()); }
static bool is_pos(const rational & d) {return d.is_pos();}
static bool is_neg(const rational & d) {return d.is_neg();}
static bool is_int(const rational & d) {return d.is_int();}
static mpq ceil_ratio(const mpq & a, const mpq & b) {
return ceil(a / b);
}
static mpq floor_ratio(const mpq & a, const mpq & b) {
return floor(a / b);
}
};
#endif
template <typename X, typename Y>
struct convert_struct {
static X convert(const Y & y){ return X(y);}
static bool is_epsilon_small(const X & x, const double & y) { return std::abs(numeric_traits<X>::get_double(x)) < y; }
static bool below_bound_numeric(const X &, const X &, const Y &) { /*SASSERT(false);*/ return false;}
static bool above_bound_numeric(const X &, const X &, const Y &) { /*SASSERT(false);*/ return false; }
static bool below_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false;}
static bool above_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false; }
};
@ -148,7 +182,7 @@ struct numeric_pair {
}
numeric_pair operator/(const numeric_pair &) const {
// SASSERT(false);
// lp_unreachable();
}
@ -157,7 +191,7 @@ struct numeric_pair {
}
numeric_pair operator*(const numeric_pair & /*a*/) const {
// SASSERT(false);
// lp_unreachable();
}
numeric_pair& operator+=(const numeric_pair & a) {
@ -203,6 +237,11 @@ struct numeric_pair {
std::string to_string() const {
return std::string("(") + T_to_string(x) + ", " + T_to_string(y) + ")";
}
bool is_int() const {
return x.is_int() && y.is_zero();
}
};
@ -229,7 +268,7 @@ numeric_pair<T> operator/(const numeric_pair<T> & r, const X & a) {
}
// template <numeric_pair, typename T> bool precise() { return numeric_traits<T>::precise();}
template <typename T> double get_double(const lp::numeric_pair<T> & ) { /* SASSERT(false); */ return 0;}
template <typename T> double get_double(const lp::numeric_pair<T> & ) { /* lp_unreachable(); */ return 0;}
template <typename T>
class numeric_traits<lp::numeric_pair<T>> {
public:
@ -237,7 +276,7 @@ class numeric_traits<lp::numeric_pair<T>> {
static lp::numeric_pair<T> zero() { return lp::numeric_pair<T>(numeric_traits<T>::zero(), numeric_traits<T>::zero()); }
static bool is_zero(const lp::numeric_pair<T> & v) { return numeric_traits<T>::is_zero(v.x) && numeric_traits<T>::is_zero(v.y); }
static double get_double(const lp::numeric_pair<T> & v){ return numeric_traits<T>::get_double(v.x); } // just return the double of the first coordinate
static double one() { /*SASSERT(false);*/ return 0;}
static double one() { /*lp_unreachable();*/ return 0;}
static bool is_pos(const numeric_pair<T> &p) {
return numeric_traits<T>::is_pos(p.x) ||
(numeric_traits<T>::is_zero(p.x) && numeric_traits<T>::is_pos(p.y));
@ -246,7 +285,9 @@ class numeric_traits<lp::numeric_pair<T>> {
return numeric_traits<T>::is_neg(p.x) ||
(numeric_traits<T>::is_zero(p.x) && numeric_traits<T>::is_neg(p.y));
}
static bool is_int(const numeric_pair<T> & p) {
return numeric_traits<T>::is_int(p.x) && numeric_traits<T>::is_zero(p.y);
}
};
template <>
@ -267,11 +308,11 @@ struct convert_struct<numeric_pair<T>, double> {
return convert_struct<T, double>::is_epsilon_small(p.x, eps) && convert_struct<T, double>::is_epsilon_small(p.y, eps);
}
static bool below_bound_numeric(const numeric_pair<T> &, const numeric_pair<T> &, const double &) {
// SASSERT(false);
// lp_unreachable();
return false;
}
static bool above_bound_numeric(const numeric_pair<T> &, const numeric_pair<T> &, const double &) {
// SASSERT(false);
// lp_unreachable();
return false;
}
};
@ -328,4 +369,26 @@ struct convert_struct<double, double> {
template <typename X> bool is_epsilon_small(const X & v, const double &eps) { return convert_struct<X, double>::is_epsilon_small(v, eps);}
template <typename X> bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct<X, double>::below_bound_numeric(x, bound, eps);}
template <typename X> bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct<X, double>::above_bound_numeric(x, bound, eps);}
template <typename T> T floor(const numeric_pair<T> & r) {
if (r.x.is_int()) {
if (r.y.is_nonneg()) {
return r.x;
}
return r.x - mpq::one();
}
return floor(r.x);
}
template <typename T> T ceil(const numeric_pair<T> & r) {
if (r.x.is_int()) {
if (r.y.is_nonpos()) {
return r.x;
}
return r.x + mpq::one();
}
return ceil(r.x);
}
}

View file

@ -19,7 +19,7 @@ Revision History:
--*/
#include <memory>
#include "util/vector.h"
#include "util/lp/permutation_matrix.hpp"
#include "util/lp/permutation_matrix_def.h"
#include "util/lp/numeric_pair.h"
template void lp::permutation_matrix<double, double>::apply_from_right(vector<double>&);
template void lp::permutation_matrix<double, double>::init(unsigned int);

View file

@ -101,7 +101,7 @@ class permutation_matrix : public tail_matrix<T, X> {
void apply_reverse_from_right_to_X(vector<X> & w);
void set_val(unsigned i, unsigned pi) {
SASSERT(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; }
lp_assert(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; }
void transpose_from_left(unsigned i, unsigned j);
@ -132,8 +132,6 @@ class permutation_matrix : public tail_matrix<T, X> {
unsigned size() const { return static_cast<unsigned>(m_rev.size()); }
unsigned * values() const { return m_permutation.c_ptr(); }
void resize(unsigned size) {
unsigned old_size = m_permutation.size();
m_permutation.resize(size);

View file

@ -64,8 +64,7 @@ void permutation_matrix<T, X>::apply_from_left(vector<X> & w, lp_settings & ) {
// L * deb_w = clone_vector<L>(w, row_count());
// deb.apply_from_left(deb_w);
#endif
// std::cout << " apply_from_left " << std::endl;
SASSERT(m_X_buffer.size() == w.size());
lp_assert(m_X_buffer.size() == w.size());
unsigned i = size();
while (i-- > 0) {
m_X_buffer[i] = w[m_permutation[i]];
@ -75,7 +74,7 @@ void permutation_matrix<T, X>::apply_from_left(vector<X> & w, lp_settings & ) {
w[i] = m_X_buffer[i];
}
#ifdef Z3DEBUG
// SASSERT(vectors_are_equal<L>(deb_w, w, row_count()));
// lp_assert(vectors_are_equal<L>(deb_w, w, row_count()));
// delete [] deb_w;
#endif
}
@ -101,7 +100,7 @@ template <typename T, typename X> void permutation_matrix<T, X>::apply_from_righ
// T * deb_w = clone_vector<T>(w, row_count());
// deb.apply_from_right(deb_w);
#endif
SASSERT(m_T_buffer.size() == w.size());
lp_assert(m_T_buffer.size() == w.size());
for (unsigned i = 0; i < size(); i++) {
m_T_buffer[i] = w[m_rev[i]];
}
@ -110,7 +109,7 @@ template <typename T, typename X> void permutation_matrix<T, X>::apply_from_righ
w[i] = m_T_buffer[i];
}
#ifdef Z3DEBUG
// SASSERT(vectors_are_equal<T>(deb_w, w, row_count()));
// lp_assert(vectors_are_equal<T>(deb_w, w, row_count()));
// delete [] deb_w;
#endif
}
@ -132,9 +131,9 @@ template <typename T, typename X> void permutation_matrix<T, X>::apply_from_righ
unsigned pj = m_permutation[j];
w.set_value(buffer[i], pj);
}
SASSERT(w.is_OK());
lp_assert(w.is_OK());
#ifdef Z3DEBUG
SASSERT(vectors_are_equal(wcopy, w.m_data));
lp_assert(vectors_are_equal(wcopy, w.m_data));
#endif
}
@ -181,7 +180,7 @@ void permutation_matrix<T, X>::apply_reverse_from_left(indexed_vector<L> & w) {
w.m_index[i] = j;
}
#ifdef Z3DEBUG
// SASSERT(vectors_are_equal<L>(deb_w, w.m_data, row_count()));
// lp_assert(vectors_are_equal<L>(deb_w, w.m_data, row_count()));
// delete [] deb_w;
#endif
}
@ -189,7 +188,7 @@ void permutation_matrix<T, X>::apply_reverse_from_left(indexed_vector<L> & w) {
template <typename T, typename X>
void permutation_matrix<T, X>::apply_reverse_from_left_to_T(vector<T> & w) {
// the result will be w = p(-1) * w
SASSERT(m_T_buffer.size() == w.size());
lp_assert(m_T_buffer.size() == w.size());
unsigned i = size();
while (i-- > 0) {
m_T_buffer[m_permutation[i]] = w[i];
@ -202,7 +201,7 @@ void permutation_matrix<T, X>::apply_reverse_from_left_to_T(vector<T> & w) {
template <typename T, typename X>
void permutation_matrix<T, X>::apply_reverse_from_left_to_X(vector<X> & w) {
// the result will be w = p(-1) * w
SASSERT(m_X_buffer.size() == w.size());
lp_assert(m_X_buffer.size() == w.size());
unsigned i = size();
while (i-- > 0) {
m_X_buffer[m_permutation[i]] = w[i];
@ -216,7 +215,7 @@ void permutation_matrix<T, X>::apply_reverse_from_left_to_X(vector<X> & w) {
template <typename T, typename X>
void permutation_matrix<T, X>::apply_reverse_from_right_to_T(vector<T> & w) {
// the result will be w = w * p(-1)
SASSERT(m_T_buffer.size() == w.size());
lp_assert(m_T_buffer.size() == w.size());
unsigned i = size();
while (i-- > 0) {
m_T_buffer[i] = w[m_permutation[i]];
@ -234,7 +233,7 @@ void permutation_matrix<T, X>::apply_reverse_from_right_to_T(indexed_vector<T> &
// vector<T> wcopy(w.m_data);
// apply_reverse_from_right_to_T(wcopy);
#endif
SASSERT(w.is_OK());
lp_assert(w.is_OK());
vector<T> tmp;
vector<unsigned> tmp_index(w.m_index);
for (auto i : w.m_index) {
@ -247,15 +246,15 @@ void permutation_matrix<T, X>::apply_reverse_from_right_to_T(indexed_vector<T> &
w.set_value(tmp[k], m_rev[j]);
}
// SASSERT(w.is_OK());
// SASSERT(vectors_are_equal(w.m_data, wcopy));
// lp_assert(w.is_OK());
// lp_assert(vectors_are_equal(w.m_data, wcopy));
}
template <typename T, typename X>
void permutation_matrix<T, X>::apply_reverse_from_right_to_X(vector<X> & w) {
// the result will be w = w * p(-1)
SASSERT(m_X_buffer.size() == w.size());
lp_assert(m_X_buffer.size() == w.size());
unsigned i = size();
while (i-- > 0) {
m_X_buffer[i] = w[m_permutation[i]];
@ -268,7 +267,7 @@ void permutation_matrix<T, X>::apply_reverse_from_right_to_X(vector<X> & w) {
template <typename T, typename X> void permutation_matrix<T, X>::transpose_from_left(unsigned i, unsigned j) {
// the result will be this = (i,j)*this
SASSERT(i < size() && j < size() && i != j);
lp_assert(i < size() && j < size() && i != j);
auto pi = m_rev[i];
auto pj = m_rev[j];
set_val(pi, j);
@ -277,7 +276,7 @@ template <typename T, typename X> void permutation_matrix<T, X>::transpose_from_
template <typename T, typename X> void permutation_matrix<T, X>::transpose_from_right(unsigned i, unsigned j) {
// the result will be this = this * (i,j)
SASSERT(i < size() && j < size() && i != j);
lp_assert(i < size() && j < size() && i != j);
auto pi = m_permutation[i];
auto pj = m_permutation[j];
set_val(i, pj);
@ -286,7 +285,7 @@ template <typename T, typename X> void permutation_matrix<T, X>::transpose_from_
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_from_left(permutation_matrix<T, X> & p) {
m_work_array = m_permutation;
SASSERT(p.size() == size());
lp_assert(p.size() == size());
unsigned i = size();
while (i-- > 0) {
set_val(i, m_work_array[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
@ -296,7 +295,7 @@ template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_per
// this is multiplication in the matrix sense
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_from_right(permutation_matrix<T, X> & p) {
m_work_array = m_permutation;
SASSERT(p.size() == size());
lp_assert(p.size() == size());
unsigned i = size();
while (i-- > 0)
set_val(i, p[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
@ -304,7 +303,7 @@ template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_per
}
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_reverse_from_right(permutation_matrix<T, X> & q){ // todo : condensed permutations ?
SASSERT(q.size() == size());
lp_assert(q.size() == size());
m_work_array = m_permutation;
// the result is this = this*q(-1)
unsigned i = size();

115
src/util/lp/polynomial.h Normal file
View file

@ -0,0 +1,115 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
--*/
#pragma once
namespace lp {
struct polynomial {
static mpq m_local_zero;
// the polynomial evaluates to m_coeffs + m_a
vector<monomial> m_coeffs;
mpq m_a; // the free coefficient
polynomial(const vector<monomial>& p, const mpq & a) : m_coeffs(p), m_a(a) {}
polynomial(const vector<monomial>& p) : polynomial(p, zero_of_type<mpq>()) {}
polynomial(): m_a(zero_of_type<mpq>()) {}
polynomial(const polynomial & p) : m_coeffs(p.m_coeffs), m_a(p.m_a) {}
const mpq & coeff(var_index j) const {
for (const auto & t : m_coeffs) {
if (j == t.var()) {
return t.coeff();
}
}
return m_local_zero;
}
polynomial & operator+=(const polynomial & p) {
m_a += p.m_a;
for (const auto & t: p.m_coeffs)
*this += monomial(t.coeff(), t.var());
return *this;
}
void add(const mpq & c, const polynomial &p) {
m_a += p.m_a * c;
for (const auto & t: p.m_coeffs)
*this += monomial(c * t.coeff(), t.var());
}
void clear() {
m_coeffs.clear();
m_a = zero_of_type<mpq>();
}
bool is_empty() const { return m_coeffs.size() == 0 && numeric_traits<mpq>::is_zero(m_a); }
unsigned number_of_monomials() const { return m_coeffs.size();}
void add(const monomial &m ){
if (is_zero(m.coeff())) return;
for (unsigned k = 0; k < m_coeffs.size(); k++) {
auto & l = m_coeffs[k];
if (m.var() == l.var()) {
l.coeff() += m.coeff();
if (l.coeff() == 0)
m_coeffs.erase(m_coeffs.begin() + k);
return;
}
}
m_coeffs.push_back(m);
lp_assert(is_correct());
}
polynomial & operator+=(const monomial &m ){
add(m);
return *this;
}
polynomial & operator+=(const mpq &c ){
m_a += c;
return *this;
}
bool is_correct() const {
std::unordered_set<var_index> s;
for (auto & l : m_coeffs) {
if (l.coeff() == 0)
return false;
s.insert(l.var());
}
return m_coeffs.size() == s.size();
}
bool var_coeff_is_unit(unsigned j) const {
const mpq & a = coeff(j);
return a == 1 || a == -1;
}
template <typename c> // c plays a role of a map from indices to impq
mpq value(const c& v) const {
mpq r = m_a;
for (auto & p : m_coeffs)
r += v[p.var()].x * p.coeff();
return r;
}
const vector<monomial> & coeffs() const { return m_coeffs; }
};
}

View file

@ -35,7 +35,7 @@ void quick_xplain::copy_constraint_and_add_constraint_vars(const lar_constraint&
vector < std::pair<mpq, unsigned>> ls;
for (auto & p : lar_c.get_left_side_coefficients()) {
unsigned j = p.second;
unsigned lj = m_qsol.add_var(j);
unsigned lj = m_qsol.add_var(j, false);
ls.push_back(std::make_pair(p.first, lj));
}
m_constraints_in_local_vars.push_back(lar_constraint(ls, lar_c.m_kind, lar_c.m_right_side));
@ -44,7 +44,7 @@ void quick_xplain::copy_constraint_and_add_constraint_vars(const lar_constraint&
bool quick_xplain::infeasible() {
m_qsol.solve();
return m_qsol.get_status() == INFEASIBLE;
return m_qsol.get_status() == lp_status::INFEASIBLE;
}
// u - unexplored constraints
@ -71,7 +71,7 @@ void quick_xplain::minimize(const vector<unsigned>& u) {
}
}
if (m > 0) {
SASSERT(m_qsol.constraint_stack_size() >= initial_stack_size);
lp_assert(m_qsol.constraint_stack_size() >= initial_stack_size);
m_qsol.pop(m_qsol.constraint_stack_size() - initial_stack_size);
for (auto j : m_x)
add_constraint_to_qsol(j);
@ -88,7 +88,7 @@ void quick_xplain::minimize(const vector<unsigned>& u) {
void quick_xplain::run(vector<std::pair<mpq, constraint_index>> & explanation, const lar_solver & ls){
if (explanation.size() <= 2) return;
lar_solver qsol;
SASSERT(ls.explanation_is_correct(explanation));
lp_assert(ls.explanation_is_correct(explanation));
quick_xplain q(explanation, ls, qsol);
q.solve();
}
@ -109,13 +109,13 @@ bool quick_xplain::is_feasible(const vector<unsigned> & x, unsigned k) const {
vector < std::pair<mpq, unsigned>> ls;
const lar_constraint & c = m_constraints_in_local_vars[i];
for (auto & p : c.get_left_side_coefficients()) {
unsigned lj = l.add_var(p.second);
unsigned lj = l.add_var(p.second, false);
ls.push_back(std::make_pair(p.first, lj));
}
l.add_constraint(ls, c.m_kind, c.m_right_side);
}
l.solve();
return l.get_status() != INFEASIBLE;
return l.get_status() != lp_status::INFEASIBLE;
}
bool quick_xplain::x_is_minimal() const {
@ -124,7 +124,7 @@ bool quick_xplain::x_is_minimal() const {
x.push_back(j);
for (unsigned k = 0; k < x.size(); k++) {
SASSERT(is_feasible(x, x[k]));
lp_assert(is_feasible(x, x[k]));
}
return true;
}
@ -132,8 +132,8 @@ bool quick_xplain::x_is_minimal() const {
void quick_xplain::solve() {
copy_constraints_to_local_constraints();
m_qsol.push();
SASSERT(m_qsol.constraint_count() == 0);
vector<unsigned> u;
lp_assert(m_qsol.constraint_count() == 0)
vector<unsigned> u;
for (unsigned k = 0; k < m_constraints_in_local_vars.size(); k++)
u.push_back(k);
minimize(u);
@ -142,10 +142,10 @@ void quick_xplain::solve() {
for (unsigned i : m_x)
add_constraint_to_qsol(i);
m_qsol.solve();
SASSERT(m_qsol.get_status() == INFEASIBLE);
lp_assert(m_qsol.get_status() == lp_status::INFEASIBLE);
m_qsol.get_infeasibility_explanation(m_explanation);
SASSERT(m_qsol.explanation_is_correct(m_explanation));
SASSERT(x_is_minimal());
lp_assert(m_qsol.explanation_is_correct(m_explanation));
lp_assert(x_is_minimal());
for (auto & p : m_explanation) {
p.second = this->m_local_constraint_offset_to_external_ci[m_local_ci_to_constraint_offsets[p.second]];
}

View file

@ -17,4 +17,5 @@ Revision History:
--*/
#include "util/lp/random_updater.hpp"
#include "util/lp/random_updater_def.h"

View file

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

View file

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

View file

@ -0,0 +1,95 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
<name>
Abstract:
<abstract>
Author:
Lev Nachmanson (levnach)
Revision History:
--*/
#include "util/lp/random_updater.h"
#include "util/lp/static_matrix.h"
#include "util/lp/lar_solver.h"
#include "util/vector.h"
namespace lp {
random_updater::random_updater(
lar_solver & lar_solver,
const vector<unsigned> & column_indices) :
m_lar_solver(lar_solver),
m_range(100000) {
for (unsigned j : column_indices)
add_column_to_sets(j);
}
bool random_updater::shift_var(unsigned v) {
return m_lar_solver.get_int_solver()->shift_var(v, m_range);
}
bool random_updater::random_shift_var(unsigned j) {
if (m_lar_solver.A_r().m_columns.size() >= 50) {
return false;
}
return shift_var(j);
}
void random_updater::update() {
for (auto j : m_var_set) {
if (m_var_set.size() <= m_values.size()) {
break; // we are done
}
auto old_x = m_lar_solver.get_column_value(j);
if (random_shift_var(j)) {
remove_value(old_x);
add_value(m_lar_solver.get_column_value(j));
}
}
}
void random_updater::add_value(const numeric_pair<mpq>& v) {
auto it = m_values.find(v);
if (it == m_values.end()) {
m_values[v] = 1;
} else {
it->second++;
}
}
void random_updater::remove_value(const numeric_pair<mpq>& v) {
std::unordered_map<numeric_pair<mpq>, unsigned>::iterator it = m_values.find(v);
lp_assert(it != m_values.end());
it->second--;
if (it->second == 0)
m_values.erase((std::unordered_map<numeric_pair<mpq>, unsigned>::const_iterator)it);
}
void random_updater::add_column_to_sets(unsigned j) {
if (m_lar_solver.get_core_solver().m_r_heading[j] < 0) {
m_var_set.insert(j);
add_value(m_lar_solver.get_core_solver().m_r_x[j]);
} else {
unsigned row = m_lar_solver.get_core_solver().m_r_heading[j];
for (auto & row_c : m_lar_solver.get_core_solver().m_r_A.m_rows[row]) {
unsigned cj = row_c.m_j;
if (m_lar_solver.get_core_solver().m_r_heading[cj] < 0) {
m_var_set.insert(cj);
add_value(m_lar_solver.get_core_solver().m_r_x[cj]);
}
}
}
}
}

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